diff --git a/examples/src/main/java/me/didit/examples/Quickstart.java b/examples/src/main/java/me/didit/examples/Quickstart.java index 43fa26c..07a6ea1 100644 --- a/examples/src/main/java/me/didit/examples/Quickstart.java +++ b/examples/src/main/java/me/didit/examples/Quickstart.java @@ -3,9 +3,9 @@ import java.nio.charset.StandardCharsets; import java.util.Map; import me.didit.Didit; +import me.didit.types.SessionDecision; import me.didit.types.SessionsCreateParams; import me.didit.types.SessionsCreateResponse; -import me.didit.types.SessionsRetrieveDecisionResponse; import me.didit.webhooks.SessionStatusUpdatedEvent; import me.didit.webhooks.WebhookEvent; import me.didit.webhooks.Webhooks; @@ -40,8 +40,7 @@ static String run(Didit client, String workflowId) { System.out.println("Session " + session.sessionId() + ": send the user to " + session.url()); // 2. Later, read the decision. - SessionsRetrieveDecisionResponse decision = - client.sessions().retrieveDecision(session.sessionId()); + SessionDecision decision = client.sessions().retrieveDecision(session.sessionId()); System.out.println( "Decision: " + decision.status().map(Object::toString).orElse("not available yet")); return session.sessionId(); diff --git a/openapi/SOURCE.json b/openapi/SOURCE.json index a1faa6a..47946a1 100644 --- a/openapi/SOURCE.json +++ b/openapi/SOURCE.json @@ -1,16 +1,16 @@ { "verification.json": { "url": "https://docs.didit.me/openapi-25.json", - "sha256": "d0fe3a250c30a0b07d17276463c04ecd26be50b07ac2896995a77563cfde201b", - "fetched_at": "2026-10-17T10:00:00Z" + "sha256": "9ada6192b24a5d85e983e57e87fccb6e762d74060b6f0b892928356dd432137c", + "fetched_at": "2026-10-23T06:48:14Z" }, "account.json": { "url": "https://docs.didit.me/openapi-auth.json", - "sha256": "171f5294c6d4a53d96f03b8820afc04a025f344ce2be137b5378399f6fc44ee4", - "fetched_at": "2026-10-17T10:00:00Z" + "sha256": "5fafb88b306ab60c7c105b0bf00fa2a6a49b5d2ca8115eb003b332f073844b39", + "fetched_at": "2026-10-23T06:48:14Z" }, "webhook-vectors.json": { "generator": "product-automations scripts/server-sdks/make_vectors.py (seeded 2026-10-17)", "source": "service-didit-verification@9773c66e0a src/sessions/utils/webhooks.py" } -} \ No newline at end of file +} diff --git a/openapi/account.json b/openapi/account.json index 7e0d535..ce6a041 100644 --- a/openapi/account.json +++ b/openapi/account.json @@ -27,6 +27,12 @@ "summary": "Register Account", "description": "Step 1 of programmatic onboarding. Creates a pending account and emails a 6-char code (10 min TTL). Rate limit: 5/IP/hour.", "operationId": "programmatic_register", + "x-didit-sdk": { + "resource": "account", + "method": "register", + "auth": "none", + "server": true + }, "tags": [ "Programmatic Auth" ], @@ -145,7 +151,7 @@ } }, "429": { - "description": "Rate limited. More than 5 registration attempts from the same IP in the last hour.", + "description": "Rate limited. More than 5 registration attempts from the same IP in the last hour. Also returned, with a `Retry-After: 5` header, when the service is momentarily checking as many passwords or codes as it can at once; retry after the indicated number of seconds.", "content": { "application/json": { "schema": { @@ -156,6 +162,12 @@ "value": { "detail": "Too many registration attempts. Please try again later." } + }, + "PasswordChecksBusy": { + "summary": "Too many password checks in progress", + "value": { + "detail": "Too many sign-in attempts are being processed right now. Please try again in a few seconds." + } } } } @@ -178,6 +190,9 @@ "label": "JavaScript", "source": "const resp = await fetch(\n \"https://apx.didit.me/auth/v2/programmatic/register/\",\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n email: \"you@yourdomain.com\",\n password: \"MyStr0ng!Pass\",\n }),\n },\n);\nif (!resp.ok) throw new Error(`Register failed: ${resp.status}`);\nconst data = await resp.json();\nconsole.log(data);" } + ], + "security": [ + {} ] } }, @@ -186,6 +201,12 @@ "summary": "Verify Email and Issue Credentials", "description": "Step 2 of onboarding. Exchanges the 6-char code for a verified account, a default org+app, and a JWT pair. Persist the returned `api_key`.", "operationId": "programmatic_verify_email", + "x-didit-sdk": { + "resource": "account", + "method": "verify_email", + "auth": "none", + "server": true + }, "tags": [ "Programmatic Auth" ], @@ -230,7 +251,7 @@ "uuid": "b2c3d4e5-6789-01bc-defg-222222222222", "name": "developer", "client_id": "S9LIYGSoWNuGMLHsvEt9dQ", - "api_key": "05mHcOWL8GathLZlz8oIDawYj9qFAcoSHtz-75PAkuo" + "api_key": "sk_live_example_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } } @@ -288,6 +309,24 @@ } } } + }, + "429": { + "description": "The service is momentarily checking as many passwords or codes as it can at once. Sent with a `Retry-After: 5` header; retry after the indicated number of seconds.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitError" + }, + "examples": { + "PasswordChecksBusy": { + "summary": "Too many password checks in progress", + "value": { + "detail": "Too many sign-in attempts are being processed right now. Please try again in a few seconds." + } + } + } + } + } } }, "x-codeSamples": [ @@ -306,6 +345,9 @@ "label": "JavaScript", "source": "const resp = await fetch(\n \"https://apx.didit.me/auth/v2/programmatic/verify-email/\",\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n email: \"you@yourdomain.com\",\n code: \"A3K9F2\",\n }),\n },\n);\nif (!resp.ok) throw new Error(`Verify failed: ${resp.status}`);\nconst { application, access_token } = await resp.json();\n// application.api_key -> x-api-key for verification.didit.me/v3\n// access_token -> Authorization: Bearer ... for this auth API" } + ], + "security": [ + {} ] } }, @@ -314,6 +356,12 @@ "summary": "Programmatic Login", "description": "Authenticate any verified email/password Didit account and receive an RS256 JWT pair (default lifetime 86400s). Console-created email/password accounts can use this endpoint; OAuth-only console accounts must set a password first.", "operationId": "programmatic_login", + "x-didit-sdk": { + "resource": "account", + "method": "login", + "auth": "none", + "server": true + }, "tags": [ "Programmatic Auth" ], @@ -425,7 +473,7 @@ } }, "429": { - "description": "Account locked (too many recent failures on this email) or IP rate-limited (too many attempts from this address).", + "description": "Account locked (too many recent failures on this email) or IP rate-limited (too many attempts from this address). Also returned, with a `Retry-After: 5` header, when the service is momentarily checking as many passwords or codes as it can at once; retry after the indicated number of seconds.", "content": { "application/json": { "schema": { @@ -443,6 +491,12 @@ "value": { "detail": "Too many login attempts from this IP address. Please wait before trying again." } + }, + "PasswordChecksBusy": { + "summary": "Too many password checks in progress", + "value": { + "detail": "Too many sign-in attempts are being processed right now. Please try again in a few seconds." + } } } } @@ -465,6 +519,9 @@ "label": "JavaScript", "source": "const resp = await fetch(\n \"https://apx.didit.me/auth/v2/programmatic/login/\",\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n email: \"you@yourdomain.com\",\n password: \"MyStr0ng!Pass\",\n }),\n },\n);\nif (!resp.ok) throw new Error(`Login failed: ${resp.status}`);\nconst { access_token } = await resp.json();\n\n// Use against the Account Management endpoints on this host\nconst orgs = await fetch(\n \"https://apx.didit.me/auth/v2/organizations/me/\",\n { headers: { Authorization: `Bearer ${access_token}` } },\n).then((r) => r.json());" } + ], + "security": [ + {} ] } }, @@ -473,6 +530,12 @@ "summary": "List My Organizations", "description": "List organizations the authenticated user belongs to. Each entry's `uuid` is the `org_id` used on the application endpoints.", "operationId": "list_my_organizations", + "x-didit-sdk": { + "resource": "account.organizations", + "method": "list", + "auth": "account_token", + "server": true + }, "tags": [ "Account Management" ], @@ -483,43 +546,50 @@ ], "responses": { "200": { - "description": "List of organizations the authenticated user belongs to. Use the `uuid` of an entry as `{org_id}` on the application endpoints.", + "description": "Page of organizations the authenticated user belongs to (`limit`/`offset`, 50 per page by default). Use the `uuid` of an entry as `{org_id}` on the application endpoints.", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Organization" - } + "$ref": "#/components/schemas/PaginatedOrganizationList" }, "examples": { "Success": { "summary": "A freshly-registered programmatic account", - "value": [ - { - "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111", - "name": "developer", - "contact_email": "developer@gmail.com", - "created_at": "2025-06-01T10:00:00Z" - } - ] + "value": { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111", + "name": "developer", + "contact_email": "developer@gmail.com", + "created_at": "2025-06-01T10:00:00Z" + } + ] + } }, "Multi": { "summary": "A reseller account that manages several customer organizations", - "value": [ - { - "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111", - "name": "Acme HQ", - "contact_email": "ops@acme.example", - "created_at": "2025-06-01T10:00:00Z" - }, - { - "uuid": "f9e8d7c6-5432-10ab-cdef-444444444444", - "name": "Acme Customer A", - "contact_email": "ops@acme.example", - "created_at": "2025-06-12T08:11:00Z" - } - ] + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111", + "name": "Acme HQ", + "contact_email": "ops@acme.example", + "created_at": "2025-06-01T10:00:00Z" + }, + { + "uuid": "f9e8d7c6-5432-10ab-cdef-444444444444", + "name": "Acme Customer A", + "contact_email": "ops@acme.example", + "created_at": "2025-06-12T08:11:00Z" + } + ] + } } } } @@ -569,12 +639,36 @@ { "lang": "python", "label": "Python", - "source": "import requests\n\nresp = requests.get(\n \"https://apx.didit.me/auth/v2/organizations/me/\",\n headers={\"Authorization\": f\"Bearer {access_token}\"},\n timeout=10,\n)\nresp.raise_for_status()\norgs = resp.json()\nfirst_org_id = orgs[0][\"uuid\"]" + "source": "import requests\n\nresp = requests.get(\n \"https://apx.didit.me/auth/v2/organizations/me/\",\n headers={\"Authorization\": f\"Bearer {access_token}\"},\n timeout=10,\n)\nresp.raise_for_status()\norgs = resp.json()[\"results\"] # paginated: count, next, previous, results\nfirst_org_id = orgs[0][\"uuid\"]" }, { "lang": "javascript", "label": "JavaScript", - "source": "const resp = await fetch(\n \"https://apx.didit.me/auth/v2/organizations/me/\",\n { headers: { Authorization: `Bearer ${accessToken}` } },\n);\nif (!resp.ok) throw new Error(`List orgs failed: ${resp.status}`);\nconst orgs = await resp.json();\nconst firstOrgId = orgs[0].uuid;" + "source": "const resp = await fetch(\n \"https://apx.didit.me/auth/v2/organizations/me/\",\n { headers: { Authorization: `Bearer ${accessToken}` } },\n);\nif (!resp.ok) throw new Error(`List orgs failed: ${resp.status}`);\nconst { results: orgs } = await resp.json(); // paginated: count, next, previous, results\nconst firstOrgId = orgs[0].uuid;" + } + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "default": 50 + }, + "description": "Page size. Defaults to 50." + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "description": "Zero-based offset of the first result." } ] } @@ -584,6 +678,12 @@ "summary": "Create Application", "description": "Create an application inside an organization. The response includes `api_key`; persist it now (recoverable via GET). Requires owner/admin JWT.", "operationId": "create_organization_application", + "x-didit-sdk": { + "resource": "account.applications", + "method": "create", + "auth": "account_token", + "server": true + }, "tags": [ "Account Management" ], @@ -654,7 +754,7 @@ "uuid": "b2c3d4e5-6789-01bc-defg-222222222222", "name": "Acme Customer App", "client_id": "S9LIYGSoWNuGMLHsvEt9dQ", - "api_key": "05mHcOWL8GathLZlz8oIDawYj9qFAcoSHtz-75PAkuo", + "api_key": "sk_live_example_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "website_url": "https://acme.example", "redirect_uris": [ "https://acme.example/callback" @@ -662,7 +762,31 @@ "terms_url": "https://acme.example/terms", "privacy_url": "https://acme.example/privacy", "description": null, - "created_at": "2025-06-01T10:00:00Z" + "created_at": "2025-06-01T10:00:00Z", + "products": [ + { + "product": "identity", + "status": "enabled", + "modules": { + "user_verification": true, + "business_verification": true, + "case_management": true + } + }, + { + "product": "transactions", + "status": "disabled", + "modules": { + "travel_rule": true, + "case_management": true + } + }, + { + "product": "authentication", + "status": "disabled", + "modules": {} + } + ] } } } @@ -689,7 +813,7 @@ } }, "401": { - "description": "Access token missing, malformed, or expired.", + "description": "Access token malformed, invalid, or expired. Get a new one with `POST /programmatic/login/`.", "content": { "application/json": { "schema": { @@ -700,20 +824,38 @@ "value": { "detail": "Invalid access token" } + }, + "ExpiredToken": { + "value": { + "detail": "Expired access token" + } } } } } }, "403": { - "description": "The authenticated user is a member of the organization but is not an owner or admin. Only owners and admins can create applications.", + "description": "You cannot create applications in this organization. A member of the organization who is not an owner or admin gets `Only organization owners and admins can manage applications.` An owner or admin whose access to the organization is suspended gets `Your access to this organization is suspended.` A request without an `Authorization` header gets `You do not have permission to perform this action.`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthError" }, "examples": { - "Forbidden": { + "NotOwnerOrAdmin": { + "summary": "Member who is not an owner or admin", + "value": { + "detail": "Only organization owners and admins can manage applications." + } + }, + "Suspended": { + "summary": "Membership suspended", + "value": { + "detail": "Your access to this organization is suspended." + } + }, + "MissingToken": { + "summary": "No Authorization header", "value": { "detail": "You do not have permission to perform this action." } @@ -723,16 +865,17 @@ } }, "404": { - "description": "Organization not found, or the authenticated user is not a member of it.", + "description": "The organization does not exist or you are not a member of it.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthError" }, "examples": { - "NotFound": { + "OrganizationNotFound": { + "summary": "Organization not found, or you are not a member", "value": { - "detail": "Not found." + "detail": "Organization not found." } } } @@ -761,10 +904,16 @@ "get": { "summary": "List the organization's applications", "operationId": "get_org_applications_list", + "x-didit-sdk": { + "resource": "account.applications", + "method": "list", + "auth": "account_token", + "server": true + }, "tags": [ "Account Management" ], - "description": "List every application in the organization. Use this to discover `app_id` values when recovering credentials from a new machine — follow with `GET /organizations/me/{org_id}/applications/{app_id}/` for the API key. Requires an owner or admin Bearer token.", + "description": "List the organization's applications, 50 per page by default. Use this to discover `app_id` values when recovering credentials from a new machine, then call `GET /organizations/me/{org_id}/applications/{app_id}/` for the API key. Requires an owner or admin Bearer token.", "parameters": [ { "name": "org_id", @@ -776,17 +925,218 @@ "format": "uuid", "example": "a1b2c3d4-5678-90ab-cdef-111111111111" } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "default": 50 + }, + "description": "Page size. Defaults to 50." + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "description": "Zero-based offset of the first result." + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "Number of applications per page. Defaults to 50.", + "schema": { + "type": "integer", + "minimum": 1, + "default": 50, + "example": 50 + } + }, + { + "name": "offset", + "in": "query", + "required": false, + "description": "Number of applications to skip before the page starts. Defaults to 0.", + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "example": 0 + } } ], "responses": { "200": { - "description": "Array of applications (same item shape as the application detail response)." + "description": "Page of the organization's applications (`limit`/`offset`, 50 per page by default), each with the same shape as the application detail response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedApplicationList", + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Total number of applications in the organization.", + "example": 1 + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the next page, or `null` on the last page.", + "example": null + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the previous page, or `null` on the first page.", + "example": null + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Application" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "examples": { + "Success": { + "summary": "An organization with one application", + "value": { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "uuid": "b2c3d4e5-6789-01bc-defg-222222222222", + "name": "My App", + "client_id": "S9LIYGSoWNuGMLHsvEt9dQ", + "api_key": "sk_live_example_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "website_url": "https://example.com", + "redirect_uris": [ + "https://example.com/callback" + ], + "terms_url": "https://example.com/terms", + "privacy_url": "https://example.com/privacy", + "description": "Production application", + "created_at": "2025-06-01T10:00:00Z", + "products": [ + { + "product": "identity", + "status": "enabled", + "modules": { + "user_verification": true, + "business_verification": true, + "case_management": true + } + }, + { + "product": "transactions", + "status": "disabled", + "modules": { + "travel_rule": true, + "case_management": true + } + }, + { + "product": "authentication", + "status": "disabled", + "modules": {} + } + ] + } + ] + } + } + } + } + } }, "401": { - "description": "Missing/invalid Bearer token." + "description": "Access token malformed, invalid, or expired. Get a new one with `POST /programmatic/login/`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthError" + }, + "examples": { + "InvalidToken": { + "value": { + "detail": "Invalid access token" + } + }, + "ExpiredToken": { + "value": { + "detail": "Expired access token" + } + } + } + } + } }, "403": { - "description": "Token holder is not an owner or admin of this organization." + "description": "You cannot list this organization's applications. A member of the organization who is not an owner or admin gets `Only organization owners and admins can manage applications.` An owner or admin whose access to the organization is suspended gets `Your access to this organization is suspended.` A request without an `Authorization` header gets `You do not have permission to perform this action.`", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthError" + }, + "examples": { + "NotOwnerOrAdmin": { + "summary": "Member who is not an owner or admin", + "value": { + "detail": "Only organization owners and admins can manage applications." + } + }, + "Suspended": { + "summary": "Membership suspended", + "value": { + "detail": "Your access to this organization is suspended." + } + }, + "MissingToken": { + "summary": "No Authorization header", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "The organization does not exist or you are not a member of it.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthError" + }, + "examples": { + "OrganizationNotFound": { + "summary": "Organization not found, or you are not a member", + "value": { + "detail": "Organization not found." + } + } + } + } + } } }, "security": [ @@ -801,6 +1151,12 @@ "summary": "Get Application Credentials", "description": "Read one application's full record including `client_id` and `api_key`. Use this to recover a lost `api_key`.", "operationId": "get_application_credentials", + "x-didit-sdk": { + "resource": "account.applications", + "method": "retrieve", + "auth": "account_token", + "server": true + }, "tags": [ "Account Management" ], @@ -847,7 +1203,7 @@ "uuid": "b2c3d4e5-6789-01bc-defg-222222222222", "name": "My App", "client_id": "S9LIYGSoWNuGMLHsvEt9dQ", - "api_key": "05mHcOWL8GathLZlz8oIDawYj9qFAcoSHtz-75PAkuo", + "api_key": "sk_live_example_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "website_url": "https://example.com", "redirect_uris": [ "https://example.com/callback" @@ -855,7 +1211,31 @@ "terms_url": "https://example.com/terms", "privacy_url": "https://example.com/privacy", "description": "Production application", - "created_at": "2025-06-01T10:00:00Z" + "created_at": "2025-06-01T10:00:00Z", + "products": [ + { + "product": "identity", + "status": "enabled", + "modules": { + "user_verification": true, + "business_verification": true, + "case_management": true + } + }, + { + "product": "transactions", + "status": "disabled", + "modules": { + "travel_rule": true, + "case_management": true + } + }, + { + "product": "authentication", + "status": "disabled", + "modules": {} + } + ] } } } @@ -863,7 +1243,7 @@ } }, "401": { - "description": "Access token missing, malformed, or expired.", + "description": "Access token malformed, invalid, or expired. Get a new one with `POST /programmatic/login/`.", "content": { "application/json": { "schema": { @@ -874,20 +1254,38 @@ "value": { "detail": "Invalid access token" } + }, + "ExpiredToken": { + "value": { + "detail": "Expired access token" + } } } } } }, "403": { - "description": "Bearer token is valid but the user does not have permission to read credentials for this application.", + "description": "You cannot read this application. A member of the organization who is not an owner or admin gets `Only organization owners and admins can manage applications.` An owner or admin whose access to the organization is suspended gets `Your access to this organization is suspended.` A request without an `Authorization` header gets `You do not have permission to perform this action.`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthError" }, "examples": { - "Forbidden": { + "NotOwnerOrAdmin": { + "summary": "Member who is not an owner or admin", + "value": { + "detail": "Only organization owners and admins can manage applications." + } + }, + "Suspended": { + "summary": "Membership suspended", + "value": { + "detail": "Your access to this organization is suspended." + } + }, + "MissingToken": { + "summary": "No Authorization header", "value": { "detail": "You do not have permission to perform this action." } @@ -897,16 +1295,23 @@ } }, "404": { - "description": "Organization not found, application not found in that organization, or the authenticated user is not a member of the organization.", + "description": "The organization does not exist or you are not a member of it, or the application does not exist in that organization.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthError" }, "examples": { - "NotFound": { + "OrganizationNotFound": { + "summary": "Organization not found, or you are not a member", + "value": { + "detail": "Organization not found." + } + }, + "ApplicationNotFound": { + "summary": "No such application in this organization", "value": { - "detail": "Not found." + "detail": "Application not found." } } } @@ -934,8 +1339,14 @@ }, "patch": { "summary": "Update Application", - "description": "Update application metadata (name, URLs, redirect URIs). `api_key` and `client_id` are never rotated. Requires owner/admin JWT.", + "description": "Update application metadata (name, URLs, redirect URIs). `api_key` and `client_id` are never rotated. `products` is read-only: a `products` key in the body is ignored. Requires owner/admin JWT.", "operationId": "update_organization_application", + "x-didit-sdk": { + "resource": "account.applications", + "method": "update", + "auth": "account_token", + "server": true + }, "tags": [ "Account Management" ], @@ -1006,7 +1417,7 @@ }, "responses": { "200": { - "description": "Application updated. The response is the full application record after the change; `uuid`, `client_id`, and `api_key` are unchanged.", + "description": "Application updated. The response is the full application record after the change, including `products`; `uuid`, `client_id`, and `api_key` are unchanged.", "content": { "application/json": { "schema": { @@ -1018,7 +1429,7 @@ "uuid": "b2c3d4e5-6789-01bc-defg-222222222222", "name": "Acme Production App", "client_id": "S9LIYGSoWNuGMLHsvEt9dQ", - "api_key": "05mHcOWL8GathLZlz8oIDawYj9qFAcoSHtz-75PAkuo", + "api_key": "sk_live_example_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "website_url": "https://app.acme.example", "redirect_uris": [ "https://acme.example/callback" @@ -1026,7 +1437,31 @@ "terms_url": "https://acme.example/terms", "privacy_url": "https://acme.example/privacy", "description": null, - "created_at": "2025-06-01T10:00:00Z" + "created_at": "2025-06-01T10:00:00Z", + "products": [ + { + "product": "identity", + "status": "enabled", + "modules": { + "user_verification": true, + "business_verification": true, + "case_management": true + } + }, + { + "product": "transactions", + "status": "disabled", + "modules": { + "travel_rule": true, + "case_management": true + } + }, + { + "product": "authentication", + "status": "disabled", + "modules": {} + } + ] } } } @@ -1053,7 +1488,7 @@ } }, "401": { - "description": "Access token missing, malformed, or expired.", + "description": "Access token malformed, invalid, or expired. Get a new one with `POST /programmatic/login/`.", "content": { "application/json": { "schema": { @@ -1064,20 +1499,38 @@ "value": { "detail": "Invalid access token" } + }, + "ExpiredToken": { + "value": { + "detail": "Expired access token" + } } } } } }, "403": { - "description": "The user is a member of the organization but is not an owner or admin. Only owners and admins can update applications.", + "description": "You cannot update this application. A member of the organization who is not an owner or admin gets `Only organization owners and admins can manage applications.` An owner or admin whose access to the organization is suspended gets `Your access to this organization is suspended.` A request without an `Authorization` header gets `You do not have permission to perform this action.`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthError" }, "examples": { - "Forbidden": { + "NotOwnerOrAdmin": { + "summary": "Member who is not an owner or admin", + "value": { + "detail": "Only organization owners and admins can manage applications." + } + }, + "Suspended": { + "summary": "Membership suspended", + "value": { + "detail": "Your access to this organization is suspended." + } + }, + "MissingToken": { + "summary": "No Authorization header", "value": { "detail": "You do not have permission to perform this action." } @@ -1087,16 +1540,23 @@ } }, "404": { - "description": "Organization not found, application not found in that organization, or user is not a member of the organization.", + "description": "The organization does not exist or you are not a member of it, or the application does not exist in that organization.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthError" }, "examples": { - "NotFound": { + "OrganizationNotFound": { + "summary": "Organization not found, or you are not a member", + "value": { + "detail": "Organization not found." + } + }, + "ApplicationNotFound": { + "summary": "No such application in this organization", "value": { - "detail": "Not found." + "detail": "Application not found." } } } @@ -1127,6 +1587,12 @@ "post": { "summary": "Resend the email verification code", "operationId": "post_programmatic_resend_otp", + "x-didit-sdk": { + "resource": "account", + "method": "resend_otp", + "auth": "none", + "server": true + }, "tags": [ "Programmatic Auth" ], @@ -1167,8 +1633,27 @@ "description": "A new code was emailed.", "content": { "application/json": { + "schema": { + "type": "object", + "required": [ + "message", + "email" + ], + "properties": { + "message": { + "type": "string", + "description": "Always `A new verification code has been sent to your email.`" + }, + "email": { + "type": "string", + "format": "email", + "description": "The address the code was sent to, normalized: lowercased, with any `+tag` removed from the local part (`Dev+test@Example.com` becomes `dev@example.com`)." + } + } + }, "example": { - "detail": "A new verification code has been sent to your email." + "message": "A new verification code has been sent to your email.", + "email": "you@yourdomain.com" } } } @@ -1193,17 +1678,30 @@ } }, "429": { - "description": "Cooldown active (60s between resends) or IP budget exhausted (5/hour, shared with registration).", + "description": "Cooldown active (60s between resends) or IP budget exhausted (5/hour, shared with registration). Also returned, with a `Retry-After: 5` header, when the service is momentarily checking as many passwords or codes as it can at once; retry after the indicated number of seconds.", "content": { "application/json": { - "example": { - "detail": "Please wait before requesting a new code." + "examples": { + "CooldownActive": { + "summary": "Cooldown or IP budget", + "value": { + "detail": "Please wait before requesting a new code." + } + }, + "PasswordChecksBusy": { + "summary": "Too many password checks in progress", + "value": { + "detail": "Too many sign-in attempts are being processed right now. Please try again in a few seconds." + } + } } } } } }, - "security": [] + "security": [ + {} + ] } } }, @@ -1400,8 +1898,8 @@ }, "api_key": { "type": "string", - "description": "Use this as `x-api-key` on every verification API call.", - "example": "05mHcOWL8GathLZlz8oIDawYj9qFAcoSHtz-75PAkuo" + "description": "Use this as `x-api-key` on every verification API call. New keys start with `sk_live_` (live application) or `sk_test_` (sandbox application), followed by 43 URL-safe characters (letters, digits, `-` and `_`). Keys created before this format have no prefix and keep working unchanged.", + "example": "sk_live_example_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } }, @@ -1427,8 +1925,8 @@ }, "api_key": { "type": "string", - "description": "Long-lived secret (also called `client_secret`). Use as the `x-api-key` header for every call to `https://verification.didit.me/v3/...` (sessions, workflows, AML, etc.). Treat as a credential; never expose client-side.", - "example": "05mHcOWL8GathLZlz8oIDawYj9qFAcoSHtz-75PAkuo" + "description": "Long-lived secret (also called `client_secret`). Use as the `x-api-key` header for every call to `https://verification.didit.me/v3/...` (sessions, workflows, AML, etc.). Treat as a credential; never expose client-side. New keys start with `sk_live_` (live application) or `sk_test_` (sandbox application), followed by 43 URL-safe characters (letters, digits, `-` and `_`). Keys created before this format have no prefix and keep working unchanged.", + "example": "sk_live_example_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, "website_url": { "type": "string", @@ -1467,6 +1965,38 @@ "type": "string", "format": "date-time", "example": "2025-06-01T10:00:00Z" + }, + "products": { + "type": "array", + "readOnly": true, + "description": "The application's products. Always all three, in this order: `identity`, `transactions`, `authentication`. A new application has `identity` enabled with every module on, and `transactions` and `authentication` disabled. Read-only: a `products` key in a create or update body is ignored.", + "items": { + "$ref": "#/components/schemas/ApplicationProduct" + }, + "example": [ + { + "product": "identity", + "status": "enabled", + "modules": { + "user_verification": true, + "business_verification": true, + "case_management": true + } + }, + { + "product": "transactions", + "status": "disabled", + "modules": { + "travel_rule": true, + "case_management": true + } + }, + { + "product": "authentication", + "status": "disabled", + "modules": {} + } + ] } } }, @@ -1512,7 +2042,7 @@ }, "ApplicationUpdateRequest": { "type": "object", - "description": "PATCH body for application metadata. Send only the fields you want to change; omitted fields are preserved. `uuid`, `client_id`, and `api_key` cannot be updated.", + "description": "PATCH body for application metadata. Send only the fields you want to change; omitted fields are preserved. `uuid`, `client_id`, and `api_key` cannot be updated. `products` is read-only: a `products` key in the body is ignored.", "properties": { "name": { "type": "string", @@ -1606,6 +2136,115 @@ "example": "Too many login attempts from this IP address. Please wait before trying again." } } + }, + "PaginatedOrganizationList": { + "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], + "properties": { + "count": { + "type": "integer", + "description": "Total number of organizations." + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the next page, or `null` on the last page." + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the previous page, or `null` on the first page." + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Organization" + } + } + } + }, + "PaginatedApplicationList": { + "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], + "properties": { + "count": { + "type": "integer", + "description": "Total number of applications." + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the next page, or `null` on the last page." + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the previous page, or `null` on the first page." + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Application" + } + } + } + }, + "ApplicationProduct": { + "type": "object", + "description": "One product of an application and its state. Read-only: the application endpoints return it and ignore it in a request body.", + "properties": { + "product": { + "type": "string", + "enum": [ + "identity", + "transactions", + "authentication" + ], + "description": "`identity` (Identity verification), `transactions` (Transaction monitoring) or `authentication` (Authentication).", + "example": "identity" + }, + "status": { + "type": "string", + "enum": [ + "enabled", + "early_access", + "disabled" + ], + "description": "Whether the product is on for this application. `early_access` applies only to `authentication`, which Didit turns on for you.", + "example": "enabled" + }, + "modules": { + "type": "object", + "additionalProperties": { + "type": "boolean" + }, + "description": "Module switches of the product, each `true` or `false`. `identity` has `user_verification`, `business_verification` and `case_management`. `transactions` has `travel_rule` and `case_management`. `authentication` has none (`{}`). `case_management` is one setting shown under both `identity` and `transactions`, so it has the same value on both.", + "example": { + "user_verification": true, + "business_verification": true, + "case_management": true + } + } + }, + "required": [ + "product", + "status", + "modules" + ] } } } diff --git a/openapi/overlay.json b/openapi/overlay.json index 327aa4d..334f75b 100644 --- a/openapi/overlay.json +++ b/openapi/overlay.json @@ -1,1708 +1,5 @@ { "description": "SDK naming (x-didit-sdk) for operations whose upstream OpenAPI definition does not carry it yet. Keys: \" \" for verification.json, \"account \" for account.json. Upstream x-didit-sdk wins; delete an entry once upstream carries the same value. See docs/DESIGN.md section 4.", "version": 1, - "operations": { - "DELETE /v3/biometric-templates/{template_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "biometric_templates", - "server": true - }, - "DELETE /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "DELETE /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/draft/": { - "auth": "api_key", - "method": "discard_draft", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "DELETE /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/draft/": { - "auth": "api_key", - "method": "discard_draft", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "DELETE /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "cases", - "scope": "application", - "server": true - }, - "DELETE /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/links/{link_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "cases.links", - "scope": "application", - "server": true - }, - "DELETE /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/{note_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "cases.notes", - "scope": "application", - "server": true - }, - "DELETE /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/notes/{note_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "networks.notes", - "scope": "application", - "server": true - }, - "DELETE /v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "regulatory_reports", - "scope": "application", - "server": true - }, - "DELETE /v3/organization/{organization_id}/application/{application_id}/report-templates/{template_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "report_templates", - "scope": "application", - "server": true - }, - "DELETE /v3/questionnaires/{questionnaire_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "questionnaires", - "server": true - }, - "DELETE /v3/session/{sessionId}/delete/": { - "auth": "api_key", - "method": "delete", - "resource": "sessions", - "server": true - }, - "DELETE /v3/transactions/rules/install/": { - "auth": "api_key", - "method": "uninstall_library", - "resource": "transactions.rules", - "server": true - }, - "DELETE /v3/transactions/rules/{rule_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "transactions.rules", - "server": true - }, - "DELETE /v3/travel-rule/wallet-addresses/{entry_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "travel_rule.wallet_addresses", - "server": true - }, - "DELETE /v3/webhook/destinations/{destination_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "webhooks.destinations", - "server": true - }, - "DELETE /v3/workflows/{settings_uuid}/": { - "auth": "api_key", - "method": "delete", - "resource": "workflows", - "server": true - }, - "GET /system/healthcheck": { - "auth": "public", - "method": "healthcheck", - "resource": "system", - "server": false - }, - "GET /v1/transactions/{transaction_id}/": { - "auth": "device_token", - "method": "retrieve_from_device", - "resource": "transactions", - "server": false - }, - "GET /v3/aml-monitoring/jobs/{job_id}/": { - "auth": "api_key", - "method": "retrieve_job", - "resource": "aml_monitoring", - "server": true - }, - "GET /v3/billing/balance/": { - "auth": "api_key", - "method": "retrieve_balance", - "resource": "billing", - "server": true - }, - "GET /v3/biometric-templates/": { - "auth": "api_key", - "method": "list", - "resource": "biometric_templates", - "server": true - }, - "GET /v3/biometric-templates/count/": { - "auth": "api_key", - "method": "count", - "resource": "biometric_templates", - "server": true - }, - "GET /v3/biometric-templates/{template_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "biometric_templates", - "server": true - }, - "GET /v3/businesses/": { - "auth": "api_key", - "method": "list", - "resource": "businesses", - "server": true - }, - "GET /v3/businesses/{vendor_data}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "businesses", - "server": true - }, - "GET /v3/case-requests/{request_uuid}/": { - "auth": "device_token", - "method": "retrieve", - "resource": "case_requests", - "server": false - }, - "GET /v3/customization/": { - "auth": "api_key", - "method": "retrieve", - "resource": "customization", - "server": true - }, - "GET /v3/fraud/checks/{check_id}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "fraud_checks", - "server": true - }, - "GET /v3/id-verification/wallets/": { - "auth": "api_key", - "method": "list", - "resource": "id_wallets", - "server": true - }, - "GET /v3/id-verification/wallets/verifications/{request_id}/": { - "auth": "api_key", - "method": "retrieve_verification", - "resource": "id_wallets", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/business-sessions/{business_session_id}/networks/": { - "auth": "api_key", - "method": "list_networks", - "resource": "business_sessions", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/businesses/toggle-ongoing-monitoring/{job_id}/": { - "auth": "api_key", - "method": "retrieve_session_ongoing_monitoring_job", - "resource": "businesses", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/businesses/{vendor_business_uuid}/networks/": { - "auth": "api_key", - "method": "list_networks", - "resource": "businesses", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/": { - "auth": "api_key", - "method": "list", - "resource": "cases", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/": { - "auth": "api_key", - "method": "list", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/presets/": { - "auth": "api_key", - "method": "list_presets", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/draft/": { - "auth": "api_key", - "method": "retrieve_draft", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/history/": { - "auth": "api_key", - "method": "list_history", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/reviewers/": { - "auth": "api_key", - "method": "list_reviewers", - "resource": "cases", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/": { - "auth": "api_key", - "method": "list", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/draft/": { - "auth": "api_key", - "method": "retrieve_draft", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/history/": { - "auth": "api_key", - "method": "list_history", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/statistics/": { - "auth": "api_key", - "method": "retrieve_statistics", - "resource": "cases", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/teams/": { - "auth": "api_key", - "method": "list", - "resource": "cases.teams", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/teams/{record_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "cases.teams", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "cases", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/action-receipts/": { - "auth": "api_key", - "method": "list_action_receipts", - "resource": "cases", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/aml/": { - "auth": "api_key", - "method": "list_aml_hits", - "resource": "cases", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/checklist/": { - "auth": "api_key", - "method": "list", - "resource": "cases.checklist", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/events/": { - "auth": "api_key", - "method": "list_events", - "resource": "cases", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/links/": { - "auth": "api_key", - "method": "list", - "resource": "cases.links", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/": { - "auth": "api_key", - "method": "list", - "resource": "cases.notes", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/presign/": { - "auth": "api_key", - "method": "retrieve_attachment_url", - "resource": "cases.notes", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/print/": { - "auth": "api_key", - "method": "print", - "resource": "cases", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/regulatory-reports/": { - "auth": "api_key", - "method": "list", - "resource": "cases.regulatory_reports", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/": { - "auth": "api_key", - "method": "list", - "resource": "cases.requests", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/options/": { - "auth": "api_key", - "method": "retrieve_options", - "resource": "cases.requests", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/attachment/": { - "auth": "api_key", - "method": "retrieve_attachment", - "resource": "cases.requests", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/networks/": { - "auth": "api_key", - "method": "list", - "resource": "networks", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "networks", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/graph/": { - "auth": "api_key", - "method": "retrieve_graph", - "resource": "networks", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/map/": { - "auth": "api_key", - "method": "retrieve_map", - "resource": "networks", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/members/": { - "auth": "api_key", - "method": "list_members", - "resource": "networks", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/notes/": { - "auth": "api_key", - "method": "list", - "resource": "networks.notes", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/report/": { - "auth": "api_key", - "method": "download_report", - "resource": "networks", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/signals/": { - "auth": "api_key", - "method": "list_signals", - "resource": "networks", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/timeline/": { - "auth": "api_key", - "method": "retrieve_timeline", - "resource": "networks", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/regulatory-reports/": { - "auth": "api_key", - "method": "list", - "resource": "regulatory_reports", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "regulatory_reports", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/download-url/": { - "auth": "api_key", - "method": "retrieve_download_url", - "resource": "regulatory_reports", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/report-templates/": { - "auth": "api_key", - "method": "list", - "resource": "report_templates", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/report-templates/{template_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "report_templates", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/sessions/{session_id}/networks/": { - "auth": "api_key", - "method": "list_networks", - "resource": "sessions", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/networks/": { - "auth": "api_key", - "method": "list_networks", - "resource": "transactions", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/parties/{transaction_party_uuid}/networks/": { - "auth": "api_key", - "method": "list_party_networks", - "resource": "transactions", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/users/{vendor_user_uuid}/networks/": { - "auth": "api_key", - "method": "list_networks", - "resource": "users", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/vendor-businesses/toggle-ongoing-monitoring/{job_id}/": { - "auth": "api_key", - "method": "retrieve_ongoing_monitoring_job", - "resource": "businesses", - "scope": "application", - "server": true - }, - "GET /v3/organization/{organization_id}/application/{application_id}/vendor-users/toggle-ongoing-monitoring/{job_id}/": { - "auth": "api_key", - "method": "retrieve_ongoing_monitoring_job", - "resource": "users", - "scope": "application", - "server": true - }, - "GET /v3/questionnaires/": { - "auth": "api_key", - "method": "list", - "resource": "questionnaires", - "server": true - }, - "GET /v3/questionnaires/{questionnaire_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "questionnaires", - "server": true - }, - "GET /v3/session/imports/template/": { - "auth": "api_key", - "method": "retrieve_template", - "resource": "sessions.imports", - "server": true - }, - "GET /v3/session/imports/{importId}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "sessions.imports", - "server": true - }, - "GET /v3/session/imports/{importId}/errors/": { - "auth": "api_key", - "method": "list_errors", - "resource": "sessions.imports", - "server": true - }, - "GET /v3/session/{sessionId}/decision/": { - "auth": "api_key", - "method": "retrieve_decision", - "resource": "sessions", - "server": true - }, - "GET /v3/session/{sessionId}/generate-pdf/": { - "auth": "api_key", - "method": "generate_pdf", - "resource": "sessions", - "server": true - }, - "GET /v3/sessions/": { - "auth": "api_key", - "method": "list", - "resource": "sessions", - "server": true - }, - "GET /v3/sessions/delete/{job_id}/": { - "auth": "api_key", - "method": "retrieve_deletion_job", - "resource": "sessions", - "server": true - }, - "GET /v3/sessions/{session_id}/reviews/": { - "auth": "api_key", - "method": "list", - "resource": "sessions.reviews", - "server": true - }, - "GET /v3/transactions/": { - "auth": "api_key", - "method": "list", - "resource": "transactions", - "server": true - }, - "GET /v3/transactions/rules/": { - "auth": "api_key", - "method": "list", - "resource": "transactions.rules", - "server": true - }, - "GET /v3/transactions/rules/library/": { - "auth": "api_key", - "method": "list_library", - "resource": "transactions.rules", - "server": true - }, - "GET /v3/transactions/rules/{rule_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "transactions.rules", - "server": true - }, - "GET /v3/transactions/{transaction_id}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "transactions", - "server": true - }, - "GET /v3/travel-rule/pickup/{token}/": { - "auth": "public", - "method": "retrieve_pickup", - "resource": "travel_rule", - "server": false - }, - "GET /v3/travel-rule/settings/": { - "auth": "api_key", - "method": "retrieve_settings", - "resource": "travel_rule", - "server": true - }, - "GET /v3/travel-rule/vasps/": { - "auth": "api_key", - "method": "list_vasps", - "resource": "travel_rule", - "server": true - }, - "GET /v3/travel-rule/wallet-addresses/": { - "auth": "api_key", - "method": "list", - "resource": "travel_rule.wallet_addresses", - "server": true - }, - "GET /v3/travel-rule/widget/{token}/": { - "auth": "public", - "method": "retrieve_widget_context", - "resource": "travel_rule", - "server": false - }, - "GET /v3/users/": { - "auth": "api_key", - "method": "list", - "resource": "users", - "server": true - }, - "GET /v3/users/{vendor_data}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "users", - "server": true - }, - "GET /v3/users/{vendor_data}/generate-pdf/": { - "auth": "api_key", - "method": "generate_pdf", - "resource": "users", - "server": true - }, - "GET /v3/webhook/destinations/": { - "auth": "api_key", - "method": "list", - "resource": "webhooks.destinations", - "server": true - }, - "GET /v3/webhook/destinations/{destination_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "webhooks.destinations", - "server": true - }, - "GET /v3/workflows/": { - "auth": "api_key", - "method": "list", - "resource": "workflows", - "server": true - }, - "GET /v3/workflows/{settings_uuid}/": { - "auth": "api_key", - "method": "retrieve", - "resource": "workflows", - "server": true - }, - "PATCH /v3/businesses/{vendor_data}/": { - "auth": "api_key", - "method": "update", - "resource": "businesses", - "server": true - }, - "PATCH /v3/businesses/{vendor_data}/update-status/": { - "auth": "api_key", - "method": "update_status", - "resource": "businesses", - "server": true - }, - "PATCH /v3/customization/": { - "auth": "api_key", - "method": "update", - "resource": "customization", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/businesses/toggle-ongoing-monitoring/": { - "auth": "api_key", - "method": "set_session_ongoing_monitoring", - "resource": "businesses", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/cases/teams/{record_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "cases.teams", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "cases", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/checklist/{item_uuid}/": { - "auth": "api_key", - "method": "toggle_item", - "resource": "cases.checklist", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/{note_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "cases.notes", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "regulatory_reports", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/report-templates/{template_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "report_templates", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/vendor-businesses/toggle-ongoing-monitoring/": { - "auth": "api_key", - "method": "set_ongoing_monitoring", - "resource": "businesses", - "scope": "application", - "server": true - }, - "PATCH /v3/organization/{organization_id}/application/{application_id}/vendor-users/toggle-ongoing-monitoring/": { - "auth": "api_key", - "method": "set_ongoing_monitoring", - "resource": "users", - "scope": "application", - "server": true - }, - "PATCH /v3/questionnaires/{questionnaire_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "questionnaires", - "server": true - }, - "PATCH /v3/session/{sessionId}/bulk-update-aml-hit-status/": { - "auth": "api_key", - "method": "bulk_update_aml_hit_status", - "resource": "sessions", - "server": true - }, - "PATCH /v3/session/{sessionId}/features/{nodeId}/update-status/": { - "auth": "api_key", - "method": "update_feature_status", - "resource": "sessions", - "server": true - }, - "PATCH /v3/session/{sessionId}/kyb/parties/{partyUuid}/": { - "auth": "api_key", - "method": "update_kyb_party", - "resource": "sessions", - "server": true - }, - "PATCH /v3/session/{sessionId}/kyb/{companyUuid}/update-data/": { - "auth": "api_key", - "method": "update_kyb_company_data", - "resource": "sessions", - "server": true - }, - "PATCH /v3/session/{sessionId}/update-aml-hit-status/": { - "auth": "api_key", - "method": "update_aml_hit_status", - "resource": "sessions", - "server": true - }, - "PATCH /v3/session/{sessionId}/update-data/": { - "auth": "api_key", - "method": "update_data", - "resource": "sessions", - "server": true - }, - "PATCH /v3/session/{sessionId}/update-poa-data/": { - "auth": "api_key", - "method": "update_poa_data", - "resource": "sessions", - "server": true - }, - "PATCH /v3/session/{sessionId}/update-status/": { - "auth": "api_key", - "method": "update_status", - "resource": "sessions", - "server": true - }, - "PATCH /v3/transactions/rules/{rule_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "transactions.rules", - "server": true - }, - "PATCH /v3/transactions/{transaction_id}/travel-rule/": { - "auth": "api_key", - "method": "update_transfer", - "resource": "transactions.travel_rule", - "server": true - }, - "PATCH /v3/travel-rule/wallet-addresses/{entry_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "travel_rule.wallet_addresses", - "server": true - }, - "PATCH /v3/users/{vendor_data}/": { - "auth": "api_key", - "method": "update", - "resource": "users", - "server": true - }, - "PATCH /v3/users/{vendor_data}/update-status/": { - "auth": "api_key", - "method": "update_status", - "resource": "users", - "server": true - }, - "PATCH /v3/webhook/destinations/{destination_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "webhooks.destinations", - "server": true - }, - "PATCH /v3/workflows/{settings_uuid}/": { - "auth": "api_key", - "method": "update", - "resource": "workflows", - "server": true - }, - "POST /v1/transactions/": { - "auth": "device_token", - "method": "submit_from_device", - "resource": "transactions", - "server": false - }, - "POST /v3/age-estimation/": { - "auth": "api_key", - "method": "estimate", - "resource": "age_estimation", - "server": true - }, - "POST /v3/aml/": { - "auth": "api_key", - "method": "screen", - "resource": "aml", - "server": true - }, - "POST /v3/billing/top-up/": { - "auth": "api_key", - "method": "top_up", - "resource": "billing", - "server": true - }, - "POST /v3/biometric-templates/delete/": { - "auth": "api_key", - "method": "batch_delete", - "resource": "biometric_templates", - "server": true - }, - "POST /v3/businesses/aml-monitoring/": { - "auth": "api_key", - "method": "set_aml_monitoring", - "resource": "businesses", - "server": true - }, - "POST /v3/businesses/create/": { - "auth": "api_key", - "method": "create", - "resource": "businesses", - "server": true - }, - "POST /v3/businesses/delete/": { - "auth": "api_key", - "method": "batch_delete", - "resource": "businesses", - "server": true - }, - "POST /v3/case-requests/{request_uuid}/": { - "auth": "device_token", - "method": "respond", - "resource": "case_requests", - "server": false - }, - "POST /v3/database-validation/": { - "auth": "api_key", - "method": "validate", - "resource": "database_validation", - "server": true - }, - "POST /v3/document-ai/": { - "auth": "api_key", - "method": "analyze", - "resource": "document_ai", - "server": true - }, - "POST /v3/email/check/": { - "auth": "api_key", - "method": "check_code", - "resource": "email", - "server": true - }, - "POST /v3/email/risk/": { - "auth": "api_key", - "method": "assess_risk", - "resource": "email", - "server": true - }, - "POST /v3/email/send/": { - "auth": "api_key", - "method": "send_code", - "resource": "email", - "server": true - }, - "POST /v3/face-match/": { - "auth": "api_key", - "method": "compare", - "resource": "face_match", - "server": true - }, - "POST /v3/face-search/": { - "auth": "api_key", - "method": "search", - "resource": "face_search", - "server": true - }, - "POST /v3/fraud/checks/": { - "auth": "api_key", - "method": "create", - "resource": "fraud_checks", - "server": true - }, - "POST /v3/fraud/checks/plan/": { - "auth": "api_key", - "method": "plan", - "resource": "fraud_checks", - "server": true - }, - "POST /v3/id-verification/": { - "auth": "api_key", - "method": "verify", - "resource": "id_verification", - "server": true - }, - "POST /v3/id-verification/wallets/audkenni/": { - "auth": "api_key", - "method": "start_audkenni", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/bankid-cz/": { - "auth": "api_key", - "method": "start_bankid_cz", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/bankid-no/": { - "auth": "api_key", - "method": "start_bankid_no", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/bankid-se/": { - "auth": "api_key", - "method": "start_bankid_se", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/buypass/": { - "auth": "api_key", - "method": "start_buypass", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/connectid/": { - "auth": "api_key", - "method": "start_connectid", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/diia/": { - "auth": "api_key", - "method": "start_diia", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/eparaksts-mobile/": { - "auth": "api_key", - "method": "start_eparaksts_mobile", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/eudi/": { - "auth": "api_key", - "method": "start_eudi", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/franceconnect/": { - "auth": "api_key", - "method": "start_franceconnect", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/frejaid/": { - "auth": "api_key", - "method": "start_frejaid", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/ftn/": { - "auth": "api_key", - "method": "start_ftn", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/govbr/": { - "auth": "api_key", - "method": "start_govbr", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/govuk-wallet/": { - "auth": "api_key", - "method": "start_govuk_wallet", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/idcard-ee/": { - "auth": "api_key", - "method": "start_idcard_ee", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/idin/": { - "auth": "api_key", - "method": "start_idin", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/itsme/": { - "auth": "api_key", - "method": "start_itsme", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/mitid/": { - "auth": "api_key", - "method": "start_mitid", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/mobile-id/": { - "auth": "api_key", - "method": "start_mobile_id", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/mojeid/": { - "auth": "api_key", - "method": "start_mojeid", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/oneid/": { - "auth": "api_key", - "method": "start_oneid", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/personalausweis/": { - "auth": "api_key", - "method": "start_personalausweis", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/smart-id/": { - "auth": "api_key", - "method": "start_smart_id", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/uae-pass/": { - "auth": "api_key", - "method": "start_uae_pass", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/verifications/{request_id}/cancel/": { - "auth": "api_key", - "method": "cancel_verification", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/verifications/{request_id}/poll/": { - "auth": "api_key", - "method": "poll_verification", - "resource": "id_wallets", - "server": true - }, - "POST /v3/id-verification/wallets/vipps/": { - "auth": "api_key", - "method": "start_vipps", - "resource": "id_wallets", - "server": true - }, - "POST /v3/ip/risk/": { - "auth": "api_key", - "method": "assess_risk", - "resource": "ip", - "server": true - }, - "POST /v3/kyb/search/": { - "auth": "api_key", - "method": "search", - "resource": "kyb", - "server": true - }, - "POST /v3/kyb/select/": { - "auth": "api_key", - "method": "select", - "resource": "kyb", - "server": true - }, - "POST /v3/kyb/shareholders/": { - "auth": "api_key", - "method": "list_shareholders", - "resource": "kyb", - "server": true - }, - "POST /v3/kyb/ubo/": { - "auth": "api_key", - "method": "list_ubos", - "resource": "kyb", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/": { - "auth": "api_key", - "method": "create", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/": { - "auth": "api_key", - "method": "create", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/presets/install/": { - "auth": "api_key", - "method": "install_presets", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/publish/": { - "auth": "api_key", - "method": "publish", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/bulk/assign/": { - "auth": "api_key", - "method": "bulk_assign", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/bulk/status/": { - "auth": "api_key", - "method": "bulk_update_status", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/duplicates/": { - "auth": "api_key", - "method": "find_duplicates", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/": { - "auth": "api_key", - "method": "create", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/preview/": { - "auth": "api_key", - "method": "preview", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/publish/": { - "auth": "api_key", - "method": "publish", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/teams/": { - "auth": "api_key", - "method": "create", - "resource": "cases.teams", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/action-receipts/{receipt_uuid}/retry/": { - "auth": "api_key", - "method": "retry_action_receipt", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/approve/": { - "auth": "api_key", - "method": "approve_resolution", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/assign/": { - "auth": "api_key", - "method": "assign", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/entity-actions/": { - "auth": "api_key", - "method": "apply_entity_action", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/escalate/": { - "auth": "api_key", - "method": "escalate", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/links/": { - "auth": "api_key", - "method": "create", - "resource": "cases.links", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/": { - "auth": "api_key", - "method": "create", - "resource": "cases.notes", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/presign/": { - "auth": "api_key", - "method": "create_attachment_upload", - "resource": "cases.notes", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/regulatory-reports/": { - "auth": "api_key", - "method": "create", - "resource": "cases.regulatory_reports", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/reject-approval/": { - "auth": "api_key", - "method": "reject_resolution", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/reopen/": { - "auth": "api_key", - "method": "reopen", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/request-info/": { - "auth": "api_key", - "method": "request_info", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/": { - "auth": "api_key", - "method": "create", - "resource": "cases.requests", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/preview/": { - "auth": "api_key", - "method": "preview", - "resource": "cases.requests", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/cancel/": { - "auth": "api_key", - "method": "cancel", - "resource": "cases.requests", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/resolve/": { - "auth": "api_key", - "method": "resolve", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/transfer/": { - "auth": "api_key", - "method": "transfer", - "resource": "cases", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/notes/": { - "auth": "api_key", - "method": "create", - "resource": "networks.notes", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/regulatory-reports/": { - "auth": "api_key", - "method": "create", - "resource": "regulatory_reports", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/finalize/": { - "auth": "api_key", - "method": "finalize", - "resource": "regulatory_reports", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/validate/": { - "auth": "api_key", - "method": "validate", - "resource": "regulatory_reports", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/report-templates/": { - "auth": "api_key", - "method": "create", - "resource": "report_templates", - "scope": "application", - "server": true - }, - "POST /v3/organization/{organization_id}/application/{application_id}/vendor-users/by-id/{didit_internal_id}/faces/upload/": { - "auth": "api_key", - "method": "upload_face", - "resource": "users", - "scope": "application", - "server": true - }, - "POST /v3/passive-liveness/": { - "auth": "api_key", - "method": "check", - "resource": "passive_liveness", - "server": true - }, - "POST /v3/phone/check/": { - "auth": "api_key", - "method": "check_code", - "resource": "phone", - "server": true - }, - "POST /v3/phone/risk/": { - "auth": "api_key", - "method": "assess_risk", - "resource": "phone", - "server": true - }, - "POST /v3/phone/send/": { - "auth": "api_key", - "method": "send_code", - "resource": "phone", - "server": true - }, - "POST /v3/poa/": { - "auth": "api_key", - "method": "verify", - "resource": "proof_of_address", - "server": true - }, - "POST /v3/questionnaires/": { - "auth": "api_key", - "method": "create", - "resource": "questionnaires", - "server": true - }, - "POST /v3/session/": { - "auth": "api_key", - "method": "create", - "resource": "sessions", - "server": true - }, - "POST /v3/session/import-shared/": { - "auth": "api_key", - "method": "import_shared", - "resource": "sessions", - "server": true - }, - "POST /v3/session/imports/": { - "auth": "api_key", - "method": "create", - "resource": "sessions.imports", - "server": true - }, - "POST /v3/session/{sessionId}/resubmission-preview/": { - "auth": "api_key", - "method": "preview_resubmission", - "resource": "sessions", - "server": true - }, - "POST /v3/session/{sessionId}/sandbox/arm/": { - "auth": "device_token", - "method": "arm_sandbox", - "resource": "sessions", - "server": false - }, - "POST /v3/session/{sessionId}/share/": { - "auth": "api_key", - "method": "share", - "resource": "sessions", - "server": true - }, - "POST /v3/sessions/delete/": { - "auth": "api_key", - "method": "batch_delete", - "resource": "sessions", - "server": true - }, - "POST /v3/sessions/{session_id}/reviews/": { - "auth": "api_key", - "method": "create", - "resource": "sessions.reviews", - "server": true - }, - "POST /v3/transactions/": { - "auth": "api_key", - "method": "create", - "resource": "transactions", - "server": true - }, - "POST /v3/transactions/rules/": { - "auth": "api_key", - "method": "create", - "resource": "transactions.rules", - "server": true - }, - "POST /v3/transactions/rules/backtest/": { - "auth": "api_key", - "method": "backtest", - "resource": "transactions.rules", - "server": true - }, - "POST /v3/transactions/rules/install/": { - "auth": "api_key", - "method": "install_library", - "resource": "transactions.rules", - "server": true - }, - "POST /v3/transactions/sdk-token/": { - "auth": "api_key", - "method": "create_sdk_token", - "resource": "transactions", - "server": true - }, - "POST /v3/transactions/{transaction_id}/travel-rule/ownership/": { - "auth": "api_key", - "method": "confirm_ownership", - "resource": "transactions.travel_rule", - "server": true - }, - "POST /v3/travel-rule/inbound/": { - "auth": "api_key", - "method": "register_inbound", - "resource": "travel_rule", - "server": true - }, - "POST /v3/travel-rule/pickup/{token}/respond/": { - "auth": "public", - "method": "respond_pickup", - "resource": "travel_rule", - "server": false - }, - "POST /v3/travel-rule/wallet-addresses/": { - "auth": "api_key", - "method": "create", - "resource": "travel_rule.wallet_addresses", - "server": true - }, - "POST /v3/travel-rule/widget-session/": { - "auth": "api_key", - "method": "create_widget_session", - "resource": "travel_rule", - "server": true - }, - "POST /v3/users/aml-monitoring/": { - "auth": "api_key", - "method": "set_aml_monitoring", - "resource": "users", - "server": true - }, - "POST /v3/users/create/": { - "auth": "api_key", - "method": "create", - "resource": "users", - "server": true - }, - "POST /v3/users/delete/": { - "auth": "api_key", - "method": "batch_delete", - "resource": "users", - "server": true - }, - "POST /v3/wallet-screening/": { - "auth": "api_key", - "method": "screen", - "resource": "wallet_screening", - "server": true - }, - "POST /v3/wallet-screening/pdf/": { - "auth": "api_key", - "method": "screen_pdf", - "resource": "wallet_screening", - "server": true - }, - "POST /v3/webhook/destinations/": { - "auth": "api_key", - "method": "create", - "resource": "webhooks.destinations", - "server": true - }, - "POST /v3/workflows/": { - "auth": "api_key", - "method": "create", - "resource": "workflows", - "server": true - }, - "PUT /v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/draft/": { - "auth": "api_key", - "method": "save_draft", - "resource": "cases.blueprints", - "scope": "application", - "server": true - }, - "PUT /v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/draft/": { - "auth": "api_key", - "method": "save_draft", - "resource": "cases.routing_rules", - "scope": "application", - "server": true - }, - "PUT /v3/travel-rule/settings/": { - "auth": "api_key", - "method": "update_settings", - "resource": "travel_rule", - "server": true - }, - "account GET /organizations/me/": { - "resource": "account.organizations", - "method": "list", - "auth": "account_token", - "server": true - }, - "account GET /organizations/me/{org_id}/applications/": { - "resource": "account.applications", - "method": "list", - "auth": "account_token", - "server": true - }, - "account GET /organizations/me/{org_id}/applications/{app_id}/": { - "resource": "account.applications", - "method": "retrieve", - "auth": "account_token", - "server": true - }, - "account PATCH /organizations/me/{org_id}/applications/{app_id}/": { - "resource": "account.applications", - "method": "update", - "auth": "account_token", - "server": true - }, - "account POST /organizations/me/{org_id}/applications/": { - "resource": "account.applications", - "method": "create", - "auth": "account_token", - "server": true - }, - "account POST /programmatic/login/": { - "resource": "account", - "method": "login", - "auth": "none", - "server": true - }, - "account POST /programmatic/register/": { - "resource": "account", - "method": "register", - "auth": "none", - "server": true - }, - "account POST /programmatic/resend-otp/": { - "resource": "account", - "method": "resend_otp", - "auth": "none", - "server": true - }, - "account POST /programmatic/verify-email/": { - "resource": "account", - "method": "verify_email", - "auth": "none", - "server": true - } - } -} \ No newline at end of file + "operations": {} +} diff --git a/openapi/verification.json b/openapi/verification.json index c721f93..73528cc 100644 --- a/openapi/verification.json +++ b/openapi/verification.json @@ -17,10 +17,18 @@ "summary": "Service healthcheck (public, unauthenticated)", "description": "Public unauthenticated liveness probe. Returns `HTTP 200` with `{status: \"ok\", timestamp}` when service, DB, cache, and broker are healthy.", "operationId": "get_system_healthcheck", + "x-didit-sdk": { + "resource": "system", + "method": "healthcheck", + "auth": "public", + "server": false + }, "tags": [ "System" ], - "security": [], + "security": [ + {} + ], "parameters": [], "responses": { "200": { @@ -93,6 +101,12 @@ "summary": "Risk-score an email address", "description": "Return deliverability, disposable-address, breach, cross-session match, and normalized risk-intelligence signals without sending an OTP. The endpoint creates an API session and returns its id as `request_id`.\n\n**Decision.** The top-level `status` mirrors `email.status`. It is normally `Approved`; the risk evaluation can produce `In Review` or `Declined`.\n\n**Compatibility aliases.** Live responses expose the normalized intelligence object as both `email.email_intelligence` and `email.enrichment`. The values are identical. New integrations can read `email.enrichment`. Both keys are absent when no usable intelligence data was returned.\n\n**Billing.** Live calls use Email Risk API pricing. The request is billable once the lookup starts, even when some best-effort fields are unavailable.\n\n**Sandbox.** Sandbox keys validate the request and return deterministic approved data without sending an email, consuming credits, creating a session, or contributing to Networks.\n\n**Authentication.** Send your application's API key in `x-api-key`. Missing or invalid credentials return `403` - this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests per minute per API key across all POST/PATCH/DELETE endpoints.\n\n**Social footprint.** Set `enable_social` to `true` to add a `email_social` block: the online platforms this address is registered on, with per-category counts and any profile details available. `profiles_registered: 0` means the platforms were checked and the address was found on none of them - a valid, billable answer. The block is absent when the add-on was not requested or the check could not be completed, and it is not billed in that case. Billing adds one `email_social` unit per request on top of this endpoint's own price.", "operationId": "post_v3email_risk", + "x-didit-sdk": { + "resource": "email", + "method": "assess_risk", + "auth": "api_key", + "server": true + }, "tags": [ "Standalone APIs" ], @@ -305,6 +319,12 @@ "summary": "Risk-score an IP address", "description": "Return geolocation, network ownership, VPN/Tor and data-center flags, threat data, and `country_mismatch` for a supplied IP address. The endpoint creates an API session and returns its id as `request_id`.\n\n**Decision.** `status` is `In Review` when the resolved country conflicts with `claimed_country`, or when the address is associated with VPN, Tor, or data-center infrastructure. Otherwise it is `Approved`.\n\n**Best effort.** IP enrichment is best effort. If no enrichment source returns usable data, the endpoint returns an approved response with `ip.ip_address: null`, `ip.country_mismatch: null`, and an empty `ip.threat` object.\n\n**Billing.** Live calls use IP Risk API pricing. The request is billable once the lookup starts.\n\n**Sandbox.** Sandbox keys validate the request and return deterministic approved data without consuming credits, creating a session, or contributing to Networks.\n\n**Authentication.** Send your application's API key in `x-api-key`. Missing or invalid credentials return `403` - this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests per minute per API key across all POST/PATCH/DELETE endpoints.", "operationId": "post_v3ip_risk", + "x-didit-sdk": { + "resource": "ip", + "method": "assess_risk", + "auth": "api_key", + "server": true + }, "tags": [ "Standalone APIs" ], @@ -459,6 +479,12 @@ "summary": "Risk-score a phone number", "description": "Resolve a phone number and return carrier, line-type, porting, disposable-number, cross-session match, and normalized risk-intelligence signals without sending an OTP. The endpoint creates an API session and returns its id as `request_id`.\n\n**Decision.** The top-level `status` mirrors `phone.status`. It is normally `Approved`; the risk evaluation can produce `In Review` or `Declined`.\n\n**Compatibility aliases.** Live responses expose the normalized intelligence object as both `phone.phone_intelligence` and `phone.enrichment`. The values are identical. New integrations can read `phone.enrichment`. Both keys are absent when no usable intelligence data was returned.\n\n**Billing.** Live calls use Phone Risk API pricing. The request is billable once the lookup starts, even when some best-effort fields are unavailable.\n\n**Sandbox.** Sandbox keys validate the request and return deterministic approved data without sending an OTP, consuming credits, creating a session, or contributing to Networks.\n\n**Authentication.** Send your application's API key in `x-api-key`. Missing or invalid credentials return `403` - this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests per minute per API key across all POST/PATCH/DELETE endpoints.\n\n**Social footprint.** Set `enable_social` to `true` to add a `phone_social` block: the online platforms this number is registered on, with per-category counts and any profile details available. `profiles_registered: 0` means the platforms were checked and the number was found on none of them - a valid, billable answer. The block is absent when the add-on was not requested or the check could not be completed, and it is not billed in that case. Billing adds one `phone_social` unit per request on top of this endpoint's own price.", "operationId": "post_v3phone_risk", + "x-didit-sdk": { + "resource": "phone", + "method": "assess_risk", + "auth": "api_key", + "server": true + }, "tags": [ "Standalone APIs" ], @@ -664,6 +690,12 @@ "summary": "Create a verification session", "description": "Create a User Verification (KYC) or Business Verification (KYB) session and receive a hosted verification `url` plus a `session_token` to redirect or embed for your end user. The `workflow_id` selects which verification steps run and whether the session is KYC or KYB.\n\nCall this from your backend (never the browser — the API key is a secret) whenever a user or business needs to be verified: at signup, before a sensitive action, or when re-verification is required. Prerequisites: a published workflow (created in the Console [Workflows](https://docs.didit.me/console/workflows) page) and an application API key with available credits. Sandbox applications bypass the credit check.\n\n**Idempotency:** when `vendor_data` is provided and an unfinished session (`Not Started`, `In Progress`, `Resubmitted`, or `Awaiting User`) with the same `vendor_data` already exists on the workflow's **current latest published version** (and that session already has a hosted verification URL), the existing session is returned (still `201`) instead of creating a duplicate — with its `callback` and `metadata` updated to the newly provided values. An unfinished session created on an older version of the workflow (one that has since been republished) is **not** reused; a new session pinned to the latest published version is created instead. Sessions in `Approved`, `Declined`, `In Review`, `Expired`, `Abandoned`, or `Kyc Expired` are never reused.\n\n**Side effects:** the hosted verification URL is generated and stored; if `contact_details.send_notification_emails` is `true` and an email is provided, a verification invite email is sent. The session expires after the workflow's configured session expiration time. For KYB workflows, `contact_details.phone` and `expected_details` are ignored.", "operationId": "post_v3_session_create", + "x-didit-sdk": { + "resource": "sessions", + "method": "create", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], @@ -743,7 +775,7 @@ "type": "string", "format": "uri", "nullable": true, - "description": "Final redirect URL the user is sent to after completing the flow. Didit appends `?verificationSessionId={session_id}&status={status}` to this URL. Falls back to the workflow's configured callback URL when not provided in the request; `null` when neither is set.", + "description": "Final redirect URL the user is sent to after completing the flow. Didit appends `?verificationSessionId={session_id}&status={status}` to this URL. Falls back to the workflow's configured callback URL when not provided in the request; `null` when neither is set. When the application has allowed domains and this URL is a web URL outside them (for example a workflow default set before the list changed), the hosted flow does not redirect the user to it.", "example": "https://example.com/verification/callback" }, "workflow_id": { @@ -800,7 +832,7 @@ } }, "400": { - "description": "Bad request — the payload failed validation (missing or unknown `workflow_id`, invalid `language`, blocklisted `vendor_data`, malformed `portrait_image`, `address` without `country`/`poa_country`, invalid `sandbox_scenario`, etc.) or your organization does not have enough credits to start the session.\n\nThe body is an object keyed by the offending field name (or `detail` for non-field errors). Values are an **array of strings** for input-validation failures (e.g. missing required field, invalid language, oversized portrait image) and a **plain string** for failures detected while creating the session (unknown `workflow_id`, blocklisted `vendor_data`, no reference face available for a Face Match workflow). Handle both shapes.", + "description": "Bad request — the payload failed validation (missing or unknown `workflow_id`, invalid `language`, blocklisted `vendor_data`, malformed `portrait_image`, `address` without `country`/`poa_country`, invalid `sandbox_scenario`, `sign` documents, fields or values the workflow's Electronic Signature step refuses, such as `signature_document_not_supported`, `signature_document_conversion_failed`, `field_anchor_not_found` or `field_value_missing`, etc.) or your organization does not have enough credits to start the session.\n\nThe body is an object keyed by the offending field name (or `detail` for non-field errors). Values are an **array of strings** for input-validation failures (e.g. missing required field, invalid language, oversized portrait image) and a **plain string** for failures detected while creating the session (unknown `workflow_id`, blocklisted `vendor_data`, no reference face available for a Face Match workflow). Handle both shapes.", "content": { "application/json": { "schema": { @@ -866,7 +898,7 @@ } }, "403": { - "description": "Authentication or authorization failed. The endpoint returns `403` whenever the `x-api-key` header is missing, malformed, the API key has expired, or the API key is valid but the client does not have permission to create sessions in this application (for example the API key belongs to a different organization). Re-fetch the application API key via the [Auth API](/auth-api/get-credentials) and retry.\n\n**Note:** All four failure modes return the same response body — `{\"detail\": \"You do not have permission to perform this action.\"}` — and there is no machine-readable discriminator (no error code field, no `WWW-Authenticate` challenge) that lets you tell them apart. If you need to differentiate (for example to surface a more specific error to the end user), check the `x-api-key` header is present and belongs to the intended application before calling this endpoint.", + "description": "Authentication or authorization failed. The endpoint returns `403` whenever the `x-api-key` header is missing, malformed, the API key has expired, or the API key is valid but the client does not have permission to create sessions in this application (for example the API key belongs to a different organization). These failures share the body `{\"detail\": \"You do not have permission to perform this action.\"}`. Check that the `x-api-key` header is present and belongs to the intended application, re-fetch the application API key via the [Auth API](/auth-api/get-credentials) if needed, and retry.\n\n**Deleted application:** for a few minutes after an application is deleted, a request made with one of its API keys can answer `{\"detail\": \"This application was deleted.\", \"code\": \"application_deleted\"}`. Do not retry: the application and its API keys no longer exist. After those few minutes, the key is refused like any other invalid key.", "content": { "application/json": { "schema": { @@ -875,8 +907,16 @@ "detail": { "type": "string", "example": "You do not have permission to perform this action." + }, + "code": { + "type": "string", + "description": "Machine-readable reason, present only on some failures. `application_deleted`: the API key belongs to an application that was deleted.", + "example": "application_deleted" } - } + }, + "required": [ + "detail" + ] }, "examples": { "Missing or invalid API key": { @@ -890,6 +930,13 @@ "value": { "detail": "You do not have permission to perform this action." } + }, + "Application deleted": { + "summary": "API key of a deleted application", + "value": { + "detail": "This application was deleted.", + "code": "application_deleted" + } } } } @@ -1007,7 +1054,7 @@ }, "callback": { "type": "string", - "description": "URL to redirect the user to after verification completes. Didit automatically appends `verificationSessionId` and `status` (Approved, Declined, In Review) as query parameters. Custom URL schemes (e.g. `myapp://`) are supported for mobile callbacks. If omitted, the workflow's default `callback_url` is used.", + "description": "URL to redirect the user to after verification completes. Didit automatically appends `verificationSessionId` and `status` (Approved, Declined, In Review) as query parameters. Custom URL schemes (e.g. `myapp://`) are supported for mobile callbacks. If omitted, the workflow's default `callback_url` is used. When the application has allowed domains (`allowed_domains` on `PATCH /v3/webhook/`), an `http` or `https` callback must be on one of those origins (same scheme, host and port), otherwise the request fails with 400 on `callback`: `callback must be on one of the application's allowed domains: https://app.example.com`. Custom URL schemes are always accepted.", "example": "https://example.com/verification/callback", "x-readme-id": "0.2" }, @@ -1332,6 +1379,39 @@ "type": "string", "description": "Sandbox-only scenario slug. Only accepted when the API key belongs to a sandbox-mode application; rejected on live applications. When set, the create flow expands the scenario's bundled magic values onto the session's input fields (contact_details, expected_details) so the mocked providers reproduce the scenario's outcome through the real workflow pipeline. See the scenario catalog at GET /v1/sandbox/scenarios/ or [Sandbox & Test Data](/integration/sandbox-testing). You can also arm (or re-arm) a scenario after create via POST /v3/session/{sessionId}/sandbox/arm/, as long as the session is still `Not Started`. For non-doc lookup, use `lookup_partial_match`, `lookup_no_match`, or `lookup_provider_error` to test the configured attempt budget and fallback. For identity wallets, use `wallet_cancelled`, `wallet_timeout`, or `wallet_provider_error`; `approve` simulates a verified wallet. All catalog wallets can be tested in sandbox, including schemes unavailable on live applications. Simulated attempts are not charged.", "example": "decline_document_expired" + }, + "sign": { + "type": "object", + "description": "Electronic Signature input for this session. `documents` are the documents to sign, for a workflow whose Electronic Signature step takes documents from the session (`signature_documents_source` `session` or `both`): up to 20 documents and 50 MB in total. `field_values` fills the fields whose `prefill` is `api.`, on workflow and session documents alike. Creating a session again for the same `workflow_id` and `vendor_data` returns the pending session; if that call sends `sign`, its documents, fields and values must be identical to the first ones, and anything different returns `400` with `{\"sign\": \"signature_document_changed\"}`. See [Electronic signature documents](/sessions-api/create-session#electronic-signature-documents).", + "properties": { + "documents": { + "type": "array", + "minItems": 1, + "maxItems": 20, + "items": { + "$ref": "#/components/schemas/SessionSignDocument" + } + }, + "field_values": { + "type": "object", + "description": "Values keyed by the `` of each `api.` pre-fill source: strings of 1 to 500 characters for `text` and `data` fields, booleans for a `checkbox`. `400` with `field_value_unknown` for a key no field uses, `field_value_missing` for a required field without its value, and `field_value_invalid` for a value that fails its field's validation.", + "additionalProperties": { + "oneOf": [ + { + "type": "string", + "minLength": 1, + "maxLength": 500 + }, + { + "type": "boolean" + } + ] + }, + "example": { + "credit_limit": "3,000 EUR" + } + } + } } }, "example": { @@ -1431,6 +1511,12 @@ "summary": "Create a verification import job", "description": "Create an asynchronous import job for historical User Verification sessions, Business Verification sessions, transaction monitoring records, or workflow custom status rules. Use this to migrate from providers such as Sumsub, MetaMap, Veriff, Onfido, Persona, Trulioo, Jumio, Incode, iDenfy, or any other provider after transforming the export into Didit's canonical CSV/NDJSON format. Imports are disabled by default and must be enabled per organization by Didit.", "operationId": "post_v3_session_imports_create", + "x-didit-sdk": { + "resource": "sessions.imports", + "method": "create", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], @@ -1665,6 +1751,12 @@ "summary": "Download verification import template", "description": "Download the canonical CSV header for verification imports. The column set is determined by `import_type`. The first column of session templates is the idempotency key `external_id`.", "operationId": "get_v3_session_imports_template", + "x-didit-sdk": { + "resource": "sessions.imports", + "method": "retrieve_template", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], @@ -1725,6 +1817,12 @@ "summary": "Retrieve a verification import job", "description": "Retrieve import progress counters and terminal status for one job. Poll until `status` is `COMPLETED` or `FAILED`; `failed_count` > 0 means some rows were rejected — list them via the errors endpoint.", "operationId": "get_v3_session_imports_retrieve", + "x-didit-sdk": { + "resource": "sessions.imports", + "method": "retrieve", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], @@ -1792,6 +1890,12 @@ "summary": "List verification import row errors", "description": "List row-level errors for a verification import job, ordered by `row_number`. Use this endpoint to produce a corrected retry file containing only failed rows.", "operationId": "get_v3_session_imports_errors", + "x-didit-sdk": { + "resource": "sessions.imports", + "method": "list_errors", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], @@ -1923,8 +2027,17 @@ "/v3/session/{sessionId}/decision/": { "get": { "summary": "Retrieve the full decision for a verification session", - "description": "Retrieve the complete decision report for a verification session — the rolled-up session `status` plus one report per verification feature the workflow executed. This is the canonical read endpoint for both User Verification (KYC) and Business Verification (KYB) sessions: the same path serves both, and the top-level `session_kind` discriminator tells you which shape you received (`\"user\"` or `\"business\"`).\n\n**Every feature report is a plural array.** V3 workflows are graphs that can run the same feature more than once, so each block (`id_verifications`, `nfc_verifications`, `liveness_checks`, `face_matches`, `poa_verifications`, `document_ai_documents`, `phone_verifications`, `email_verifications`, `aml_screenings`, `ip_analyses`, `database_validations`, `questionnaire_responses`) is a JSON array whose items each carry a `node_id` identifying the workflow graph node that produced them. Read NFC as `response.nfc_verifications[0]` — never as a singular `nfc` field. A block is `null` until the workflow has run that feature at least once; features the workflow does not include never appear with data.\n\n**When to call.** The recommended pattern is event-driven: wait for the `status.updated` webhook that fires when the session reaches a decision (`Approved`, `Declined`, `In Review`), then call this endpoint once to fetch the full report. Polling also works — the endpoint can be called at any point in the session lifecycle and returns the data produced so far — but webhook-then-fetch is cheaper and faster. Media URLs in the response (document images, videos, PDFs) are short-lived presigned links: fetch them promptly or re-request the decision to get fresh ones, and do not persist them as long-term references.\n\n**Statuses.** The top-level `status` is the session lifecycle status (`Not Started`, `In Progress`, `Awaiting User`, `In Review`, `Approved`, `Declined`, `Resubmitted`, `Expired`, `Kyc Expired`, `Abandoned`). Each feature item additionally carries its own feature-level `status` (`Not Finished`, `Approved`, `Declined`, `In Review`, ...). A session can be `Approved` overall while an individual feature is `In Review`, depending on the workflow's decision rules.\n\n**Authentication and visibility.** Authenticate with the application API key in the `x-api-key` header (a console user Bearer token with the `read:sessions` permission also works). Only sessions owned by the authenticated application are visible. If your workflow restricts response attributes, non-allowed fields inside feature items are returned as `null` while `status`, `warnings`, and `node_id` are always preserved.\n\n**Rate limits.** GET requests are limited to 600 per minute per API key. Exceeding the limit returns `429` with `Retry-After` and `X-RateLimit-*` headers.", + "description": "Retrieve the complete decision report for a verification session — the rolled-up session `status` plus one report per verification feature the workflow executed. This is the canonical read endpoint for both User Verification (KYC) and Business Verification (KYB) sessions: the same path serves both, and the top-level `session_kind` discriminator tells you which shape you received (`\"user\"` or `\"business\"`).\n\n**Every feature report is a plural array.** V3 workflows are graphs that can run the same feature more than once, so each block (`id_verifications`, `nfc_verifications`, `liveness_checks`, `face_matches`, `poa_verifications`, `document_ai_documents`, `phone_verifications`, `email_verifications`, `aml_screenings`, `ip_analyses`, `database_validations`, `questionnaire_responses`) is a JSON array whose items each carry a `node_id` identifying the workflow graph node that produced them. Read NFC as `response.nfc_verifications[0]` — never as a singular `nfc` field. A block is `null` until the workflow has run that feature at least once; features the workflow does not include never appear with data. Electronic Signature results are in `signatures`, which is present only when the workflow has an Electronic Signature step.\n\n**When to call.** The recommended pattern is event-driven: wait for the `status.updated` webhook that fires when the session reaches a decision (`Approved`, `Declined`, `In Review`), then call this endpoint once to fetch the full report. Polling also works — the endpoint can be called at any point in the session lifecycle and returns the data produced so far — but webhook-then-fetch is cheaper and faster. Media URLs in the response (document images, videos, PDFs) are short-lived presigned links: fetch them promptly or re-request the decision to get fresh ones, and do not persist them as long-term references.\n\n**Statuses.** The top-level `status` is the session lifecycle status (`Not Started`, `In Progress`, `Awaiting User`, `In Review`, `Approved`, `Declined`, `Resubmitted`, `Expired`, `Kyc Expired`, `Abandoned`). Each feature item additionally carries its own feature-level `status` (`Not Finished`, `Approved`, `Declined`, `In Review`, ...). A session can be `Approved` overall while an individual feature is `In Review`, depending on the workflow's decision rules.\n\n**Authentication and visibility.** Authenticate with the application API key in the `x-api-key` header (a console user Bearer token with the `read:sessions` permission also works). Only sessions owned by the authenticated application are visible. If your workflow restricts response attributes, non-allowed fields inside feature items are returned as `null` while `status`, `warnings`, and `node_id` are always preserved.\n\n**Rate limits.** GET requests are limited to 600 per minute per API key. Exceeding the limit returns `429` with `Retry-After` and `X-RateLimit-*` headers.", "operationId": "get_v3_session_decision", + "x-didit-sdk": { + "resource": "sessions", + "method": "retrieve_decision", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], "responses": { "200": { "description": "Full decision report. `session_kind` discriminates the two shapes: `user` sessions carry the KYC feature arrays shown below; `business` sessions instead carry `registry_checks`, `document_verifications` and `key_people_checks` alongside the shared arrays. Feature arrays are `null` until the workflow has run that feature; media URLs are short-lived presigned links.", @@ -2322,6 +2435,8 @@ "status": "In Review", "is_nfc_skipped": false, "skip_reason": null, + "chip_protocol": "icao", + "jpki": null, "portrait_image": "https:///portrait.jpg", "signature_image": "https:///signature.jpg", "chip_data": { @@ -4085,6123 +4200,24344 @@ } } }, + "schema": { + "$ref": "#/components/schemas/SessionDecision" + } + } + } + }, + "403": { + "description": "Authentication or authorization failed. **This endpoint never returns `401`** — Didit's API-key authentication does not emit a `WWW-Authenticate` challenge, so missing or invalid credentials also surface as `403`. Two `detail` strings distinguish the failure modes:\n\n- `\"Authentication credentials were not provided or are invalid.\"` — no `x-api-key` header (and no `Authorization: Bearer` token), or the supplied credential failed introspection (revoked, expired, or malformed).\n- `\"You do not have permission to perform this action.\"` — the credential is valid but not allowed to read this session: a console-user Bearer token lacks the `read:sessions` permission or cannot access the owning application, or an API key belongs to a different organization than the session.\n\nNote that for a completely unknown `sessionId` the endpoint resolves the owning application before validating credentials, so a missing session returns `404` even when the credential is missing or invalid.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "Authentication credentials were not provided or are invalid." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "No x-api-key header, or the key failed introspection", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Insufficient permissions": { + "summary": "Valid credential lacking read:sessions or access to the owning application", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "Session not found. Returned when no live session matches the supplied `sessionId` as either a User Verification (KYC) or a Business Verification (KYB) session — a session that has been deleted also lands here. Body is the standard `{\"detail\": ...}` envelope.\n\n**Two distinct `detail` strings.** If the `sessionId` is unknown as both a KYC and a KYB session, the pre-auth owner lookup fails and you receive `{\"detail\": \"Not found.\"}` — this happens before credential validation, so it is returned even for unauthenticated calls. If the session exists but cannot be retrieved under the authenticated application (for example a stale or just-deleted record), the view returns `{\"detail\": \"Session not found.\"}`. Both indicate the same outcome — the session is not accessible — so match on the `404` status code rather than the string.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "Not found." + } + } + }, + "examples": { + "Unknown session id": { + "summary": "sessionId is unknown to this application", + "value": { + "detail": "Not found." + } + }, + "Session not retrievable": { + "summary": "Session exists but is not retrievable for this application", + "value": { + "detail": "Session not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. GET requests are limited to **600 per minute per credential** (API key, Bearer token, or source IP when unauthenticated). Inspect `Retry-After` / `X-RateLimit-Reset` and back off before retrying. If you hit this limit while polling for decisions, switch to the `status.updated` webhook and fetch the decision once per status change instead.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying the request.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Maximum number of requests allowed in the current window (600 for GET requests).", + "schema": { + "type": "integer", + "example": 600 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests remaining in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix timestamp (seconds) at which the current window resets.", + "schema": { + "type": "integer", + "example": 1750000000 + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable explanation of the rate-limit breach.", + "example": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + }, + "examples": { + "Throttled": { + "summary": "More than 600 GET requests in one minute from the same credential", + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + } + } + }, + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "description": "UUID of the verification session whose decision you want to retrieve. This is the `session_id` returned by `POST /v3/session/`. The same path works for both User Verification (KYC) and Business Verification (KYB) sessions — the server resolves the id against both session types.\n\n**Must be a valid UUID.** The route only matches well-formed UUIDs, so a malformed value is rejected by the URL router before any application code runs and the response is a `404` **HTML** page rather than the JSON `{\"detail\": ...}` envelope shown below. Validate the UUID format client-side so you can distinguish an input error from a genuine missing-session `404`.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + }, + { + "name": "include", + "in": "query", + "required": false, + "description": "Comma-separated list of extra payload sections. Supported values: `events` and `resubmissions`, alone or together (`events,resubmissions`). When `include=events` is passed, the response additionally contains the session event timeline (`events`, including the `WORKFLOW_BRANCH_EVALUATED` / `WORKFLOW_STATUS_NODE_REACHED` routing events described on the Retrieve Session page), the full activity log (`activity_reviews`), blocklist flags (`blocklisted`), a per-session `cost_breakdown`, workflow metadata (`workflow_type`, `workflow_version`, `workflow_version_id`, and `workflow_terminal_node_id` - the last recorded workflow status node that set a session status, or `null` if none has been recorded; historical provenance that is not cleared by manual approvals, declines or resubmissions, and is replaced by a later workflow status node), and session metadata (`api_service` — string or `null`, set only for standalone-API sessions; `session_type` — `API`, `HOSTED`, or `MIGRATED`; `has_device_nfc_support` — boolean). Two fields change relative to the plain shape: the top-level `session_kind` discriminator is **omitted** (the events shape only exists for user sessions), and the `features` array switches from plain strings to `{feature, node_id}` objects — where the email feature is reported as `EMAIL` instead of `EMAIL_VERIFICATION`. This expanded shape is primarily used by the Didit Console; most API integrations should omit the parameter. **KYB limitation:** `include=events` only applies to user (KYC) sessions. Business sessions (`session_kind = \"business\"`) always return the standard business decision shape — `events`, `activity_reviews`, `blocklisted`, `cost_breakdown` and the extra `workflow_*`/session metadata are never returned for business sessions, and their `features` array stays a plain string array. With `include=resubmissions` (user and business sessions alike) the response also contains `resubmissions`: one entry per resubmission request, oldest first, each with `number`, `created_at`, `previous_status`, `nodes`, `reasons`, `requested_by` and `results` (what the resubmitted steps returned before the request, under the same keys and in the same shape as the current results). Each `cost_breakdown` item carries `usage_type`, `price`, `details` and `created_at`: a resubmitted step that runs again is billed as a new item.", + "schema": { + "type": "string", + "enum": [ + "events", + "resubmissions", + "events,resubmissions" + ], + "example": "events" + } + } + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X GET 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/decision/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Accept: application/json'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nsession_id = \"11111111-2222-3333-4444-555555555555\"\nurl = f\"https://verification.didit.me/v3/session/{session_id}/decision/\"\nheaders = {\n 'x-api-key': 'YOUR_API_KEY',\n \"Accept\": \"application/json\",\n}\n\nresponse = requests.get(url, headers=headers, timeout=15)\nresponse.raise_for_status()\ndecision = response.json()\n\nprint(\"session_kind:\", decision[\"session_kind\"])\nprint(\"top-level status:\", decision[\"status\"])\n\n# nfc_verifications is ALWAYS an array — never a singular `nfc` field.\nfor nfc in decision.get(\"nfc_verifications\") or []:\n print(\"nfc node_id:\", nfc[\"node_id\"], \"status:\", nfc[\"status\"])\n\nfor id_check in decision.get(\"id_verifications\") or []:\n print(\"id node_id:\", id_check[\"node_id\"], \"status:\", id_check[\"status\"])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const sessionId = '11111111-2222-3333-4444-555555555555';\nconst response = await fetch(\n `https://verification.didit.me/v3/session/${sessionId}/decision/`,\n {\n method: 'GET',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n Accept: 'application/json',\n },\n }\n);\n\nif (!response.ok) {\n throw new Error(`Decision fetch failed: ${response.status}`);\n}\nconst decision = await response.json();\n\nconsole.log('session_kind:', decision.session_kind);\nconsole.log('top-level status:', decision.status);\n\n// nfc_verifications is ALWAYS an array — never a singular `nfc` field.\nfor (const nfc of decision.nfc_verifications ?? []) {\n console.log('nfc node_id:', nfc.node_id, 'status:', nfc.status);\n}\n\nfor (const idCheck of decision.id_verifications ?? []) {\n console.log('id node_id:', idCheck.node_id, 'status:', idCheck.status);\n}" + } + ] + } + }, + "/v3/session/{sessionId}/delete/": { + "delete": { + "summary": "Delete a verification session (KYC or KYB)", + "description": "Delete a single verification session, User Verification (KYC) or Business Verification (KYB), by its `session_id`. The id is resolved against KYC sessions first, then KYB sessions, so both kinds are deleted through this one URL.\n\n**What is deleted:**\n- The session and every verification attempt it holds, including results that a resubmission replaced: the decision, extracted data, all related feature records (ID verification, face, liveness, face match, AML, registry checks, IP analysis, and so on), its reviews and its activity log.\n- All stored files owned by the session: document front/back photos (full, cropped, and privacy-blurred variants), document-capture videos, portrait crops, NFC chip portrait and signature images, face reference images, liveness videos, face-match source/target images, Proof of Address documents, and any extra uploaded files (KYC); uploaded company documents and extra files (KYB). Electronic Signature documents, signed PDFs, evidence PDFs and signature images (KYC and KYB): their download paths stop working. Previously issued media URLs (`https:///...`) stop resolving.\n- The session's face data. Its face embedding is deleted too, unless you choose to keep an image-free template (see **Face biometric data**).\n- Webhook deliveries still queued for the session, and their retries. Webhooks already delivered are not affected.\n- For a Business Verification (KYB) session: the verifications it created for its key people (KYC) and its corporate owners (KYB), recursively, reported as `deleted_child_verifications`. A verification it only links to, such as an existing approved KYC it reused, is not deleted.\n\nThe session disappears from every read endpoint immediately: `GET /v3/session/{sessionId}/decision/` returns `404` and `GET /v3/sessions/` stops listing it. Ongoing AML monitoring and KYB registry monitoring started by the deleted verifications stop.\n\n**What is not affected:** blocklist entries created from this session (face or document) are **not** removed; manage those with the blocklist endpoints. Share tokens issued for the session are not revoked, but there is nothing left for them to import: an import with one returns `400` with `{\"share_token\": [\"Original session does not exist.\"]}`. Credits already consumed are **not** refunded. The parent User or Business entity is **not** deleted; use `POST /v3/users/delete/` or `POST /v3/businesses/delete/` for those. If the User's profile portrait came from this session, the portrait is removed with it.\n\n**Face biometric data:** by default the session's face embedding is deleted with the session (`face_retention_outcome: deleted`). An application that opted in to biometric-template retention (`face_retention_policy: retain_until_user_deleted` on `PATCH /v3/webhook/`), or a request with `retain_face_embeddings: true`, keeps one separately managed, image-free face biometric template anchored to the session's User (`retained_with_user`) so that duplicate detection, Face Search, and biometric authentication keep working. The template never contains the face image, media, identity fields, the decision, or the deleted session's id; it is biometric data deleted at `biometric_template_expires_at`, or earlier on User deletion, privacy erasure, or an explicit purge through `/v3/biometric-templates/`. A session without a linked User cannot retain a template (`ineligible_no_vendor_user`). Send `deletion_instruction: privacy_erasure` for data-subject requests: it purges every retained template for that User and never retains a new one.\n\n**Irreversible:** deletion is permanent and there is no restore endpoint. Export anything you need (for example a decision PDF) before you call it.\n\n**Side effects:** no webhook is emitted for deletions.\n\n**Response:** `200 OK` once the deletion has completed, with a JSON body reporting the `face_retention_outcome`, the retained template's expiry and, for a KYB session, `deleted_child_verifications`. Earlier versions of this endpoint returned `204 No Content`; update clients that assert on `204`. Keep your `instruction_id` in your own erasure log.\n\n**Idempotency:** not idempotent at the HTTP level: the first call returns `200`; repeating it returns `404` because the session no longer resolves. After a `503` the session is still in place; repeat the same request to finish deleting it.\n\n**Authentication:** send your application's API key in the `x-api-key` header; the session must belong to that application. Console user access tokens (`Authorization: Bearer ...`) may also call this endpoint when they carry the `delete:sessions` permission. Authentication and permission failures both return `403`; this API never returns `401`.\n\n**Rate limit:** shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\nTo bulk-delete KYC sessions by their numeric `session_number`, use `POST /v3/sessions/delete/` instead.", + "operationId": "delete_v3_session_by_id", + "x-didit-sdk": { + "resource": "sessions", + "method": "delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "in": "path", + "name": "sessionId", + "required": true, + "description": "UUID (`session_id`) of the User Verification (KYC) or Business Verification (KYB) session to delete, as returned when the session was created. Must be a canonical hyphenated UUID — a non-UUID value does not match the route and returns `404`.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + } + ], + "responses": { + "200": { + "description": "Session deleted. The body reports what happened to the session's face biometric data and, for a KYB session, how many verifications it created were deleted with it.", + "content": { + "application/json": { "schema": { "type": "object", "properties": { "session_id": { "type": "string", "format": "uuid", - "description": "The unique identifier of the session for which to retrieve verification results." - }, - "session_kind": { - "type": "string", - "enum": [ - "user", - "business" - ], - "description": "Discriminator indicating whether this is a User Verification (KYC) or Business Verification (KYB) session. When `user`, the KYC feature arrays (`id_verifications`, `nfc_verifications`, `liveness_checks`, `face_matches`, `poa_verifications`, `document_ai_documents`, `database_validations`, ...) apply and the business arrays are absent. When `business`, the payload instead carries `registry_checks`, `document_verifications`, `key_people_checks` plus the shared arrays (`aml_screenings`, `phone_verifications`, `email_verifications`, `ip_analyses`, `questionnaire_responses`). **Omitted when `include=events` is passed** — the events shape only exists for user sessions, so the discriminator is dropped from that variant." + "description": "The deleted session." }, "session_number": { "type": "integer", - "description": "The number of the session.", - "nullable": true - }, - "session_url": { - "type": "string", - "format": "uri", - "description": "The URL of the session.", - "nullable": true + "nullable": true, + "description": "The deleted session's number." }, - "status": { + "face_retention_outcome": { "type": "string", "enum": [ - "Not Started", - "In Progress", - "Awaiting User", - "In Review", - "Approved", - "Declined", - "Resubmitted", - "Expired", - "Kyc Expired", - "Abandoned" + "retained_with_user", + "deleted", + "none", + "ineligible_no_vendor_user" ], - "description": "Overall lifecycle status of the session — the rolled-up workflow result. Terminal values are `Approved`, `Declined`, `In Review`, `Expired`, `Kyc Expired`, and `Abandoned`; `Not Started`, `In Progress`, `Awaiting User`, and `Resubmitted` indicate the session is still moving through the workflow." + "description": "What happened to the session's face biometric data. `deleted`: the face embedding was deleted with the session (default). `retained_with_user`: the session is deleted and one image-free biometric template stays anchored to the User; see `biometric_template_uuid`. `none`: the session had no face embedding (for example a KYB session, or a KYC session without a liveness selfie). `ineligible_no_vendor_user`: retention was requested but the session has no linked User, so the embedding was deleted with the session. A biometric-store failure is never reported through this field on this endpoint: it returns `503` and the session is not deleted." }, - "workflow_id": { + "biometric_template_uuid": { "type": "string", "format": "uuid", - "description": "Stable identifier of the workflow group that produced this session. Workflows are versioned — this value identifies the workflow itself, not the specific version.", - "nullable": true - }, - "features": { - "type": "array", - "items": { - "type": "string", - "description": "The feature of the session." - }, - "description": "List of verification features configured for this session, as display names: `ID_VERIFICATION`, `NFC`, `LIVENESS`, `FACE_MATCH`, `POA`, `PHONE`, `EMAIL_VERIFICATION`, `AML`, `IP_ANALYSIS`, `DATABASE_VALIDATION`, `QUESTIONNAIRE`, `AGE_ESTIMATION`, `FACE_SEARCH` (Business Verification sessions use the KYB feature set: `KYB_REGISTRY`, `KYB_DOCUMENTS`, `KYB_KEY_PEOPLE`, `AML`, `PHONE`, `EMAIL_VERIFICATION`, `IP_ANALYSIS`, `QUESTIONNAIRE`). With `include=events` each entry becomes an object `{feature, node_id}` instead of a plain string, and the email feature is renamed: it appears as `EMAIL` in events mode instead of the `EMAIL_VERIFICATION` used here." + "nullable": true, + "description": "Id of the retained biometric template when `face_retention_outcome` is `retained_with_user`; `null` otherwise. Manage it through `/v3/biometric-templates/{template_uuid}/`." }, - "vendor_data": { + "biometric_template_expires_at": { "type": "string", - "description": "The vendor data of the session.", - "nullable": true - }, - "metadata": { - "type": "object", - "description": "The metadata of the session.", - "nullable": true - }, - "expected_details": { - "type": "object", - "description": "Expected details supplied at session creation — used for cross-validation against extracted data (KYC) or to pre-fill the company registry search (KYB). Echoes back exactly the keys that were provided; `null` when none were supplied. KYC sessions carry the person fields; Business (KYB) sessions carry `company_name`, `registry_country` and `registration_number`.", - "properties": { - "first_name": { - "type": "string", - "description": "User's first name. For example, `John`." - }, - "last_name": { - "type": "string", - "description": "User's last name. For example, `Doe`." - }, - "date_of_birth": { - "type": "string", - "format": "date", - "description": "User's date of birth with format: YYYY-MM-DD. For example, `1990-05-15`." - }, - "gender": { - "type": "string", - "nullable": true, - "enum": [ - "M", - "F" - ], - "default": null, - "description": "User's gender. Must be either 'M', 'F', or null." - }, - "nationality": { - "type": "string", - "description": "ISO 3166-1 alpha-3 country code representing the applicant's country of origin. For example, `USA`." - }, - "country": { - "type": "string", - "description": "ISO 3166-1 alpha-3 country code representing the country of the Proof of Address document, or the country of the applicant's ID document, which may differ from nationality. For example, `GBR`." - }, - "address": { - "type": "string", - "description": "The address in a human readable format, including as much information as possible. For example, `123 Main St, San Francisco, CA 94105, USA`." - }, - "identification_number": { - "type": "string", - "description": "The user's document number, personal number, or tax number. For example, `123456789`." - }, - "ip_address": { - "type": "string", - "format": "ipv4", - "description": "Expected IP address for the session. If the actual IP address differs from this value, a warning will be logged. For example, `192.168.1.100`." - }, - "expected_document_types": { - "type": "array", - "description": "Document types the ID verification step was restricted to at session creation. Empty/unset means no restriction. Codes: `P` (passport), `ID` (national ID), `DL` (driver's license), `RP` (residence permit), `HIC` (health insurance card), `TC` (tax card), `SSC` (social security card).", - "items": { - "type": "string", - "enum": [ - "P", - "ID", - "DL", - "RP", - "HIC", - "TC", - "SSC" - ] - } - }, - "id_country": { - "type": "string", - "nullable": true, - "description": "Expected ID-document issuing country (ISO 3166-1 alpha-3), when restricted at creation time.", - "example": "ESP" - }, - "poa_country": { - "type": "string", - "nullable": true, - "description": "Expected Proof of Address document country (ISO 3166-1 alpha-3), when restricted at creation time.", - "example": "ESP" - }, - "company_name": { - "type": "string", - "description": "Expected legal name of the business. Business (KYB) sessions only." - }, - "registry_country": { - "type": "string", - "description": "Expected registry country as `XX` or `XX-YY` (ISO 3166-1 alpha-2, optionally with an ISO 3166-2 subdivision for state-level registries, e.g. `US-CA`). Business (KYB) sessions only." - }, - "registration_number": { - "type": "string", - "description": "Expected company registration number. Business (KYB) sessions only." - } - }, - "nullable": true - }, - "contact_details": { - "type": "object", - "description": "Contact details supplied when the session was created (used for notification emails, prefilled phone/email verification, and communication language). `null` when neither an email nor a phone number was provided.", - "properties": { - "email": { - "type": "string", - "description": "Email address of the user (e.g., \"john.doe@example.com\") that will be used during the Email Verification step. If not provided, the user must provide it during the verification flow." - }, - "email_lang": { - "type": "string", - "description": "Language code (ISO 639-1) for email notifications. Controls the language of all email communications (e.g., \"en\", \"es\", \"fr\")." - }, - "send_notification_emails": { - "type": "boolean", - "default": false, - "description": "If true and an email is provided, Didit sends the initial \"Verify your identity\" email asynchronously when the User Verification (KYC) or Business Verification (KYB) session is created. Didit also sends verification status notifications for sessions requiring manual review to the provided email address." - }, - "phone": { - "type": "string", - "nullable": true, - "description": "Phone number of the user in E.164 format if available (e.g., +14155552671). `null` when only an email was supplied at session creation. Note: This field returns the original phone number provided during session creation. If the number was invalid, it may not be in E.164 format." - } - }, - "nullable": true + "format": "date-time", + "nullable": true, + "description": "When the retained biometric template is deleted, when `face_retention_outcome` is `retained_with_user`; `null` when no template is retained." }, - "callback": { - "type": "string", - "format": "uri", - "description": "The callback URL of the session.", - "nullable": true + "deleted_child_verifications": { + "type": "integer", + "description": "For a Business Verification (KYB) session: how many verifications it created (its key people's KYC sessions and its corporate owners' KYB sessions, recursively) were deleted with it. Always `0` for a KYC session." + } + } + }, + "examples": { + "Deleted (default)": { + "value": { + "session_id": "4c5c7f3a-3b1d-4b7e-9c39-2b4f7e6a1d02", + "session_number": 1024, + "face_retention_outcome": "deleted", + "biometric_template_uuid": null, + "biometric_template_expires_at": null, + "deleted_child_verifications": 0 + } + }, + "Retained with User": { + "value": { + "session_id": "4c5c7f3a-3b1d-4b7e-9c39-2b4f7e6a1d02", + "session_number": 1024, + "face_retention_outcome": "retained_with_user", + "biometric_template_uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", + "biometric_template_expires_at": "2027-10-09T10:15:00Z", + "deleted_child_verifications": 0 + } + }, + "Ineligible: no User": { + "summary": "Retention requested on a session created without vendor_data", + "value": { + "session_id": "7a1e9c2d-5f4b-4c3a-8e2d-1b0a9f8e7d6c", + "session_number": 1031, + "face_retention_outcome": "ineligible_no_vendor_user", + "biometric_template_uuid": null, + "biometric_template_expires_at": null, + "deleted_child_verifications": 0 + } + }, + "KYB session": { + "value": { + "session_id": "0f3c9b7e-2d1a-4e5f-8c6b-7a9d8e1f2c3b", + "session_number": 88, + "face_retention_outcome": "none", + "biometric_template_uuid": null, + "biometric_template_expires_at": null, + "deleted_child_verifications": 3 }, - "id_verifications": { - "type": "array", - "nullable": true, - "description": "**Array of ID verification (OCR) reports** produced by ID-document steps in the workflow graph. Always a JSON array — read `id_verifications[0]` and iterate if your workflow can run more than one ID check (for example a primary check plus a step-up). `null` until at least one ID step has produced data. Each item is the full report, including `node_id`, feature-level `status`, extracted document fields, image URLs, quality scores, MRZ, address parsing, cross-session matches, and `warnings[]`.", - "items": { - "type": "object", - "description": "One ID verification report - the full report for a single ID Verification step. `verification_method` says which of the three methods produced it (document capture, non-doc lookup or wallet sign-in) and the method-specific evidence block is populated accordingly.", - "properties": { - "node_id": { - "type": "string", - "description": "Workflow graph node id of the OCR step that produced this report." - }, - "verification_method": { - "type": "string", - "enum": [ - "document", - "id_lookup", - "wallet" - ], - "description": "Which ID Verification method produced this report. `document` - the user photographed a physical ID (the default, and the value on every session created before the other methods existed). `id_lookup` - the user typed their national ID number and it was checked against a government register or another authoritative source for that country (`id_lookup.source` names which). `wallet` - the user signed in with a government or bank digital identity." - }, - "assurance": { - "type": "string", - "enum": [ - "documentary", - "data_match", - "cryptographic" - ], - "description": "Assurance tier derived from `verification_method`: `documentary` for a document that was captured and checked, `data_match` for details that matched a record held by the source queried, `cryptographic` for attributes an identity provider authenticated and signed." - }, - "wallet_provider": { - "type": "string", - "nullable": true, - "description": "Catalog wallet id when `verification_method` is `wallet` (`mitid`, `bankid_se`, `itsme`, ...). `null` otherwise." - }, - "fallback_from": { - "type": "object", - "nullable": true, - "description": "Why a non-document method ended the ID step. Set on the declined report when a lookup or a wallet sign-in failed and the country's fallback switch was `decline`. `null` otherwise.", - "properties": { - "method": { - "type": "string", - "enum": [ - "id_lookup", - "wallet" - ], - "description": "The method that failed." - }, - "reason": { - "type": "string", - "description": "The outcome that ended it, e.g. `no_match`, `provider_error`, `cancelled`, `timeout`." - }, - "action": { - "type": "string", - "description": "The configured action that was taken, e.g. `decline`." - } - } - }, - "id_lookup": { - "type": "object", - "nullable": true, - "description": "Registry evidence. Present only when `verification_method` is `id_lookup`; `null` otherwise.", - "properties": { - "source": { - "type": "string", - "description": "Customer-facing name of the source queried, e.g. `Department of Home Affairs` (a government register) or `UK credit bureau and financial services records` (not a government register). This is the only statement of provenance for the lookup; do not assume a government source." - }, - "checked_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the register answered." - }, - "attempts": { - "type": "integer", - "description": "1-based attempt number this report came from." - }, - "max_attempts": { - "type": "integer", - "description": "Attempt budget configured for the country (1 to 5, default 1)." - }, - "outcome": { - "type": "string", - "enum": [ - "match", - "partial_match", - "no_match", - "provider_error", - "format_rejected" - ], - "nullable": true, - "description": "What the register answered. Only `match`, `partial_match` and `no_match` count against the attempt budget and bill." - }, - "comparison": { - "type": "array", - "description": "One row per comparable field the source answered on, plus one row per source error. Comparison rows use the normalised field names below rather than the country's request field keys, because several request fields (first and last name, or any of the identifier aliases) collapse into a single comparison. A source that failed appends a row with `field: \"provider\"` and `result: \"error\"`, so read `result` before treating a row as a field verdict.", - "items": { - "type": "object", - "required": [ - "field", - "label", - "provided", - "record", - "result" - ], - "properties": { - "field": { - "type": "string", - "enum": [ - "identification_number", - "full_name", - "date_of_birth", - "address", - "provider" - ], - "description": "Normalised comparison field. `provider` is not a user field: it marks a source error row." - }, - "label": { - "type": "string", - "description": "Display label for the row, e.g. `ID number`, `Full name`, `Provider`." - }, - "provided": { - "type": "string", - "nullable": true, - "description": "What the user typed. `null` on a `provider` error row." - }, - "record": { - "type": "string", - "nullable": true, - "description": "What the source held. On a `provider` error row this carries the error message instead." - }, - "result": { - "type": "string", - "enum": [ - "match", - "partial", - "no_match", - "error" - ], - "description": "Per-field comparison result. `error` appears only on a `provider` row and means that source did not answer; treat it as evidence about the query, never as a field mismatch." - } - } - } - }, - "registry_portrait": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Short-lived presigned URL to the portrait the source returned. Only the four government registers that return one (South Africa, Nigeria, Argentina, Panama)." - }, - "face_match_score": { - "type": "number", - "nullable": true, - "description": "Selfie against the registry portrait, 0-100. Only where the register returns a portrait." - } - } - }, - "wallet_verification": { - "type": "object", - "nullable": true, - "description": "Credential evidence. Present only when `verification_method` is `wallet`; `null` otherwise.", - "properties": { - "provider": { - "type": "string", - "description": "Catalog wallet id, e.g. `mitid`." - }, - "provider_name": { - "type": "string", - "nullable": true, - "description": "Wallet name as the end user knows it, e.g. `MitID`." + "summary": "A business verification deleted with the three verifications it created for its key people" + } + } + } + } + }, + "400": { + "description": "Validation error — nothing is deleted.", + "content": { + "application/json": { + "examples": { + "Privacy erasure cannot retain": { + "value": { + "retain_face_embeddings": [ + "Privacy erasure cannot retain biometric templates." + ] + } + }, + "No finite retention duration": { + "summary": "Retention requested without face_retention_days on the request or the application", + "value": { + "detail": "A finite biometric-template retention duration is required." + } + } + } + } + } + }, + "403": { + "description": "Authentication or permission failure. Missing/invalid credentials also return `403` — this API never returns `401`. Exception: when the `sessionId` does not resolve to a live session, the pre-auth owner lookup returns `404` first, even with missing or invalid credentials.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Missing or invalid API key": { + "summary": "No x-api-key header, or the key is invalid", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Insufficient permission": { + "summary": "User token without delete:sessions, or credentials for an application that does not own the session", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No live KYC or KYB session with this `session_id` exists — the id is unknown or the session was already deleted (repeating a successful delete lands here).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Not Found": { + "summary": "Unknown or already-deleted session", + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Rate limited": { + "summary": "Write budget exhausted", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + } + } + } + }, + "503": { + "description": "The deletion could not finish because a storage service, a biometric store, or a monitoring provider was unavailable. The session is still in place; repeat the same request until it returns `200`. Some of its stored files or its face embedding may already be deleted, so do not rely on them after a `503`. Retries converge on a single template.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Session deletion unavailable": { + "summary": "A storage service or a monitoring provider was unavailable", + "value": { + "detail": "The session could not be deleted because a storage service or a monitoring provider is unavailable. The session is still in place; repeat the request to finish deleting it." + } + }, + "Biometric store unavailable": { + "summary": "A biometric store was unavailable while retaining or purging a template", + "value": { + "detail": "The biometric-template operation could not finish because an external store is unavailable. The operation is retryable." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl (default policy)", + "source": "curl -X DELETE \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/ \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "curl", + "label": "curl (retain template)", + "source": "curl -X DELETE \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"retain_face_embeddings\": true, \"face_retention_days\": 365}'" + }, + { + "lang": "curl", + "label": "curl (privacy erasure)", + "source": "curl -X DELETE \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"deletion_instruction\": \"privacy_erasure\", \"instruction_id\": \"dsar-2026-0142\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.delete(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json={\"retain_face_embeddings\": True, \"face_retention_days\": 365},\n)\nresponse.raise_for_status()\nprint(response.json()[\"face_retention_outcome\"])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/',\n {\n method: 'DELETE',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ deletion_instruction: 'privacy_erasure', instruction_id: 'dsar-2026-0142' }),\n },\n);\nconst body = await resp.json();\nconsole.log(body.face_retention_outcome);" + } + ], + "requestBody": { + "required": false, + "description": "Optional. Omit the body entirely to follow the application's retention policy with an operational deletion.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "retain_face_embeddings": { + "type": "boolean", + "nullable": true, + "description": "Override the application's `face_retention_policy` for this deletion. `true` keeps one image-free face biometric template anchored to the session's User after the session is deleted; `false` deletes the face embedding with the session; omit or `null` to follow the application policy. Ignored for KYB sessions and for sessions without a face embedding.", + "example": true + }, + "face_retention_days": { + "type": "integer", + "minimum": 1, + "maximum": 3650, + "description": "Finite retention duration, in days, for a retained template. Required when a template is retained unless the application already sets `face_retention_days`. The template's `expires_at` is the earliest of this duration, the application's general data-retention window, and `face_retention_deadline`.", + "example": 365 + }, + "face_retention_deadline": { + "type": "string", + "format": "date-time", + "description": "Optional hard expiry for a retained template. Must be in the future.", + "example": "2027-08-28T00:00:00Z" + }, + "deletion_instruction": { + "type": "string", + "enum": [ + "operational_session_delete", + "privacy_erasure" + ], + "default": "operational_session_delete", + "description": "The class of instruction you are giving. `operational_session_delete` deletes the session and retains a template only when the policy or `retain_face_embeddings` says so. `privacy_erasure` purges every retained biometric template anchored to the session's User before deleting the session, cannot be overridden by the application policy, and cannot be combined with `retain_face_embeddings: true` (`400`).", + "example": "operational_session_delete" + }, + "instruction_id": { + "type": "string", + "maxLength": 255, + "description": "Your durable reference for this deletion instruction (for example an erasure-ticket id). Recorded on the audit trail and on any retained template. Generated by Didit when omitted.", + "example": "dsar-2026-0142" + } + } + }, + "examples": { + "Retain a biometric template": { + "summary": "Delete the session, keep an image-free template for 365 days", + "value": { + "retain_face_embeddings": true, + "face_retention_days": 365, + "instruction_id": "post-approval-cleanup-2026-08" + } + }, + "Force deletion on an opted-in application": { + "summary": "Delete the face embedding regardless of the application policy", + "value": { + "retain_face_embeddings": false + } + }, + "Privacy erasure": { + "summary": "Data-subject request: delete the session and purge every retained template for that person", + "value": { + "deletion_instruction": "privacy_erasure", + "instruction_id": "dsar-2026-0142" + } + } + } + } + } + } + } + }, + "/v3/session/{sessionId}/generate-pdf/": { + "get": { + "summary": "Download a PDF report for a verification session", + "description": "Render and download a compliance-ready PDF report for a finished verification session. The same path serves both User Verification (KYC) and Business Verification (KYB) sessions — the server resolves the `sessionId` against both session types and picks the matching report template.\n\n**Eligible statuses.** The session must already carry a final or reviewable status:\n\n- KYC sessions: `Approved`, `Declined`, `In Review`, or `Kyc Expired`.\n- KYB sessions: `Approved`, `Declined`, or `In Review`.\n\nAny other status (`Not Started`, `In Progress`, `Expired`, `Abandoned`, …) returns `403` with an explanatory `detail` string — see the `403` response below.\n\n**What the report contains.** The PDF mirrors the console session view at the moment of the request:\n\n- **KYC** — rolled-up session status, the warnings list, and one section per executed feature: ID Verification (document images plus extracted fields), NFC, Liveness, Face Match, Email Verification, Phone Verification, Proof of Address, Questionnaire answers, Database Validation, AML Screening, and IP Analysis — followed by session tags, console review activity (comments and status changes), and the session event timeline.\n- **KYB** — registry checks (company data), AML screenings, business document verifications, key-people checks, questionnaire responses, phone/email verifications, IP analyses, and the session event timeline, followed by tags and review activity.\n\n**Branding.** If your application has white-label customization, the report is rendered with your logo and links to your privacy-policy URL; otherwise it carries Didit branding. Reports are rendered in English — there is no language parameter.\n\n**No caching.** Every call re-renders the PDF from the current session data, so a report generated after a manual review reflects the reviewer’s decision. Two calls for the same session can produce byte-different files — archive the downloaded file if you need an immutable copy.\n\n**Latency.** Rendering downloads every stored image (document sides, selfies, extra files) before composing the PDF, so media-heavy sessions can take several seconds. Use a generous client read timeout (60 s recommended) and stream the body to disk.\n\n**Trailing slash.** The canonical route ends with a trailing slash (`…/generate-pdf/`). A request without it is served directly with the same response — there is no `301` redirect, so no client has to follow redirects and `curl` does not need `-L`. Use the slashed URL as in the samples.\n\nIf you need the underlying data as JSON instead of a rendered report, use `GET /v3/session/{sessionId}/decision/`. For every reportable session of one user in a single document, use `GET /v3/users/{vendor_data}/generate-pdf/`.", + "operationId": "get_v3_session_generate_pdf", + "x-didit-sdk": { + "resource": "sessions", + "method": "generate_pdf", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "description": "UUID of the User Verification (KYC) or Business Verification (KYB) session to render — the `session_id` returned by `POST /v3/session/`. The same path works for both session kinds; use the `Content-Disposition` filename on the response to tell which report type you received.\n\n**Must be a well-formed, lowercase UUID.** The route only matches valid UUIDs, so a malformed value is rejected by the URL router before any application code runs and the response is a `404` **HTML** page rather than the JSON `{\"detail\": ...}` envelope shown below. The route's UUID converter only matches the canonical lowercase form — uppercase-hex UUIDs are rejected with the HTML 404.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + } + ], + "responses": { + "200": { + "description": "The rendered PDF document, returned directly as binary `application/pdf` — there is no JSON wrapper and no download-URL indirection. The body starts with the `%PDF` magic bytes (PDF 1.7). Save it to a `.pdf` file or stream it through to your caller.", + "headers": { + "Content-Disposition": { + "description": "`attachment; filename=session_{sessionId}.pdf` for KYC sessions, `attachment; filename=business_{sessionId}.pdf` for KYB sessions — the prefix tells you which report template was rendered.", + "schema": { + "type": "string", + "example": "attachment; filename=session_11111111-2222-3333-4444-555555555555.pdf" + } + }, + "Content-Length": { + "description": "Total size of the PDF in bytes; the body is not chunked.", + "schema": { + "type": "integer", + "example": 25191 + } + } + }, + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "403": { + "description": "Authentication, authorization, or session-status failure. **This endpoint never returns `401`** — Didit's API-key authentication does not emit a `WWW-Authenticate` challenge, so missing or invalid credentials also surface as `403`. Distinguish the failure modes by the `detail` string:\n\n- `\"Authentication credentials were not provided or are invalid.\"` — no `x-api-key` header (and no `Authorization: Bearer` token), or the supplied credential failed introspection (revoked, expired, or malformed).\n- `\"You do not have permission to perform this action.\"` — the credential is valid but cannot read this session: a console-user Bearer token lacks the `read:sessions` permission or cannot access the owning application, or an API key belongs to a different organization than the session.\n- `\"You can only generate a PDF for sessions in review, declined, approved or kyc expired\"` — the KYC session exists but is not in an eligible status yet (for example `Not Started` or `In Progress`). Wait for the `status.updated` webhook before requesting the report.\n- `\"You can only generate a PDF for sessions in review, declined or approved\"` — same condition for KYB sessions.\n\nNote that for a completely unknown `sessionId` the endpoint resolves the owning application before validating credentials, so a missing session returns `404` even when the credential is missing or invalid.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "You can only generate a PDF for sessions in review, declined, approved or kyc expired" + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "No x-api-key header, or the key failed introspection", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Insufficient permissions": { + "summary": "Valid credential lacking read:sessions or access to the owning application", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "KYC session not finished": { + "summary": "KYC session still Not Started / In Progress", + "value": { + "detail": "You can only generate a PDF for sessions in review, declined, approved or kyc expired" + } + }, + "KYB session not finished": { + "summary": "KYB session still Not Started / In Progress", + "value": { + "detail": "You can only generate a PDF for sessions in review, declined or approved" + } + } + } + } + } + }, + "404": { + "description": "No active session with this `sessionId` exists — it was never created, or it has been deleted. Owner resolution happens before credential validation, so this `404` is returned even for unauthenticated requests. Depending on which lookup misses, the `detail` string is `\"Not found.\"` (the common case) or `\"Session not found.\"`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "Not found." + } + } + }, + "examples": { + "Unknown session": { + "summary": "sessionId does not match any KYC or KYB session", + "value": { + "detail": "Not found." + } + }, + "Recently deleted session": { + "summary": "Session was deleted between lookups", + "value": { + "detail": "Session not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. PDF generation has a dedicated limit of **50 requests per minute per credential** (API key, Bearer token, or source IP when unauthenticated), in addition to the global 600/min GET limit. Wait `Retry-After` seconds (also reflected in the `X-RateLimit-*` headers), then retry. Reports are not cached server-side, so download each report once and store the file instead of re-fetching it.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying the request.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Maximum number of PDF generations allowed in the current window (50).", + "schema": { + "type": "integer", + "example": 50 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests remaining in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix timestamp (seconds) at which the current window resets.", + "schema": { + "type": "integer", + "example": 1750000000 + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable explanation of the rate-limit breach.", + "example": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute." + } + } + }, + "examples": { + "Throttled": { + "summary": "More than 50 PDF generations in one minute from the same credential", + "value": { + "detail": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl --fail \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/generate-pdf/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n --output report.pdf" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.get(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/generate-pdf/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n stream=True,\n timeout=60,\n)\nresponse.raise_for_status()\nwith open(\"report.pdf\", \"wb\") as fh:\n for chunk in response.iter_content(chunk_size=8192):\n fh.write(chunk)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import { writeFile } from 'node:fs/promises';\n\nconst response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/generate-pdf/',\n { headers: { 'x-api-key': 'YOUR_API_KEY' } },\n);\nif (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);\nawait writeFile('report.pdf', Buffer.from(await response.arrayBuffer()));" + } + ] + } + }, + "/v3/session/{sessionId}/sandbox/arm/": { + "post": { + "summary": "Arm a sandbox scenario on a session", + "description": "Persist a sandbox scenario on a `Not Started` session and expand its bundled magic values (email, phone, or `expected_details` fields) onto the session, so the mocked providers reproduce the scenario's outcome once the flow runs. This is the endpoint the pre-flow scenario picker in Didit's hosted verification UI calls when a tester chooses an outcome before starting capture. Build your own picker against it, or call it directly from a sandbox test harness.\n\nAuthenticate with the session's own `Session-Token` (returned in `session_token` by [Create Session](/sessions-api/create-session)), not your application `x-api-key`: this endpoint is meant to be called from the client, not your backend. A scenario is only accepted while `status` is `Not Started`. Calling it again while still `Not Started` re-arms the session: it swaps in the new scenario's magic values and clears only the values the *previous* scenario wrote, so any value the user already typed by hand (email, first name, etc.) survives the re-arm. Arming a session that has moved past `Not Started`, or that belongs to a live (non-sandbox) application, is rejected.\n\nSee [Sandbox & Test Data](/integration/sandbox-testing) for the full scenario catalog and the other two ways to drive an outcome: `sandbox_scenario` at session create, and typing magic values directly.", + "operationId": "post_v3_session_sandbox_arm", + "x-didit-sdk": { + "resource": "sessions", + "method": "arm_sandbox", + "auth": "device_token", + "server": false + }, + "tags": [ + "Sessions" + ], + "security": [ + { + "SessionTokenAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "sessionId", + "required": true, + "description": "UUID of the sandbox verification session to arm. Must match the session encoded in the Session-Token used to authenticate.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "scenario" + ], + "properties": { + "scenario": { + "type": "string", + "description": "Slug of the scenario to arm. See the catalog at GET /v1/sandbox/scenarios/ or the scenario table in [Sandbox & Test Data](/integration/sandbox-testing).", + "example": "decline_document_expired" + } + } + }, + "examples": { + "Arm": { + "summary": "Arm the AML PEP-hit scenario", + "value": { + "scenario": "decline_aml_hit" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Scenario armed. Its magic values are now on the session. `status` reflects the session's current status, which this call never changes: it is always `Not Started` on success.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid" + }, + "scenario": { + "type": "string", + "description": "The armed scenario slug (echoes the request)." + }, + "status": { + "type": "string", + "example": "Not Started" + } + } + }, + "examples": { + "Armed": { + "summary": "Scenario armed on a Not Started session", + "value": { + "session_id": "11111111-2222-3333-4444-555555555555", + "scenario": "decline_aml_hit", + "status": "Not Started" + } + } + } + } + } + }, + "400": { + "description": "Validation error: an unknown scenario slug, or the session has already progressed past `Not Started`.", + "content": { + "application/json": { + "examples": { + "Unknown Scenario": { + "summary": "scenario is not in the catalog", + "value": { + "scenario": [ + "Unknown sandbox scenario: 'not-a-real-scenario'." + ] + } + }, + "Already Started": { + "summary": "Session moved past Not Started", + "value": [ + "Scenario can only be armed before the verification starts." + ] + } + } + } + } + }, + "404": { + "description": "The Session-Token does not resolve to this `session_id`, or the session belongs to a live (non-sandbox) application.", + "content": { + "application/json": { + "examples": { + "Not Found": { + "summary": "Live session, or session_id / token mismatch", + "value": { + "detail": "Not found." + } + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/sandbox/arm/ \\\n -H 'Session-Token: YOUR_SESSION_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"scenario\": \"decline_aml_hit\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/sandbox/arm/\",\n headers={\n \"Session-Token\": \"YOUR_SESSION_TOKEN\",\n \"Content-Type\": \"application/json\",\n },\n json={\"scenario\": \"decline_aml_hit\"},\n)\nresponse.raise_for_status()\nprint(response.json()) # {'session_id': '...', 'scenario': 'decline_aml_hit', 'status': 'Not Started'}" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/sandbox/arm/',\n {\n method: 'POST',\n headers: {\n 'Session-Token': sessionToken,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ scenario: 'decline_aml_hit' }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst { session_id, scenario, status } = await response.json();" + } + ] + } + }, + "/v3/session/{sessionId}/update-status/": { + "patch": { + "summary": "Approve, decline, or request resubmission for a verification session", + "description": "Manually override the final decision of a KYC or KYB verification session. Set `new_status` to `Approved` or `Declined` to record a manual decision, or to `Resubmitted` to clear specific verification steps and let the user redo them through the same verification URL. The endpoint accepts both user (KYC) and business (KYB) session IDs.\n\nCall it once a session has reached a reviewable state: the current status must be `Approved`, `Declined`, `In Review`, `Kyc Expired`, `Abandoned`, or `Resubmitted`. Sessions that are still `Not Started`, `In Progress`, or `Awaiting User`, or that ended as `Expired`, return `400`. Your API key needs the `write:sessions` permission (a scoped API key needs write access to decisions). The eligible-state check runs before body validation, so an ineligible session returns the `Wrong Current Status` error even when the payload is also invalid.\n\nFor `Resubmitted`, pass `nodes_to_resubmit` to choose which workflow steps the user must redo, or omit it to auto-select existing feature attempts in a resubmittable state (`Declined`, `In Review`, `Not Finished`, `Expired`). Never-attempted features are not auto-selected: list them explicitly in `nodes_to_resubmit`, or the request returns `400` when no recorded attempt needs resubmission. The selected steps' previous results move to the resubmission's history (returned by Get Decision with `include=resubmissions`), the steps are reordered to match the workflow graph, the session's expiration window restarts, and the original verification URL becomes usable again. Backend-only steps (`AML`, `DATABASE_VALIDATION`, `IP_ANALYSIS`) at the head of the list run immediately without user interaction; if every resubmitted step is backend-only, the session re-finalizes within the same request. `KYB_REGISTRY` and `KYB_KEY_PEOPLE` cannot be resubmitted directly: resubmit the relevant child KYC sessions instead and the parent business session recomputes automatically.\n\nEvery call that changes the status fires the `status.updated` [webhook](/integration/webhooks) once the change commits and appends an activity entry (with actor attribution and your `comment`) to the session's `reviews`, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and in the console activity timeline. The user-profile aggregates linked to the session's `vendor_data` update as well. Declining disables ongoing AML monitoring for the session; re-approving re-enables it (from `Resubmitted`, `Declined`, or `Kyc Expired`) when the workflow has monitoring turned on. Approving or declining a child KYC that belongs to a KYB key-people check recomputes the parent business session's status. Set `send_email: true` (with `email_address`) to notify the user: a status notice for `Approved`/`Declined`, or a resubmission email containing the verification link and per-step reasons for `Resubmitted`. To check the email first, preview it with [Preview Decision Email](/sessions-api/preview-decision-email) (`Approved`, `Declined`) or [Preview Resubmission Email](/sessions-api/preview-resubmission) (`Resubmitted`) and pass its `preview_token` as `email_preview_token`: the update then sends exactly the previewed email, or returns `409` before changing anything when the email or the session changed since the preview or the receipt is older than 15 minutes.\n\nRepeating `Approved` or `Declined` on a session that already has that status changes nothing and returns `200`: no webhook, no activity entry and no email. Repeating `Resubmitted` returns `400` (`The new status is the same as the current status.`). Missing or invalid credentials and missing permissions return `403`; an expired scoped API key returns `401`.", + "operationId": "patch_v3_session_update_status", + "x-didit-sdk": { + "resource": "sessions", + "method": "update_status", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "sessionId", + "required": true, + "description": "UUID of the verification session to update. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "new_status" + ], + "properties": { + "new_status": { + "type": "string", + "description": "Target status. `Approved` and `Declined` record a final manual decision (each can also overturn the other). `Resubmitted` clears the selected steps and sends the session back to the user. Any other value returns `400`.", + "enum": [ + "Approved", + "Declined", + "Resubmitted" + ], + "example": "Approved" + }, + "comment": { + "type": "string", + "description": "Free-text reason for the change, stored on the session's review trail and returned in the `reviews` array of [Get Decision](#get-/v3/session/-sessionId-/decision/). For example `Duplicated user`.", + "example": "All checks passed manual review" + }, + "nodes_to_resubmit": { + "type": "array", + "description": "Workflow steps the user must redo. Only acted on when `new_status` is `Resubmitted`. For `Approved`/`Declined` the entries are still schema-validated (an invalid `feature` value returns `400`) but schema-valid entries are semantically ignored. If omitted, the server auto-selects existing OCR, Liveness, Face Match, POA, Phone, Email, AML, Database Validation, and Questionnaire attempts whose status is `Declined`, `In Review`, `Not Finished`, or `Expired` (NFC, IP analysis, age estimation, face search, and KYB document attempts are never auto-selected; non-face-match face attempts are selected as `LIVENESS`) — features the user never attempted (no recorded attempt) are NOT selected, so a session with zero attempts returns `400` (\"No features found that need resubmission\") even though nothing was approved; pass `nodes_to_resubmit` explicitly in that case. Steps are executed in workflow-graph order regardless of the order you send them. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and the `KYB` alias are rejected with `400` — those parent checks recompute from their child KYC sessions.", + "items": { + "type": "object", + "required": [ + "node_id", + "feature" + ], + "properties": { + "node_id": { + "type": "string", + "description": "Node identifier as it appears in the session's workflow definition (for example `feature_ocr`, `feature_liveness`).", + "example": "feature_ocr" + }, + "feature": { + "type": "string", + "description": "Feature type of the node. The aliases `ID_VERIFICATION`, `POA`, `PHONE`, and `EMAIL` are normalized server-side to `OCR`, `PROOF_OF_ADDRESS`, `PHONE_VERIFICATION`, and `EMAIL_VERIFICATION`. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and `KYB` pass schema validation but are always rejected with the business-rule `400` shown in the examples.", + "enum": [ + "OCR", + "OCR_BACK", + "NFC", + "AML", + "FACE", + "LIVENESS", + "FACE_MATCH", + "IP_ANALYSIS", + "AGE_ESTIMATION", + "PROOF_OF_ADDRESS", + "PHONE_VERIFICATION", + "EMAIL_VERIFICATION", + "FACE_SEARCH", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_DOCUMENTS", + "ID_VERIFICATION", + "POA", + "PHONE", + "EMAIL", + "KYB_REGISTRY", + "KYB_KEY_PEOPLE", + "KYB" + ], + "example": "OCR" + } + } + } + }, + "send_email": { + "type": "boolean", + "description": "Whether to email the user about the change. Requires `email_address`. For `Approved`/`Declined` the user receives a status notice; for `Resubmitted` the email includes the verification link and the per-step resubmission reasons.", + "default": false, + "example": false + }, + "email_address": { + "type": "string", + "format": "email", + "description": "Recipient for the notification email. **Required when `send_email` is `true`** — omitting it returns `400`.", + "example": "user@example.com" + }, + "email_language": { + "type": "string", + "description": "Language for the notification email. Accepts any string at schema level; unsupported codes silently fall back to English (`en`).", + "default": "en", + "example": "en" + }, + "email_preview_token": { + "type": "string", + "writeOnly": true, + "minLength": 1, + "maxLength": 512, + "description": "Optional receipt from [Preview Decision Email](/sessions-api/preview-decision-email) (for `Approved` and `Declined`) or [Preview Resubmission Email](/sessions-api/preview-resubmission) (for `Resubmitted`): its `preview_token`. Valid only with `send_email: true`; without it the request returns `400`. The recipient, language, session status, sender and email (and, for a resubmission, the selected steps) must still match the preview, and the receipt expires after 15 minutes. Otherwise the request returns `409` before the session changes or an email is sent. Existing integrations may omit this field." + } + } + }, + "examples": { + "Approve Session": { + "summary": "Approve a session after manual review", + "value": { + "new_status": "Approved", + "comment": "All checks passed manual review" + } + }, + "Decline Session": { + "summary": "Decline a session", + "value": { + "new_status": "Declined", + "comment": "Suspected fraud" + } + }, + "Resubmit Specific Steps": { + "summary": "Request resubmission of chosen steps, with email", + "value": { + "new_status": "Resubmitted", + "nodes_to_resubmit": [ + { + "node_id": "feature_ocr", + "feature": "OCR" + }, + { + "node_id": "feature_liveness", + "feature": "LIVENESS" + } + ], + "send_email": true, + "email_address": "user@example.com", + "email_language": "en" + } + }, + "Resubmit Auto-select": { + "summary": "Request resubmission (auto-select non-approved steps)", + "value": { + "new_status": "Resubmitted" + } + }, + "Approve With Previewed Email": { + "summary": "Approve and send the email returned by Preview Decision Email", + "value": { + "new_status": "Approved", + "send_email": true, + "email_address": "person@example.com", + "email_language": "en", + "email_preview_token": "eyJkaWdlc3QiOiI0ZjJhIn0:1uExAm:Pl3x4mpleS1gnatureValue000000000000000" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Status updated. The response contains only the `session_id`; fetch the updated session via [Get Decision](#get-/v3/session/-sessionId-/decision/). The `status.updated` webhook fires once the change commits. Repeating `Approved` or `Declined` returns the same body and changes nothing.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "description": "UUID of the updated session." + } + } + }, + "examples": { + "Updated": { + "summary": "Returned for Approved, Declined, and Resubmitted alike", + "value": { + "session_id": "3472fb7c-8f7c-4d1a-9cf4-cf3d74ce1a60" + } + } + } + } + } + }, + "400": { + "description": "Validation error. Field errors arrive as `{\"\": [\"\"]}` (some business-rule rejections use a string value instead: `{\"nodes_to_resubmit\": \"\"}`), cross-state errors as `{\"detail\": \"\"}`, and the same-status error as a bare JSON array.", + "content": { + "application/json": { + "examples": { + "Wrong Current Status": { + "summary": "Session is not in an updatable state", + "value": { + "detail": "Only sessions with status Approved, Declined, In Review, Kyc Expired, Abandoned, Resubmitted can be updated. Current status: In Progress" + } + }, + "Invalid Status Value": { + "summary": "new_status is a real session status but not one of Approved/Declined/Resubmitted", + "value": { + "new_status": [ + "You can only update the status to 'Approved', 'Declined', or 'Resubmitted'." + ] + } + }, + "Missing new_status": { + "summary": "Required field omitted", + "value": { + "new_status": [ + "This field is required." + ] + } + }, + "Same Status": { + "summary": "Resubmitted requested on a session that is already Resubmitted", + "value": [ + "The new status is the same as the current status." + ] + }, + "Email Address Required": { + "summary": "send_email true without email_address", + "value": { + "email_address": [ + "Email address is required when send_email is true." + ] + } + }, + "Non-resubmittable Feature": { + "summary": "Resubmit targets a KYB parent check", + "value": { + "nodes_to_resubmit": "The following features cannot be resubmitted directly: KYB_REGISTRY. Resubmit the relevant child KYC sessions instead; the parent will recompute automatically." + } + }, + "Nothing To Resubmit": { + "summary": "Auto-select found no non-approved features", + "value": { + "nodes_to_resubmit": "No features found that need resubmission. All features are already approved." + } + }, + "Invalid Feature Choice": { + "summary": "Unknown feature in nodes_to_resubmit", + "value": { + "nodes_to_resubmit": [ + { + "feature": [ + "\"BOGUS\" is not a valid choice. Valid choices are: OCR, OCR_BACK, NFC, AML, FACE, LIVENESS, FACE_MATCH, IP_ANALYSIS, AGE_ESTIMATION, PROOF_OF_ADDRESS, PHONE_VERIFICATION, EMAIL_VERIFICATION, FACE_SEARCH, DATABASE_VALIDATION, QUESTIONNAIRE, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE, ID_VERIFICATION, POA, PHONE, EMAIL, KYB" + ] + } + ] + } + }, + "Invalid Status Value (not a status at all)": { + "summary": "new_status is not a valid StatusChoices value", + "value": { + "new_status": [ + "\"Bogus\" is not a valid choice." + ] + } + }, + "Preview Receipt Without Email": { + "summary": "email_preview_token sent without send_email: true", + "value": { + "email_preview_token": [ + "A preview receipt is only valid when the email is sent." + ] + } + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired.", + "content": { + "application/json": { + "examples": { + "Expired API Key": { + "summary": "Scoped API key past its expiry date", + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "Missing/invalid credentials or insufficient permissions, or a `session_id` that belongs to another application.", + "content": { + "application/json": { + "examples": { + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Missing Privilege": { + "summary": "API key lacks write:sessions", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No KYC or KYB session with this `session_id` exists (deleted sessions also return `404`).", + "content": { + "application/json": { + "examples": { + "Not Found": { + "summary": "Unknown session_id", + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "A resubmit targeting only backend-only features (AML, DATABASE_VALIDATION, IP_ANALYSIS) was repeated within the 30-second cooldown window; an immediate repeat cannot change the outcome. Back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Backend-only resubmit throttled": { + "value": { + "detail": "This session was already resubmitted for backend-only features within the last 30 seconds; repeating it cannot change the outcome. Wait before resubmitting again." + } + } + } + } + } + }, + "409": { + "description": "The email preview changed or expired. Generate a new preview before confirming; no session changes or email delivery occur.", + "content": { + "application/json": { + "examples": { + "Preview Changed": { + "summary": "The receipt no longer matches, or is older than 15 minutes", + "value": { + "detail": "This email preview changed or expired. Preview the message again before confirming." + } + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-status/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"new_status\": \"Approved\",\n \"comment\": \"All checks passed manual review\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-status/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\n \"new_status\": \"Resubmitted\",\n \"nodes_to_resubmit\": [\n {\"node_id\": \"feature_ocr\", \"feature\": \"OCR\"},\n {\"node_id\": \"feature_liveness\", \"feature\": \"LIVENESS\"},\n ],\n \"send_email\": True,\n \"email_address\": \"user@example.com\",\n \"email_language\": \"en\",\n },\n)\nresponse.raise_for_status()\nprint(response.json()) # {'session_id': '11111111-2222-3333-4444-555555555555'}" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-status/',\n {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n new_status: 'Declined',\n comment: 'Suspected fraud',\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst { session_id } = await response.json();" + } + ] + } + }, + "/v3/id-verification/": { + "post": { + "summary": "ID Verification (document OCR + fraud checks)", + "description": "OCR an identity document and run document fraud checks in one call — returns the extracted holder fields, the parsed MRZ, an `Approved`/`Declined` `status`, and a `warnings` list explaining every issue found. Supports 14,000+ document types from 220+ countries and territories.\n\n**How it works.** Send the document's `front_image` (required) and, for two-sided documents, the `back_image`. Upload uncropped photos with all four corners of the document visible — the service detects, aligns, and crops the document itself, so do not pre-crop. The document is classified automatically (you never declare the country or document type), the visual zone is read with OCR, and the MRZ and any barcodes are decoded. Cross-checks then validate dates, number formats, and MRZ check digits, and compare the visual zone against the MRZ. With `perform_document_liveness=true`, the images are additionally screened for screen replays, printed copies, and portrait manipulation.\n\n**Decision logic.** `status` is `Approved` unless at least one warning resolves to a decline. Fraud and hard-failure risks always decline: `DOCUMENT_EXPIRED`, `SCREEN_CAPTURE_DETECTED`, `PRINTED_COPY_DETECTED`, `PORTRAIT_MANIPULATION_DETECTED`, `PUBLIC_DOCUMENT_IMAGE_DETECTED`, plus extraction failures (`NAME_NOT_DETECTED`, `DATE_OF_BIRTH_NOT_DETECTED`, `DOCUMENT_NUMBER_NOT_DETECTED`). (`PORTRAIT_IMAGE_NOT_DETECTED`, `COULD_NOT_DETECT_DOCUMENT_TYPE`, `INVALID_DATE`, and `MRZ_NOT_DETECTED` exist in workflow sessions but are never produced by this standalone endpoint.) Three risk groups are configurable per request via `invalid_mrz_action`, `inconsistent_data_action`, and `expiration_date_not_detected_action` (`DECLINE` or `NO_ACTION`). All other warnings (e.g. `POSSIBLE_DUPLICATED_USER`) are informational and never decline on their own. A document that cannot be processed at all returns `400` with `{\"error\": \"COULD_NOT_RECOGNIZE_DOCUMENT\"}`; a readable but problematic document returns `200` with `status: \"Declined\"` — always inspect `id_verification.status` and `id_verification.warnings`, not just the HTTP code.\n\n**Billing.** Each `200` response consumes one ID Verification API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, the cropped document/portrait images are stored and returned as short-lived media URLs (`https:///ocr/...`), and a `status.updated` webhook is emitted to your configured webhook endpoints. When `false`, nothing is stored, `request_id` is a one-off correlation UUID, and `portrait_image`/`front_image`/`back_image` are returned inline as base64-encoded JPEG strings.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload (a fictional \"Sandbox User\" USA identity card), no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", + "operationId": "post_v3id-verification", + "x-didit-sdk": { + "resource": "id_verification", + "method": "verify", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "front_image" + ], + "properties": { + "front_image": { + "type": "string", + "format": "binary", + "description": "Front side of the identity document. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`, `pdf`. Maximum upload size: **10 MB** (larger files are rejected with `400`). PDFs are rendered to an image before OCR; encrypted PDFs require `front_image_password`. Images are automatically compressed to ~0.5 MB and EXIF orientation is applied. Upload the full, uncropped capture with all four corners of the document visible." + }, + "front_image_password": { + "type": "string", + "description": "Password to decrypt `front_image` when it is an encrypted PDF. Sending an encrypted PDF without (or with a wrong) password returns `400`.", + "writeOnly": true + }, + "back_image": { + "type": "string", + "format": "binary", + "description": "Back side of the document — send it whenever the document has one (many ID cards carry the MRZ or a barcode on the back; omitting it simply leaves the MRZ and back-side fields empty in the response — this endpoint does not raise a missing-MRZ warning). Same format and size limits as `front_image`. When omitted, `back_image` and back-side fields are `null` in the response." + }, + "back_image_password": { + "type": "string", + "description": "Password to decrypt `back_image` when it is an encrypted PDF.", + "writeOnly": true + }, + "perform_document_liveness": { + "type": "boolean", + "default": true, + "description": "Screens both images for presentation fraud: screen replays (`SCREEN_CAPTURE_DETECTED`), printed copies (`PRINTED_COPY_DETECTED`), and portrait manipulation (`PORTRAIT_MANIPULATION_DETECTED`). Any of these auto-declines. Enabled by default; set to `false` to skip fraud screening and reduce latency.", + "example": true + }, + "minimum_age": { + "type": "integer", + "nullable": true, + "default": null, + "minimum": 1, + "maximum": 120, + "description": "Accepted and validated (1–120) but **currently not applied** by this endpoint — it never produces a `MINIMUM_AGE_NOT_MET` warning from this field. Enforce age rules from the returned `date_of_birth`/`age`, or use a verification-session workflow with age restrictions." + }, + "expiration_date_not_detected_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "What to do when no expiration date can be read (`EXPIRATION_DATE_NOT_DETECTED`). Default `NO_ACTION` because many identity documents print no expiry date. Note: a *detected and past* expiry date always declines (`DOCUMENT_EXPIRED`), regardless of this option." + }, + "invalid_mrz_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "DECLINE", + "description": "What to do when the extracted MRZ fails check-digit validation (`MRZ_VALIDATION_FAILED`). A missing MRZ raises no warning on this endpoint. Only relevant for documents that carry an MRZ. `REVIEW` is not supported on this endpoint and returns `400`." + }, + "inconsistent_data_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "DECLINE", + "description": "What to do when extracted data is internally inconsistent: `DATA_INCONSISTENT`, `MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAME` (visual zone disagrees with the MRZ), or `DOCUMENT_NAME_DIFFERENT_FROM_OTHER_APPROVED_DOCUMENTS`." + }, + "critical_field_occlusion_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "DECLINE", + "description": "What to do when a critical printed field was physically covered on the capture (`CRITICAL_FIELD_OCCLUSION_DETECTED`) - an identity number, a name, the date of birth, or the expiry date. Declines by default, like this endpoint's other document-integrity risks: covering the numbers a report is reconciled against is what a redacted document looks like. The value read for a covered field is dropped from the extraction either way, so the matching `*_NOT_DETECTED` warning usually accompanies it. `REVIEW` is not supported on this endpoint and returns `400`." + }, + "document_or_personal_number_format_mismatch_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "What to do when a number does not match the format its issuing authority uses for that document type (`DOCUMENT_NUMBER_FORMAT_MISMATCH`, `PERSONAL_NUMBER_FORMAT_MISMATCH`). Default `NO_ACTION`, which reports the warning without changing the status. `REVIEW` is not supported on this endpoint and returns `400`." + }, + "preferred_characters": { + "type": "string", + "enum": [ + "latin", + "non_latin" + ], + "default": "latin", + "description": "Preferred script for name/address fields on documents that print both Latin and non-Latin text (Arabic, Cyrillic, CJK, …). `latin` returns transliterated/Latin values; `non_latin` prefers the native script." + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, announced through a `status.updated` webhook, and with document images returned as media URLs. When `false`, nothing is stored, `request_id` is a transient UUID, and images are returned inline as base64 JPEG strings.", + "example": true + }, + "vendor_data": { + "type": "string", + "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", + "example": "user-123" + }, + "metadata": { + "type": "object", + "additionalProperties": true, + "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"onboarding\"}`) — it is parsed into an object.", + "example": { + "flow": "onboarding" + } + } + } + }, + "example": { + "front_image": "(binary JPEG/PNG/PDF of the document front)", + "back_image": "(binary JPEG/PNG/PDF of the document back)", + "perform_document_liveness": true, + "save_api_request": true, + "vendor_data": "user-123", + "metadata": { + "flow": "onboarding" + } + } + } + } + }, + "responses": { + "200": { + "description": "Document processed. `id_verification.status` is `Approved` or `Declined`; every detected issue is itemized in `id_verification.warnings`. A problematic document still returns `200` with `status: \"Declined\"` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id and image fields are short-lived media URLs; with `save_api_request=false` they are inline base64 JPEG strings.", + "content": { + "application/json": { + "examples": { + "Approved": { + "summary": "Two-sided ID card read cleanly (saved — images as media URLs)", + "value": { + "request_id": "11d219ed-d59c-4b1d-8d65-9b933f12d5b8", + "id_verification": { + "status": "Approved", + "document_type": "Identity Card", + "document_subtype": "ID_CARD_GENERIC", + "document_number": "CBX164224", + "personal_number": "20446581H", + "portrait_image": "https:///ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-portrait_image-8c2f.jpg?signature=...", + "front_image": "https:///ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-front_image-4a1e.jpg?signature=...", + "back_image": "https:///ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-back_image-77b0.jpg?signature=...", + "front_image_camera_front": null, + "back_image_camera_front": null, + "front_image_camera_front_face_match_score": null, + "back_image_camera_front_face_match_score": null, + "front_image_quality_score": { + "focus_score": 78.0, + "brightness_score": 83.2, + "brightness_issue": "ok", + "is_document_fully_visible": true, + "resolution_score": 40.7, + "overall_score": 70.2 + }, + "back_image_quality_score": { + "focus_score": 100.0, + "brightness_score": 95.6, + "brightness_issue": "ok", + "is_document_fully_visible": true, + "resolution_score": 43.0, + "overall_score": 84.4 + }, + "date_of_birth": "1980-01-13", + "age": 46, + "expiration_date": "2032-03-31", + "date_of_issue": "2022-03-31", + "issuing_state": "ESP", + "issuing_state_name": "Spain", + "first_name": "Julio Francisco", + "last_name": "Fores Sena", + "full_name": "Julio Francisco Fores Sena", + "gender": "M", + "address": "Brda. Urb. El Cardonal 0052 52 Po3 B,Taco,San Cristobal De La Laguna,Santa Cruz De Tenerife", + "formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain", + "place_of_birth": "Valencia, Valencia", + "marital_status": "UNKNOWN", + "nationality": "ESP", + "extra_fields": { + "first_surname": "Fores", + "second_surname": "Sena" + }, + "mrz": { + "surname": "FORES SENA", + "name": "JULIO FRANCISCO", + "country": "ESP", + "nationality": "ESP", + "birth_date": "800113", + "expiry_date": "320331", + "sex": "M", + "document_type": "ID", + "document_number": "CBX164224", + "optional_data": "20446581H", + "optional_data_2": "", + "birth_date_hash": "9", + "expiry_date_hash": "8", + "document_number_hash": "3", + "final_hash": "3", + "personal_number": "20446581H", + "warnings": [], + "errors": [], + "mrz_type": "TD1", + "mrz_string": "IDESPCBX164224320446581H<<<<<<\n8001139M3203318ESP<<<<<<<<<<<3\nFORES/ocr/f61681b8-9422-4378-ac3e-04c6a247fb45-portrait_image-1d9a.jpg?signature=...", + "front_image": "https:///ocr/f61681b8-9422-4378-ac3e-04c6a247fb45-front_image-5c2b.jpg?signature=...", + "back_image": null, + "front_image_camera_front": null, + "back_image_camera_front": null, + "front_image_camera_front_face_match_score": null, + "back_image_camera_front_face_match_score": null, + "front_image_quality_score": { + "focus_score": 100.0, + "brightness_score": 95.7, + "brightness_issue": "ok", + "is_document_fully_visible": true, + "resolution_score": 22.2, + "overall_score": 79.3 + }, + "back_image_quality_score": null, + "date_of_birth": "1997-01-30", + "age": 29, + "expiration_date": "2025-11-26", + "date_of_issue": null, + "issuing_state": "ESP", + "issuing_state_name": "Spain", + "first_name": "Lucia", + "last_name": "Marin Castro", + "full_name": "Lucia Marin Castro", + "gender": "F", + "address": null, + "formatted_address": null, + "place_of_birth": null, + "marital_status": "UNKNOWN", + "nationality": "ESP", + "extra_fields": { + "first_surname": "Marin", + "second_surname": "Castro" + }, + "mrz": {}, + "parsed_address": null, + "warnings": [ + { + "risk": "DOCUMENT_EXPIRED", + "feature": "ID_VERIFICATION", + "additional_data": null, + "log_type": "error", + "short_description": "Document expired", + "long_description": "The document's expiration date has passed, rendering it no longer valid for use." + } + ], + "barcodes": [] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T02:25:48.117204+00:00" + } + } + }, + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + }, + "id_verification": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined" + ], + "description": "`Approved` when no warning resolves to a decline; `Declined` otherwise — the `warnings` list explains why." + }, + "document_type": { + "type": "string", + "nullable": true, + "description": "Detected document type display name: `Identity Card`, `Passport`, `Driver's License`, `Residence Permit`, `Health Insurance Card`, or `Tax Card`. `null` when the document could not be classified.", + "example": "Identity Card" + }, + "document_subtype": { + "type": "string", + "nullable": true, + "description": "Finer-grained subtype when available (e.g. `ID_CARD_GENERIC`, `EPASSPORT`).", + "example": "ID_CARD_GENERIC" + }, + "document_number": { + "type": "string", + "nullable": true, + "example": "CBX164224" + }, + "personal_number": { + "type": "string", + "nullable": true, + "description": "Secondary personal/national number printed on some documents.", + "example": "20446581H" + }, + "portrait_image": { + "type": "string", + "nullable": true, + "description": "Cropped portrait photo from the document. A short-lived media URL (`https:///ocr/...`) when `save_api_request=true` (default); an inline base64-encoded JPEG string when `save_api_request=false`. `null` when no portrait was detected." + }, + "front_image": { + "type": "string", + "nullable": true, + "description": "Aligned crop of the document front. Media URL when saved, inline base64 JPEG otherwise." + }, + "back_image": { + "type": "string", + "nullable": true, + "description": "Aligned crop of the document back. `null` when no `back_image` was uploaded." + }, + "front_image_camera_front": { + "type": "string", + "nullable": true, + "description": "Always `null` on this endpoint (populated only in camera-capture verification sessions)." + }, + "back_image_camera_front": { + "type": "string", + "nullable": true, + "description": "Always `null` on this endpoint." + }, + "front_image_camera_front_face_match_score": { + "type": "number", + "nullable": true, + "description": "Always `null` on this endpoint." + }, + "back_image_camera_front_face_match_score": { + "type": "number", + "nullable": true, + "description": "Always `null` on this endpoint." + }, + "front_image_quality_score": { + "type": "object", + "nullable": true, + "description": "Capture-quality diagnostics for the uploaded image (`null` when the side was not provided or scoring failed). All scores are 0–100.", + "properties": { + "focus_score": { + "type": "number", + "format": "float", + "description": "Sharpness of the document crop (higher is sharper)." + }, + "brightness_score": { + "type": "number", + "format": "float", + "description": "Exposure quality (higher is better)." + }, + "brightness_issue": { + "type": "string", + "nullable": true, + "description": "`ok` when exposure is fine, otherwise the detected issue (e.g. too dark / too bright)." + }, + "is_document_fully_visible": { + "type": "boolean", + "nullable": true, + "description": "Whether all four corners of the document are inside the frame." + }, + "resolution_score": { + "type": "number", + "format": "float", + "description": "Effective resolution of the document area." + }, + "overall_score": { + "type": "number", + "format": "float", + "description": "Aggregate quality score." + } + } + }, + "back_image_quality_score": { + "type": "object", + "nullable": true, + "description": "Capture-quality diagnostics for the uploaded image (`null` when the side was not provided or scoring failed). All scores are 0–100.", + "properties": { + "focus_score": { + "type": "number", + "format": "float", + "description": "Sharpness of the document crop (higher is sharper)." + }, + "brightness_score": { + "type": "number", + "format": "float", + "description": "Exposure quality (higher is better)." + }, + "brightness_issue": { + "type": "string", + "nullable": true, + "description": "`ok` when exposure is fine, otherwise the detected issue (e.g. too dark / too bright)." + }, + "is_document_fully_visible": { + "type": "boolean", + "nullable": true, + "description": "Whether all four corners of the document are inside the frame." + }, + "resolution_score": { + "type": "number", + "format": "float", + "description": "Effective resolution of the document area." + }, + "overall_score": { + "type": "number", + "format": "float", + "description": "Aggregate quality score." + } + } + }, + "date_of_birth": { + "type": "string", + "format": "date", + "nullable": true, + "example": "1980-01-13" + }, + "age": { + "type": "integer", + "nullable": true, + "description": "Holder age in years, computed from `date_of_birth`.", + "example": 46 + }, + "expiration_date": { + "type": "string", + "format": "date", + "nullable": true, + "example": "2032-03-31" + }, + "date_of_issue": { + "type": "string", + "format": "date", + "nullable": true, + "example": "2022-03-31" + }, + "issuing_state": { + "type": "string", + "nullable": true, + "description": "Issuing country (ISO 3166-1 alpha-3).", + "example": "ESP" + }, + "issuing_state_name": { + "type": "string", + "nullable": true, + "example": "Spain" + }, + "first_name": { + "type": "string", + "nullable": true, + "example": "Julio Francisco" + }, + "last_name": { + "type": "string", + "nullable": true, + "example": "Fores Sena" + }, + "full_name": { + "type": "string", + "nullable": true, + "example": "Julio Francisco Fores Sena" + }, + "gender": { + "type": "string", + "nullable": true, + "description": "`M`, `F`, or `null` when not printed/detected.", + "example": "M" + }, + "address": { + "type": "string", + "nullable": true, + "description": "Address exactly as printed on the document." + }, + "formatted_address": { + "type": "string", + "nullable": true, + "description": "Geocoded, normalized version of `address`." + }, + "place_of_birth": { + "type": "string", + "nullable": true, + "example": "Valencia, Valencia" + }, + "marital_status": { + "type": "string", + "nullable": true, + "description": "`UNKNOWN` unless the document prints marital status.", + "example": "UNKNOWN" + }, + "nationality": { + "type": "string", + "nullable": true, + "description": "Holder nationality (ISO 3166-1 alpha-3).", + "example": "ESP" + }, + "extra_fields": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Document-specific extras that have no dedicated column (e.g. `first_surname`/`second_surname` on Spanish IDs, license classes on driving licenses)." + }, + "mrz": { + "type": "object", + "description": "Parsed machine-readable zone. An **empty object** (`{}`) when the document has no MRZ or it could not be read — also check the `warnings` list in that case. Dates inside the MRZ use the raw `YYMMDD` format.", + "properties": { + "surname": { + "type": "string", + "example": "FORES SENA" + }, + "name": { + "type": "string", + "example": "JULIO FRANCISCO" + }, + "country": { + "type": "string", + "description": "Issuing country (ISO 3166-1 alpha-3).", + "example": "ESP" + }, + "nationality": { + "type": "string", + "description": "Holder nationality (ISO 3166-1 alpha-3).", + "example": "ESP" + }, + "birth_date": { + "type": "string", + "description": "Raw MRZ date of birth (`YYMMDD`).", + "example": "800113" + }, + "expiry_date": { + "type": "string", + "description": "Raw MRZ expiry date (`YYMMDD`).", + "example": "320331" + }, + "sex": { + "type": "string", + "example": "M" + }, + "document_type": { + "type": "string", + "description": "MRZ document-type code (e.g. `ID`, `P`).", + "example": "ID" + }, + "document_number": { + "type": "string", + "example": "CBX164224" + }, + "optional_data": { + "type": "string", + "nullable": true, + "example": "20446581H" + }, + "optional_data_2": { + "type": "string", + "nullable": true, + "example": "" + }, + "birth_date_hash": { + "type": "string", + "description": "MRZ check digit for the birth date.", + "example": "9" + }, + "expiry_date_hash": { + "type": "string", + "description": "MRZ check digit for the expiry date.", + "example": "8" + }, + "document_number_hash": { + "type": "string", + "description": "MRZ check digit for the document number.", + "example": "3" + }, + "final_hash": { + "type": "string", + "description": "Composite MRZ check digit.", + "example": "3" + }, + "personal_number": { + "type": "string", + "nullable": true, + "example": "20446581H" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Non-fatal MRZ parsing warnings." + }, + "errors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "MRZ check-digit/parsing errors. Non-empty errors surface as an `MRZ_VALIDATION_FAILED` warning." + }, + "mrz_type": { + "type": "string", + "description": "MRZ layout: `TD1` (ID cards, 3 lines), `TD2`, or `TD3` (passports, 2 lines).", + "example": "TD1" + }, + "mrz_string": { + "type": "string", + "description": "Raw MRZ text as read, lines separated by `\\n`." + }, + "mrz_key": { + "type": "string", + "description": "Composite key (document number + check digits) used for cross-checks." + } + } + }, + "parsed_address": { + "type": "object", + "nullable": true, + "description": "Structured, geocoded breakdown of `address`. `null` when the document carries no address or it could not be parsed.", + "properties": { + "street_1": { + "type": "string", + "nullable": true, + "example": "Avenida el Cardonal 52" + }, + "street_2": { + "type": "string", + "nullable": true, + "example": "b" + }, + "city": { + "type": "string", + "nullable": true, + "example": "La Laguna" + }, + "region": { + "type": "string", + "nullable": true, + "example": "Canarias" + }, + "country": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country code.", + "example": "ES" + }, + "postal_code": { + "type": "string", + "nullable": true, + "example": "38108" + }, + "address_type": { + "type": "string", + "nullable": true, + "example": "Avenida" + }, + "formatted_address": { + "type": "string", + "nullable": true, + "description": "Canonical geocoded address string.", + "example": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain" + }, + "raw_results": { + "type": "object", + "additionalProperties": true, + "description": "Raw geocoding payload (`address_components`, `geometry.location`, `place_id`, …) for advanced consumers." + }, + "document_location": { + "type": "object", + "nullable": true, + "properties": { + "latitude": { + "type": "number" + }, + "longitude": { + "type": "number" + } + }, + "description": "Geocoded coordinates of the document address." + }, + "label": { + "type": "string", + "nullable": true, + "description": "Which document field the address was read from.", + "example": "Spain Identity Card Address" + }, + "is_verified": { + "type": "boolean", + "description": "Whether geocoding confirmed the address exists." + }, + "category": { + "type": "string", + "nullable": true, + "description": "Address category (e.g. `Residential`, `Commercial`).", + "example": "Residential" + } + } + }, + "warnings": { + "type": "array", + "description": "Empty on a clean approval. Every entry explains one detected risk; entries with `log_type: \"error\"` set `status` to `Declined`.", + "items": { + "type": "object", + "properties": { + "risk": { + "type": "string", + "description": "Machine-readable risk code. Always-decline risks: `DOCUMENT_EXPIRED`, `MINIMUM_AGE_NOT_MET`, `PORTRAIT_IMAGE_NOT_DETECTED`, `SCREEN_CAPTURE_DETECTED`, `PRINTED_COPY_DETECTED`, `PORTRAIT_MANIPULATION_DETECTED`, `PUBLIC_DOCUMENT_IMAGE_DETECTED`, `INVALID_DATE`, `COULD_NOT_RECOGNIZE_DOCUMENT` (400-only, never a warning), `DOCUMENT_NUMBER_NOT_DETECTED`, `COULD_NOT_DETECT_DOCUMENT_TYPE`, `NAME_NOT_DETECTED`, `DATE_OF_BIRTH_NOT_DETECTED`. Governed by request options: `MRZ_NOT_DETECTED` / `MRZ_VALIDATION_FAILED` (`invalid_mrz_action`), `DATA_INCONSISTENT` / `MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAME` / `DOCUMENT_NAME_DIFFERENT_FROM_OTHER_APPROVED_DOCUMENTS` (`inconsistent_data_action`), `EXPIRATION_DATE_NOT_DETECTED` (`expiration_date_not_detected_action`). Other risks (e.g. `POSSIBLE_DUPLICATED_USER`) are informational.", + "example": "DOCUMENT_EXPIRED" + }, + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION" + ], + "description": "Feature that raised the warning. Always `ID_VERIFICATION` on this endpoint." + }, + "additional_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Extra context for some risks (e.g. `POSSIBLE_DUPLICATED_USER` includes `duplicated_session_id`); `null` for most." + }, + "log_type": { + "type": "string", + "enum": [ + "error", + "warning", + "information" + ], + "description": "Severity. `error` warnings drive `status` to `Declined`; `information` entries are advisory and never decline on their own. (`warning` is not produced by this endpoint — its options only accept `DECLINE` or `NO_ACTION`.)" + }, + "short_description": { + "type": "string", + "description": "Human-readable one-line summary of the risk." + }, + "long_description": { + "type": "string", + "description": "Human-readable explanation of the risk." + } + } + } + }, + "barcodes": { + "type": "array", + "description": "Barcodes decoded from the document (PDF417, QR, …). Empty when the document has none or none could be read.", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Barcode type (e.g. `PDF417`, `QR_CODE`, `UNKNOWN`)." + }, + "position": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Location of the barcode in the image, when available." + }, + "data": { + "type": "string", + "description": "Decoded payload." + }, + "data_raw": { + "type": "string", + "description": "Raw (undecoded) payload, when different." + }, + "side": { + "type": "string", + "nullable": true, + "description": "`front` or `back`." + } + } + } + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, or `null`." + }, + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` object you sent, or `null`." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp (UTC) of when the response was generated." + } + } + } + } + } + }, + "400": { + "description": "Validation or processing error. Field-level problems return DRF's standard envelope (one array of messages per offending field). Encrypted-PDF problems return `{\"detail\": ...}`. A document image in which no document can be recognized at all returns `{\"error\": \"COULD_NOT_RECOGNIZE_DOCUMENT\"}`. PDF uploads can additionally fail with \"The submitted PDF file is corrupted or invalid.\" or a no-pages error.", + "content": { + "application/json": { + "examples": { + "Missing front image": { + "summary": "`front_image` not included in the form data", + "value": { + "front_image": [ + "No file was submitted." + ] + } + }, + "Unsupported file extension": { + "summary": "File extension outside tiff/jpg/jpeg/png/webp/pdf", + "value": { + "front_image": [ + "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp, pdf." + ] + } + }, + "File too large": { + "summary": "Upload exceeds the 10 MB limit", + "value": { + "front_image": [ + "File size should not exceed 10 MB" + ] + } + }, + "Invalid option value": { + "summary": "Options only accept DECLINE or NO_ACTION", + "value": { + "invalid_mrz_action": [ + "\"REVIEW\" is not a valid choice." + ] + } + }, + "Encrypted PDF": { + "summary": "PDF uploaded without the required password", + "value": { + "detail": "The PDF is encrypted. Please upload a decrypted PDF or a photo instead." + } + }, + "Wrong PDF password": { + "summary": "`front_image_password` / `back_image_password` does not decrypt the PDF", + "value": { + "detail": "The PDF password is incorrect. Please provide the correct password." + } + }, + "Document not recognized": { + "summary": "No identity document could be detected in the image", + "value": { + "error": "COULD_NOT_RECOGNIZE_DOCUMENT" + } + }, + "PDF conversion failure": { + "summary": "Uploaded PDF could not be converted to an image", + "value": { + "error": "Failed to convert front image PDF." + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X POST 'https://verification.didit.me/v3/id-verification/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'front_image=@./id_front.jpg' \\\n -F 'back_image=@./id_back.jpg' \\\n -F 'perform_document_liveness=true' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nurl = 'https://verification.didit.me/v3/id-verification/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('id_front.jpg', 'rb') as front_f, open('id_back.jpg', 'rb') as back_f:\n files = {\n 'front_image': ('id_front.jpg', front_f, 'image/jpeg'),\n 'back_image': ('id_back.jpg', back_f, 'image/jpeg'),\n }\n data = {\n 'perform_document_liveness': 'true',\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=120)\n\nresp.raise_for_status()\nid_v = resp.json()['id_verification']\nprint('status:', id_v['status'])\nprint('name:', id_v.get('full_name'))\nprint('document:', id_v.get('document_type'), id_v.get('document_number'))\nfor w in id_v.get('warnings', []):\n print('warning:', w['risk'], '-', w['log_type'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('front_image', new Blob([fs.readFileSync('./id_front.jpg')]), 'id_front.jpg');\nform.append('back_image', new Blob([fs.readFileSync('./id_back.jpg')]), 'id_back.jpg');\nform.append('perform_document_liveness', 'true');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/id-verification/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`ID verification failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.id_verification.status);\nconsole.log('warnings:', body.id_verification.warnings.map((w) => w.risk));" + } + ] + } + }, + "/v3/face-match/": { + "post": { + "summary": "Face Match", + "description": "Compare two face images (1:1) and get back a similarity `score` (0–100) plus an `Approved`/`Declined` `status`.\n\n**How it works.** Both images are decoded and analyzed by a biometric model (set `rotate_image=true` if captures may be sideways or upside down), then a similarity score between the two faces is computed. A score **strictly above** `face_match_score_decline_threshold` (default `30`) returns `status: \"Approved\"`; a score at or below it returns `Declined` with a `LOW_FACE_MATCH_SIMILARITY` warning. If no face pair can be scored at all, the call still returns `200` with `status: \"Declined\"` and a `NO_REFERENCE_IMAGE` warning — `score` is `null` on the default save path, or `0` with `save_api_request=false`. Submit one clear, front-facing face per image for best results.\n\n**Billing.** Each `200` response consumes one Face Match API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, both images are stored as the session's face-match source/target images, and a `status.updated` webhook is emitted to your configured webhook endpoints. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.\n\n**Face search index.** This endpoint never enrolls faces into your face search index and never runs blocklist or duplicate screening — use `POST /v3/passive-liveness/` or `POST /v3/face-search/` for those.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", + "operationId": "post_v3face-match", + "x-didit-sdk": { + "resource": "face_match", + "method": "compare", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "user_image", + "ref_image" + ], + "properties": { + "user_image": { + "type": "string", + "format": "binary", + "description": "Live or candidate face image to verify (e.g. a selfie). Submit a single front-facing photo with the subject's face clearly visible and well lit. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.5 MB before processing, so very high resolutions do not improve accuracy." + }, + "ref_image": { + "type": "string", + "format": "binary", + "description": "Reference face image to compare against (e.g. an ID document portrait or a previously enrolled photo). Same format and size limits as `user_image`." + }, + "face_match_score_decline_threshold": { + "type": "number", + "format": "float", + "default": 30, + "minimum": 0, + "maximum": 100, + "description": "Similarity threshold (0–100) that drives the response `status`. A computed score **at or below** this value sets `status` to `Declined` and adds a `LOW_FACE_MATCH_SIMILARITY` warning; a score strictly above it returns `Approved`. Default `30` is permissive — raise it (e.g. `50`–`60`) for stricter verification. Values outside 0–100 return `400`.", + "example": 50 + }, + "rotate_image": { + "type": "boolean", + "default": false, + "description": "When `true`, the service tries the input images in 90-degree increments (0, 90, 180, 270) and uses the orientation that yields the best face detection — useful for mobile captures with missing EXIF orientation. Adds latency, so only enable it if you cannot guarantee upright images.", + "example": false + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, and announced through a `status.updated` webhook. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only.", + "example": true + }, + "vendor_data": { + "type": "string", + "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", + "example": "user-123" + }, + "metadata": { + "type": "object", + "additionalProperties": true, + "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"login\"}`) — it is parsed into an object.", + "example": { + "flow": "login", + "device_id": "abc123" + } + } + } + }, + "example": { + "user_image": "(binary JPEG/PNG of the live selfie)", + "ref_image": "(binary JPEG/PNG of the reference photo)", + "face_match_score_decline_threshold": 50, + "rotate_image": false, + "save_api_request": true, + "vendor_data": "user-123", + "metadata": { + "flow": "login" + } + } + } + } + }, + "responses": { + "200": { + "description": "Face match computed. `face_match.score` is a 0–100 float (two decimals); `status` is `Approved` when the score is strictly above `face_match_score_decline_threshold`, otherwise `Declined` with an explanatory warning. A low similarity or unscorable pair still returns `200` — inspect `face_match.status` and `face_match.warnings`, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", + "content": { + "application/json": { + "examples": { + "Approved": { + "summary": "Same person — score above the threshold", + "value": { + "request_id": "f9f5a777-8002-44c6-baaf-128bcfd6e226", + "face_match": { + "status": "Approved", + "score": 99.41, + "user_image": { + "entities": [ + { + "bbox": [ + 661, + 728, + 1688, + 2188 + ], + "confidence": 0.732973, + "age": 26.91, + "gender": "male", + "race": null + } + ], + "best_angle": 0 + }, + "ref_image": { + "entities": [ + { + "bbox": [ + 653, + 745, + 1691, + 2185 + ], + "confidence": 0.722082, + "age": 27.0, + "gender": "male", + "race": null + } + ], + "best_angle": 0 + }, + "warnings": [] + }, + "vendor_data": "user-123", + "metadata": { + "flow": "login" + }, + "created_at": "2026-06-12T01:04:42.763237+00:00" + } + }, + "Declined - low similarity": { + "summary": "Score at or below the decline threshold", + "value": { + "request_id": "7cdda24e-311a-46d3-a3bb-e5d9b564ce73", + "face_match": { + "status": "Declined", + "score": 24.87, + "user_image": { + "entities": [ + { + "bbox": [ + 661, + 728, + 1688, + 2188 + ], + "confidence": 0.732973, + "age": 26.91, + "gender": "male", + "race": null + } + ], + "best_angle": 0 + }, + "ref_image": { + "entities": [ + { + "bbox": [ + 156, + 234, + 679, + 898 + ], + "confidence": 0.717775, + "age": 41.2, + "gender": "male", + "race": null + } + ], + "best_angle": 0 + }, + "warnings": [ + { + "risk": "LOW_FACE_MATCH_SIMILARITY", + "feature": "FACEMATCH", + "additional_data": null, + "log_type": "error", + "short_description": "Low face match similarity", + "long_description": "The facial features of the provided image don't closely match the reference image, suggesting a potential identity mismatch." + } + ] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T01:08:19.859116+00:00" + } + } + }, + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + }, + "face_match": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined" + ], + "description": "`Approved` when the similarity score is strictly above `face_match_score_decline_threshold` and a score could be computed; `Declined` otherwise (a warning explains why)." + }, + "score": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 100, + "description": "Similarity score from 0 to 100 — the model's confidence that both images depict the same person. Rounded to two decimals only when `save_api_request` is true; with `save_api_request=false` the raw float is returned. `null` when no face pair could be scored (with the default `save_api_request=true`); with `save_api_request=false` the same condition can surface as `score: 0`. Either way a `NO_REFERENCE_IMAGE` warning is present and `status` is `Declined`.", + "example": 99.41 + }, + "user_image": { + "type": "object", + "description": "Face-detection results for the analyzed images.", + "properties": { + "entities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "bbox": { + "type": "array", + "items": { + "type": "integer" + }, + "minItems": 4, + "maxItems": 4, + "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.", + "example": [ + 661, + 728, + 1688, + 2188 + ] + }, + "confidence": { + "type": "number", + "format": "float", + "minimum": 0, + "maximum": 1, + "description": "Face-detection confidence (0–1).", + "example": 0.732973 + }, + "age": { + "type": "number", + "format": "float", + "description": "Model-estimated age of the detected face, in years. Informational only — it does not affect the face-match decision.", + "example": 26.91 + }, + "gender": { + "type": "string", + "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.", + "example": "male" + }, + "race": { + "type": "string", + "nullable": true, + "description": "Reserved field — always `null` in responses from this endpoint.", + "example": null + } + } + }, + "description": "One entry per detected face." + }, + "best_angle": { + "type": "integer", + "description": "Rotation (degrees: 0, 90, 180, or 270) that produced the best face detection. Only non-zero when `rotate_image=true` corrected the orientation.", + "example": 0 + } + } + }, + "ref_image": { + "type": "object", + "description": "Face-detection results for the reference image, same shape as `user_image`.", + "properties": { + "entities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "bbox": { + "type": "array", + "items": { + "type": "integer" + }, + "minItems": 4, + "maxItems": 4, + "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.", + "example": [ + 661, + 728, + 1688, + 2188 + ] + }, + "confidence": { + "type": "number", + "format": "float", + "minimum": 0, + "maximum": 1, + "description": "Face-detection confidence (0–1).", + "example": 0.732973 + }, + "age": { + "type": "number", + "format": "float", + "description": "Model-estimated age of the detected face, in years. Informational only — it does not affect the face-match decision.", + "example": 26.91 + }, + "gender": { + "type": "string", + "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.", + "example": "male" + }, + "race": { + "type": "string", + "nullable": true, + "description": "Reserved field — always `null` in responses from this endpoint.", + "example": null + } + } + }, + "description": "One entry per detected face." + }, + "best_angle": { + "type": "integer", + "example": 0 + } + } + }, + "warnings": { + "type": "array", + "description": "Empty on a clean approval. `NO_REFERENCE_IMAGE` (`error`) — no face pair could be scored; `LOW_FACE_MATCH_SIMILARITY` (`error`) — score at or below the decline threshold. Any of these sets `status` to `Declined`.", + "items": { + "type": "object", + "properties": { + "risk": { + "type": "string", + "enum": [ + "NO_REFERENCE_IMAGE", + "LOW_FACE_MATCH_SIMILARITY" + ], + "description": "Machine-readable risk code." + }, + "feature": { + "type": "string", + "enum": [ + "FACEMATCH" + ], + "description": "Feature that raised the warning. Always `FACEMATCH` on this endpoint." + }, + "additional_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Always `null` for face-match warnings." + }, + "log_type": { + "type": "string", + "enum": [ + "error", + "warning", + "information" + ], + "description": "Severity. `error` warnings drive `status` to `Declined`; `warning` and `information` entries are advisory and never decline on their own." + }, + "short_description": { + "type": "string", + "description": "Human-readable one-line summary of the risk." + }, + "long_description": { + "type": "string", + "description": "Human-readable explanation of the risk." + } + } + } + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, or `null`." + }, + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` object you sent, or `null`." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp (UTC) of when the response was generated, e.g. `2026-06-12T01:04:42.763237+00:00`." + } + } + } + } + } + }, + "400": { + "description": "Validation error. Returned when a required image is missing, exceeds 5 MB, has an unsupported extension, or when an option is out of range. The body is DRF's standard field-error envelope: one array of messages per offending field.", + "content": { + "application/json": { + "examples": { + "Missing required image": { + "summary": "`ref_image` (or `user_image`) not included in the form data", + "value": { + "ref_image": [ + "No file was submitted." + ] + } + }, + "Unsupported file extension": { + "summary": "File extension outside tiff/jpg/jpeg/png/webp", + "value": { + "user_image": [ + "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp." + ] + } + }, + "File too large": { + "summary": "Upload exceeds the 5 MB limit", + "value": { + "user_image": [ + "File size should not exceed 5 MB" + ] + } + }, + "Threshold out of range": { + "summary": "`face_match_score_decline_threshold` outside 0–100", + "value": { + "face_match_score_decline_threshold": [ + "Ensure this value is less than or equal to 100." + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X POST 'https://verification.didit.me/v3/face-match/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'user_image=@./selfie.jpg' \\\n -F 'ref_image=@./id_portrait.jpg' \\\n -F 'face_match_score_decline_threshold=50' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nurl = 'https://verification.didit.me/v3/face-match/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as user_f, open('id_portrait.jpg', 'rb') as ref_f:\n files = {\n 'user_image': ('selfie.jpg', user_f, 'image/jpeg'),\n 'ref_image': ('id_portrait.jpg', ref_f, 'image/jpeg'),\n }\n data = {\n 'face_match_score_decline_threshold': 50,\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nbody = resp.json()\nprint('status:', body['face_match']['status'])\nprint('score:', body['face_match']['score'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('ref_image', new Blob([fs.readFileSync('./id_portrait.jpg')]), 'id_portrait.jpg');\nform.append('face_match_score_decline_threshold', '50');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/face-match/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`Face match failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.face_match.status, 'score:', body.face_match.score);" + } + ] + } + }, + "/v3/age-estimation/": { + "post": { + "summary": "Age Estimation (face age + passive liveness)", + "description": "Estimate a person's age from a single face photo and run a passive liveness check in the same call — returns the model-predicted `age_estimation` (in years), a liveness `score` (0–100), and an `Approved`/`Declined` `status`. The age is **model-predicted from the face, not verified against a document** — use it for age gates and low-friction checks, not as legal proof of age.\n\n**How it works.** The image is analyzed for faces (set `rotate_image=true` if captures may be sideways or upside down); each detected face gets an estimated age, and the largest face in the frame drives `age_estimation`. In parallel, a passive liveness model scores whether the photo shows a live person (no user challenge required) and detects presentation attacks (photos of photos, screens, masks). Submit one clear, front-facing, well-lit face per image for best results.\n\n**Decision logic.** Any warning declines: `status` is `Approved` only when the `warnings` array is empty. A liveness `score` at or below `face_liveness_score_decline_threshold` (default `30`) adds `LOW_LIVENESS_SCORE`; a detected attack adds `LIVENESS_FACE_ATTACK`; no face found by the liveness model adds `NO_FACE_DETECTED`; no estimable face age adds `AGE_NOT_DETECTED` (`age_estimation` is `null`); an estimated age below `age_estimation_decline_threshold` (default `18`) adds `AGE_BELOW_MINIMUM`. Borderline ages near your threshold deserve a fallback to document-based verification — the model's error grows for faces close to the limit.\n\n**Billing.** Each `200` response consumes one Age Estimation API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/` (the result is exposed there in `liveness_checks[]`, with `features: [\"AGE_ESTIMATION\"]`), the face crop and reference image are stored, the face embedding is added to your application's face index (used by face search and duplicate detection), and a `status.updated` webhook is emitted. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload (`age_estimation: 30`, no warnings), no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", + "operationId": "post_v3age-estimation", + "x-didit-sdk": { + "resource": "age_estimation", + "method": "estimate", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "user_image" + ], + "properties": { + "user_image": { + "type": "string", + "format": "binary", + "description": "Face photo to analyze (e.g. a selfie). Submit a single front-facing photo with the subject's face clearly visible and well lit. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp` (no PDF). Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.3 MB before processing, so very high resolutions do not improve accuracy." + }, + "face_liveness_score_decline_threshold": { + "type": "number", + "format": "float", + "default": 30, + "minimum": 0, + "maximum": 100, + "description": "Liveness threshold (0–100). A liveness `score` **at or below** this value adds a `LOW_LIVENESS_SCORE` warning and declines. Raise it for stricter anti-spoofing; values outside 0–100 return `400`.", + "example": 30 + }, + "age_estimation_decline_threshold": { + "type": "integer", + "default": 18, + "minimum": 0, + "maximum": 100, + "description": "Minimum acceptable estimated age in years. An estimated age **below** this value adds an `AGE_BELOW_MINIMUM` warning and declines. Set `0` to disable the age check (the call then only runs liveness + age estimation without an age-based decision).", + "example": 18 + }, + "rotate_image": { + "type": "boolean", + "default": false, + "description": "When `true`, the service tries the input image in 90-degree increments (0, 90, 180, 270) and uses the orientation that yields the best face detection — useful for mobile captures with missing EXIF orientation. Adds latency, so only enable it if you cannot guarantee upright images.", + "example": false + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, announced through a `status.updated` webhook, and with the detected face added to your application's face index. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only.", + "example": true + }, + "vendor_data": { + "type": "string", + "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", + "example": "user-123" + }, + "metadata": { + "type": "object", + "additionalProperties": true, + "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"age-gate\"}`) — it is parsed into an object.", + "example": { + "flow": "age-gate" + } + } + } + }, + "example": { + "user_image": "(binary JPEG/PNG of the selfie)", + "face_liveness_score_decline_threshold": 30, + "age_estimation_decline_threshold": 18, + "rotate_image": false, + "save_api_request": true, + "vendor_data": "user-123", + "metadata": { + "flow": "age-gate" + } + } + } + } + }, + "responses": { + "200": { + "description": "Age estimation and passive liveness completed. `age_estimation.age_estimation` is the model-predicted age in years of the largest detected face (`null` if none); `age_estimation.score` is the passive-liveness score (0–100); `status` is `Approved` only when `warnings` is empty. An underage or spoofed capture still returns `200` with `status: \"Declined\"` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", + "content": { + "application/json": { + "examples": { + "Approved": { + "summary": "Live adult face — both checks passed", + "value": { + "request_id": "0c40ba43-64ab-4e2e-b4b8-7d1f12f81bc1", + "age_estimation": { + "status": "Approved", + "method": "PASSIVE", + "score": 97.5, + "user_image": { + "entities": [ + { + "age": 27.33, + "bbox": [ + 40, + 40, + 100, + 100 + ], + "confidence": 0.7177750468254089, + "gender": "male" + } + ], + "best_angle": 0 + }, + "age_estimation": 27.33, + "warnings": [] + }, + "vendor_data": "user-123", + "metadata": { + "flow": "age-gate" + }, + "created_at": "2026-06-12T02:24:11.512941+00:00" + } + }, + "Declined - age below threshold": { + "summary": "Estimated age below `age_estimation_decline_threshold`", + "value": { + "request_id": "9be41a6f-2f4e-4f57-92f4-b3a4f6f0a1c2", + "age_estimation": { + "status": "Declined", + "method": "PASSIVE", + "score": 95.0, + "user_image": { + "entities": [ + { + "age": 16.5, + "bbox": [ + 40, + 40, + 100, + 100 + ], + "confidence": 0.7177750468254089, + "gender": "female" + } + ], + "best_angle": 0 + }, + "age_estimation": 16.5, + "warnings": [ + { + "risk": "AGE_BELOW_MINIMUM", + "feature": "LIVENESS", + "additional_data": null, + "log_type": "error", + "short_description": "Age below minimum", + "long_description": "The age of the face is below the minimum age threshold for the application." + } + ] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T02:26:40.118332+00:00" + } + }, + "Declined - liveness too low": { + "summary": "Liveness score at or below the decline threshold (possible spoof)", + "value": { + "request_id": "5f6f2f1f-7c4f-43b9-8d62-0a8f4c2d9e77", + "age_estimation": { + "status": "Declined", + "method": "PASSIVE", + "score": 5.0, + "user_image": { + "entities": [ + { + "age": 25.0, + "bbox": [ + 40, + 40, + 100, + 100 + ], + "confidence": 0.7177750468254089, + "gender": "male" + } + ], + "best_angle": 0 + }, + "age_estimation": 25.0, + "warnings": [ + { + "risk": "LOW_LIVENESS_SCORE", + "feature": "LIVENESS", + "additional_data": null, + "log_type": "error", + "short_description": "Low liveness score", + "long_description": "The liveness check resulted in a low score, indicating potential use of non-live facial representations or poor-quality biometric data." + } + ] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T02:27:02.901547+00:00" + } + } + }, + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + }, + "age_estimation": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined" + ], + "description": "`Approved` when no warnings were raised; `Declined` otherwise (every warning this endpoint produces declines)." + }, + "method": { + "type": "string", + "enum": [ + "PASSIVE" + ], + "description": "Liveness method. Always `PASSIVE` on this endpoint (single-image, no user challenge)." + }, + "score": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 100, + "description": "Passive-liveness score from 0 to 100 — the model's confidence that the photo shows a live person. Compared against `face_liveness_score_decline_threshold`; at or below it the call declines with `LOW_LIVENESS_SCORE`. `null` when the liveness model returns no confidence — the call still declines with `LOW_LIVENESS_SCORE` because null is treated as 0 for the threshold comparison.", + "example": 97.5 + }, + "user_image": { + "type": "object", + "description": "Face-detection results for the analyzed image.", + "properties": { + "entities": { + "type": "array", + "description": "One entry per detected face. Empty when no face was found (then `age_estimation` is `null` and an `AGE_NOT_DETECTED` warning is added).", + "items": { + "type": "object", + "properties": { + "age": { + "type": "number", + "format": "float", + "description": "Model-estimated age of this face, in years.", + "example": 27.33 + }, + "bbox": { + "type": "array", + "items": { + "type": "integer" + }, + "minItems": 4, + "maxItems": 4, + "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.", + "example": [ + 40, + 40, + 100, + 100 + ] + }, + "confidence": { + "type": "number", + "format": "float", + "minimum": 0, + "maximum": 1, + "description": "Face-detection confidence (0–1).", + "example": 0.7177750468254089 + }, + "gender": { + "type": "string", + "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.", + "example": "male" + } + } + } + }, + "best_angle": { + "type": "integer", + "nullable": true, + "description": "Rotation (degrees: 0, 90, 180, or 270) that produced the best face detection. Only non-zero when `rotate_image=true` corrected the orientation.", + "example": 0 + } + } + }, + "age_estimation": { + "type": "number", + "format": "float", + "nullable": true, + "description": "Model-predicted age in years of the **largest** detected face. `null` when no face age could be estimated (an `AGE_NOT_DETECTED` warning is added and the call declines).", + "example": 27.33 + }, + "warnings": { + "type": "array", + "description": "Empty on a clean approval. Any entry sets `status` to `Declined`.", + "items": { + "type": "object", + "properties": { + "risk": { + "type": "string", + "enum": [ + "NO_FACE_DETECTED", + "LOW_LIVENESS_SCORE", + "LIVENESS_FACE_ATTACK", + "AGE_NOT_DETECTED", + "AGE_BELOW_MINIMUM" + ], + "description": "Machine-readable risk code. `NO_FACE_DETECTED` — the liveness model found no face; `LOW_LIVENESS_SCORE` — liveness `score` at or below `face_liveness_score_decline_threshold`; `LIVENESS_FACE_ATTACK` — a presentation attack (photo/screen/mask) was detected; `AGE_NOT_DETECTED` — no face age could be estimated; `AGE_BELOW_MINIMUM` — estimated age below `age_estimation_decline_threshold`. `NO_FACE_DETECTED` and `LOW_LIVENESS_SCORE` are mutually exclusive — when no face is found only `NO_FACE_DETECTED` is emitted." + }, + "feature": { + "type": "string", + "enum": [ + "LIVENESS" + ], + "description": "Feature that raised the warning. Always `LIVENESS` on this endpoint." + }, + "additional_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Always `null` for age-estimation warnings." + }, + "log_type": { + "type": "string", + "enum": [ + "error" + ], + "description": "Severity. Every warning this endpoint produces is an `error` and sets `status` to `Declined`." + }, + "short_description": { + "type": "string", + "description": "Human-readable one-line summary of the risk." + }, + "long_description": { + "type": "string", + "description": "Human-readable explanation of the risk." + } + } + } + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, or `null`." + }, + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` object you sent, or `null`." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp (UTC) of when the response was generated." + } + } + } + } + } + }, + "400": { + "description": "Validation error. Returned when `user_image` is missing, exceeds 5 MB, has an unsupported extension, or when a threshold is out of range. The body is DRF's standard field-error envelope: one array of messages per offending field.", + "content": { + "application/json": { + "examples": { + "Missing user image": { + "summary": "`user_image` not included in the form data", + "value": { + "user_image": [ + "No file was submitted." + ] + } + }, + "Unsupported file extension": { + "summary": "File extension outside tiff/jpg/jpeg/png/webp", + "value": { + "user_image": [ + "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp." + ] + } + }, + "File too large": { + "summary": "Upload exceeds the 5 MB limit", + "value": { + "user_image": [ + "File size should not exceed 5 MB" + ] + } + }, + "Threshold out of range": { + "summary": "`face_liveness_score_decline_threshold` outside 0–100", + "value": { + "face_liveness_score_decline_threshold": [ + "Ensure this value is less than or equal to 100." + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X POST 'https://verification.didit.me/v3/age-estimation/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'user_image=@./selfie.jpg' \\\n -F 'age_estimation_decline_threshold=18' \\\n -F 'face_liveness_score_decline_threshold=30' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nurl = 'https://verification.didit.me/v3/age-estimation/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as f:\n files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}\n data = {\n 'age_estimation_decline_threshold': 18,\n 'face_liveness_score_decline_threshold': 30,\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nresult = resp.json()['age_estimation']\nprint('status:', result['status'])\nprint('estimated age:', result['age_estimation'])\nprint('liveness score:', result['score'])\nfor w in result['warnings']:\n print('warning:', w['risk'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('age_estimation_decline_threshold', '18');\nform.append('face_liveness_score_decline_threshold', '30');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/age-estimation/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`Age estimation failed: ${response.status}`);\nconst { age_estimation } = await response.json();\nconsole.log('status:', age_estimation.status, 'age:', age_estimation.age_estimation, 'liveness:', age_estimation.score);" + } + ] + } + }, + "/v3/poa/": { + "post": { + "summary": "Proof of Address (document OCR + address checks)", + "description": "Extract and validate a Proof of Address document — utility bills, bank statements, and government-issued documents — in one call. Returns the issuer, issue/expiry dates, the holder name, the raw and parsed/geocoded address, an `Approved`/`Declined` `status`, and a `warnings` list explaining every issue found.\n\n**Latency.** Extraction is LLM-based: typical calls take **5–15 seconds**, multi-page PDFs up to **~30 seconds**. Configure a client timeout of **at least 45 seconds** and do not retry before the call completes.\n\n**How it works.** The document (image or PDF, up to 15 MB; encrypted PDFs require `document_password`) is classified into a document type and subtype (e.g. `UTILITY_BILL` / `WATER_BILL`), and the issuer, dates, holder name, and address are extracted with LLM-based extraction. The address is parsed and geocoded into `poa_parsed_address`. PDF/EXIF forensics screen for tampering — modification after digital signing, known PDF editors, overlay-text manipulation, suspicious re-exports — surfacing `SUSPECTED_DOCUMENT_MANIPULATION` or `DOCUMENT_METADATA_MISMATCH`. Document age is validated against `poa_document_age_months`, and the language against `poa_languages_allowed`. If you pass `expected_address`, `expected_country`, or `expected_first_name`/`expected_last_name`, the extracted values are cross-checked and mismatches are flagged.\n\n**Decision logic.** `status` is `Approved` unless at least one warning resolves to a decline. Hard failures always decline: `MISSING_ADDRESS_INFORMATION`, `POA_DOCUMENT_EXPIRED`, `INVALID_DOCUMENT_TYPE`, `UNABLE_TO_VALIDATE_DOCUMENT_AGE`, plus `UNABLE_TO_EXTRACT_ISSUE_DATE` and `POA_NAME_NOT_DETECTED` (not configurable on this endpoint). Five risk groups are configurable per request via the `poa_*_action` options (`DECLINE` or `NO_ACTION`, all defaulting to `DECLINE`). `POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION` and `UNPARSABLE_OR_INVALID_ADDRESS` are informational on this endpoint and never decline. A document that cannot be processed at all returns `400`; a readable but problematic document returns `200` with `status: \"Declined\"` — always inspect `poa.status` and `poa.warnings`, not just the HTTP code.\n\n**Billing.** Each `200` response consumes one Proof of Address API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any document processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the uploaded document is stored, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/` (the decision additionally exposes `document_file` and the `document_metadata` forensics, which this response omits), and a `status.updated` webhook is emitted. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload (a fictional \"Sandbox Power Co.\" electricity bill), no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\nNote: `FUTURE_ISSUE_DATE` and `POOR_DOCUMENT_QUALITY` exist in workflow sessions but are never produced by this standalone endpoint — a future-dated document is not auto-declined here.", + "operationId": "post_v3poa", + "x-didit-sdk": { + "resource": "proof_of_address", + "method": "verify", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "document" + ], + "properties": { + "document": { + "type": "string", + "format": "binary", + "description": "The Proof of Address document. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `pdf`, `webp`. Maximum upload size: **15 MB** (larger files are rejected with `400`). Multi-page PDFs are supported (expect higher latency); encrypted PDFs require `document_password`. Images are automatically compressed before processing." + }, + "document_password": { + "type": "string", + "description": "Password to decrypt `document` when it is an encrypted PDF. Sending an encrypted PDF without (or with a wrong) password returns `400`.", + "writeOnly": true + }, + "expected_address": { + "type": "string", + "description": "Address to verify against the document. It is geocoded and compared with the extracted address; a mismatch (or an expected address that cannot be geocoded/verified) adds `ADDRESS_MISMATCH_WITH_PROVIDED`, governed by `poa_address_mismatch_action`. The geocoded form is echoed back in `expected_details_formatted_address` / `expected_details_parsed_address`.", + "example": "Av. Monseñor Tavella 1291, Salta, Argentina" + }, + "expected_country": { + "type": "string", + "description": "Country to verify against the document (ISO 3166-1 code, e.g. `ARG` or `AR`). A mismatch with the document's country adds `POA_COUNTRY_MISMATCH_WITH_PROVIDED`, governed by `poa_address_mismatch_action`.", + "example": "ARG" + }, + "expected_first_name": { + "type": "string", + "description": "First name to verify against `name_on_document` (fuzzy match, transliteration-aware). A score below the match threshold adds `NAME_MISMATCH_WITH_PROVIDED`, governed by `poa_address_mismatch_action`.", + "example": "Sophia" + }, + "expected_last_name": { + "type": "string", + "description": "Last name to verify against `name_on_document`, combined with `expected_first_name` for the match score.", + "example": "Martinez" + }, + "poa_languages_allowed": { + "type": "string", + "description": "Comma-separated list of accepted document languages as ISO 639-1 codes (e.g. `en,es,fr`). When omitted or blank, **all 51 supported languages** are accepted. A document in a language outside the list adds `UNSUPPORTED_DOCUMENT_LANGUAGE` (governed by `poa_unsupported_language_action`); an unknown code in the list returns `400` with the full set of supported codes.", + "example": "en,es" + }, + "poa_document_age_months": { + "type": "string", + "description": "Comma-separated `key:value` pairs setting the maximum document age in months per document type — keys: `utility_bill`, `bank_statement`, `government_issued_document`, `other_poa_document`; values: `1`–`120`, or `-1` for unlimited (no age check). Defaults when omitted: `utility_bill:3,bank_statement:3,government_issued_document:12,other_poa_document:12`. A document older than its limit declines with `POA_DOCUMENT_EXPIRED`. **Caution:** when you provide this field, list *every* type you accept — omitted types are treated as not accepted (informational `POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION`) and their age check is skipped.", + "example": "utility_bill:3,bank_statement:6" + }, + "poa_name_mismatch_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "DECLINE", + "description": "Accepted but **currently not applied** — name-mismatch warnings (`NAME_MISMATCH_WITH_PROVIDED`) follow `poa_address_mismatch_action` instead. Set `poa_address_mismatch_action` to control both name and address mismatch behavior." + }, + "poa_document_issues_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "DECLINE", + "description": "What to do on file-integrity problems — in practice only `DOCUMENT_METADATA_MISMATCH` is produced by this endpoint." + }, + "poa_document_authenticity_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "DECLINE", + "description": "What to do when document manipulation is suspected (`SUSPECTED_DOCUMENT_MANIPULATION`) — e.g. the PDF was modified after digital signing, processed by a known PDF editor, shows overlay-text manipulation, or has inconsistent EXIF dates." + }, + "poa_unsupported_language_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "DECLINE", + "description": "What to do when the document language is outside `poa_languages_allowed` (`UNSUPPORTED_DOCUMENT_LANGUAGE`)." + }, + "poa_address_mismatch_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "DECLINE", + "description": "What to do when the extracted details disagree with the `expected_*` fields: `ADDRESS_MISMATCH_WITH_PROVIDED`, `NAME_MISMATCH_WITH_PROVIDED`, and `POA_COUNTRY_MISMATCH_WITH_PROVIDED` all follow this action." + }, + "poa_issuer_not_identified_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "DECLINE", + "description": "What to do when no issuing company/organization can be identified on the document (`ISSUER_NOT_IDENTIFIED`)." + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, announced through a `status.updated` webhook, and with the uploaded document stored. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only.", + "example": true + }, + "vendor_data": { + "type": "string", + "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", + "example": "user-123" + }, + "metadata": { + "type": "object", + "additionalProperties": true, + "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"onboarding\"}`) — it is parsed into an object.", + "example": { + "flow": "onboarding" + } + } + } + }, + "example": { + "document": "(binary JPEG/PNG/PDF of the utility bill or bank statement)", + "expected_address": "Av. Monseñor Tavella 1291, Salta, Argentina", + "expected_country": "ARG", + "expected_first_name": "Sophia", + "expected_last_name": "Martinez", + "poa_languages_allowed": "en,es", + "save_api_request": true, + "vendor_data": "user-123" + } + } + } + }, + "responses": { + "200": { + "description": "Document processed. `poa.status` is `Approved` or `Declined`; every detected issue is itemized in `poa.warnings`. An expired, mismatched, or suspicious document still returns `200` with `status: \"Declined\"` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", + "content": { + "application/json": { + "examples": { + "Approved": { + "summary": "Utility bill read cleanly, no expected-details checks", + "value": { + "request_id": "4b523a8d-ed32-45e4-8aa8-a89eda459d88", + "poa": { + "issuing_state": "ARG", + "document_type": "UTILITY_BILL", + "document_subtype": "WATER_BILL", + "document_language": "es", + "issuer": "Aguas del Norte", + "issue_date": "2026-04-10", + "expiration_date": null, + "poa_address": "AVDA. MONSEÑOR TAVELLA N° 1291, B° VILLA LAVALLE CP: 4400, CAPITAL - SALTA", + "poa_formatted_address": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina", + "poa_parsed_address": { + "street_1": "Avenida Monseñor Tavella 1291", + "street_2": "b", + "city": "Salta", + "region": "Salta", + "country": "AR", + "postal_code": "A4400", + "document_location": { + "latitude": -24.8208682, + "longitude": -65.4130954 + } + }, + "expected_details_address": null, + "expected_details_formatted_address": null, + "expected_details_parsed_address": null, + "name_on_document": "OESTE EMBOTELLADORA S.A.", + "extra_fields": { + "bank_account_number": null, + "bank_iban": null, + "bank_sort_code": null, + "bank_routing_number": null, + "bank_swift_bic": null, + "bank_branch_name": null, + "bank_branch_address": null, + "document_phone_number": null, + "additional_names": [ + "LA EMBOTELLADORA DEL NORTE SA" + ] + }, + "detected_codes": [ + { + "type": "QR_CODE", + "page_number": 1, + "position": [ + [ + 820, + 1120 + ], + [ + 1002, + 1120 + ], + [ + 1002, + 1302 + ], + [ + 820, + 1302 + ] + ], + "x": 820, + "y": 1120, + "width": 182, + "height": 182, + "page_width": 1190, + "page_height": 1684, + "raw_payload": "aHR0cHM6Ly9hZ3Vhc2RlbG5vcnRlLmNvbS9mYWN0dXJhLzk5ODgxMQ==", + "parsed_payload": "https://aguasdelnorte.com/factura/998811" + }, + { + "type": "CODE_128", + "page_number": 1, + "position": [ + [ + 112, + 1480 + ], + [ + 560, + 1480 + ], + [ + 560, + 1540 + ], + [ + 112, + 1540 + ] + ], + "x": 112, + "y": 1480, + "width": 448, + "height": 60, + "page_width": 1190, + "page_height": 1684, + "raw_payload": "MDA5OTg4MTEwMDA0Mg==", + "parsed_payload": "0099881100042" + } + ], + "status": "Approved", + "warnings": [] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T02:29:02.439655+00:00" + } + }, + "Declined - document too old": { + "summary": "Issue date older than the allowed `poa_document_age_months`", + "value": { + "request_id": "f61681b8-9422-4378-ac3e-04c6a247fb45", + "poa": { + "issuing_state": "ARG", + "document_type": "UTILITY_BILL", + "document_subtype": "WATER_BILL", + "document_language": "es", + "issuer": "Aguas del Norte", + "issue_date": "2021-02-10", + "expiration_date": null, + "poa_address": "AVDA. MONSEÑOR TAVELLA N° 1291, B° VILLA LAVALLE CP: 4400, CAPITAL - SALTA", + "poa_formatted_address": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina", + "poa_parsed_address": { + "street_1": "Avenida Monseñor Tavella 1291", + "street_2": "b", + "city": "Salta", + "region": "Salta", + "country": "AR", + "postal_code": "A4400", + "document_location": { + "latitude": -24.8208682, + "longitude": -65.4130954 + } + }, + "expected_details_address": null, + "expected_details_formatted_address": null, + "expected_details_parsed_address": null, + "name_on_document": "OESTE EMBOTELLADORA S.A.", + "extra_fields": { + "bank_account_number": null, + "bank_iban": null, + "bank_sort_code": null, + "bank_routing_number": null, + "bank_swift_bic": null, + "bank_branch_name": null, + "bank_branch_address": null, + "document_phone_number": null, + "additional_names": [ + "LA EMBOTELLADORA DEL NORTE SA" + ] + }, + "status": "Declined", + "warnings": [ + { + "risk": "POA_DOCUMENT_EXPIRED", + "feature": "PROOF_OF_ADDRESS", + "additional_data": { + "expiration_reason": "age_window", + "max_age_months": 3, + "document_subtype": "WATER_BILL", + "document_type": "UTILITY_BILL", + "issue_date": "2021-02-10" + }, + "log_type": "error", + "short_description": "Document expired", + "long_description": "The submitted document is older than the maximum accepted age of 3 months from its issue date, which exceeds the acceptable time period for validity." + } + ] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T02:23:02.165493+00:00" + } + }, + "Declined - name mismatch": { + "summary": "`expected_first_name`/`expected_last_name` do not match `name_on_document`", + "value": { + "request_id": "0a7f2f9c-58f1-4b8e-b1e3-2f6f9d4f7a21", + "poa": { + "issuing_state": "ARG", + "document_type": "UTILITY_BILL", + "document_subtype": "WATER_BILL", + "document_language": "es", + "issuer": "Aguas del Norte", + "issue_date": "2021-02-10", + "expiration_date": null, + "poa_address": "AVDA. MONSEÑOR TAVELLA N° 1291, B° VILLA LAVALLE CP: 4400, CAPITAL - SALTA", + "poa_formatted_address": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina", + "poa_parsed_address": { + "street_1": "Avenida Monseñor Tavella 1291", + "street_2": "b", + "city": "Salta", + "region": "Salta", + "country": "AR", + "postal_code": "A4400", + "document_location": { + "latitude": -24.8208682, + "longitude": -65.4130954 + } + }, + "expected_details_address": null, + "expected_details_formatted_address": null, + "expected_details_parsed_address": null, + "name_on_document": "OESTE EMBOTELLADORA S.A.", + "extra_fields": { + "bank_account_number": null, + "bank_iban": null, + "bank_sort_code": null, + "bank_routing_number": null, + "bank_swift_bic": null, + "bank_branch_name": null, + "bank_branch_address": null, + "document_phone_number": null, + "additional_names": [ + "LA EMBOTELLADORA DEL NORTE SA" + ] + }, + "status": "Declined", + "warnings": [ + { + "risk": "NAME_MISMATCH_WITH_PROVIDED", + "feature": "PROOF_OF_ADDRESS", + "additional_data": null, + "log_type": "error", + "short_description": "Name mismatch with provided information", + "long_description": "The full name on the document does not match the name from the user's verified identity documents, or the full name sent by API." + } + ] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T02:27:21.330172+00:00" + } + } + }, + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + }, + "poa": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined" + ], + "description": "`Approved` when no warning resolves to a decline; `Declined` otherwise — the `warnings` list explains why." + }, + "issuing_state": { + "type": "string", + "nullable": true, + "description": "Country the document was issued in, as extracted from the document — usually ISO 3166-1 alpha-3 (e.g. `ARG`).", + "example": "ARG" + }, + "document_type": { + "type": "string", + "enum": [ + "UTILITY_BILL", + "BANK_STATEMENT", + "GOVERNMENT_ISSUED_DOCUMENT", + "OTHER_POA_DOCUMENT", + "UNKNOWN" + ], + "description": "Detected document category. `UNKNOWN` means the document could not be classified as an acceptable Proof of Address and auto-declines with `INVALID_DOCUMENT_TYPE`. Never null — classification failures fall back to `UNKNOWN`." + }, + "document_subtype": { + "type": "string", + "description": "Finer-grained classification within `document_type` — e.g. `ELECTRICITY_BILL`, `WATER_BILL`, `GAS_BILL`, `INTERNET_BILL`, `PHONE_BILL` (utility bills); `ACCOUNT_STATEMENT`, `CREDIT_CARD_STATEMENT` (bank statements); `TAX_ASSESSMENT`, `RESIDENCY_CERTIFICATE` (government documents). `UNKNOWN` when no subtype could be determined. Never null — classification failures fall back to `UNKNOWN`.", + "example": "WATER_BILL" + }, + "document_language": { + "type": "string", + "nullable": true, + "description": "Detected document language (ISO 639-1).", + "example": "es" + }, + "issuer": { + "type": "string", + "nullable": true, + "description": "Company or organization that issued the document. `null` adds `ISSUER_NOT_IDENTIFIED`.", + "example": "Aguas del Norte" + }, + "issue_date": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Document issue date (`YYYY-MM-DD`). `null` when it could not be extracted — that declines with `UNABLE_TO_EXTRACT_ISSUE_DATE` on this endpoint.", + "example": "2021-02-10" + }, + "expiration_date": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Explicit expiry/validity date printed on the document, when present. Independent of the `poa_document_age_months` age check." + }, + "poa_address": { + "type": "string", + "nullable": true, + "description": "Address exactly as printed on the document. `null` auto-declines with `MISSING_ADDRESS_INFORMATION`." + }, + "poa_formatted_address": { + "type": "string", + "nullable": true, + "description": "Geocoded, normalized version of `poa_address`.", + "example": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina" + }, + "poa_parsed_address": { + "type": "object", + "nullable": true, + "description": "Structured, geocoded breakdown of the document address. `null` when the address could not be parsed.", + "properties": { + "street_1": { + "type": "string", + "nullable": true, + "example": "Avenida Monseñor Tavella 1291" + }, + "street_2": { + "type": "string", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true, + "example": "Salta" + }, + "region": { + "type": "string", + "nullable": true, + "example": "Salta" + }, + "country": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country code.", + "example": "AR" + }, + "postal_code": { + "type": "string", + "nullable": true, + "example": "A4400" + }, + "document_location": { + "type": "object", + "nullable": true, + "properties": { + "latitude": { + "type": "number" + }, + "longitude": { + "type": "number" + } + }, + "description": "Geocoded coordinates of the document address." + } + } + }, + "expected_details_address": { + "type": "string", + "nullable": true, + "description": "Echo of the `expected_address` you sent, or `null`." + }, + "expected_details_formatted_address": { + "type": "string", + "nullable": true, + "description": "Geocoded, normalized version of `expected_address`; `null` when not provided or not geocodable." + }, + "expected_details_parsed_address": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Full geocoding result for `expected_address` (street/city/region/country/postal code plus raw geocoder output and an `is_verified` flag). `null` when `expected_address` was not provided." + }, + "name_on_document": { + "type": "string", + "nullable": true, + "description": "Primary holder name extracted from the document; compared against `expected_first_name`/`expected_last_name` when provided. `null` declines with `POA_NAME_NOT_DETECTED` on this endpoint.", + "example": "OESTE EMBOTELLADORA S.A." + }, + "extra_fields": { + "type": "object", + "description": "Document-specific extras. Bank fields are populated for bank statements when printed on the document; `null` otherwise.", + "properties": { + "bank_account_number": { + "type": "string", + "nullable": true + }, + "bank_iban": { + "type": "string", + "nullable": true + }, + "bank_sort_code": { + "type": "string", + "nullable": true + }, + "bank_routing_number": { + "type": "string", + "nullable": true + }, + "bank_swift_bic": { + "type": "string", + "nullable": true + }, + "bank_branch_name": { + "type": "string", + "nullable": true + }, + "bank_branch_address": { + "type": "string", + "nullable": true + }, + "document_phone_number": { + "type": "string", + "nullable": true, + "description": "Contact phone number printed on the document." + }, + "additional_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Other holder names found on the document (e.g. co-account holders, previous account names)." + } + } + }, + "warnings": { + "type": "array", + "description": "Empty on a clean approval. Every entry explains one detected risk; entries with `log_type: \"error\"` set `status` to `Declined`.", + "items": { + "type": "object", + "properties": { + "risk": { + "type": "string", + "description": "Machine-readable risk code. Always-decline: `MISSING_ADDRESS_INFORMATION`, `POA_DOCUMENT_EXPIRED`, `INVALID_DOCUMENT_TYPE`, `UNABLE_TO_VALIDATE_DOCUMENT_AGE`, plus `UNABLE_TO_EXTRACT_ISSUE_DATE` and `POA_NAME_NOT_DETECTED` (fixed to decline on this endpoint). Governed by request options: `NAME_MISMATCH_WITH_PROVIDED` / `ADDRESS_MISMATCH_WITH_PROVIDED` / `POA_COUNTRY_MISMATCH_WITH_PROVIDED` (`poa_address_mismatch_action`), `POOR_DOCUMENT_QUALITY` (never produced by this endpoint) / `DOCUMENT_METADATA_MISMATCH` (`poa_document_issues_action`), `SUSPECTED_DOCUMENT_MANIPULATION` (`poa_document_authenticity_action`), `UNSUPPORTED_DOCUMENT_LANGUAGE` (`poa_unsupported_language_action`), `ISSUER_NOT_IDENTIFIED` (`poa_issuer_not_identified_action`). Informational on this endpoint: `POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION`, `UNPARSABLE_OR_INVALID_ADDRESS`.", + "example": "POA_DOCUMENT_EXPIRED" + }, + "feature": { + "type": "string", + "enum": [ + "PROOF_OF_ADDRESS" + ], + "description": "Feature that raised the warning. Always `PROOF_OF_ADDRESS` on this endpoint." + }, + "additional_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Extra context for some risks — e.g. `POA_DOCUMENT_EXPIRED` includes `expiration_reason` (`printed_expiration` or `age_window`), `document_type`, `document_subtype`, and `issue_date`, plus `expiration_date` on the `printed_expiration` path or `max_age_months` on the `age_window` path; `SUSPECTED_DOCUMENT_MANIPULATION` includes `reason` and `detection_method`. `null` for most risks." + }, + "log_type": { + "type": "string", + "enum": [ + "error", + "information" + ], + "description": "Severity. `error` warnings drive `status` to `Declined`; `information` entries are advisory and never decline on their own. (`warning` is not produced by this endpoint — its options only accept `DECLINE` or `NO_ACTION`.)" + }, + "short_description": { + "type": "string", + "description": "Human-readable one-line summary of the risk." + }, + "long_description": { + "type": "string", + "description": "Human-readable explanation of the risk." + } + } + } + }, + "detected_codes": { + "type": "array", + "description": "QR codes and barcodes detected across every processed page of the document. Empty when none are found. A located but undecodable QR code has `parsed_payload: null`.", + "items": { + "$ref": "#/components/schemas/DocumentAIDetectedCode" + } + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, or `null`." + }, + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` object you sent, or `null`." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp (UTC) of when the response was generated." + } + } + } + } + } + }, + "400": { + "description": "Validation or processing error. Field-level problems return DRF's standard envelope (one array of messages per offending field). Encrypted-PDF problems return `{\"detail\": ...}`. A document the extraction pipeline cannot process returns `{\"error\": [\"Error extracting POA information\"]}`.", + "content": { + "application/json": { + "examples": { + "Missing document": { + "summary": "`document` not included in the form data", + "value": { + "document": [ + "No file was submitted." + ] + } + }, + "Unsupported file extension": { + "summary": "File extension outside tiff/jpg/jpeg/png/pdf/webp", + "value": { + "document": [ + "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, pdf, webp." + ] + } + }, + "File too large": { + "summary": "Upload exceeds the 15 MB limit", + "value": { + "document": [ + "File size should not exceed 15 MB" + ] + } + }, + "Encrypted PDF": { + "summary": "PDF uploaded without the required password", + "value": { + "detail": "The PDF is encrypted. Please upload a decrypted PDF or a photo instead." + } + }, + "Wrong PDF password": { + "summary": "`document_password` does not decrypt the PDF", + "value": { + "detail": "The PDF password is incorrect. Please provide the correct password." + } + }, + "Invalid language code": { + "summary": "`poa_languages_allowed` contains an unsupported code", + "value": { + "poa_languages_allowed": [ + "Invalid language code: 'xx'. Must be one of the supported languages: ['ar', 'bn', 'hy', 'bg', 'bs', 'ca', 'cnr', 'sq', 'zh', 'hr', 'cs', 'da', 'nl', 'en', 'et', 'fi', 'fr', 'ka', 'kk', 'de', 'el', 'he', 'hi', 'hu', 'id', 'it', 'ja', 'ko', 'ky', 'lv', 'lt', 'mk', 'mn', 'ms', 'no', 'fa', 'pl', 'pt', 'ro', 'ru', 'sr', 'sk', 'sl', 'so', 'es', 'sv', 'th', 'tr', 'uk', 'uz', 'vi']." + ] + } + }, + "Invalid document age value": { + "summary": "`poa_document_age_months` value outside 1–120 / -1", + "value": { + "poa_document_age_months": [ + "Invalid integer value for 'utility_bill': '999'. Must be -1 (unlimited) or a positive integer between 1 and 120." + ] + } + }, + "Extraction failure": { + "summary": "The document could not be processed by the extraction pipeline", + "value": { + "error": [ + "Error extracting POA information" + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any document processing happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X POST 'https://verification.didit.me/v3/poa/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'document=@./utility-bill.pdf' \\\n -F 'expected_address=Av. Monseñor Tavella 1291, Salta, Argentina' \\\n -F 'expected_country=ARG' \\\n -F 'expected_first_name=Sophia' \\\n -F 'expected_last_name=Martinez' \\\n -F 'poa_languages_allowed=en,es' \\\n -F 'vendor_data=user-123' \\\n --max-time 45" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nurl = 'https://verification.didit.me/v3/poa/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('utility-bill.pdf', 'rb') as f:\n files = {'document': ('utility-bill.pdf', f, 'application/pdf')}\n data = {\n 'expected_address': 'Av. Monseñor Tavella 1291, Salta, Argentina',\n 'expected_country': 'ARG',\n 'expected_first_name': 'Sophia',\n 'expected_last_name': 'Martinez',\n 'poa_languages_allowed': 'en,es',\n 'vendor_data': 'user-123',\n }\n # LLM-based extraction takes 5-15s (multi-page PDFs up to ~30s) - allow headroom\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=45)\n\nresp.raise_for_status()\npoa = resp.json()['poa']\nprint('status:', poa['status'])\nprint('issuer:', poa['issuer'], '| issued:', poa['issue_date'])\nprint('address:', poa['poa_formatted_address'])\nfor w in poa['warnings']:\n print('warning:', w['risk'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('document', new Blob([fs.readFileSync('./utility-bill.pdf')]), 'utility-bill.pdf');\nform.append('expected_address', 'Av. Monseñor Tavella 1291, Salta, Argentina');\nform.append('expected_country', 'ARG');\nform.append('expected_first_name', 'Sophia');\nform.append('expected_last_name', 'Martinez');\nform.append('poa_languages_allowed', 'en,es');\nform.append('vendor_data', 'user-123');\n\n// LLM-based extraction takes 5-15s (multi-page PDFs up to ~30s) - allow >=45s\nconst response = await fetch('https://verification.didit.me/v3/poa/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n signal: AbortSignal.timeout(45_000),\n});\n\nif (!response.ok) throw new Error(`POA verification failed: ${response.status}`);\nconst { poa } = await response.json();\nconsole.log('status:', poa.status);\nconsole.log('warnings:', poa.warnings.map((w) => w.risk));" + } + ] + } + }, + "/v3/document-ai/": { + "post": { + "summary": "Document AI (custom field extraction + tamper checks)", + "description": "Extract the fields **you** define from any supporting document — payslips, proof of funds, source-of-wealth letters, tax statements, contracts, invoices — in one call. Send the file plus a `fields` definition (key, name, extraction instruction, type) and get back typed values (`text`, `number`, `date` as `YYYY-MM-DD`), an optional name-match score against the expected person or company, PDF/EXIF tamper signals, an `Approved`/`Declined`/`In Review` `status`, and a `warnings` list explaining every issue found. This is the server-to-server counterpart of the hosted Document AI workflow step (see `/core-technology/document-ai/overview`); it uses Didit's default extraction model and does not expose provider selection.\n\n**Latency.** Extraction is LLM-based: typical calls take **5–15 seconds**, multi-page PDFs up to **~30 seconds**. Configure a client timeout of **at least 45 seconds** and do not retry before the call completes.\n\n**Request format.** `multipart/form-data` only. `fields` is **one JSON-encoded array string** (1–30 items, at most 16 KB); bracket-style form arrays are rejected with `400`. Each item is `{key, name, instruction, type, required, is_full_name}` — `key` is the JSON key you get back (`^[a-z0-9_]{1,64}$`, unique), `instruction` tells the model what to read, `type` is `text` (default) | `number` | `date`, `required` raises `DOCUMENT_AI_MISSING_REQUIRED_FIELDS` when the value cannot be read, and at most one field may set `is_full_name: true` to mark the value compared against the expected name.\n\n**Name matching.** Pass `expected_first_name` + `expected_last_name` (person) **or** `expected_company_name` (+ optional `expected_company_country`) — not both. The value of the `is_full_name` field (or, when none is marked, the first text field whose key looks like a name) is compared fuzzily; `name_match_score` (0–100) and `name_on_document` are always returned when both sides exist, and a score below `document_ai_name_match_score_threshold` (default 80) adds `DOCUMENT_AI_NAME_MISMATCH`, governed by `document_ai_name_mismatch_action`.\n\n**Decision logic.** Each risk maps to a configurable action: `DECLINE` → `status: \"Declined\"`, `REVIEW` → `status: \"In Review\"`, `NO_ACTION` → informational only. Defaults mirror the workflow step: unreadable document `REVIEW`, missing required fields `REVIEW`, suspected manipulation `DECLINE`, name mismatch `NO_ACTION`, unsupported file `REVIEW`. A document that cannot be processed at all returns `400`; a readable but problematic document returns `200` with `Declined`/`In Review` — always inspect `document_ai.status` and `document_ai.warnings`, not just the HTTP code.\n\n**Billing.** Each `200` response consumes one Document AI API credit — **$0.20 per document, no free tier** (standalone APIs never have one). When the organization's balance cannot cover the call, the endpoint returns `403` before any processing. Sandbox API keys return a deterministic mock response and are not charged.\n\n**Persistence.** With `save_api_request=true` (default) the call is stored as an API-type session: the document, the extracted values, the field definitions and the name-match snapshot are kept, the session shows up in the Business Console, and `request_id` works with `GET /v3/session/{sessionId}/decision/`.", + "operationId": "post_v3document-ai", + "x-didit-sdk": { + "resource": "document_ai", + "method": "analyze", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "document", + "fields" + ], + "properties": { + "document": { + "type": "string", + "format": "binary", + "description": "The document to read. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `pdf`, `webp`, `zip`. Maximum upload size: **30 MB** (larger files are rejected with `400`). Multi-page PDFs are supported (expect higher latency); encrypted PDFs are rejected with `400`. Images are automatically compressed before processing." + }, + "fields": { + "type": "string", + "description": "One JSON-encoded array string with **1–30** field definitions (maximum 16 KB). Each item: `key` (required, `^[a-z0-9_]{1,64}$`, unique — the key returned in `extracted_data`), `name` (≤100 chars, shown in messages), `instruction` (≤500 chars, what the model should read), `type` (`text` default | `number` | `date`), `required` (boolean, default `false`), `is_full_name` (boolean, default `false`, at most one per request). Bracket-style form arrays, invalid JSON, duplicate keys, or more than one `is_full_name` return `400` before any processing or billing.", + "example": "[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]" + }, + "expected_first_name": { + "type": "string", + "description": "First name to verify against the document's name field (fuzzy match, transliteration-aware). Use together with `expected_last_name`; cannot be combined with `expected_company_name`.", + "example": "Sophia" + }, + "expected_last_name": { + "type": "string", + "description": "Last name to verify against the document's name field, combined with `expected_first_name` for `name_match_score`.", + "example": "Martinez" + }, + "expected_company_name": { + "type": "string", + "description": "Company name to verify against the document's name field (for business documents). Cannot be combined with `expected_first_name` / `expected_last_name` (no example value on purpose — the generated request sample uses the person fields; swap them for this one when verifying a business document)." + }, + "expected_company_country": { + "type": "string", + "description": "Optional ISO 3166-1 alpha-2 country code (two ASCII letters, e.g. `FR`) that tunes company-name normalization for `expected_company_name`. Only used with `expected_company_name`." + }, + "document_ai_name_match_score_threshold": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "default": 80, + "description": "Minimum `name_match_score` (0–100) to consider the name matched. A lower score adds `DOCUMENT_AI_NAME_MISMATCH`, governed by `document_ai_name_mismatch_action`.", + "example": 80 + }, + "document_ai_unreadable_document_action": { + "type": "string", + "enum": [ + "DECLINE", + "REVIEW", + "NO_ACTION" + ], + "default": "REVIEW", + "description": "What to do when no field could be read from the document (`DOCUMENT_AI_UNREADABLE_DOCUMENT`)." + }, + "document_ai_missing_required_fields_action": { + "type": "string", + "enum": [ + "DECLINE", + "REVIEW", + "NO_ACTION" + ], + "default": "REVIEW", + "description": "What to do when at least one `required` field could not be read (`DOCUMENT_AI_MISSING_REQUIRED_FIELDS`; `additional_data.missing_fields` lists them)." + }, + "document_ai_document_tampering_action": { + "type": "string", + "enum": [ + "DECLINE", + "REVIEW", + "NO_ACTION" + ], + "default": "DECLINE", + "description": "What to do when PDF/EXIF forensics suspect manipulation — modified after digital signing, known PDF editor, overlay-text manipulation, suspicious re-export, image anomalies (`DOCUMENT_AI_SUSPECTED_MANIPULATION`)." + }, + "document_ai_name_mismatch_action": { + "type": "string", + "enum": [ + "DECLINE", + "REVIEW", + "NO_ACTION" + ], + "default": "NO_ACTION", + "description": "What to do when `name_match_score` is below the threshold (`DOCUMENT_AI_NAME_MISMATCH`)." + }, + "document_ai_unsupported_file_action": { + "type": "string", + "enum": [ + "DECLINE", + "REVIEW", + "NO_ACTION" + ], + "default": "REVIEW", + "description": "What to do when the upload is empty or not a processable file (`DOCUMENT_AI_UNSUPPORTED_FILE`)." + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, with the uploaded document, the extracted values, the field definitions and the name-match snapshot stored. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only; the call is still billed.", + "example": true + }, + "vendor_data": { + "type": "string", + "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", + "example": "user-123" + }, + "metadata": { + "type": "object", + "additionalProperties": true, + "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"onboarding\"}`) — it is parsed into an object.", + "example": { + "flow": "onboarding" + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Document processed. `document_ai.status` is `Approved`, `Declined`, or `In Review`; every detected issue is itemized in `document_ai.warnings`. A problematic document still returns `200` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", + "content": { + "application/json": { + "examples": { + "Approved": { + "summary": "Payslip read cleanly, name matched", + "value": { + "request_id": "9d6a3b1e-2f0c-4a8e-9b7d-1c2e3f4a5b6c", + "document_ai": { + "status": "Approved", + "extracted_data": { + "employer_name": "Acme Payments Ltd", + "employee_name": "Sophia Martinez", + "net_pay": 3120.5, + "pay_date": "2026-07-31" + }, + "detected_codes": [ + { + "type": "CODE_128", + "page_number": 1, + "position": [ + [ + 96, + 456 + ], + [ + 96, + 616 + ], + [ + 1012, + 611 + ], + [ + 1015, + 456 + ] + ], + "x": 96, + "y": 456, + "width": 919, + "height": 160, + "page_width": 1200, + "page_height": 1700, + "raw_payload": "SU5WLTIwMjYtMDA0Mg==", + "parsed_payload": "INV-2026-0042" + } + ], + "fields": [ + { + "key": "employer_name", + "name": "Employer name", + "instruction": "The employer or company issuing the payslip.", + "type": "text", + "required": true, + "is_full_name": false + }, + { + "key": "employee_name", + "name": "Employee name", + "instruction": "The full name of the employee as printed on the payslip.", + "type": "text", + "required": true, + "is_full_name": true + }, + { + "key": "net_pay", + "name": "Net pay", + "instruction": "The net amount paid to the employee for the period.", + "type": "number", + "required": true, + "is_full_name": false + }, + { + "key": "pay_date", + "name": "Pay date", + "instruction": "The payment date shown on the payslip.", + "type": "date", + "required": false, + "is_full_name": false + } + ], + "name_match_score": 100.0, + "name_on_document": "Sophia Martinez", + "warnings": [] + }, + "vendor_data": "user-123", + "metadata": { + "flow": "onboarding" + }, + "created_at": "2026-08-19T16:40:12.318Z" + } + }, + "In Review — missing required field": { + "summary": "`net_pay` could not be read; default action REVIEW", + "value": { + "request_id": "3f2a9c7b-5d4e-4b1a-8c6f-0e9d8c7b6a5f", + "document_ai": { + "status": "In Review", + "extracted_data": { + "employer_name": "Acme Payments Ltd", + "employee_name": "Sophia Martinez", + "net_pay": null, + "pay_date": "2026-07-31" + }, + "detected_codes": [], + "fields": [ + { + "key": "employer_name", + "name": "Employer name", + "instruction": "The employer or company issuing the payslip.", + "type": "text", + "required": true, + "is_full_name": false + }, + { + "key": "employee_name", + "name": "Employee name", + "instruction": "The full name of the employee as printed on the payslip.", + "type": "text", + "required": true, + "is_full_name": true + }, + { + "key": "net_pay", + "name": "Net pay", + "instruction": "The net amount paid to the employee for the period.", + "type": "number", + "required": true, + "is_full_name": false + }, + { + "key": "pay_date", + "name": "Pay date", + "instruction": "The payment date shown on the payslip.", + "type": "date", + "required": false, + "is_full_name": false + } + ], + "name_match_score": 100.0, + "name_on_document": "Sophia Martinez", + "warnings": [ + { + "risk": "DOCUMENT_AI_MISSING_REQUIRED_FIELDS", + "feature": "DOCUMENT_AI", + "additional_data": { + "missing_fields": [ + "Net pay" + ] + }, + "log_type": "warning", + "short_description": "Required fields could not be extracted", + "long_description": "One or more fields configured as required could not be extracted from the uploaded document." + } + ] + }, + "vendor_data": null, + "metadata": null, + "created_at": "2026-08-19T16:41:05.002Z" + } + }, + "Declined — suspected manipulation": { + "summary": "PDF forensics flagged a known editor; default action DECLINE", + "value": { + "request_id": "7c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f", + "document_ai": { + "status": "Declined", + "extracted_data": { + "employer_name": "Acme Payments Ltd", + "employee_name": "Sophia Martinez", + "net_pay": 3120.5, + "pay_date": "2026-07-31" + }, + "detected_codes": [], + "fields": [ + { + "key": "employer_name", + "name": "Employer name", + "instruction": "The employer or company issuing the payslip.", + "type": "text", + "required": true, + "is_full_name": false + }, + { + "key": "employee_name", + "name": "Employee name", + "instruction": "The full name of the employee as printed on the payslip.", + "type": "text", + "required": true, + "is_full_name": true + }, + { + "key": "net_pay", + "name": "Net pay", + "instruction": "The net amount paid to the employee for the period.", + "type": "number", + "required": true, + "is_full_name": false + }, + { + "key": "pay_date", + "name": "Pay date", + "instruction": "The payment date shown on the payslip.", + "type": "date", + "required": false, + "is_full_name": false + } + ], + "name_match_score": null, + "name_on_document": "Sophia Martinez", + "warnings": [ + { + "risk": "DOCUMENT_AI_SUSPECTED_MANIPULATION", + "feature": "DOCUMENT_AI", + "additional_data": { + "detection_method": "known_editor", + "processed_by_known_editor": "ilovepdf" + }, + "log_type": "error", + "short_description": "Suspected document manipulation", + "long_description": "The system detected signs of potential manipulation or editing in the uploaded document." + } + ] + }, + "vendor_data": null, + "metadata": null, + "created_at": "2026-08-19T16:42:40.771Z" + } + } + }, + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + }, + "document_ai": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "In Review" + ], + "description": "`Approved` when no warning resolves to a decline or review; `Declined` when at least one risk maps to `DECLINE`; otherwise `In Review` when at least one maps to `REVIEW`. The `warnings` list explains why." + }, + "extracted_data": { + "type": "object", + "additionalProperties": true, + "description": "One entry per requested field key. Values are typed per the field `type` — strings for `text`, numbers for `number`, `YYYY-MM-DD` strings for `date` — or `null` when the value could not be read.", + "example": { + "employer_name": "Acme Payments Ltd", + "employee_name": "Sophia Martinez", + "net_pay": 3120.5, + "pay_date": "2026-07-31" + } + }, + "detected_codes": { + "type": "array", + "description": "QR codes and barcodes detected across every processed page. Empty when none are found. A located but undecodable QR code has `parsed_payload: null`.", + "items": { + "$ref": "#/components/schemas/DocumentAIDetectedCode" + } + }, + "fields": { + "type": "array", + "description": "The normalized field definitions that were used for this extraction, echoed back (also stored with the session when `save_api_request=true`).", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "instruction": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "text", + "number", + "date" + ] + }, + "required": { + "type": "boolean" + }, + "is_full_name": { + "type": "boolean" + } + } + } + }, + "name_match_score": { + "type": "number", + "nullable": true, + "minimum": 0, + "maximum": 100, + "description": "Fuzzy similarity (0–100) between `name_on_document` and the expected person/company name. Returned whenever both a name value and an expected name exist — on matches **and** mismatches; `null` otherwise.", + "example": 96.0 + }, + "name_on_document": { + "type": "string", + "nullable": true, + "description": "The name value taken from the document (the `is_full_name` field, or the first name-like text field) that was compared against the expected name. `null` when no name value was available.", + "example": "Sophia Martinez" + }, + "warnings": { + "type": "array", + "description": "Empty on a clean approval. Every entry explains one detected risk; entries with `log_type: \"error\"` set `status` to `Declined`, entries with `log_type: \"warning\"` set it to `In Review`.", + "items": { + "type": "object", + "properties": { + "risk": { + "type": "string", + "enum": [ + "DOCUMENT_AI_UNREADABLE_DOCUMENT", + "DOCUMENT_AI_MISSING_REQUIRED_FIELDS", + "DOCUMENT_AI_SUSPECTED_MANIPULATION", + "DOCUMENT_AI_NAME_MISMATCH", + "DOCUMENT_AI_UNSUPPORTED_FILE" + ], + "description": "Machine-readable risk code. Each one is governed by the matching `document_ai_*_action` request option.", + "example": "DOCUMENT_AI_MISSING_REQUIRED_FIELDS" + }, + "feature": { + "type": "string", + "enum": [ + "DOCUMENT_AI" + ], + "description": "Feature that raised the warning. Always `DOCUMENT_AI` on this endpoint." + }, + "additional_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Extra context for the risk: `{\"missing_fields\": [\"Net pay\"]}` for missing required fields, `{\"name_on_document\", \"score\", \"threshold\"}` for a name mismatch, and the forensic finding (`detection_method`, producer/editor details) for suspected manipulation. `null` when there is nothing to add.", + "example": { + "missing_fields": [ + "Net pay" + ] + } + }, + "log_type": { + "type": "string", + "enum": [ + "error", + "warning", + "information" + ], + "description": "Severity derived from the configured action: `error` (DECLINE), `warning` (REVIEW), `information` (NO_ACTION)." + }, + "short_description": { + "type": "string", + "description": "Human-readable one-liner for the risk.", + "example": "Required fields could not be extracted" + }, + "long_description": { + "type": "string", + "description": "Human-readable explanation, interpolated with `additional_data` where relevant." + } + } + } + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, or `null`." + }, + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` object you sent, or `null`." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the request was processed (UTC, ISO 8601)." + } + } + } + } + } + }, + "400": { + "description": "Validation or processing error. Field-level problems return DRF's standard envelope (one array of messages per offending field). Encrypted or unreadable documents and extraction failures return `{\"error\": ...}`.", + "content": { + "application/json": { + "examples": { + "Missing document": { + "summary": "`document` not included in the form data", + "value": { + "document": [ + "No file was submitted." + ] + } + }, + "Invalid fields payload": { + "summary": "`fields` is not one JSON-encoded array string", + "value": { + "fields": [ + "fields must be a valid JSON array string." + ] + } + }, + "Duplicate field keys": { + "summary": "Two items share the same `key`", + "value": { + "fields": [ + "Every fields item must have a unique key." + ] + } + }, + "Conflicting expectations": { + "summary": "Person and company expectations sent together", + "value": { + "non_field_errors": [ + "Provide either person expected-name fields or expected_company_name, not both." + ] + } + }, + "Invalid metadata": { + "summary": "`metadata` is not a JSON object (or contains a NUL character)", + "value": { + "metadata": [ + "Metadata must be a JSON object." + ] + } + }, + "Oversized vendor_data": { + "summary": "`vendor_data` over the 2,000-byte cap", + "value": { + "vendor_data": [ + "vendor_data is too long (maximum 2000 bytes)." + ] + } + }, + "Encrypted PDF": { + "summary": "Password-protected PDF", + "value": { + "error": "The PDF is encrypted. Please upload a decrypted PDF or a photo instead." + } + }, + "Extraction failed": { + "summary": "The extraction pipeline could not process the document", + "value": { + "error": [ + "Error extracting Document AI information" + ] + } + } + }, + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls — and contract-billed organizations whose rate card has no Document AI API entry — return `403` with `{\"error\": ...}` before any document processing happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + }, + "Contract rate missing": { + "summary": "Contract-billed organization whose rate card has no Document AI API entry", + "value": { + "error": "Document AI API is not included in your contract rate card. Contact support@didit.me to enable it." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "error": { + "type": "string" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "/v3/aml/": { + "post": { + "summary": "AML screening (standalone)", + "operationId": "post_v3aml", + "x-didit-sdk": { + "resource": "aml", + "method": "screen", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Optional retry-safety key, up to 255 characters. Reusing the same key with the same request body returns the original response without running or billing a second screening. Reusing the key with a different body returns `409`.", + "schema": { + "type": "string", + "maxLength": 255 + } + } + ], + "description": "Screen a person or company against PEP, sanctions, warning/watchlist, and adverse-media datasets in one call.\n\n**Two-score model.** Each hit carries a `match_score` (0–100, identity confidence) and a `risk_score` (0–100, entity risk). Hits whose `match_score` is below `aml_match_score_threshold` (default **93**) are tagged `review_status: \"False Positive\"` and ignored for risk purposes; the highest `risk_score` among the remaining hits becomes `aml.score`. The final `aml.status` follows your thresholds: `score` > `aml_score_review_threshold` (default **100**) → `Declined`; `score` > `aml_score_approve_threshold` (default **80**) → `In Review`; otherwise `Approved`. Whenever `score` is non-zero and ≥ the approve threshold a `POSSIBLE_MATCH_FOUND` warning is added (a score of exactly 0 never warns).\n\n**Match-score weights.** `aml_name_weight` + `aml_dob_weight` + `aml_country_weight` must sum to exactly 100 (defaults 60/25/15). Components missing from the request are re-normalized away; a matching `document_number` acts as a golden key that overrides `match_score` to 100.\n\n**Latency.** Plain screenings typically complete in a few seconds. With `include_adverse_media=true` the API waits for adverse-media enrichment (an initial ~5 s pause plus up to ~25 s of polling) — configure a client timeout of **at least 60 seconds**.\n\n**Persistence.** `save_api_request` defaults to **true**: the screening is stored as a session (`request_id` works with `GET /v3/session/{sessionId}/decision/`), it appears in the console, a `status.updated` webhook fires, and hit review statuses can be updated later. With `save_api_request=false` nothing is stored and `request_id` is a transient correlation id. `include_ongoing_monitoring=true` keeps re-screening the profile and requires `save_api_request=true` (enforced with a `400`).\n\n**Idempotency.** Send an `Idempotency-Key` header when retrying after a timeout or transient error. The key is scoped to your application and the exact request body. The first completed request stores its response; later identical retries return that same response and do not run or bill another AML screening. If the original request is still running, retry later with the same key. If the same key is reused with a different body, the API returns `409`.\n\n**Billing.** One AML API credit is charged after the first successful screening. Replayed idempotent responses are not charged again; an insufficient balance returns `403` before any new screening happens.\n\n**Sandbox.** Keys from sandbox applications skip screening and billing entirely and return a mocked result — `Approved` with zero hits unless you pass `sandbox_scenario`. `save_api_request` works the same as on live keys, so a saved sandbox screening is retrievable through `GET /v3/session/{sessionId}/decision/` and `GET /v3/session/{sessionId}/generate-pdf/`.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "full_name" + ], + "properties": { + "full_name": { + "type": "string", + "minLength": 2, + "description": "Full name of the person or company to screen. Minimum **2 characters** - the screening provider's own floor. A shorter or blank name returns `400` on this field before any screening runs, so nothing is billed.", + "example": "Antonio Ejemplo Modelo" + }, + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ], + "default": "person", + "description": "Type of entity to screen. Defaults to `person`." + }, + "date_of_birth": { + "type": "string", + "format": "date", + "description": "Date of birth (persons) or incorporation date (companies), `YYYY-MM-DD`. Improves match precision via the DOB weight.", + "example": "1972-02-29" + }, + "nationality": { + "type": "string", + "description": "ISO 3166-1 **alpha-2** country code (e.g. `ES`). Invalid codes return `400`. Improves match precision via the country weight.", + "example": "ES" + }, + "document_number": { + "type": "string", + "description": "Identity document number. Not weighted — acts as a golden key: an exact match overrides the hit's `match_score` to 100, a hard mismatch penalizes it. Sent to the screening provider as a search filter only when it is **2-50 characters**; a value outside that range is accepted and stored, and the screening still runs and is billed, just not narrowed by document number." + }, + "aml_score_approve_threshold": { + "type": "number", + "minimum": 0, + "maximum": 100, + "default": 80, + "description": "Risk-score threshold at or below which the result is `Approved`. Must be ≤ `aml_score_review_threshold` (else `400`)." + }, + "aml_score_review_threshold": { + "type": "number", + "minimum": 0, + "maximum": 100, + "default": 100, + "description": "Risk-score threshold above which the result is `Declined`. Scores between the two thresholds produce `In Review`." + }, + "aml_name_weight": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "default": 60, + "description": "Weight of name similarity in the per-hit match score. The three weights must sum to exactly 100 (else `400`)." + }, + "aml_dob_weight": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "default": 25, + "description": "Weight of date of birth in the per-hit match score." + }, + "aml_country_weight": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "default": 15, + "description": "Weight of country/nationality in the per-hit match score." + }, + "aml_match_score_threshold": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "default": 93, + "description": "Per-hit cutoff: hits with `match_score` below this are `False Positive` (excluded from risk assessment); at or above are `Unreviewed` possible matches." + }, + "include_adverse_media": { + "type": "boolean", + "default": false, + "description": "Also search news media for negative coverage. Adds up to ~30 s of latency while results are gathered — use a ≥60 s client timeout." + }, + "include_ongoing_monitoring": { + "type": "boolean", + "default": false, + "description": "Keep re-screening this profile and push changes via webhook. Requires `save_api_request=true` (else `400`)." + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "Persist the screening as a session (console visibility, decision endpoint, webhooks, hit review). Set to `false` for a stateless check." + }, + "vendor_data": { + "type": "string", + "description": "Your identifier for the screened user; echoed back and stored with the session." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Free-form JSON stored with the request and echoed back." + }, + "sandbox_scenario": { + "type": "string", + "description": "Sandbox-only scenario slug. Only accepted when the API key belongs to a sandbox-mode application; rejected on live applications. A standalone screening has no session to arm, so the slug goes on the screening request itself: `decline_aml_hit` returns one production-shaped hit and settles the screening as `Declined`, `review_aml_possible_match` settles it `In Review`, and a scenario that arms no AML outcome leaves the screening clean. See the scenario catalog at GET /v1/sandbox/scenarios/ or [Sandbox & Test Data](/integration/sandbox-testing).", + "example": "decline_aml_hit" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/aml/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -H 'Idempotency-Key: aml-user-1234-2026-08-20' \\\n -d '{\n \"full_name\": \"Antonio Ejemplo Modelo\",\n \"entity_type\": \"person\",\n \"date_of_birth\": \"1972-02-29\",\n \"nationality\": \"ES\",\n \"include_adverse_media\": false,\n \"vendor_data\": \"user-1234\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/aml/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n \"Idempotency-Key\": \"aml-user-1234-2026-08-20\",\n },\n json={\n \"full_name\": \"Antonio Ejemplo Modelo\",\n \"entity_type\": \"person\",\n \"date_of_birth\": \"1972-02-29\",\n \"nationality\": \"ES\",\n \"include_adverse_media\": False,\n \"vendor_data\": \"user-1234\",\n },\n timeout=60, # adverse media enrichment can add ~30s\n)\nresp.raise_for_status()\nprint(resp.json()[\"aml\"][\"status\"]) # Approved / In Review / Declined" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/aml/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n 'Idempotency-Key': 'aml-user-1234-2026-08-20',\n },\n body: JSON.stringify({\n full_name: 'Antonio Ejemplo Modelo',\n entity_type: 'person',\n date_of_birth: '1972-02-29',\n nationality: 'ES',\n include_adverse_media: false,\n vendor_data: 'user-1234',\n }),\n});\nconst data = await res.json();\nconsole.log(data.aml.status);" + } + ], + "responses": { + "200": { + "description": "Screening completed. `aml.status` reflects your thresholds; every hit at or above the internal inclusion floor is listed in `aml.hits` with its identity `match_score`, entity `risk_score`, and a `score_breakdown` explaining the match.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + }, + "aml": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "In Review", + "Declined" + ], + "description": "`Declined` when `score` > `aml_score_review_threshold`; `In Review` when `score` > `aml_score_approve_threshold`; `Approved` otherwise (including when no score is available)." + }, + "total_hits": { + "type": "integer", + "description": "Number of entries in `hits`." + }, + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ], + "description": "Echo of the screened entity type." + }, + "hits": { + "type": "array", + "items": { + "type": "object", + "description": "One screening hit. `match_score` (0–100) measures identity confidence — hits below `aml_match_score_threshold` are tagged `review_status: \"False Positive\"` and excluded from the risk assessment; hits at or above it are `\"Unreviewed\"` possible matches. `risk_score` (0–100) measures how risky the matched entity is and drives the top-level `aml.score`.", + "properties": { + "id": { + "type": "string", + "nullable": true, + "description": "Stable identifier of the matched entity in the screening datasets." + }, + "url": { + "type": "string", + "nullable": true, + "description": "Source URL for the matched entity, when available." + }, + "match": { + "type": "boolean", + "nullable": true, + "description": "Legacy exact-match flag. Prefer `match_score` + `review_status`." + }, + "score": { + "type": "number", + "nullable": true, + "description": "DEPRECATED weighted match score in the 0–1 range. Use `match_score` instead." + }, + "match_score": { + "type": "number", + "nullable": true, + "description": "Identity confidence (0–100). Compared against `aml_match_score_threshold` to split False Positives from Possible Matches." + }, + "risk_score": { + "type": "number", + "nullable": true, + "description": "Entity risk score (0–100). The highest `risk_score` among non-false-positive hits becomes the top-level `aml.score`." + }, + "target": { + "type": "boolean", + "nullable": true + }, + "caption": { + "type": "string", + "nullable": true, + "description": "Display name of the matched entity." + }, + "datasets": { + "type": "array", + "nullable": true, + "items": { + "type": "string" + }, + "description": "Datasets the entity appears in (e.g. `PEP`, `PEP Level 1`, sanctions list names)." + }, + "features": { + "type": "object", + "nullable": true + }, + "properties": { + "type": "object", + "nullable": true, + "description": "Entity profile: `name`, `alias`, `notes`, `gender`, `birthDate`, `birthPlace`, `nationality`, `position`, and other biographical arrays (each nullable)." + }, + "rca_name": { + "type": "string", + "nullable": true, + "description": "Name of the related close associate through which the entity was matched, when the hit is an RCA." + }, + "pep_matches": { + "type": "array", + "description": "PEP list entries backing this hit.", + "items": { + "type": "object", + "properties": { + "list_name": { + "type": "string" + }, + "publisher": { + "type": "string" + }, + "source_url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "matched_name": { + "type": "string" + }, + "pep_position": { + "type": "string" + }, + "date_of_birth": { + "type": "string" + }, + "place_of_birth": { + "type": "string" + }, + "aliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "education": { + "type": "array", + "items": { + "type": "string" + } + }, + "other_sources": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "sanction_matches": { + "type": "array", + "items": { + "type": "object" + }, + "description": "Sanctions list entries backing this hit (same backing-list shape as `pep_matches`, plus sanction-specific fields)." + }, + "warning_matches": { + "type": "array", + "items": { + "type": "object" + }, + "description": "Warning/watchlist entries backing this hit." + }, + "adverse_media_matches": { + "type": "array", + "description": "Adverse-media articles. Populated only when `include_adverse_media=true`.", + "items": { + "type": "object", + "properties": { + "headline": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "sentiment": { + "type": "string" + }, + "sentiment_score": { + "type": "number" + }, + "source_url": { + "type": "string" + }, + "thumbnail": { + "type": "string", + "nullable": true + }, + "country": { + "type": "string", + "nullable": true + }, + "author_name": { + "type": "string", + "nullable": true + }, + "publication_date": { + "type": "string", + "nullable": true + }, + "adverse_keywords": { + "type": "array", + "items": { + "type": "string" + } + }, + "other_sources": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "adverse_media_details": { + "type": "object", + "nullable": true, + "description": "Aggregated adverse-media sentiment for the entity (`sentiment`, `sentiment_score`, `adverse_keywords` frequency map). Populated only when `include_adverse_media=true`." + }, + "first_seen": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the entity first appeared in the datasets." + }, + "last_seen": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Most recent dataset refresh that included the entity." + }, + "linked_entities": { + "type": "array", + "description": "Known relationships of the matched entity (family, associates, companies).", + "items": { + "type": "object", + "properties": { + "name": { + "type": "array", + "items": { + "type": "string" + } + }, + "relation": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "array", + "items": { + "type": "string" + } + }, + "details": { + "type": "array", + "items": { + "type": "string" + } + }, + "active": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "risk_view": { + "type": "object", + "nullable": true, + "description": "Per-dimension risk breakdown (`crimes`, `countries`, `categories`, `custom_list`), each with `score`, `weightage`, `risk_level`, and a `risk_scores` map." + }, + "additional_information": { + "type": "object", + "nullable": true, + "description": "Free-form extra context, e.g. a `flag_summary` array explaining why the entity is flagged." + }, + "review_status": { + "type": "string", + "nullable": true, + "enum": [ + "False Positive", + "Unreviewed", + "Confirmed Match", + "Inconclusive" + ], + "description": "`False Positive` — `match_score` below `aml_match_score_threshold`, excluded from risk assessment. `Unreviewed` — possible match awaiting review. `Confirmed Match` / `Inconclusive` — set manually by a reviewer (via the Business Console) on saved sessions; that review capability is not part of this spec." + }, + "score_breakdown": { + "type": "object", + "nullable": true, + "description": "How `match_score` was computed. Name, date of birth, and country each contribute `_score` × `_weight_normalized` points; weights are re-normalized when a component is missing from the screened data. The document number is not weighted — it acts as a 'golden key': `MATCH` overrides the score to 100, `NEUTRAL` leaves it unchanged, `HARD_MISMATCH` applies a penalty.", + "properties": { + "name_score": { + "type": "integer", + "nullable": true, + "description": "Raw name similarity (0–100)." + }, + "name_weight": { + "type": "integer", + "nullable": true, + "description": "Configured name weight (`aml_name_weight`)." + }, + "name_weight_normalized": { + "type": "number", + "nullable": true, + "description": "Name weight after re-normalization (0–100)." + }, + "name_contribution": { + "type": "number", + "nullable": true, + "description": "Points the name contributed to `total_score`." + }, + "dob_score": { + "type": "integer", + "nullable": true, + "description": "Date-of-birth score as a percentage of its weight (−100 to 100)." + }, + "dob_weight": { + "type": "integer", + "nullable": true, + "description": "Configured DOB weight (`aml_dob_weight`)." + }, + "dob_weight_normalized": { + "type": "number", + "nullable": true, + "description": "DOB weight after re-normalization (0–100)." + }, + "dob_contribution": { + "type": "number", + "nullable": true, + "description": "Points the DOB contributed to `total_score`." + }, + "country_score": { + "type": "integer", + "nullable": true, + "description": "Country score as a percentage of its weight (−100 to 100)." + }, + "country_weight": { + "type": "integer", + "nullable": true, + "description": "Configured country weight (`aml_country_weight`)." + }, + "country_weight_normalized": { + "type": "number", + "nullable": true, + "description": "Country weight after re-normalization (0–100)." + }, + "country_contribution": { + "type": "number", + "nullable": true, + "description": "Points the country contributed to `total_score`." + }, + "document_number_match_type": { + "type": "string", + "nullable": true, + "enum": [ + "MATCH", + "NEUTRAL", + "HARD_MISMATCH" + ], + "description": "Golden-key result for the document number comparison." + }, + "document_number_effect": { + "type": "string", + "nullable": true, + "description": "Human-readable description of the document-number effect." + }, + "total_score": { + "type": "integer", + "nullable": true, + "description": "Final weighted match score (0–100) — mirrors `match_score`." + } + } + } + } + } + }, + "score": { + "type": "number", + "nullable": true, + "description": "Overall AML risk score (0–100): the highest `risk_score` among hits that are not False Positives. Drives `status` via the two thresholds." + }, + "screened_data": { + "type": "object", + "description": "Echo of what was screened: `full_name`, `date_of_birth`, `nationality`, `document_number` (absent fields are `null`).", + "properties": { + "full_name": { + "type": "string" + }, + "date_of_birth": { + "type": "string", + "nullable": true + }, + "nationality": { + "type": "string", + "nullable": true + }, + "document_number": { + "type": "string", + "nullable": true + } + } + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "description": "Feature that raised the warning." + }, + "risk": { + "type": "string", + "description": "Machine-readable risk identifier." + }, + "additional_data": { + "type": "object", + "nullable": true, + "description": "Extra context for the warning, when available." + }, + "log_type": { + "type": "string", + "description": "`warning`, `information`, or `error` — how the risk affected the outcome." + }, + "short_description": { + "type": "string", + "description": "One-line human-readable summary." + }, + "long_description": { + "type": "string", + "description": "Full human-readable explanation." + } + } + }, + "description": "Contains a `POSSIBLE_MATCH_FOUND` warning whenever `score` is non-zero and ≥ `aml_score_approve_threshold` (a score of exactly 0 never warns); empty otherwise." + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Echo of the `metadata` you sent." + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "examples": { + "No hits (Approved)": { + "summary": "Clean screening — zero hits", + "value": { + "request_id": "d27c5509-7a66-45a9-b05e-d69d9f4c336a", + "aml": { + "status": "Approved", + "total_hits": 0, + "entity_type": "person", + "hits": [], + "score": 0, + "screened_data": { + "full_name": "Maximilian Mustermann-Doe", + "date_of_birth": "1992-03-14", + "nationality": "DE", + "document_number": null + }, + "warnings": [] + }, + "vendor_data": "user-1234", + "metadata": { + "internal_ref": "abc-123" + }, + "created_at": "2026-06-11T10:21:07.013938+00:00" + } + }, + "PEP hit (Approved — risk below approve threshold)": { + "summary": "One PEP possible match; risk score 73 stays under the default approve threshold of 80", + "value": { + "request_id": "0b7e7a3e-65a1-4f3d-9c7a-1de2f9a40111", + "aml": { + "status": "Approved", + "total_hits": 1, + "entity_type": "person", + "hits": [ + { + "id": "kDpfszrbkDdaxpMVZw3NLg", + "url": "https://news.example.org/articles/sample", + "match": false, + "score": 0.93, + "match_score": 93.0, + "risk_score": 73.0, + "target": null, + "caption": "Antonio Ejemplo Modelo", + "datasets": [ + "PEP Level 1", + "PEP" + ], + "features": null, + "rca_name": "", + "first_seen": "2026-01-07T00:00:00", + "last_seen": "2026-01-07T00:00:00", + "review_status": "Unreviewed", + "properties": { + "name": [ + "Antonio Ejemplo Modelo", + "Antonio Ejemplo" + ], + "alias": [ + "Antonio Ejemplo Modelo", + "Ejemplo Modelo, Pedro" + ], + "notes": [ + "Prime Minister of Spain" + ], + "gender": [ + "male" + ], + "birthDate": [ + "1972-02-29" + ], + "birthPlace": [ + "Madrid, Spain" + ], + "nationality": null, + "position": null + }, + "risk_view": { + "crimes": { + "score": 0.0, + "weightage": 20, + "risk_level": "Low", + "risk_scores": {} + }, + "countries": { + "score": 9.64, + "weightage": 30, + "risk_level": "Low", + "risk_scores": { + "Spain": 25.71 + } + }, + "categories": { + "score": 62.5, + "weightage": 50, + "risk_level": "High", + "risk_scores": { + "PEP": 100.0, + "PEP Level 1": 100.0 + } + }, + "custom_list": {} + }, + "score_breakdown": { + "name_score": 89, + "name_weight": 60, + "name_weight_normalized": 60.0, + "name_contribution": 53.4, + "dob_score": 100, + "dob_weight": 25, + "dob_weight_normalized": 25.0, + "dob_contribution": 25.0, + "country_score": 100, + "country_weight": 15, + "country_weight_normalized": 15.0, + "country_contribution": 15.0, + "document_number_match_type": "NEUTRAL", + "document_number_effect": "No document number provided for screening", + "total_score": 93 + }, + "pep_matches": [ + { + "aliases": [], + "education": [], + "list_name": "Presidency of the Government of Spain", + "publisher": "Presidency of the Government of Spain", + "source_url": "https://news.example.org/articles/sample", + "description": "The Presidency of the Government is the central institutional body in Spain that supports the Prime Minister and coordinates the general action of the central government.", + "matched_name": "Antonio Ejemplo Modelo", + "pep_position": "", + "date_of_birth": "1972-02-29", + "other_sources": [], + "place_of_birth": "Madrid, Spain" + } + ], + "sanction_matches": [], + "warning_matches": [], + "adverse_media_matches": [], + "adverse_media_details": null, + "linked_entities": [ + { + "name": [ + "Maria Begona Gomez Fernandez" + ], + "active": [], + "status": [], + "details": [], + "relation": [ + "spouse" + ] + } + ], + "additional_information": { + "flag_summary": [ + "Antonio Ejemplo Modelo has been flagged as a Level 1 Politically Exposed Person (PEP) from Spain, serving as the President of the Government of Spain." + ] + } + } + ], + "score": 73.0, + "screened_data": { + "full_name": "Pedro Sanchez Perez-Castejon", + "date_of_birth": "1972-02-29", + "nationality": "ES", + "document_number": null + }, + "warnings": [] + }, + "vendor_data": null, + "metadata": null, + "created_at": "2026-06-11T10:23:44.812000+00:00" + } + } + } + } + } + }, + "400": { + "description": "Validation error. Field-level problems return DRF's standard envelope (one array of messages per offending field); cross-field rules (weight sum, threshold ordering, monitoring-without-save) return `{\"error\": [...]}`.", + "content": { + "application/json": { + "examples": { + "Missing full_name": { + "summary": "`full_name` is the only required field", + "value": { + "full_name": [ + "This field is required." + ] + } + }, + "Short full_name": { + "summary": "`full_name` must be at least 2 characters", + "value": { + "full_name": [ + "Ensure this field has at least 2 characters." + ] + } + }, + "Invalid nationality": { + "summary": "`nationality` must be ISO 3166-1 alpha-2", + "value": { + "nationality": [ + "Invalid ISO 3166-1 alpha-2 country code." + ] + } + }, + "Weights do not sum to 100": { + "summary": "`aml_name_weight` + `aml_dob_weight` + `aml_country_weight` ≠ 100", + "value": { + "error": [ + "AML weights must sum to 100. Current sum: 90 (name: 50, dob: 25, country: 15)" + ] + } + }, + "Thresholds out of order": { + "summary": "Approve threshold above review threshold", + "value": { + "error": [ + "Approve threshold (90.0) cannot be greater than review threshold (80.0)" + ] + } + }, + "Monitoring without saving": { + "summary": "`include_ongoing_monitoring=true` with `save_api_request=false`", + "value": { + "error": [ + "Ongoing monitoring can only be enabled when the API request is saved." + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "/v3/face-search/": { + "post": { + "summary": "Face Search", + "description": "Search a face image (1:N) against your application's face search index — faces enrolled from verification sessions, saved standalone API calls, direct user-profile face uploads, and your block/allow lists — and get back ranked similarity matches plus blocklist and duplicate warnings.\n\n**How it works.** The largest detected face in `user_image` is embedded and searched against the index, scoped to your application. Up to **5 matches** above the similarity floor are returned in `matches`, each with `similarity_percentage`, the originating session (`session_id`, `session_number`, `status`, `vendor_data`), `user_details` extracted during that session's document verification, `is_blocklisted` / `is_allowlisted` flags, and a `source` discriminator (`session`, `imported`, or `list_entry`).\n\n- `search_type=most_similar` (default) ranks every enrolled face by similarity — use it for deduplication and fraud-ring investigation.\n- `search_type=blocklisted_or_approved` restricts candidates to blocklisted faces, allowlisted faces, faces from approved sessions, and imported user-profile faces, ranking blocklisted entries first — use it when the primary goal is blocklist screening.\n\n`status` is `Declined` only when a `FACE_IN_BLOCKLIST` or `POSSIBLE_FACE_IN_BLOCKLIST` warning fires; `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` (`information`) and `MULTIPLE_FACES_DETECTED` (`warning`) never decline. Unlike Passive Liveness, this endpoint does **not** exclude prior sessions with the same `vendor_data` from matching. A passive-liveness analysis also runs internally and is stored with the persisted session, but its result is not part of this response.\n\n**Billing.** Each `200` response consumes one Face Search API credit (standalone APIs have no free tier). Insufficient balance returns `403` before any image processing.\n\n**Session persistence and face enrollment (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session (Business Console, `GET /v3/session/{sessionId}/decision/` via the returned `request_id`, `status.updated` webhook) and the searched face is enrolled into your face search index. Faces enrolled by Face Search calls are excluded from future Face Search results, so repeated searches never match each other. When `false`, the call is one-shot: nothing is stored, the face is not enrolled, and `match_image_url` values are internal storage paths rather than downloadable URLs.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", + "operationId": "post_v3face-search", + "x-didit-sdk": { + "resource": "face_search", + "method": "search", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "user_image" + ], + "properties": { + "user_image": { + "type": "string", + "format": "binary", + "description": "Front-facing face image to search with. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.5 MB before processing, so very high resolutions do not improve accuracy. The image must contain at least one detectable face — otherwise the endpoint returns `400`. When several faces are present, the largest one is searched and a `MULTIPLE_FACES_DETECTED` warning is added." + }, + "search_type": { + "type": "string", + "default": "most_similar", + "enum": [ + "most_similar", + "blocklisted_or_approved" + ], + "description": "Search policy. `most_similar` (default) ranks every enrolled face in your application by similarity. `blocklisted_or_approved` restricts candidates to blocklisted faces, allowlisted faces, faces from approved sessions, and imported user-profile faces — with blocklisted entries ranked first.", + "example": "most_similar" + }, + "rotate_image": { + "type": "boolean", + "default": false, + "description": "When `true`, the service tries 90-degree rotations of the input and uses the orientation that yields the best face detection. Useful when EXIF orientation is missing. Adds latency.", + "example": false + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "When `true` (default), persists the call as an API-type session, emits a `status.updated` webhook, presigns `match_image_url` values, and enrolls the searched face into your face search index (Face Search enrollments are excluded from future searches). When `false`, the search is one-shot — no session is stored and the face is not enrolled.", + "example": true + }, + "vendor_data": { + "type": "string", + "description": "Optional opaque identifier (your user id, email, UUID…) stored on the persisted session and echoed back. It does not exclude same-user faces from the search results.", + "example": "user-123" + }, + "metadata": { + "type": "object", + "additionalProperties": true, + "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back. In multipart requests, send it as a JSON-encoded string field — it is parsed into an object.", + "example": { + "flow": "dedup_check" + } + } + } + }, + "example": { + "user_image": "(binary JPEG/PNG selfie)", + "search_type": "most_similar", + "rotate_image": false, + "save_api_request": true, + "vendor_data": "user-123", + "metadata": { + "flow": "dedup_check" + } + } + } + } + }, + "responses": { + "200": { + "description": "Face search completed. `face_search.matches` holds up to 5 similar faces ordered by similarity; an empty array means no enrolled face exceeded the similarity floor. `status` is `Declined` only on blocklist hits — duplicate matches alone return `Approved`, so inspect `matches` and `warnings`, not just `status`. When `save_api_request=true`, `request_id` is the persisted session id. Each match carries `source` (`session`, `imported`, `list_entry`, or `retained_template`), `vendor_user_id`, and `biometric_template_id`; a `retained_template` match comes from an image-free biometric template kept after the source session was deleted and has `null` session fields, `match_image_url`, and `user_details`.", + "content": { + "application/json": { + "examples": { + "Blocklist hit (Declined)": { + "summary": "The face matches a blocklisted face", + "value": { + "request_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef", + "face_search": { + "status": "Declined", + "total_matches": 1, + "matches": [ + { + "session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86", + "session_number": 323442, + "similarity_percentage": 99.99, + "source": "session", + "vendor_data": "user-1", + "verification_date": "2025-01-01T00:00:00Z", + "user_details": { + "full_name": "Jane Marie Doe", + "document_type": "ID", + "document_number": "X1234567" + }, + "match_image_url": "https:///face/3f6a1c2e/reference.jpg", + "status": "Approved", + "is_blocklisted": true, + "is_allowlisted": false, + "api_service": null, + "vendor_user_id": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f", + "biometric_template_id": null + } + ], + "user_image": { + "entities": [ + { + "bbox": [ + 40, + 40, + 120, + 120 + ], + "confidence": 0.732973 + } + ], + "best_angle": 0 + }, + "warnings": [ + { + "risk": "FACE_IN_BLOCKLIST", + "feature": "LIVENESS", + "additional_data": { + "blocklisted_session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86", + "blocklisted_session_number": 323442, + "api_service": null + }, + "log_type": "error", + "short_description": "Face in blocklist", + "long_description": "The system identified a face in the blocklist, which means the face is not allowed to be verified." + } + ] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T01:04:42.763237+00:00" + } + }, + "Duplicate found (Approved)": { + "summary": "The face already appeared in another approved session — informational, no decline", + "value": { + "request_id": "5e0c3a1f-7b2d-4c8e-9f10-2a3b4c5d6e7f", + "face_search": { + "status": "Approved", + "total_matches": 1, + "matches": [ + { + "session_id": "1f2e3d4c-5b6a-7980-1122-334455667788", + "session_number": 1024, + "similarity_percentage": 87.42, + "source": "session", + "vendor_data": "user-9", + "verification_date": "2025-11-20T09:15:00Z", + "user_details": null, + "match_image_url": "https:///face/9c8b7a6d/reference.jpg", + "status": "Approved", + "is_blocklisted": false, + "is_allowlisted": false, + "api_service": "PASSIVE_LIVENESS", + "vendor_user_id": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f", + "biometric_template_id": null + } + ], + "user_image": { + "entities": [ + { + "bbox": [ + 40, + 40, + 120, + 120 + ], + "confidence": 0.732973 + } + ], + "best_angle": 0 + }, + "warnings": [ + { + "risk": "DUPLICATED_FACE", + "feature": "LIVENESS", + "additional_data": { + "duplicated_session_id": "1f2e3d4c-5b6a-7980-1122-334455667788", + "duplicated_session_number": 1024, + "api_service": "PASSIVE_LIVENESS" + }, + "log_type": "information", + "short_description": "Duplicated face from other approved session", + "long_description": "The system identified a duplicated face from another approved session, requiring further investigation." + } + ] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T01:04:42.763237+00:00" + } + }, + "No matches (Approved)": { + "summary": "No enrolled face exceeded the similarity floor", + "value": { + "request_id": "9d8c7b6a-5f4e-3d2c-1b0a-998877665544", + "face_search": { + "status": "Approved", + "total_matches": 0, + "matches": [], + "user_image": { + "entities": [ + { + "bbox": [ + 40, + 40, + 120, + 120 + ], + "confidence": 0.732973 + } + ], + "best_angle": 0 + }, + "warnings": [] + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T01:04:42.763237+00:00" + } + } + }, + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + }, + "face_search": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined" + ], + "description": "`Declined` only when a `FACE_IN_BLOCKLIST` or `POSSIBLE_FACE_IN_BLOCKLIST` warning fired. Duplicate matches and multiple-face warnings never decline." + }, + "total_matches": { + "type": "integer", + "description": "Number of entries in `matches` (0–5).", + "example": 1 + }, + "matches": { + "type": "array", + "maxItems": 5, + "description": "Up to 5 faces from your face search index that exceed the similarity floor, ordered by similarity (with blocklisted entries ranked first when `search_type=blocklisted_or_approved`). Empty when no enrolled face is similar enough.", + "items": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Id of the verification session the matched face belongs to. `null` when `source` is `imported`, `list_entry`, or `retained_template`.", + "example": "882c42d5-8a4d-4d20-8080-a22f57822c86" + }, + "session_number": { + "type": "integer", + "nullable": true, + "description": "Human-friendly session number shown in the Business Console. `null` for non-session matches.", + "example": 323442 + }, + "similarity_percentage": { + "type": "number", + "format": "float", + "minimum": 0, + "maximum": 100, + "description": "Similarity between the submitted face and the matched face (0–100).", + "example": 99.99 + }, + "source": { + "type": "string", + "enum": [ + "session", + "imported", + "list_entry", + "retained_template" + ], + "description": "Where the matched face was enrolled from: `session` — a verification session or a saved standalone API call; `imported` — a face uploaded directly to a User profile; `list_entry` — a face attached to one of your lists; `retained_template` — an image-free biometric template stored on a User, listed under `/v3/biometric-templates/`." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of the matched session or linked User for imported and retained-template faces.", + "example": "user-1" + }, + "vendor_user_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Linked User id. `null` for list entries and session matches without a User.", + "example": "2f7c1a9e-4b28-4a6d-97f1-83576f80e1b4" + }, + "biometric_template_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Id of the retained biometric template. Set only when `source` is `retained_template`; `null` otherwise.", + "example": null + }, + "verification_date": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the matched face was originally captured, or when a retained template was created (`YYYY-MM-DDThh:mm:ssZ`). `null` for `list_entry` matches.", + "example": "2025-01-01T00:00:00Z" + }, + "user_details": { + "type": "object", + "nullable": true, + "description": "Identity data extracted during the matched session's document verification. `null` when the matched session has no document data (e.g. liveness-only sessions) and always `null` for `retained_template` matches. For `source: imported` matches it is populated from the User profile instead: `full_name` set, `document_type`/`document_number` null.", + "properties": { + "full_name": { + "type": "string", + "nullable": true, + "example": "Jane Marie Doe" + }, + "document_type": { + "type": "string", + "nullable": true, + "example": "ID" + }, + "document_number": { + "type": "string", + "nullable": true, + "example": "X1234567" + } + } + }, + "match_image_url": { + "type": "string", + "nullable": true, + "description": "Time-limited URL (`https:///...`) of the matched face's reference image when `save_api_request=true`. With `save_api_request=false` this is an internal storage path that is not directly downloadable. Always `null` for `retained_template` matches because retained templates contain no image.", + "example": "https:///face/3f6a1c2e/reference.jpg" + }, + "status": { + "type": "string", + "nullable": true, + "description": "Status of the matched session (`Approved`, `Declined`, `In Review`, …). `null` for `imported`/`list_entry` matches.", + "example": "Approved" + }, + "is_blocklisted": { + "type": "boolean", + "description": "`true` when the matched face is on your face blocklist. Any sufficiently similar blocklisted match declines the search." + }, + "is_allowlisted": { + "type": "boolean", + "description": "`true` when the matched face is on one of your allowlists. Allowlisted matches suppress duplicate warnings." + }, + "api_service": { + "type": "string", + "nullable": true, + "description": "Set when the matched face was enrolled by a standalone API call (e.g. `PASSIVE_LIVENESS`); `null` for regular verification sessions and imported faces.", + "example": null + } + } + } + }, + "user_image": { + "type": "object", + "description": "Face-detection results for the submitted image. Entries here carry only `bbox` and `confidence`.", + "properties": { + "entities": { + "type": "array", + "description": "One entry per detected face. The largest face is the one searched.", + "items": { + "type": "object", + "properties": { + "bbox": { + "type": "array", + "items": { + "type": "integer" + }, + "minItems": 4, + "maxItems": 4, + "description": "Bounding box `[x_min, y_min, x_max, y_max]` in pixels.", + "example": [ + 40, + 40, + 120, + 120 + ] + }, + "confidence": { + "type": "number", + "format": "float", + "minimum": 0, + "maximum": 1, + "description": "Face-detection confidence (0–1).", + "example": 0.732973 + } + } + } + }, + "best_angle": { + "type": "integer", + "description": "Rotation (degrees) that produced the best face detection; non-zero only when `rotate_image=true` corrected the orientation. Never null on this endpoint (defaults to 0 when the model omits it).", + "example": 0 + } + } + }, + "warnings": { + "type": "array", + "description": "Risk signals. `FACE_IN_BLOCKLIST` / `POSSIBLE_FACE_IN_BLOCKLIST` (`error`) decline the search; `MULTIPLE_FACES_DETECTED` (`warning`) and `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` (`information`) are advisory.", + "items": { + "type": "object", + "properties": { + "risk": { + "type": "string", + "enum": [ + "MULTIPLE_FACES_DETECTED", + "FACE_IN_BLOCKLIST", + "POSSIBLE_FACE_IN_BLOCKLIST", + "DUPLICATED_FACE", + "POSSIBLE_DUPLICATED_FACE" + ], + "description": "Machine-readable risk code." + }, + "feature": { + "type": "string", + "enum": [ + "LIVENESS" + ], + "description": "Feature that raised the warning. Always `LIVENESS` on this endpoint." + }, + "additional_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "`null` for `MULTIPLE_FACES_DETECTED`. Blocklist hits carry `{blocklisted_session_id, blocklisted_session_number, api_service}`; duplicate hits carry `{duplicated_session_id, duplicated_session_number, api_service}` pointing at the first matching session." + }, + "log_type": { + "type": "string", + "enum": [ + "error", + "warning", + "information" + ], + "description": "Severity. `error` warnings drive `status` to `Declined`; `warning` and `information` entries are advisory and never decline on their own." + }, + "short_description": { + "type": "string", + "description": "Human-readable one-line summary of the risk." + }, + "long_description": { + "type": "string", + "description": "Human-readable explanation of the risk." + } + } + } + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, or `null`." + }, + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` object you sent, or `null`." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp (UTC) of when the response was generated, e.g. `2026-06-12T01:04:42.763237+00:00`." + } + } + } + } + } + }, + "400": { + "description": "Validation error. Returned when `user_image` is missing, exceeds 5 MB, has an unsupported extension, or when **no face is detected** in the submitted image. Field errors use DRF's `{field: [messages]}` envelope; face-detection failures use `{\"error\": ...}`.", + "content": { + "application/json": { + "examples": { + "No face detected": { + "summary": "The image decodes fine but contains no detectable face", + "value": { + "error": "No face detected in the image" + } + }, + "Missing image": { + "summary": "`user_image` not included in the form data", + "value": { + "user_image": [ + "No file was submitted." + ] + } + }, + "Unsupported file extension": { + "summary": "File extension outside tiff/jpg/jpeg/png/webp", + "value": { + "user_image": [ + "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp." + ] + } + }, + "File too large": { + "summary": "Upload exceeds the 5 MB limit", + "value": { + "user_image": [ + "File size should not exceed 5 MB" + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X POST 'https://verification.didit.me/v3/face-search/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'user_image=@./selfie.jpg' \\\n -F 'search_type=most_similar' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nurl = 'https://verification.didit.me/v3/face-search/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as f:\n files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}\n data = {\n 'search_type': 'most_similar',\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nbody = resp.json()\nprint('status:', body['face_search']['status'])\nprint('matches:', body['face_search']['total_matches'])\nfor m in body['face_search']['matches']:\n print(f\" session={m['session_id']} similarity={m['similarity_percentage']} blocklisted={m['is_blocklisted']}\")" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('search_type', 'most_similar');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/face-search/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`Face search failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.face_search.status, 'matches:', body.face_search.total_matches);\nbody.face_search.matches.forEach(m =>\n console.log(` session=${m.session_id} similarity=${m.similarity_percentage} blocklisted=${m.is_blocklisted}`)\n);" + } + ] + } + }, + "/v3/sessions/": { + "get": { + "summary": "List verification sessions (KYC and/or KYB) with filters and pagination", + "description": "Returns a paginated list of verification sessions for the application identified by your API key, newest first (descending `session_number`).\n\n**Canonical URL:** the route is `/v3/sessions/` (trailing slash). Requests to `/v3/sessions` without the slash receive a `301` redirect to the slashed URL with the query string preserved; most HTTP clients follow it transparently on GET.\n\n**When to use this endpoint vs webhooks.** Use this endpoint for dashboards, back-office search, reconciliation, and batch exports. Do not poll it to track the progress of individual verifications - configure [webhooks](/integration/webhooks) instead, which push `status.updated` events the moment a session changes. For one session's full result, call `GET /v3/session/{sessionId}/decision/`.\n\n**KYC and KYB sessions.** `session_kind` selects which kinds are listed:\n- `user` (default) - User Verification (KYC) sessions only. Backward-compatible behavior.\n- `business` - Business Verification (KYB) sessions only. Rows have a different, smaller shape (see the response schema).\n- `all` - both kinds in one response: the KYC page and the KYB page for the same `limit`/`offset` are concatenated in `results` (KYC rows first), so a page can contain up to `2 x limit` rows. `count` is the sum of both kinds and `next`/`previous` reflect the KYC-side pagination. For strict pagination of mixed data, call `user` and `business` separately.\n\nEvery row carries a `session_kind` discriminator (`\"user\"` or `\"business\"`).\n\n**Pagination.** Standard limit/offset: `limit` (default 50) and `offset` (default 0), with `next`/`previous` ready-made page URLs. For KYC rows `count` is exact. For KYB rows `count` is computed with a bounded scan and is capped at 100 - when there are more than 100 matches it stays at 100, so iterate using `next` (or by checking whether a full page came back) rather than computing total pages from `count`.\n\n**Filter semantics.** All filters combine with AND and apply to KYC rows. When `session_kind` is `business` or `all`, the KYB side honors the shared parameters `vendor_data`, `status`, `workflow_id`, `country` (registry-company country code), `date_from`, `date_to`, `search` (company name, registration number — type registration numbers without separators, e.g. `DPC001` not `DPC-001` — vendor_data, session number, or session UUID), `warning`, and `organization_name`; the remaining KYC-only filters are ignored for KYB rows. Comma-separated parameters (`status`, `country`, `document_type`, `region`, `workflow_id`, `document_name`, `warning`, `organization_name`, `session_type`) match any of the listed values (OR within the parameter). `vendor_data` is an exact match - use `search` for partial matching. Unknown values in list filters simply return no rows (no error); malformed typed values (`date_from`, `date_to`, `didit_internal_id`, `api_service`, `session_kind`) return `400` with a per-field error object.\n\n**Search.** `search` adapts to the input: a full UUID looks up `session_id`; digits (with optional `+ - . space`) match session-number prefix, document/personal numbers, phone-number prefix, or `vendor_data`; values containing `@` match email prefixes; any other text matches names (accent-insensitive) and `vendor_data`.\n\nNo fixed per-endpoint rate limit is enforced, but keep list polling infrequent and rely on webhooks for real-time updates.", + "operationId": "get_v3_sessions", + "x-didit-sdk": { + "resource": "sessions", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "name": "session_kind", + "in": "query", + "required": false, + "description": "Which kind of sessions to list: `user` (KYC only, default), `business` (KYB only), or `all` (both, concatenated - see the description for merged-pagination semantics). Matching is case-insensitive (`BUSINESS` works); any value other than a case variant of `user`/`business`/`all` returns `400`.", + "schema": { + "type": "string", + "enum": [ + "user", + "business", + "all" + ], + "default": "user" + }, + "example": "all" + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "Maximum number of rows per page (per kind when `session_kind=all`). Defaults to 50.", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "example": 20 + }, + { + "name": "offset", + "in": "query", + "required": false, + "description": "Number of rows to skip before the first returned row. Defaults to 0. Prefer following the `next`/`previous` URLs from the response.", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "example": 20 + }, + { + "name": "status", + "in": "query", + "required": false, + "description": "Comma-separated list of session statuses to include, e.g. `Approved,In Review`. Values are case-sensitive; see [Verification Statuses](/integration/verification-statuses). Unknown values match nothing.", + "schema": { + "type": "string" + }, + "example": "Approved,In Review" + }, + { + "name": "vendor_data", + "in": "query", + "required": false, + "description": "Exact-match filter on the `vendor_data` you sent at session creation (your own user/business identifier). For partial matches use `search` instead.", + "schema": { + "type": "string" + }, + "example": "your-user-id-123" + }, + { + "name": "search", + "in": "query", + "required": false, + "description": "Smart free-text search. A full UUID matches `session_id`; numeric input matches session-number prefix, document/personal numbers, phone prefix, or `vendor_data`; input containing `@` matches email prefixes; other text matches names (accent-insensitive) and `vendor_data`. Free text containing digits additionally matches document/personal-number search fields and session-number prefixes.", + "schema": { + "type": "string" + }, + "example": "jane doe" + }, + { + "name": "workflow_id", + "in": "query", + "required": false, + "description": "Comma-separated workflow UUIDs (find them on the Workflows page in the Console). Malformed UUIDs in the list are ignored; if none are valid the result is empty.", + "schema": { + "type": "string" + }, + "example": "9f9b1234-aaaa-bbbb-cccc-1234567890ab" + }, + { + "name": "date_from", + "in": "query", + "required": false, + "description": "Only sessions created on or after this date (UTC), format `YYYY-MM-DD`. Malformed dates return `400`.", + "schema": { + "type": "string", + "format": "date" + }, + "example": "2026-06-01" + }, + { + "name": "date_to", + "in": "query", + "required": false, + "description": "Only sessions created up to this date (UTC), format `YYYY-MM-DD`. For KYC rows the entire day is included; for KYB rows the comparison is `created_at <= date_to` (midnight), so use the next day's date to include a full day of KYB sessions.", + "schema": { + "type": "string", + "format": "date" + }, + "example": "2026-06-30" + }, + { + "name": "country", + "in": "query", + "required": false, + "description": "Comma-separated country filter. For KYC rows: ISO 3166-1 alpha-3 codes of the ID document's issuing state (e.g. `ESP,USA`), which may differ from nationality. For KYB rows: the registry company's country code.", + "schema": { + "type": "string" + }, + "example": "ESP,USA" + }, + { + "name": "document_type", + "in": "query", + "required": false, + "description": "Comma-separated ID-document type codes verified in the session (KYC rows only): `P` (Passport), `ID` (Identity Card), `DL` (Driver's License), `RP` (Residence Permit), `SSC` (Social Security Card), `HIC` (Health Insurance Card), `WP` (Work Permit), `TC` (Tax Card), `VISA`, `PSC` (Public Service Card), `BC` (Birth Certificate), `OTHER`. Note the response field `document_type` contains the display name, not the code.", + "schema": { + "type": "string" + }, + "example": "P,ID" + }, + { + "name": "document_name", + "in": "query", + "required": false, + "description": "Exact document names; multiple names can be passed comma-separated, but the value is only split on commas when it contains more than one ` - ` separator (a lone name with an embedded comma is matched whole). Names are as recognized by document verification (front or back side), e.g. `Spain - Id Card (2021)`. KYC rows only.", + "schema": { + "type": "string" + }, + "example": "Spain - Id Card (2021)" + }, + { + "name": "region", + "in": "query", + "required": false, + "description": "Comma-separated document region/state values (for documents issued per region, such as US driver's licenses). KYC rows only.", + "schema": { + "type": "string" + }, + "example": "CA,TX" + }, + { + "name": "didit_internal_id", + "in": "query", + "required": false, + "description": "Filter by Didit's internal user identifier (the `didit_internal_id` response field) to list all sessions of one verified user. Must be a valid UUID; malformed values return `400`. KYC rows only.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147" + }, + { + "name": "session_type", + "in": "query", + "required": false, + "description": "Comma-separated session creation channels: `API`, `HOSTED`, `MIGRATED`. The special value `ALL` (or omitting the parameter) disables the filter. KYC rows only.", + "schema": { + "type": "string" + }, + "example": "HOSTED" + }, + { + "name": "api_service", + "in": "query", + "required": false, + "description": "Filter standalone API sessions by service. Invalid values return `400`. KYC rows only.", + "schema": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "FACE_MATCH", + "AGE_ESTIMATION", + "FACE_SEARCH", + "POA", + "AML", + "KYB_REGISTRY", + "PASSIVE_LIVENESS", + "DATABASE_VALIDATION", + "PHONE_VERIFICATION", + "EMAIL_VERIFICATION" + ] + }, + "example": "AML" + }, + { + "name": "screened_full_name", + "in": "query", + "required": false, + "description": "Accent-insensitive name search inside AML and database-validation screened data. When combined with `api_service=AML` or `api_service=DATABASE_VALIDATION`, the search is restricted to that feature's screened data. KYC rows only.", + "schema": { + "type": "string" + }, + "example": "Jane Doe" + }, + { + "name": "warning", + "in": "query", + "required": false, + "description": "Comma-separated warning risk codes; returns sessions that raised any of them (e.g. `POSSIBLE_FRAUD`). Risk codes are listed per feature in the [warnings documentation](/integration/webhooks). Applies to KYC rows and, with business warning codes, to KYB rows.", + "schema": { + "type": "string" + }, + "example": "POSSIBLE_FRAUD" + }, + { + "name": "status_initialized_as_in_review", + "in": "query", + "required": false, + "description": "When `true`, only sessions whose decision initially landed in `In Review` (useful to audit manual-review volume). KYC rows only.", + "schema": { + "type": "boolean" + }, + "example": true + }, + { + "name": "is_kyc_to_be_reviewed", + "in": "query", + "required": false, + "description": "`true` returns sessions whose document verification is flagged for manual review; `false` returns sessions with document verification explicitly not flagged. Omit to disable. KYC rows only.", + "schema": { + "type": "boolean" + }, + "example": true + }, + { + "name": "euuid", + "in": "query", + "required": false, + "description": "Filter by the end user's euuid (UUID assigned to the verifying user, used by Console deep links). Must be a valid UUID; malformed values return a 400 field error. KYC rows only.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "0f8fad5b-d9cb-469f-a165-70867728950e" + }, + { + "name": "organization_name", + "in": "query", + "required": false, + "description": "Comma-separated organization names. Console-oriented; with an API key (already scoped to one application) it only matches your own organization's name.", + "schema": { + "type": "string" + }, + "example": "Acme Inc" + } + ], + "responses": { + "200": { + "description": "Paginated session list. `results` items are KYC rows, KYB rows, or both depending on `session_kind`; discriminate with each row's `session_kind` field.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], + "properties": { + "count": { + "type": "integer", + "description": "Number of sessions matching the filters. Exact for KYC rows; for KYB rows it is capped at 100 (use `next` to know whether more pages exist). For `session_kind=all` it is the sum of both kinds.", + "example": 2 + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Absolute URL of the next page, or `null` on the last page.", + "example": "https://verification.didit.me/v3/sessions/?limit=20&offset=20" + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Absolute URL of the previous page, or `null` on the first page.", + "example": null + }, + "results": { + "type": "array", + "description": "Session rows, newest first. With `session_kind=all`, KYC rows come first, then KYB rows.", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserSessionListItem" + }, + { + "$ref": "#/components/schemas/BusinessSessionListItem" + } + ], + "discriminator": { + "propertyName": "session_kind", + "mapping": { + "user": "#/components/schemas/UserSessionListItem", + "business": "#/components/schemas/BusinessSessionListItem" + } + } + } + } + } + }, + "examples": { + "KYC sessions (default)": { + "summary": "session_kind=user - one completed and one not-started session", + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "session_id": "5b3720ed-d429-42ef-b67f-37ea805f48ee", + "session_kind": "user", + "session_number": 720, + "session_url": "https://verify.didit.me/session/CuXoBYCYVO5Y", + "portrait_image": "https:///portraits/5b3720ed.jpg?X-Amz-Expires=3600&X-Amz-Signature=...", + "document_type": "Passport", + "full_name": "Jane Marie Doe", + "country": "ESP", + "status": "Approved", + "vendor_data": "your-user-id-123", + "didit_internal_id": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147", + "created_at": "2026-06-09T12:26:54.328364Z", + "features": [ + { + "feature": "ID_VERIFICATION", + "status": "Approved" + }, + { + "feature": "LIVENESS", + "status": "Approved" + }, + { + "feature": "FACE_MATCH", + "status": "Approved" + }, + { + "feature": "AML", + "status": "Approved" + }, + { + "feature": "IP_ANALYSIS", + "status": "Approved" + } + ], + "phone_number": { + "number": "+14155552671", + "is_verified": true + }, + "email_address": { + "email": "jane@example.com", + "is_verified": true + }, + "additional_details": { + "aml": { + "total_hits": 0, + "entity_type": "person", + "is_adverse_media_fetched": false + }, + "liveness": { + "method": "PASSIVE", + "score": 0.98 + }, + "face_match": { + "score": 0.95 + } + } + }, + { + "session_id": "038ad62f-5ebd-46c2-afe9-4b2956bf687f", + "session_kind": "user", + "session_number": 719, + "session_url": "https://verify.didit.me/session/7RGKiO6ioGH6", + "portrait_image": null, + "document_type": null, + "full_name": null, + "country": null, + "status": "Not Started", + "vendor_data": "your-user-id-124", + "didit_internal_id": "eb4e679d-ca4b-461f-9685-687bac29ebdb", + "created_at": "2026-06-09T08:02:23.343053Z", + "features": [ + { + "feature": "ID_VERIFICATION", + "status": "Not Finished" + }, + { + "feature": "LIVENESS", + "status": "Not Finished" + }, + { + "feature": "FACE_MATCH", + "status": "Not Finished" + }, + { + "feature": "AML", + "status": "Not Finished" + }, + { + "feature": "IP_ANALYSIS", + "status": "Not Finished" + } + ], + "phone_number": null, + "email_address": null, + "additional_details": null + } + ] + } + }, + "Mixed KYC + KYB (session_kind=all)": { + "summary": "session_kind=all - KYC row followed by a KYB row", + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "session_id": "5b3720ed-d429-42ef-b67f-37ea805f48ee", + "session_kind": "user", + "session_number": 720, + "session_url": "https://verify.didit.me/session/CuXoBYCYVO5Y", + "portrait_image": null, + "document_type": "Identity Card", + "full_name": "Jane Marie Doe", + "country": "ESP", + "status": "In Review", + "vendor_data": "your-user-id-123", + "didit_internal_id": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147", + "created_at": "2026-06-09T12:26:54.328364Z", + "features": [ + { + "feature": "ID_VERIFICATION", + "status": "In Review" + }, + { + "feature": "LIVENESS", + "status": "Approved" + } + ], + "phone_number": null, + "email_address": null, + "additional_details": { + "liveness": { + "method": "PASSIVE", + "score": 0.98 + } + } + }, + { + "session_id": "8a1f63c2-1b9e-4f3a-9c41-2f6a1f0b7d52", + "session_kind": "business", + "session_number": 42, + "status": "Approved", + "vendor_data": "your-company-id-77", + "created_at": "2026-06-08T09:14:02.118450Z", + "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab", + "workflow_type": "kyb", + "workflow_label": "Standard KYB", + "company_name": "Acme Holdings S.L.", + "registration_number": "B12345678", + "country": "ES" + } + ] + } + } + } + } + } + }, + "301": { + "description": "Redirect to the canonical trailing-slash URL. Returned when the request path is `/v3/sessions` (no trailing slash); the `Location` header points to `/v3/sessions/` with the query string preserved. GET clients typically follow it automatically." + }, + "400": { + "description": "Invalid query parameter. The body is an object keyed by the offending parameter.", + "content": { + "application/json": { + "examples": { + "Invalid session_kind": { + "summary": "session_kind not one of user/business/all", + "value": { + "session_kind": "Must be \"user\", \"business\", or \"all\"." + } + }, + "Invalid date": { + "summary": "Malformed date_from/date_to", + "value": { + "date_from": [ + "Enter a valid date." + ] + } + }, + "Invalid didit_internal_id": { + "summary": "didit_internal_id is not a UUID", + "value": { + "didit_internal_id": [ + "Enter a valid UUID." + ] + } + }, + "Invalid api_service": { + "summary": "api_service not in the allowed choices", + "value": { + "api_service": [ + "Select a valid choice. FOO is not one of the available choices." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing, malformed, or revoked API key. This API never returns `401`; all authentication failures are `403`.\n\n**Deleted application:** for a few minutes after an application is deleted, a request made with one of its API keys can answer `{\"detail\": \"This application was deleted.\", \"code\": \"application_deleted\"}`. Do not retry: the application and its API keys no longer exist. After those few minutes, the key is refused like any other invalid key.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "You do not have permission to perform this action." + }, + "code": { + "type": "string", + "description": "Machine-readable reason, present only on some failures. `application_deleted`: the API key belongs to an application that was deleted.", + "example": "application_deleted" + } + }, + "required": [ + "detail" + ] + }, + "examples": { + "Forbidden": { + "summary": "Missing or invalid x-api-key", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "ApplicationDeleted": { + "summary": "API key of a deleted application", + "value": { + "detail": "This application was deleted.", + "code": "application_deleted" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -s 'https://verification.didit.me/v3/sessions/?status=Approved,In%20Review&date_from=2026-06-01&limit=20' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nurl = \"https://verification.didit.me/v3/sessions/\"\nheaders = {\"x-api-key\": \"YOUR_API_KEY\"}\nparams = {\n \"session_kind\": \"user\", # \"user\" (default) | \"business\" | \"all\"\n \"status\": \"Approved,In Review\", # comma-separated, OR within the list\n \"date_from\": \"2026-06-01\",\n \"limit\": 20,\n}\n\nwhile url:\n response = requests.get(url, headers=headers, params=params)\n response.raise_for_status()\n page = response.json()\n for session in page[\"results\"]:\n print(session[\"session_kind\"], session[\"session_id\"], session[\"status\"])\n url, params = page[\"next\"], None # \"next\" already carries the query string" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const params = new URLSearchParams({\n session_kind: 'all',\n status: 'Approved,In Review',\n date_from: '2026-06-01',\n limit: '20',\n});\n\nconst response = await fetch(\n `https://verification.didit.me/v3/sessions/?${params}`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\n\nconst { count, next, results } = await response.json();\nfor (const session of results) {\n // session.session_kind is \"user\" (KYC) or \"business\" (KYB)\n console.log(session.session_kind, session.session_id, session.status);\n}" + } + ] + } + }, + "/v3/session/{sessionId}/share/": { + "post": { + "summary": "Mint a share token for a finished verification session", + "description": "Mint a short-lived JWT that lets a specific Didit application import this finished session. Pair with `POST /v3/session/import-shared/`.", + "operationId": "post_v3_session_share", + "x-didit-sdk": { + "resource": "sessions", + "method": "share", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "responses": { + "200": { + "description": "Share token minted.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "share_token": { + "type": "string", + "description": "HS256-signed JWT. Pass this verbatim as `share_token` to [`POST /v3/session/import-shared/`](/sessions-api/import-shared-session) on the target application.", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + }, + "for_application_id": { + "type": "string", + "format": "uuid", + "description": "Echoes the target application UUID that the token is bound to.", + "example": "a5f3bca2-46e2-411e-90ef-a580900a57ee" + }, + "session_kind": { + "type": "string", + "enum": [ + "user", + "business" + ], + "description": "Whether the source session is a User Verification (KYC) or Business Verification (KYB) session." + } + } + }, + "examples": { + "User session": { + "summary": "Share a KYC session", + "value": { + "share_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiMjBiYjNjM2ItNjE0Ni00MzlmLTg0YTQtYmQzMGQwMGFjNmEyIiwiZnJvbV9hcHBsaWNhdGlvbl9pZCI6ImRiZDIwZTM0LTQyZTktNGYyYy1iYTkxLWNmMDc2MjAxNmY2NCIsImZvcl9hcHBsaWNhdGlvbl9pZCI6ImE1ZjNiY2EyLTQ2ZTItNDExZS05MGVmLWE1ODA5MDBhNTdlZSIsImlhdCI6MTc1MzYzMDY2NiwiZXhwIjoxNzUzNjM0MjY2fQ.JJ9pNE_hqZsOtbR0XYZIWw4JzidjdEl279iUrsIkhGE", + "for_application_id": "a5f3bca2-46e2-411e-90ef-a580900a57ee", + "session_kind": "user" + } + }, + "Business session": { + "summary": "Share a KYB session", + "value": { + "share_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "for_application_id": "a5f3bca2-46e2-411e-90ef-a580900a57ee", + "session_kind": "business" + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid credentials. Unlike most v3 endpoints, this endpoint authenticates through the permission decorator and returns `401` when no valid token/key is presented.", + "content": { + "application/json": { + "examples": { + "Unauthorized": { + "summary": "Unauthorized", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + } + } + } + } + }, + "404": { + "description": "No session with the given `session_id` exists in your application.", + "content": { + "application/json": { + "examples": { + "Not Found": { + "summary": "Session not found", + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "400": { + "description": "Validation error. Field errors are keyed by field name; the not-finished error arrives under a `detail` key (as an array).", + "content": { + "application/json": { + "examples": { + "Wrong status": { + "summary": "Source session is not finished", + "value": { + "detail": [ + "Only finished sessions (\"Approved\", \"Declined\", \"In Review\") can be shared." + ] + } + }, + "Target missing": { + "summary": "Target application does not exist", + "value": { + "for_application_id": [ + "Target application does not exist." + ] + } + }, + "Self-share": { + "summary": "Target is the calling application", + "value": { + "for_application_id": [ + "Cannot share a session with the same application." + ] + } + }, + "TTL bounds": { + "summary": "ttl_in_seconds outside bounds", + "value": { + "ttl_in_seconds": [ + "Ensure this value is greater than or equal to 60." + ] + } + } + } + } + } + }, + "403": { + "description": "The credentials are valid but lack the `write:sessions` permission for this application.", + "content": { + "application/json": { + "examples": { + "No Permission": { + "value": { + "detail": "You do not have permission to perform this action." + }, + "summary": "No Permission" + } + } + } + } + } + }, + "parameters": [ + { + "in": "path", + "name": "sessionId", + "required": true, + "description": "UUID of the source verification session to mint a token for.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + } + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "for_application_id" + ], + "properties": { + "for_application_id": { + "type": "string", + "format": "uuid", + "description": "UUID of the Didit application that will redeem the token. Must exist, must not be deleted, and must differ from the calling application. Find it in the Business Console under **Settings → Application**.", + "example": "a5f3bca2-46e2-411e-90ef-a580900a57ee" + }, + "ttl_in_seconds": { + "type": "integer", + "description": "Token lifetime, in seconds. Minimum `60`, maximum `86400` (24 h). Defaults to `3600` (1 h).", + "minimum": 60, + "maximum": 86400, + "default": 3600, + "example": 3600 + } + } + }, + "example": { + "for_application_id": "a5f3bca2-46e2-411e-90ef-a580900a57ee", + "ttl_in_seconds": 7200 + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/share/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"for_application_id\": \"a5f3bca2-46e2-411e-90ef-a580900a57ee\",\n \"ttl_in_seconds\": 7200\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/share/\",\n headers={\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n json={\n \"for_application_id\": \"a5f3bca2-46e2-411e-90ef-a580900a57ee\",\n \"ttl_in_seconds\": 7200,\n },\n)\nresponse.raise_for_status()\nshare_token = response.json()[\"share_token\"]" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/share/',\n {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n for_application_id: 'a5f3bca2-46e2-411e-90ef-a580900a57ee',\n ttl_in_seconds: 7200,\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst { share_token } = await response.json();" + } + ] + } + }, + "/v3/session/import-shared/": { + "post": { + "summary": "Redeem a share token to clone a verification session into your application", + "description": "Redeem a share token to clone a KYC or KYB session into the calling application with a fresh `session_id`. Not idempotent — a token redeems once per receiver.", + "operationId": "post_v3_session_import_shared", + "x-didit-sdk": { + "resource": "sessions", + "method": "import_shared", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "responses": { + "201": { + "description": "Session cloned. The body is the V3 decision payload for both kinds, discriminated by `session_kind`: `\"user\"` (KYC) with list-based feature blocks such as `id_verifications`, `liveness_checks`, `aml_screenings`; `\"business\"` (KYB) with blocks like `registry_checks` and `key_people_checks`. Both include `shared_from_session` — the `session_id` of the original session in the source application. With `trust_review: false` the cloned session's `status` is forced to `In Review`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionDecision" + }, + "examples": { + "User session imported": { + "summary": "Cloned KYC session (truncated — full V3 decision payload in reality)", + "value": { + "session_id": "11111111-2222-3333-4444-555555555555", + "session_kind": "user", + "shared_from_session": "99999999-8888-7777-6666-555555555555", + "session_number": 43762, + "session_url": null, + "status": "In Review", + "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab", + "vendor_data": "user-1", + "created_at": "2026-05-17T08:42:11Z", + "expires_at": "2026-05-24T08:42:11Z" + } + }, + "Business session imported": { + "summary": "Cloned KYB session (truncated — full V3 KYB decision payload in reality)", + "value": { + "session_id": "22222222-3333-4444-5555-666666666666", + "session_kind": "business", + "shared_from_session": "88888888-7777-6666-5555-444444444444", + "session_number": 982, + "session_url": null, + "status": "Approved", + "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab", + "vendor_data": "company-1" + } + } + } + } + } + }, + "400": { + "description": "Validation error (token invalid, expired, wrong target, or references a session that no longer exists).", + "content": { + "application/json": { + "examples": { + "Invalid Share Token": { + "summary": "Invalid JWT", + "value": { + "share_token": [ + "Invalid share token." + ] + } + }, + "Expired Share Token": { + "summary": "Token past `exp`", + "value": { + "share_token": [ + "Share token has expired." + ] + } + }, + "Wrong audience": { + "summary": "Token not bound to this application", + "value": { + "share_token": [ + "This token is not valid for this application." + ] + } + }, + "Original missing": { + "summary": "Source session was deleted", + "value": { + "share_token": [ + "Original session does not exist." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing/invalid API key (this endpoint returns `403`, never `401`, for authentication failures), or this source session has already been imported into the calling application (one redeem per receiver).", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "summary": "Forbidden", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Already imported": { + "summary": "Source already imported once", + "value": { + "detail": "This session has already been shared with your application." + } + } + } + } + } + }, + "404": { + "description": "`workflow_id` does not belong to the calling application.", + "content": { + "application/json": { + "examples": { + "Workflow not found": { + "summary": "Workflow unknown", + "value": { + "detail": "Workflow does not exist for this application." + } + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "share_token", + "trust_review", + "workflow_id" + ], + "properties": { + "share_token": { + "type": "string", + "description": "JWT share token issued by [`POST /v3/session/{sessionId}/share/`](/sessions-api/share-session).", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + }, + "trust_review": { + "type": "boolean", + "description": "If `true`, the cloned session keeps the source's final `status`. If `false`, it is forced into `In Review`.", + "example": false + }, + "workflow_id": { + "type": "string", + "format": "uuid", + "description": "UUID of a workflow in the calling application. Cross-application IDs are rejected with `404`.", + "example": "9f9b1234-aaaa-bbbb-cccc-1234567890ab" + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Optional override for the cloned session's `vendor_data`.", + "example": "user-1" + } + } + }, + "example": { + "share_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "trust_review": false, + "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab", + "vendor_data": "user-1" + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST \\\n https://verification.didit.me/v3/session/import-shared/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"share_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"trust_review\": false,\n \"workflow_id\": \"9f9b1234-aaaa-bbbb-cccc-1234567890ab\",\n \"vendor_data\": \"user-1\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/import-shared/\",\n headers={\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n json={\n \"share_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"trust_review\": False,\n \"workflow_id\": \"9f9b1234-aaaa-bbbb-cccc-1234567890ab\",\n \"vendor_data\": \"user-1\",\n },\n)\nresponse.raise_for_status()\nimported = response.json()" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/import-shared/',\n {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n share_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',\n trust_review: false,\n workflow_id: '9f9b1234-aaaa-bbbb-cccc-1234567890ab',\n vendor_data: 'user-1',\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst imported = await response.json();" + } + ] + } + }, + "/v3/kyb/search/": { + "post": { + "summary": "KYB registry company search (standalone)", + "operationId": "post_v3_kyb_search", + "x-didit-sdk": { + "resource": "kyb", + "method": "search", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [], + "description": "Search official company registries for candidate companies by name and/or registration number. This is step 1 of the two-step registry flow: **search** returns lightweight candidates, then `POST /v3/kyb/select/` retrieves the full profile of one candidate.\n\n**Search billing.** Successful searches using a paid company-data source cost USD 0.50 per request, regardless of candidate count. Empty results, failures and results served only by other sources are not charged. The applicable price is reserved before searching; insufficient balance returns 403 with code `insufficient_credits`. Contract rates apply where agreed, and an unconfigured contract search rate returns 403. Sandbox searches are free. Repeating a successful search can incur another charge; callback delivery does not add a charge. Profile retrieval is billed separately. This search fee also applies to v2. Candidates always come back with `fetch_status: \"pending\"`: the full company data is only fetched (and billed) on select.\n\n**`kyb_response_id` is ephemeral.** It is a per-search handle, not a stable company id. Select promptly after searching and re-search to get a fresh handle instead of storing it.\n\n**`tier` picks the provider chain.** `tier` names the registry tier the search runs on: `basic` (the default, the Lite chain), `shareholders` or `ubo`. The search uses the provider chain that serves that tier, and it is not always the same chain the Lite search reads, so an empty Lite search is not proof that the registry does not hold the company — search again at the tier you need. A country that does not offer the tier you name is searched at the highest tier it offers up to that one. Retrieve the candidate from the endpoint that sells that tier: `POST /v3/kyb/select/` (Lite), `POST /v3/kyb/shareholders/` or `POST /v3/kyb/ubo/`.\n\n**Authentication.** `POST /v3/kyb/search/` (and `/v2/`) takes your application's API key in the `x-api-key` header. The hosted `POST /v1/kyb/search/` is the session-authenticated route a hosted verification flow calls: it answers `403` to an API key and takes no `tier` — the workflow's KYB registry configuration picks the chain.\n\n**Two modes.**\n- *Synchronous (default):* the API polls the registry until the search resolves and returns the final candidate list (`search_resolved: true`).\n- *Asynchronous:* pass `webhook_url` and the API returns immediately — usually with `search_status: \"pending\"` and an empty candidate list — then POSTs a `kyb.registry_search.resolved` callback to your URL when the registry finishes. The callback body is `{\"event_id\", \"event_type\": \"kyb.registry_search.resolved\", \"request_id\", \"vendor_data\", \"metadata\", \"search_status\", \"search_resolved\", \"kyb_registry\", \"timestamp\", \"created_at\"}` — note `timestamp` and `created_at` in the callback are epoch integers, unlike this endpoint's ISO `created_at` — and is sent **unsigned** (header `X-Didit-Unsigned-Callback: true`, no `X-Signature`).\n\n**Country codes.** ISO 3166-1 alpha-2 (`GB`, `DE`), plus `XX-YY` subdivision codes where registries are regional — e.g. `US-CA` for California.\n\n**Sandbox.** Keys from sandbox applications return one static candidate without contacting any registry.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "country_code" + ], + "properties": { + "country_code": { + "type": "string", + "maxLength": 10, + "description": "ISO 3166-1 alpha-2 code of the country whose company registry to search. For example, `GB`. Where company registries operate at state or province level, append the ISO 3166-2 subdivision code as `XX-YY`: `US-CA` searches the California registry. United States searches always require the subdivision — a bare `US` is rejected. Case-insensitive; normalized to upper case.", + "example": "GB" + }, + "name": { + "type": "string", + "maxLength": 255, + "description": "Company name to search for. Either `name` or `registration_number` is required.", + "example": "Tesco" + }, + "registration_number": { + "type": "string", + "maxLength": 100, + "description": "Registry registration number to search for. Either `name` or `registration_number` is required." + }, + "tier": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo" + ], + "default": "basic", + "description": "Registry tier whose provider chain this search runs on. `basic` (default) searches the country's Lite chain; `shareholders` and `ubo` search that tier's ownership chain, which can be a different source, so an empty Lite search does not prove the registry lacks the company. A country that does not offer the named tier is searched at the highest tier it offers up to that one. Retrieve the candidate from the endpoint that sells the tier you searched: `POST /v3/kyb/select/` (Lite), `POST /v3/kyb/shareholders/` or `POST /v3/kyb/ubo/`.", + "example": "basic" + }, + "search_type": { + "type": "string", + "enum": [ + "contains" + ], + "default": "contains", + "description": "Name-matching strategy applied by the registry search. `contains` is the only accepted value: no registry source behind the search offers a prefix or fuzzy mode, so `start_with` and `fuzzy` are rejected with `400` (`not a valid choice`)." + }, + "vendor_data": { + "type": "string", + "description": "Your identifier for this lookup; echoed back and included in the webhook callback." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Free-form JSON; echoed back and included in the webhook callback." + }, + "webhook_url": { + "type": "string", + "format": "uri", + "maxLength": 500, + "description": "Switches the search to asynchronous mode: the API returns immediately and POSTs an unsigned `kyb.registry_search.resolved` callback to this URL when the candidate list is ready." + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/kyb/search/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"country_code\": \"GB\",\n \"name\": \"Tesco\",\n \"search_type\": \"contains\",\n \"vendor_data\": \"company-1234\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/kyb/search/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n timeout=60, # synchronous mode waits for the registry to resolve\n)\nresp.raise_for_status()\ncandidates = resp.json()[\"kyb_registry\"][\"companies\"]\nkyb_response_id = candidates[0][\"kyb_response_id\"] # pass to /v3/kyb/select/" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/kyb/search/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ country_code: 'GB', name: 'Tesco' }),\n});\nconst data = await res.json();\nconst kybResponseId = data.kyb_registry.companies[0].kyb_response_id;" + } + ], + "responses": { + "200": { + "description": "Search accepted. Synchronous mode returns the resolved candidate list; webhook mode returns immediately (often `search_status: \"pending\"`) and delivers the final list via the `kyb.registry_search.resolved` callback.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "With `webhook_url`: the persisted search-request id, echoed as `request_id` in the `kyb.registry_search.resolved` callback. Without `webhook_url`: a transient correlation UUID (the search is not stored)." + }, + "kyb_registry": { + "$ref": "#/components/schemas/KYBRegistrySearchResults" + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Echo of the `metadata` you sent." + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "examples": { + "Resolved search": { + "summary": "Synchronous mode — final candidate list", + "value": { + "request_id": "0c7e9a40-3a13-4f7e-8a44-9d2e2f1c5b6a", + "kyb_registry": { + "companies": [ + { + "kyb_response_id": "69aeeb95febb0f1704042259", + "name": "Tesco PLC", + "registration_number": "00445790", + "status": "active", + "type": "Public Limited Company", + "risk_level": null, + "fetch_status": "pending" + }, + { + "kyb_response_id": "69aeeb95febb0f170404225a", + "name": "E-TESCO LTD", + "registration_number": "15168476", + "status": "active", + "type": "Private Limited Company", + "risk_level": null, + "fetch_status": "pending" + } + ], + "pagination": { + "total": 2, + "page": 1, + "per_page": 25 + }, + "search_status": "resolved", + "search_resolved": true + }, + "vendor_data": "company-1234", + "metadata": null, + "created_at": "2026-06-11T10:40:00.000000+00:00" + } + }, + "Pending search (webhook mode)": { + "summary": "`webhook_url` provided — candidates arrive via the kyb.registry_search.resolved callback", + "value": { + "request_id": "5f2d8c1b-7e4a-4b9c-9f0d-3a6e8c2b1d4f", + "kyb_registry": { + "companies": [], + "pagination": { + "total": 0, + "page": 1, + "per_page": 25 + }, + "search_status": "pending", + "search_resolved": false + }, + "vendor_data": "company-1234", + "metadata": { + "source": "onboarding" + }, + "created_at": "2026-06-11T10:41:00.000000+00:00" + } + } + } + } + } + }, + "400": { + "description": "Validation error. Field-level problems use DRF's standard envelope; the name/registration-number rule reports under `non_field_errors`.", + "content": { + "application/json": { + "examples": { + "Missing search criteria": { + "summary": "Neither `name` nor `registration_number` provided", + "value": { + "non_field_errors": [ + "Either 'name' or 'registration_number' is required." + ] + } + }, + "Invalid country_code": { + "summary": "Must be `XX` (alpha-2) or `XX-YY` subdivision", + "value": { + "country_code": [ + "country_code must be a 2-letter ISO code." + ] + } + }, + "Invalid search_type": { + "summary": "Only `contains` is accepted", + "value": { + "search_type": [ + "\"start_with\" is not a valid choice." + ] + } + }, + "Invalid webhook_url": { + "summary": "`webhook_url` must be a valid URL", + "value": { + "webhook_url": [ + "Enter a valid URL." + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Also returned before searching when the applicable search fee cannot be covered (`code: \"insufficient_credits\"`), or when a contract search rate is missing.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + }, + "502": { + "description": "The KYB registry provider is temporarily unavailable - a 5xx response, timeout, or connection failure from the upstream registry. The search is not billed. Retry later, or, in Didit's hosted verification flow, the user is automatically offered manual company entry instead of a retry-only error.", + "content": { + "application/json": { + "examples": { + "Registry provider outage": { + "summary": "The registry provider did not answer the search", + "value": { + "detail": "The KYB registry provider is temporarily unavailable. Please try again shortly.", + "code": "kyb_registry_provider_unavailable" + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "/v3/kyb/select/": { + "post": { + "summary": "KYB registry company select — Lite tier (standalone)", + "operationId": "post_v3_kyb_select", + "x-didit-sdk": { + "resource": "kyb", + "method": "select", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [], + "description": "Retrieve the **Lite** registry profile of one candidate returned by `POST /v3/kyb/search/` — company details, registered address, identifiers, status, activity and the officers, plus the raw registry payload. Shareholders and ultimate beneficial owners are sold by `POST /v3/kyb/shareholders/` and `POST /v3/kyb/ubo/`.\n\n**Pairing with search.** Pass the candidate's `kyb_response_id` exactly as returned by a search of the same application. The handle is ephemeral and scoped to that search — select promptly, and re-search for a fresh handle rather than storing it.\n\n**Billing.** Each select is billed at the country's Lite price (any search fee is separate); see [KYB registry pricing](/getting-started/kyb-registry-pricing). The balance is checked before any registry call (`403` when short) and the charge is recorded against the created business session.\n\n**Persistence is mandatory.** Every select creates a business session (its id is the `request_id`) so billing and retrieval can be tracked; it appears in the console. `save_api_request` is accepted for symmetry but must be `true` (the default) — sending `false` returns `400`.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KYBRegistryCandidateRequest" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/kyb/select/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"kyb_response_id\": \"69aeeb95febb0f1704042259\",\n \"vendor_data\": \"company-1234\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os, requests\n\nsearch = requests.post(\n \"https://verification.didit.me/v3/kyb/search/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n timeout=60,\n).json()\nkyb_response_id = search[\"kyb_registry\"][\"companies\"][0][\"kyb_response_id\"]\n\nresp = requests.post(\n \"https://verification.didit.me/v3/kyb/select/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"kyb_response_id\": kyb_response_id, \"vendor_data\": \"company-1234\"},\n timeout=90, # waits for the registry to resolve the full profile\n)\nresp.raise_for_status()\ncompany = resp.json()[\"kyb_registry\"]\nprint(company[\"company_name\"], company[\"registry_status\"], company[\"status\"])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/kyb/select/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n kyb_response_id: '69aeeb95febb0f1704042259',\n vendor_data: 'company-1234',\n }),\n});\nconst data = await res.json();\nconsole.log(data.kyb_registry.company_name, data.kyb_registry.registry_status);" + } + ], + "responses": { + "200": { + "description": "Company retrieved and billed. `kyb_registry` carries the full profile; check `data_resolved` — when `false` the registry was still processing and the stored record completes automatically once it resolves.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Id of the business session created for this retrieval. The stored record (visible in the console) keeps updating if the registry resolves after the response." + }, + "registry_tier_requested": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo" + ], + "description": "The tier this endpoint sells: `basic` (Lite) for select, `shareholders`, `ubo`." + }, + "registry_tier_delivered": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo" + ], + "description": "The tier actually delivered and billed. Lower than requested when the country's registries hold no ownership data, or the registry has no ownership record for this company." + }, + "kyb_registry": { + "type": "object", + "description": "Full registry company profile.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Didit's id for this company record." + }, + "node_id": { + "type": "string", + "example": "feature_kyb_registry" + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review" + ], + "description": "Didit's assessment of the registry check (e.g. `In Review` when the company is not active in the registry or ownership data could not be derived)." + }, + "registry_status": { + "type": "string", + "nullable": true, + "enum": [ + "active", + "dissolved", + "deregistered", + "see full details", + "authorised", + "appointed representative", + "unauthorised", + "inactive", + "no longer authorised", + "closed", + "struck off" + ], + "description": "Normalized company status as reported by the registry." + }, + "data_resolved": { + "type": "boolean", + "description": "`true` once the registry returned the full profile. `false` when the registry was still processing after polling — the stored record completes automatically once the registry resolves." + }, + "company_name": { + "type": "string", + "example": "Tesco PLC" + }, + "registration_number": { + "type": "string", + "nullable": true, + "example": "00445790" + }, + "country_code": { + "type": "string", + "description": "ISO 3166-1 alpha-2.", + "example": "GB" + }, + "region": { + "type": "string", + "nullable": true, + "description": "Subdivision code when the registry is regional (e.g. `CA` for `US-CA`); empty otherwise." + }, + "company_type": { + "type": "string", + "nullable": true, + "example": "Public Limited Company" + }, + "incorporation_date": { + "type": "string", + "format": "date", + "nullable": true, + "example": "1947-11-27" + }, + "registered_address": { + "type": "string", + "nullable": true, + "example": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA" + }, + "tax_number": { + "type": "string", + "nullable": true, + "example": "GB412 8925 93" + }, + "risk_level": { + "type": "string", + "nullable": true, + "description": "Registry-reported risk level, when available." + }, + "verification_status": { + "type": "string", + "nullable": true, + "description": "Registry verification status, when available." + }, + "is_from_registry": { + "type": "boolean", + "description": "Always `true` for this endpoint — the profile was retrieved from an official registry." + }, + "fetch_status": { + "type": "string", + "enum": [ + "pending", + "resolved" + ], + "description": "Registry fetch lifecycle. Mirrored by `data_resolved`." + }, + "alternative_names": { + "type": "string", + "nullable": true, + "description": "Alternative, trading, former or original-language company names as text, when reported." + }, + "nature_of_business": { + "type": "string", + "nullable": true, + "description": "Activity description or classification code, when reported." + }, + "registered_capital": { + "type": "string", + "nullable": true + }, + "registered_capital_amount": { + "type": "string", + "nullable": true, + "description": "Decimal amount as a string, when reported." + }, + "registered_capital_currency": { + "type": "string", + "nullable": true, + "description": "ISO 4217 code, when reported." + }, + "website": { + "type": "string", + "nullable": true, + "example": "https://www.tesco.com/" + }, + "email": { + "type": "string", + "nullable": true, + "example": "customer.service@tesco.co.uk" + }, + "phone": { + "type": "string", + "nullable": true, + "example": "03301231688" + }, + "legal_entity_identifier": { + "type": "string", + "nullable": true, + "description": "LEI, when reported." + }, + "location_of_registration": { + "type": "string", + "nullable": true, + "description": "Registry/jurisdiction the company is registered with, when reported." + }, + "vat_number": { + "type": "string", + "nullable": true, + "description": "EU VAT number, when collected during the hosted registry step or edited afterwards. At select time this is typically not yet set — VAT and tax ID validation runs when the end user submits the registry step in the hosted flow." + }, + "vat_validation_status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "format_valid", + "invalid_format", + "could_not_validate", + "not_applicable" + ], + "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." + }, + "vat_validated_number": { + "type": "string", + "nullable": true, + "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." + }, + "vat_validation_source": { + "type": "string", + "nullable": true, + "enum": [ + "eu_vies", + "pl_vat_white_list", + "no_brreg", + "ch_uid_register", + "br_receita_federal", + "co_rues", + "cr_hacienda", + "ec_sri", + "py_dnit", + "format_rules", + null + ], + "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." + }, + "vat_validated_name": { + "type": "string", + "nullable": true, + "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_validated_address": { + "type": "string", + "nullable": true, + "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_checked_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the check. `null` when no check ran." + }, + "financial_summary": { + "type": "object", + "nullable": true, + "description": "Registry-reported share/capital details (currency, total shares, share classes), when available." + }, + "officers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "example": "MELISSA BETHELL" + }, + "designation": { + "type": "string", + "nullable": true, + "description": "Raw registry designation (e.g. `Director`, `Secretary`)." + }, + "role": { + "type": "string", + "description": "Normalized role derived from the designation. Full 17-value model set; registry parsing on this endpoint typically emits director, non_executive_director, secretary, chairman, or company_officer.", + "enum": [ + "director", + "non_executive_director", + "secretary", + "chairman", + "ubo", + "shareholder", + "representative", + "founder", + "legal_advisor", + "authorized_signatory", + "trustee", + "beneficiary", + "company_officer", + "settlor", + "protector", + "investor", + "other" + ] + }, + "nationality": { + "type": "string", + "nullable": true + }, + "is_active": { + "type": "boolean", + "description": "`false` when the registry marks the appointment as resigned." + }, + "kyc_status": { + "type": "string", + "nullable": true, + "enum": [ + "Approved", + "Declined", + "Pending" + ], + "description": "Status of an associated KYC session, when one was started for this officer. `null` otherwise." + }, + "kyc_session_url": { + "type": "string", + "nullable": true, + "description": "Verification URL of the associated KYC session, when one exists." + }, + "first_name": { + "type": "string", + "nullable": true, + "description": "Given name when explicitly reported by the registry; not inferred from the full name." + }, + "last_name": { + "type": "string", + "nullable": true, + "description": "Family name when explicitly reported by the registry." + }, + "address": { + "type": "string", + "nullable": true, + "description": "Officer address reported by the registry." + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Date the officer was appointed.", + "format": "date" + }, + "termination_date": { + "type": "string", + "nullable": true, + "description": "Date the appointment ended, when reported.", + "format": "date" + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", + "format": "date" + }, + "date_of_birth_year": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 9999, + "description": "Registry-reported birth year, including when the full date is unavailable." + }, + "date_of_birth_month": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 12, + "description": "Registry-reported birth month. Null when only the year is known." + } + } + }, + "description": "Directors, secretaries, and other officers extracted from the registry." + }, + "beneficial_owners": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "first_name": { + "type": "string", + "nullable": true + }, + "last_name": { + "type": "string", + "nullable": true + }, + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ], + "description": "Whether the owner is a natural person or a corporate entity." + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Normalized roles (e.g. `ubo`, `shareholder`)." + }, + "ownership_min_shares": { + "type": "string", + "nullable": true, + "description": "Lower bound of the ownership band reported by the registry (e.g. `25%`)." + }, + "ownership_max_shares": { + "type": "string", + "nullable": true, + "description": "Upper bound of the ownership band reported by the registry." + }, + "is_active": { + "type": "boolean" + }, + "kyc_status": { + "type": "string", + "nullable": true, + "enum": [ + "Approved", + "Declined", + "Pending" + ] + }, + "kyc_session_url": { + "type": "string", + "nullable": true + }, + "effective_ownership_percent": { + "type": "number", + "nullable": true, + "description": "Parsed ownership percentage (max bound, falling back to min bound)." + }, + "company_name": { + "type": "string", + "nullable": true + }, + "registration_number": { + "type": "string", + "nullable": true + }, + "country_of_birth": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + }, + "nationality": { + "type": "string", + "nullable": true + }, + "designation": { + "type": "string", + "nullable": true + }, + "voting_min_shares": { + "type": "string", + "nullable": true + }, + "voting_max_shares": { + "type": "string", + "nullable": true + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete birth date when reported.", + "format": "date" + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Appointment date when reported.", + "format": "date" + }, + "shareholding": { + "$ref": "#/components/schemas/KYBRegistryShareholding" + } + } + }, + "description": "Beneficial owners / persons with significant control extracted from the registry. Can be empty when the registry does not expose ownership." + }, + "addresses": { + "type": "array", + "nullable": true, + "description": "Every address the registry states, one canonical shape. Null when it states none.", + "items": { + "$ref": "#/components/schemas/KYBRegistryAddress" + } + }, + "industries": { + "type": "array", + "nullable": true, + "description": "The activities the registry classifies the company under, one canonical shape. Null when it states none.", + "items": { + "$ref": "#/components/schemas/KYBRegistryIndustry" + } + }, + "accounts": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." + }, + "registry_data": { + "type": "object", + "description": "Raw record returned by the registry source, kept for reviewers. Its shape differs by country and source and is not a stable contract: read the canonical fields (`addresses`, `industries`, `ownership`, `beneficial_owners[].shareholding`) instead." + }, + "user_provided_data": { + "type": "object", + "nullable": true, + "description": "Always `null` on this endpoint (used by the hosted KYB flow when end users edit registry data)." + }, + "confirmed_by_user_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Always `null` on this endpoint." + }, + "last_console_edit_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Set when a console reviewer edits the record later." + }, + "is_editable": { + "type": "boolean", + "description": "Whether the registry-sourced data can still be edited in the hosted flow/console." + }, + "registry_tier_delivered": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo" + ], + "description": "Same as the top-level `registry_tier_delivered`." + }, + "registry_ownership_status": { + "type": "string", + "nullable": true, + "enum": [ + "not_requested", + "delivered", + "unavailable" + ], + "description": "`delivered` when the ownership data came back, `unavailable` when an ownership tier was requested but the registry had none for this company (see `registry_data.ownership_error`), `not_requested` on the Lite tier." + }, + "ownership": { + "$ref": "#/components/schemas/KYBRegistryOwnership" + }, + "registry_tier": { + "type": "string", + "nullable": true, + "description": "The registry tier the workflow ordered: `basic`, `shareholders` or `ubo`. Also in `ownership.tier_requested`." + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Echo of the `metadata` you sent." + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "examples": { + "Resolved company": { + "summary": "Full registry profile (officer list truncated for brevity)", + "value": { + "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", + "kyb_registry": { + "uuid": "f1e2d3c4-b5a6-4789-9c0d-1e2f3a4b5c6d", + "node_id": "feature_kyb_registry", + "status": "In Review", + "registry_status": "active", + "data_resolved": true, + "company_name": "Tesco PLC", + "registration_number": "00445790", + "country_code": "GB", + "region": "", + "company_type": "Public Limited Company", + "incorporation_date": "1947-11-27", + "registered_address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", + "tax_number": "GB412 8925 93", + "risk_level": "low risk", + "verification_status": "verified", + "is_from_registry": true, + "fetch_status": "resolved", + "alternative_names": "TESCO STORES (HOLDINGS) PUBLIC LIMITED COMPANY", + "nature_of_business": null, + "registered_capital": null, + "registered_capital_amount": null, + "registered_capital_currency": null, + "website": "https://www.tesco.com/", + "email": "customer.service@tesco.co.uk", + "phone": "03301231688", + "legal_entity_identifier": null, + "location_of_registration": null, + "financial_summary": { + "currency": "Pound (£)", + "total_number_of_shares": "8174378721", + "meta_detail": { + "Class_Of_Share": "Ordinary" + } + }, + "officers": [ + { + "uuid": "a1b2c3d4-e5f6-4789-9a0b-1c2d3e4f5a6b", + "name": "CHRISTOPHER JON TAYLOR", + "designation": "Secretary", + "role": "secretary", + "nationality": null, + "is_active": true, + "kyc_status": null, + "kyc_session_url": null + }, + { + "uuid": "b2c3d4e5-f6a7-4890-8b1c-2d3e4f5a6b7c", + "name": "MELISSA BETHELL", + "designation": "Director", + "role": "director", + "nationality": "British", + "is_active": true, + "kyc_status": null, + "kyc_session_url": null + } + ], + "beneficial_owners": [], + "addresses": [ + { + "address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", + "type": "legal_entity_registry_address", + "description": "registered office address" + } + ], + "industries": null, + "accounts": { + "last_account": "26 February 2025", + "next_account": "26 February 2026", + "due_by_date": "26 August 2026" + }, + "registry_data": { + "name": "Tesco PLC", + "registration_number": "00445790", + "status": "active", + "type": "Public Limited Company", + "incorporation_date": "1947-11-27", + "tax_number": "GB412 8925 93", + "country_code": "GB", + "fetch_status": "resolved", + "industries_detail": [ + { + "code": "47110", + "name": "Retail Sale In Non-specialised Stores With Food, Beverages Or Tobacco Predominating", + "description": null, + "meta_detail": [] + } + ] + }, + "user_provided_data": null, + "confirmed_by_user_at": null, + "last_console_edit_at": null, + "is_editable": true + }, + "vendor_data": "company-1234", + "metadata": null, + "created_at": "2026-06-11T10:45:00.000000+00:00" + } + }, + "Pending (registry still resolving)": { + "summary": "Select stayed pending after polling — sparse fields, auto-completes server-side", + "value": { + "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", + "kyb_registry": { + "uuid": "7c0a4f7e-2b1d-4f6a-9c3e-8d5b2a1f0e9d", + "node_id": "feature_kyb_registry", + "status": "Not Finished", + "registry_status": null, + "data_resolved": false, + "company_name": "Tesco PLC", + "registration_number": "00445790", + "country_code": "GB", + "region": null, + "company_type": null, + "incorporation_date": null, + "registered_address": null, + "tax_number": null, + "risk_level": null, + "verification_status": null, + "is_from_registry": true, + "fetch_status": "pending", + "alternative_names": null, + "nature_of_business": null, + "registered_capital": null, + "registered_capital_amount": null, + "registered_capital_currency": null, + "website": null, + "email": null, + "phone": null, + "legal_entity_identifier": null, + "location_of_registration": null, + "financial_summary": {}, + "officers": [], + "beneficial_owners": [], + "addresses": null, + "industries": null, + "accounts": null, + "registry_data": {}, + "user_provided_data": null, + "confirmed_by_user_at": null, + "last_console_edit_at": null, + "is_editable": true + }, + "vendor_data": "company-1234", + "metadata": null, + "created_at": "2026-06-11T10:45:00.000000+00:00" + } + } + } + } + } + }, + "400": { + "description": "Validation error. Field-level problems use DRF's standard envelope.", + "content": { + "application/json": { + "examples": { + "Missing kyb_response_id": { + "summary": "`kyb_response_id` is required", + "value": { + "kyb_response_id": [ + "This field is required." + ] + } + }, + "save_api_request=false rejected": { + "summary": "Selects are always persisted", + "value": { + "save_api_request": [ + "KYB registry select must be saved so billing and retrieval can be tracked." + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "/v3/passive-liveness/": { + "post": { + "summary": "Passive Liveness", + "description": "Detect presentation attacks — printed photos, screen replays, masks, deepfakes — from a single face image. No video, motion, or user interaction required.\n\n**How it works.** The image is analyzed by a biometric model; the largest detected face is evaluated and a liveness `score` (0–100) is computed. `status` is `Declined` when any `error`-severity warning fires:\n- `NO_FACE_DETECTED` — the liveness model found no face,\n- `LOW_LIVENESS_SCORE` — score at or below `face_liveness_score_decline_threshold` (default `30`),\n- `LIVENESS_FACE_ATTACK` — the model detected a presentation attack,\n- `FACE_IN_BLOCKLIST` / `POSSIBLE_FACE_IN_BLOCKLIST` — the face matches an entry on your face blocklist.\n\n`MULTIPLE_FACES_DETECTED` is a `warning`-severity signal (does not decline), and `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` are `information`-severity signals that the same face already appeared in another approved session (they never decline). `face_quality` and `face_luminance` (0–100, nullable) describe capture quality.\n\n**Blocklist and duplicate screening.** Runs on **every** call — with or without `save_api_request` — by matching the detected face against your application's face search index. Prior faces with the same `vendor_data` are excluded from the entire index search — so re-running liveness for the same user does not trigger `DUPLICATED_FACE`, and a blocklisted face originating from a session with the caller's own `vendor_data` is also NOT flagged (`FACE_IN_BLOCKLIST` screening is bypassed for same-`vendor_data` faces).\n\n**Billing.** Each `200` response consumes one Passive Liveness API credit (standalone APIs have no free tier). Insufficient balance returns `403` before any image processing.\n\n**Session persistence and face enrollment (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session (Business Console, `GET /v3/session/{sessionId}/decision/` via the returned `request_id`, `status.updated` webhook), the reference image and face crop are stored, and the detected face is **enrolled into your face search index** so later Face Search calls and duplicate checks can match it. When `false`, the call is one-shot: nothing is stored and the face is not enrolled (blocklist/duplicate screening still runs).\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", + "operationId": "post_v3passive-liveness", + "x-didit-sdk": { + "resource": "passive_liveness", + "method": "check", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "user_image" + ], + "properties": { + "user_image": { + "type": "string", + "format": "binary", + "description": "Front-facing face image (e.g. a selfie). Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.5 MB before processing, so very high resolutions do not improve accuracy. The image should contain a single subject — when several faces are present, the largest one is evaluated and a `MULTIPLE_FACES_DETECTED` warning is added." + }, + "face_liveness_score_decline_threshold": { + "type": "number", + "format": "float", + "default": 30, + "minimum": 0, + "maximum": 100, + "description": "Liveness score threshold (0–100). A computed score **at or below** this value emits a `LOW_LIVENESS_SCORE` warning and sets `status` to `Declined`. Default `30`; raise it for a stricter anti-spoofing posture. Values outside 0–100 return `400`.", + "example": 30 + }, + "rotate_image": { + "type": "boolean", + "default": false, + "description": "When `true`, the service tries 90-degree rotations of the input and uses the orientation that yields the best face detection. Useful when EXIF orientation is missing. Adds latency.", + "example": false + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "When `true` (default), persists the call as an API-type session, stores the face images, emits a `status.updated` webhook, and enrolls the detected face into your face search index. When `false`, the call is one-shot — nothing is stored and the face is not enrolled. Blocklist and duplicate screening run either way.", + "example": true + }, + "vendor_data": { + "type": "string", + "description": "Optional opaque identifier (your user id, email, UUID…) stored on the persisted session and echoed back. Also used to exclude prior sessions with the same `vendor_data` from the duplicate-face check, so re-verifying the same user does not raise `DUPLICATED_FACE`.", + "example": "user-123" + }, + "metadata": { + "type": "object", + "additionalProperties": true, + "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back. In multipart requests, send it as a JSON-encoded string field — it is parsed into an object.", + "example": { + "flow": "withdrawal" + } + } + } + }, + "example": { + "user_image": "(binary JPEG/PNG selfie)", + "face_liveness_score_decline_threshold": 30, + "rotate_image": false, + "save_api_request": true, + "vendor_data": "user-123", + "metadata": { + "flow": "withdrawal" + } + } + } + } + }, + "responses": { + "200": { + "description": "Passive liveness completed. `liveness.score` is the model's 0–100 confidence that the image shows a live person; `status` is `Approved` unless an `error`-severity warning fired. A spoofed or blocklisted face still returns `200` — inspect `liveness.status` and `liveness.warnings`, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", + "content": { + "application/json": { + "examples": { + "Approved": { + "summary": "Live face — no warnings", + "value": { + "request_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef", + "liveness": { + "status": "Approved", + "method": "PASSIVE", + "score": 95.0, + "user_image": { + "entities": [ + { + "bbox": [ + 661, + 728, + 1688, + 2188 + ], + "confidence": 0.732973, + "age": 26.91, + "gender": "male", + "race": null + } + ], + "best_angle": 0 + }, + "warnings": [], + "face_quality": 84.21, + "face_luminance": 50.33 + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T01:04:42.763237+00:00" + } + }, + "Declined - presentation attack": { + "summary": "The model flagged a spoof (print, replay, mask…)", + "value": { + "request_id": "f1c8c9b2-d3e4-4f5a-9b8c-7d6e5f4a3b2c", + "liveness": { + "status": "Declined", + "method": "PASSIVE", + "score": 12.5, + "user_image": { + "entities": [ + { + "bbox": [ + 100, + 120, + 420, + 540 + ], + "confidence": 0.81, + "age": 30.1, + "gender": "female", + "race": null + } + ], + "best_angle": 0 + }, + "warnings": [ + { + "risk": "LOW_LIVENESS_SCORE", + "feature": "LIVENESS", + "additional_data": null, + "log_type": "error", + "short_description": "Low liveness score", + "long_description": "The liveness check resulted in a low score, indicating potential use of non-live facial representations or poor-quality biometric data." + }, + { + "risk": "LIVENESS_FACE_ATTACK", + "feature": "LIVENESS", + "additional_data": null, + "log_type": "error", + "short_description": "Liveness Face Attack", + "long_description": "The system detected a potential attempt to bypass the liveness check." + } + ], + "face_quality": 72.4, + "face_luminance": 48.2 + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T01:04:42.763237+00:00" + } + }, + "Declined - face on blocklist": { + "summary": "Live face, but it matches a blocklist entry", + "value": { + "request_id": "0b54a1de-22aa-4e0f-9c84-5a2b1f3c4d5e", + "liveness": { + "status": "Declined", + "method": "PASSIVE", + "score": 93.75, + "user_image": { + "entities": [ + { + "bbox": [ + 40, + 40, + 100, + 100 + ], + "confidence": 0.722082, + "age": 27.0, + "gender": "male", + "race": null + } + ], + "best_angle": 0 + }, + "warnings": [ + { + "risk": "FACE_IN_BLOCKLIST", + "feature": "LIVENESS", + "additional_data": { + "blocklisted_session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86", + "blocklisted_session_number": 3242, + "api_service": null + }, + "log_type": "error", + "short_description": "Face in blocklist", + "long_description": "The system identified a face in the blocklist, which means the face is not allowed to be verified." + } + ], + "face_quality": null, + "face_luminance": null + }, + "vendor_data": "user-123", + "metadata": null, + "created_at": "2026-06-12T01:04:42.763237+00:00" + } + } + }, + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + }, + "liveness": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined" + ], + "description": "`Declined` when at least one `error`-severity warning fired (no face, low score, presentation attack, or a blocklist hit); `Approved` otherwise. `warning`/`information` entries (multiple faces, duplicates) never decline on their own." + }, + "method": { + "type": "string", + "enum": [ + "PASSIVE" + ], + "description": "Always `PASSIVE` for this endpoint." + }, + "score": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 100, + "description": "Liveness confidence (0–100, two decimals) that the image depicts a live person rather than a spoof. `null` when the model returned no confidence.", + "example": 95.0 + }, + "user_image": { + "type": "object", + "description": "Face-detection results for the submitted image.", + "properties": { + "entities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "bbox": { + "type": "array", + "items": { + "type": "integer" + }, + "minItems": 4, + "maxItems": 4, + "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.", + "example": [ + 661, + 728, + 1688, + 2188 + ] + }, + "confidence": { + "type": "number", + "format": "float", + "minimum": 0, + "maximum": 1, + "description": "Face-detection confidence (0–1).", + "example": 0.732973 + }, + "age": { + "type": "number", + "format": "float", + "description": "Model-estimated age of the detected face, in years. Informational only — it does not affect the liveness decision.", + "example": 26.91 + }, + "gender": { + "type": "string", + "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.", + "example": "male" + }, + "race": { + "type": "string", + "nullable": true, + "description": "Reserved field — always `null` in responses from this endpoint.", + "example": null + } + } + }, + "description": "One entry per detected face. The largest face is the one evaluated." + }, + "best_angle": { + "type": "integer", + "nullable": true, + "description": "Rotation (degrees) that produced the best face detection; non-zero only when `rotate_image=true` corrected the orientation.", + "example": 0 + } + } + }, + "warnings": { + "type": "array", + "description": "Risk signals. `error` severity (declines): `NO_FACE_DETECTED`, `LOW_LIVENESS_SCORE` (score at or below the threshold), `LIVENESS_FACE_ATTACK`, `FACE_IN_BLOCKLIST`, `POSSIBLE_FACE_IN_BLOCKLIST`. `warning` severity (review, no decline): `MULTIPLE_FACES_DETECTED`. `information` severity (no decline): `DUPLICATED_FACE`, `POSSIBLE_DUPLICATED_FACE` — the same face already appeared in another approved session.", + "items": { + "type": "object", + "properties": { + "risk": { + "type": "string", + "enum": [ + "NO_FACE_DETECTED", + "LOW_LIVENESS_SCORE", + "LIVENESS_FACE_ATTACK", + "MULTIPLE_FACES_DETECTED", + "FACE_IN_BLOCKLIST", + "POSSIBLE_FACE_IN_BLOCKLIST", + "DUPLICATED_FACE", + "POSSIBLE_DUPLICATED_FACE" + ], + "description": "Machine-readable risk code." + }, + "feature": { + "type": "string", + "enum": [ + "LIVENESS" + ], + "description": "Feature that raised the warning. Always `LIVENESS` on this endpoint." + }, + "additional_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "`null` for most warnings. Blocklist hits carry `{blocklisted_session_id, blocklisted_session_number, api_service}`; duplicate hits carry `{duplicated_session_id, duplicated_session_number, api_service}` pointing at the matching session." + }, + "log_type": { + "type": "string", + "enum": [ + "error", + "warning", + "information" + ], + "description": "Severity. `error` warnings drive `status` to `Declined`; `warning` and `information` entries are advisory and never decline on their own." + }, + "short_description": { + "type": "string", + "description": "Human-readable one-line summary of the risk." + }, + "long_description": { + "type": "string", + "description": "Human-readable explanation of the risk." + } + } + } + }, + "face_quality": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 100, + "description": "Capture-quality score of the detected face, normalized to 0–100. `null` when the model could not produce a reliable value.", + "example": 84.21 + }, + "face_luminance": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 100, + "description": "Brightness of the detected face, normalized to 0–100. `null` when unavailable.", + "example": 50.33 + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, or `null`." + }, + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` object you sent, or `null`." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp (UTC) of when the response was generated, e.g. `2026-06-12T01:04:42.763237+00:00`." + } + } + } + } + } + }, + "400": { + "description": "Validation error. Returned when `user_image` is missing, exceeds 5 MB, has an unsupported extension, fails server-side image decoding, or when an option is out of range. Field errors use DRF's `{field: [messages]}` envelope; image-decoding failures use `{\"error\": ...}`.", + "content": { + "application/json": { + "examples": { + "Missing image": { + "summary": "`user_image` not included in the form data", + "value": { + "user_image": [ + "No file was submitted." + ] + } + }, + "Invalid image format": { + "summary": "Allowed extension but undecodable image content", + "value": { + "error": "Invalid user image format.", + "code": "invalid_image" + } + }, + "Unsupported file extension": { + "summary": "File extension outside tiff/jpg/jpeg/png/webp", + "value": { + "user_image": [ + "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp." + ] + } + }, + "File too large": { + "summary": "Upload exceeds the 5 MB limit", + "value": { + "user_image": [ + "File size should not exceed 5 MB" + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X POST 'https://verification.didit.me/v3/passive-liveness/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'user_image=@./selfie.jpg' \\\n -F 'face_liveness_score_decline_threshold=30' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nurl = 'https://verification.didit.me/v3/passive-liveness/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as f:\n files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}\n data = {\n 'face_liveness_score_decline_threshold': 30,\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nbody = resp.json()\nprint('status:', body['liveness']['status'])\nprint('score:', body['liveness']['score'])\nfor w in body['liveness']['warnings']:\n print('warning:', w['risk'], '-', w['log_type'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('face_liveness_score_decline_threshold', '30');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/passive-liveness/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`Passive liveness failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.liveness.status, 'score:', body.liveness.score);\nbody.liveness.warnings.forEach(w => console.log('warning:', w.risk, '-', w.log_type));" + } + ] + } + }, + "/v3/database-validation/": { + "post": { + "summary": "Database Validation (standalone)", + "operationId": "post_v3database-validation", + "x-didit-sdk": { + "resource": "database_validation", + "method": "validate", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [], + "description": "Validate a person's identity data against official government and registry sources — CPF in Brazil, RENAPER in Argentina, DNI registries in Spain and Peru, INE in Mexico, and 100+ more services across 60+ countries.\n\n**Service selection.** Each country exposes one or more catalog services identified by a `service_id` (e.g. `bra_cpf`, `arg_renaper`, `pan_cedula_sib`). Pin the services you want with the `services` array — if you omit it, exactly **one** default service runs (the longest-established live service for the country). Sending `services` also switches the response to the extended shape with `services_used` and `match_score`. Discover services, their required fields, and prices via `GET /v1/organization/database-validation-countries/` (a catalog endpoint not documented in this spec) or the Business Console workflow editor. Some services are flagged `requires_consent=true` in the catalog and demand `consent=true`; others require onboarding for your organization before they can be called.\n\n**Input fields.** `identification_number` is a universal field that maps to the right country-specific field automatically (see its description). Country format rules are enforced before any provider is called (e.g. BRA CPF must be exactly 11 digits) and format failures are never billed. Biometric services need a `selfie` upload (multipart only); Argentina RENAPER additionally requires `gender`.\n\n**Results.** Each service returns a field-by-field `validation` map plus a vendor-neutral `outcome_code` (`MATCH`, `PARTIAL_MATCH`, `NO_MATCH`, `DOCUMENT_NOT_FOUND`, `INVALID_DOCUMENT_FORMAT`, `INVALID_INPUT`, `MINOR_BLOCKED`, `DECEASED`, `BIOMETRIC_NO_MATCH`, `BIOMETRIC_IMAGE_UNUSABLE`, `INCONCLUSIVE`, `DOCUMENT_SUPERSEDED`, `REGISTRY_UNAVAILABLE`, `REGISTRY_ERROR`). Note the distinctions: `NO_MATCH` is a definitive mismatch, `INCONCLUSIVE` means the registry could not confirm either way (review, not decline), and `BIOMETRIC_IMAGE_UNUSABLE` is a technical selfie problem (retake), unlike the definitive `BIOMETRIC_NO_MATCH`. The overall `match_type` aggregates across services (any full → `full_match`, else any partial → `partial_match`, else `no_match`) and `no_match_action`/`partial_match_action` translate it into the final `status`. `validation_type` is derived: `two_by_two` when two or more distinct services produced a full match (on the identification number, on full name + date of birth together, or on the address without contradictions), else `one_by_one`.\n\n**Persistence.** `save_api_request` defaults to **true**: the result is stored as a session (`request_id` works with `GET /v3/session/{sessionId}/decision/`), appears in the console, and fires a `status.updated` webhook. It also controls the `validations` shape — per-service objects when saved, a merged `{field: match}` map when not.\n\n**Billing.** Per-service: each service that returns a billable result is charged its own catalog price. Failed or skipped services are not billed, and requests rejected with `400` cost nothing. The pre-flight balance check covers the sum of the selected services' prices (`403` when short).\n\n**Failures.** When no selected service returns a usable result, the API returns `400` with `validation_errors` if every unanswered attempt explicitly refused the input (`provider_rejected_input`, `retryable: false`). Correct the input before trying again. Otherwise it returns `502` with `validation_errors` (`empty_provider_response`, `retryable: true`), including mixed input rejection and unavailable fallback attempts; nothing is billed (a session is still recorded with status `Not Finished` when `save_api_request=true`). When only some services fail, the `200` response carries their failures in `database_validation.errors`.\n\n**Sandbox.** Keys from sandbox applications skip the registry calls and billing and return a static `full_match` response — the sandbox payload always includes `services_used` and `match_score: 100` regardless of how `services` was sent, so don't validate the live extended-shape gating or the 0.0–1.0 `match_score` fraction against sandbox responses.\n\nSend the request as `application/json`, or as `multipart/form-data` when uploading a `selfie`.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "issuing_state" + ], + "properties": { + "issuing_state": { + "type": "string", + "description": "ISO 3166-1 **alpha-3** country code of the registry to validate against (e.g. `BRA`, `ESP`, `COL`). Determines which services are available. Unsupported codes return `400` with the full list of valid options.", + "example": "BRA" + }, + "services": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Catalog `service_id`s to run for this country (e.g. `[\"bra_cpf\"]`). Also accepts a single string or a comma-separated/JSON-encoded string. **If omitted or empty, exactly one default service runs** — the longest-established live service for the country; newer, biometric, and pay-as-you-go services must be named explicitly. Sending `services` switches the response to the extended shape (adds `services_used` and `match_score`). Every id must exist and be live for the `issuing_state`; services that require onboarding return `400` until activated for your organization. Discover available services per country via `GET /v1/organization/database-validation-countries/` (a catalog endpoint not documented in this spec) or the Business Console workflow editor.", + "example": [ + "bra_cpf" + ] + }, + "validation_type": { + "type": "string", + "deprecated": true, + "description": "DEPRECATED. Accepted for backward compatibility but ignored — the response `validation_type` is now derived from how many services full-matched. Use `services` to pin specific services." + }, + "consent": { + "type": "boolean", + "default": false, + "description": "Set to `true` when the end user has explicitly consented to the selected validation services. Required for services flagged `requires_consent=true` in the catalog — without it those services return `400` with `services_requiring_consent`." + }, + "identification_number": { + "type": "string", + "description": "Universal identification number — automatically mapped to the correct country-specific field, so you can use it instead of `personal_number`/`tax_number`/`document_number`: ARG→document_number (DNI), BOL→document_number (CI), BRA→tax_number (CPF, 11 digits), CHL→personal_number (RUT), COL→personal_number (Cédula), CRI→personal_number (Cédula), DOM→personal_number (Cédula, 11 digits), ECU→personal_number (Cédula, 10 digits), ESP→personal_number (DNI/NIE), GTM→document_number (DPI), HND→document_number (DNI), MEX→personal_number (CURP, 18 chars), PAN→personal_number (Cédula), PER→personal_number (DNI, 8 digits), PRY→document_number (CI), SLV→document_number (DUI), URY→personal_number (CI), VEN→document_number (Cédula). It never overrides an explicitly provided country-specific field." + }, + "first_name": { + "type": "string", + "description": "The individual's first name. Required by some services/countries.", + "example": "John" + }, + "last_name": { + "type": "string", + "description": "The individual's last name. Required by some services/countries.", + "example": "Doe" + }, + "middle_name": { + "type": "string", + "description": "Middle name, used by some country services (AUS, NZL, …)." + }, + "full_name": { + "type": "string", + "description": "Full name — some services accept this in lieu of first/last name (e.g. CHN services, which match the native-script name)." + }, + "date_of_birth": { + "type": "string", + "format": "date", + "description": "Date of birth, `YYYY-MM-DD`. Required by many services.", + "example": "1980-01-01" + }, + "personal_number": { + "type": "string", + "description": "Government-issued unique personal identifier. Used by: CHL (RUT), COL (Cédula), CRI, DOM, ECU, ESP (DNI/NIE), MEX (CURP), PAN (Cédula), PER (DNI), URY. Consider `identification_number` instead." + }, + "tax_number": { + "type": "string", + "description": "Tax identification number. Used by: BRA (CPF, 11 digits). Consider `identification_number` instead." + }, + "document_number": { + "type": "string", + "description": "Document number. Used by: ARG (DNI), BOL (CI), GTM (DPI), HND (DNI), PRY (CI), SLV (DUI), VEN (Cédula). Consider `identification_number` instead." + }, + "document_type": { + "type": "string", + "enum": [ + "P", + "DL", + "ID", + "RP", + "SSC", + "HIC", + "WP", + "TC", + "VISA", + "PSC", + "BC", + "OTHER" + ], + "description": "Type of document being validated: `P` passport, `DL` driver license, `ID` national ID, `RP` residence permit (plus `SSC`, `HIC`, `WP`, `TC`, `VISA`, `PSC`, `BC`, `OTHER`). Required by some services (e.g. ESP)." + }, + "expiration_date": { + "type": "string", + "format": "date", + "description": "Document expiration date, `YYYY-MM-DD`. Required for ESP (Spain) validation.", + "example": "2030-01-15" + }, + "date_of_issue": { + "type": "string", + "format": "date", + "description": "Document issue date, `YYYY-MM-DD` (fecha de expedición). Required for COL (Colombia) cédula validation.", + "example": "2015-06-20" + }, + "nationality": { + "type": "string", + "description": "Nationality as ISO 3166-1 alpha-3. Required by some services." + }, + "gender": { + "type": "string", + "enum": [ + "M", + "F", + "X" + ], + "description": "Gender: `M`, `F`, or `X` (other/unknown). Required for Argentina's RENAPER validation (`arg_renaper`)." + }, + "address": { + "description": "Residential address. Prefer the structured object `{\"street_1\":\"123 Main St\",\"street_2\":\"Apt 4B\",\"city\":\"Springfield\",\"region\":\"IL\",\"postal_code\":\"62701\",\"country\":\"US\"}`; a complete single-line string is still accepted. Required by address-verification services; when a service defines address requirements, missing parts return `400` with `address_fields_required_by`.", + "oneOf": [ + { + "type": "object", + "properties": { + "street_1": { + "type": "string" + }, + "street_2": { + "type": "string" + }, + "city": { + "type": "string" + }, + "region": { + "type": "string" + }, + "postal_code": { + "type": "string" + }, + "country": { + "type": "string" + } + } + }, + { + "type": "string", + "description": "Complete single-line address (legacy)." + } + ] + }, + "address_element_1": { + "type": "string", + "description": "Street address including street number and type. If omitted, derived from `address`." + }, + "address_element_2": { + "type": "string", + "description": "Apartment, unit, building, floor, or extra address line. Only send when you have it explicitly." + }, + "address_element_3": { + "type": "string", + "description": "City, suburb, district, locality, or neighborhood. If omitted, derived from `address`." + }, + "address_element_4": { + "type": "string", + "description": "State, province, region, or town. If omitted, derived from `address`." + }, + "address_element_5": { + "type": "string", + "description": "Postcode or postal code. `postal_code` is accepted as an alias." + }, + "postal_code": { + "type": "string", + "description": "Postal code for address-based services (alias of `address_element_5`)." + }, + "driver_license_number": { + "type": "string", + "description": "Driver licence number for government document-verification services (AUS, NZL, IND)." + }, + "driver_license_card_number": { + "type": "string", + "description": "Physical driver licence card number, where required separately from the licence number (AUS)." + }, + "driver_license_state": { + "type": "string", + "description": "Driver licence state or territory of issue (AUS)." + }, + "driver_license_version": { + "type": "string", + "description": "Driver licence version code (NZL)." + }, + "passport_number": { + "type": "string", + "description": "Passport number for government passport-verification services (AUS, NZL, IND, CHN)." + }, + "passport_expiration_date": { + "type": "string", + "format": "date", + "description": "Passport expiry date, `YYYY-MM-DD`, for passport-verification services." + }, + "passport_file_number": { + "type": "string", + "description": "Passport file number (IND)." + }, + "passport_issue_country": { + "type": "string", + "description": "Issuing country of the passport (ISO 3166-1 alpha-3) for passport-verification services." + }, + "medicare_card_number": { + "type": "string", + "description": "Medicare card number (AUS)." + }, + "immi_card_number": { + "type": "string", + "description": "Australian ImmiCard number." + }, + "immi_card_expiry_date": { + "type": "string", + "format": "date", + "description": "Australian ImmiCard expiry date." + }, + "citizenship_certificate_number": { + "type": "string", + "description": "Citizenship certificate number (AUS)." + }, + "birth_registration_number": { + "type": "string", + "description": "Birth-certificate registration number (AUS)." + }, + "birth_registration_date": { + "type": "string", + "format": "date", + "description": "Birth-certificate registration date (AUS)." + }, + "birth_registration_state": { + "type": "string", + "description": "Birth-certificate registration state (AUS)." + }, + "marriage_certificate_number": { + "type": "string", + "description": "Marriage certificate number (AUS)." + }, + "change_of_name_certificate_number": { + "type": "string", + "description": "Change-of-name certificate number (AUS)." + }, + "first_partner_name": { + "type": "string", + "description": "First name of the partner on a marriage certificate (AUS)." + }, + "last_partner_name": { + "type": "string", + "description": "Last name of the partner on a marriage certificate (AUS)." + }, + "voter_id": { + "type": "string", + "description": "Voter registration number (IND EPIC, IRL)." + }, + "epic_card": { + "type": "string", + "description": "India EPIC voter card number." + }, + "pan": { + "type": "string", + "description": "India PAN (Permanent Account Number)." + }, + "national_id": { + "type": "string", + "description": "National ID number (CHN, MYS, KEN, KHM, NGA, ZAF, …)." + }, + "cic": { + "type": "string", + "description": "Mexican INE/IFE Código de Identificación de Credencial (CIC), 9 digits. Used by the MEX INE credential-validity service (`mex_ine_vigencia`)." + }, + "identificador_ciudadano": { + "type": "string", + "description": "Mexican INE Identificador del Ciudadano (9 digits). Used with `cic` for modern INE models (E/F/G/H) in `mex_ine_vigencia`." + }, + "ocr": { + "type": "string", + "description": "Mexican INE OCR number (13 digits, back of card). Used with `cic` for Model D in `mex_ine_vigencia`." + }, + "voter_number": { + "type": "string", + "description": "Mexican INE Clave de Elector (18 chars). Used with `emission_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`." + }, + "emission_number": { + "type": "string", + "description": "Mexican INE Número de Emisión. Used with `voter_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`." + }, + "bvn": { + "type": "string", + "description": "Nigerian Bank Verification Number." + }, + "bank_card_number": { + "type": "string", + "description": "Bank card number (CHN bank-card verification)." + }, + "ssn": { + "type": "string", + "description": "Social Security Number (USA)." + }, + "phone": { + "type": "string", + "description": "Phone number for phone-verification services." + }, + "landline": { + "type": "string", + "description": "Landline number for phone-verification services." + }, + "email": { + "type": "string", + "format": "email", + "description": "Email address for identity-verification services." + }, + "country_of_residence": { + "type": "string", + "description": "ISO 3166-1 alpha-2 country of residence. Used by global identity-enrichment services (e.g. `glb_identity_enrichment`) to focus the lookup.", + "example": "US" + }, + "partial_match_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "NO_ACTION", + "description": "What a `partial_match` does to `database_validation.status`. Defaults to `NO_ACTION` (status stays `Approved`, with a warning)." + }, + "no_match_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "DECLINE", + "description": "What a `no_match` does to `database_validation.status`. Defaults to `DECLINE`." + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "Persist the validation as a session (console visibility, decision endpoint, `status.updated` webhook). Also changes the `validations` response shape: per-service objects when `true` (default), a merged field map when `false`." + }, + "vendor_data": { + "type": "string", + "description": "Your identifier for the validated user; echoed back and stored with the session." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Free-form JSON stored with the request and echoed back." + } + } + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "issuing_state" + ], + "properties": { + "issuing_state": { + "type": "string", + "description": "ISO 3166-1 **alpha-3** country code of the registry to validate against (e.g. `BRA`, `ESP`, `COL`). Determines which services are available. Unsupported codes return `400` with the full list of valid options.", + "example": "BRA" + }, + "services": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Catalog `service_id`s to run for this country (e.g. `[\"bra_cpf\"]`). Also accepts a single string or a comma-separated/JSON-encoded string. **If omitted or empty, exactly one default service runs** — the longest-established live service for the country; newer, biometric, and pay-as-you-go services must be named explicitly. Sending `services` switches the response to the extended shape (adds `services_used` and `match_score`). Every id must exist and be live for the `issuing_state`; services that require onboarding return `400` until activated for your organization. Discover available services per country via `GET /v1/organization/database-validation-countries/` (a catalog endpoint not documented in this spec) or the Business Console workflow editor.", + "example": [ + "bra_cpf" + ] + }, + "validation_type": { + "type": "string", + "deprecated": true, + "description": "DEPRECATED. Accepted for backward compatibility but ignored — the response `validation_type` is now derived from how many services full-matched. Use `services` to pin specific services." + }, + "consent": { + "type": "boolean", + "default": false, + "description": "Set to `true` when the end user has explicitly consented to the selected validation services. Required for services flagged `requires_consent=true` in the catalog — without it those services return `400` with `services_requiring_consent`." + }, + "identification_number": { + "type": "string", + "description": "Universal identification number — automatically mapped to the correct country-specific field, so you can use it instead of `personal_number`/`tax_number`/`document_number`: ARG→document_number (DNI), BOL→document_number (CI), BRA→tax_number (CPF, 11 digits), CHL→personal_number (RUT), COL→personal_number (Cédula), CRI→personal_number (Cédula), DOM→personal_number (Cédula, 11 digits), ECU→personal_number (Cédula, 10 digits), ESP→personal_number (DNI/NIE), GTM→document_number (DPI), HND→document_number (DNI), MEX→personal_number (CURP, 18 chars), PAN→personal_number (Cédula), PER→personal_number (DNI, 8 digits), PRY→document_number (CI), SLV→document_number (DUI), URY→personal_number (CI), VEN→document_number (Cédula). It never overrides an explicitly provided country-specific field." + }, + "first_name": { + "type": "string", + "description": "The individual's first name. Required by some services/countries.", + "example": "John" + }, + "last_name": { + "type": "string", + "description": "The individual's last name. Required by some services/countries.", + "example": "Doe" + }, + "middle_name": { + "type": "string", + "description": "Middle name, used by some country services (AUS, NZL, …)." + }, + "full_name": { + "type": "string", + "description": "Full name — some services accept this in lieu of first/last name (e.g. CHN services, which match the native-script name)." + }, + "date_of_birth": { + "type": "string", + "format": "date", + "description": "Date of birth, `YYYY-MM-DD`. Required by many services.", + "example": "1980-01-01" + }, + "personal_number": { + "type": "string", + "description": "Government-issued unique personal identifier. Used by: CHL (RUT), COL (Cédula), CRI, DOM, ECU, ESP (DNI/NIE), MEX (CURP), PAN (Cédula), PER (DNI), URY. Consider `identification_number` instead." + }, + "tax_number": { + "type": "string", + "description": "Tax identification number. Used by: BRA (CPF, 11 digits). Consider `identification_number` instead." + }, + "document_number": { + "type": "string", + "description": "Document number. Used by: ARG (DNI), BOL (CI), GTM (DPI), HND (DNI), PRY (CI), SLV (DUI), VEN (Cédula). Consider `identification_number` instead." + }, + "document_type": { + "type": "string", + "enum": [ + "P", + "DL", + "ID", + "RP", + "SSC", + "HIC", + "WP", + "TC", + "VISA", + "PSC", + "BC", + "OTHER" + ], + "description": "Type of document being validated: `P` passport, `DL` driver license, `ID` national ID, `RP` residence permit (plus `SSC`, `HIC`, `WP`, `TC`, `VISA`, `PSC`, `BC`, `OTHER`). Required by some services (e.g. ESP)." + }, + "expiration_date": { + "type": "string", + "format": "date", + "description": "Document expiration date, `YYYY-MM-DD`. Required for ESP (Spain) validation.", + "example": "2030-01-15" + }, + "date_of_issue": { + "type": "string", + "format": "date", + "description": "Document issue date, `YYYY-MM-DD` (fecha de expedición). Required for COL (Colombia) cédula validation.", + "example": "2015-06-20" + }, + "nationality": { + "type": "string", + "description": "Nationality as ISO 3166-1 alpha-3. Required by some services." + }, + "gender": { + "type": "string", + "enum": [ + "M", + "F", + "X" + ], + "description": "Gender: `M`, `F`, or `X` (other/unknown). Required for Argentina's RENAPER validation (`arg_renaper`)." + }, + "address": { + "description": "Residential address. Prefer the structured object `{\"street_1\":\"123 Main St\",\"street_2\":\"Apt 4B\",\"city\":\"Springfield\",\"region\":\"IL\",\"postal_code\":\"62701\",\"country\":\"US\"}`; a complete single-line string is still accepted. Required by address-verification services; when a service defines address requirements, missing parts return `400` with `address_fields_required_by`.", + "oneOf": [ + { + "type": "object", + "properties": { + "street_1": { + "type": "string" + }, + "street_2": { + "type": "string" + }, + "city": { + "type": "string" + }, + "region": { + "type": "string" + }, + "postal_code": { + "type": "string" + }, + "country": { + "type": "string" + } + } + }, + { + "type": "string", + "description": "Complete single-line address (legacy)." + } + ] + }, + "address_element_1": { + "type": "string", + "description": "Street address including street number and type. If omitted, derived from `address`." + }, + "address_element_2": { + "type": "string", + "description": "Apartment, unit, building, floor, or extra address line. Only send when you have it explicitly." + }, + "address_element_3": { + "type": "string", + "description": "City, suburb, district, locality, or neighborhood. If omitted, derived from `address`." + }, + "address_element_4": { + "type": "string", + "description": "State, province, region, or town. If omitted, derived from `address`." + }, + "address_element_5": { + "type": "string", + "description": "Postcode or postal code. `postal_code` is accepted as an alias." + }, + "postal_code": { + "type": "string", + "description": "Postal code for address-based services (alias of `address_element_5`)." + }, + "driver_license_number": { + "type": "string", + "description": "Driver licence number for government document-verification services (AUS, NZL, IND)." + }, + "driver_license_card_number": { + "type": "string", + "description": "Physical driver licence card number, where required separately from the licence number (AUS)." + }, + "driver_license_state": { + "type": "string", + "description": "Driver licence state or territory of issue (AUS)." + }, + "driver_license_version": { + "type": "string", + "description": "Driver licence version code (NZL)." + }, + "passport_number": { + "type": "string", + "description": "Passport number for government passport-verification services (AUS, NZL, IND, CHN)." + }, + "passport_expiration_date": { + "type": "string", + "format": "date", + "description": "Passport expiry date, `YYYY-MM-DD`, for passport-verification services." + }, + "passport_file_number": { + "type": "string", + "description": "Passport file number (IND)." + }, + "passport_issue_country": { + "type": "string", + "description": "Issuing country of the passport (ISO 3166-1 alpha-3) for passport-verification services." + }, + "medicare_card_number": { + "type": "string", + "description": "Medicare card number (AUS)." + }, + "immi_card_number": { + "type": "string", + "description": "Australian ImmiCard number." + }, + "immi_card_expiry_date": { + "type": "string", + "format": "date", + "description": "Australian ImmiCard expiry date." + }, + "citizenship_certificate_number": { + "type": "string", + "description": "Citizenship certificate number (AUS)." + }, + "birth_registration_number": { + "type": "string", + "description": "Birth-certificate registration number (AUS)." + }, + "birth_registration_date": { + "type": "string", + "format": "date", + "description": "Birth-certificate registration date (AUS)." + }, + "birth_registration_state": { + "type": "string", + "description": "Birth-certificate registration state (AUS)." + }, + "marriage_certificate_number": { + "type": "string", + "description": "Marriage certificate number (AUS)." + }, + "change_of_name_certificate_number": { + "type": "string", + "description": "Change-of-name certificate number (AUS)." + }, + "first_partner_name": { + "type": "string", + "description": "First name of the partner on a marriage certificate (AUS)." + }, + "last_partner_name": { + "type": "string", + "description": "Last name of the partner on a marriage certificate (AUS)." + }, + "voter_id": { + "type": "string", + "description": "Voter registration number (IND EPIC, IRL)." + }, + "epic_card": { + "type": "string", + "description": "India EPIC voter card number." + }, + "pan": { + "type": "string", + "description": "India PAN (Permanent Account Number)." + }, + "national_id": { + "type": "string", + "description": "National ID number (CHN, MYS, KEN, KHM, NGA, ZAF, …)." + }, + "cic": { + "type": "string", + "description": "Mexican INE/IFE Código de Identificación de Credencial (CIC), 9 digits. Used by the MEX INE credential-validity service (`mex_ine_vigencia`)." + }, + "identificador_ciudadano": { + "type": "string", + "description": "Mexican INE Identificador del Ciudadano (9 digits). Used with `cic` for modern INE models (E/F/G/H) in `mex_ine_vigencia`." + }, + "ocr": { + "type": "string", + "description": "Mexican INE OCR number (13 digits, back of card). Used with `cic` for Model D in `mex_ine_vigencia`." + }, + "voter_number": { + "type": "string", + "description": "Mexican INE Clave de Elector (18 chars). Used with `emission_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`." + }, + "emission_number": { + "type": "string", + "description": "Mexican INE Número de Emisión. Used with `voter_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`." + }, + "bvn": { + "type": "string", + "description": "Nigerian Bank Verification Number." + }, + "bank_card_number": { + "type": "string", + "description": "Bank card number (CHN bank-card verification)." + }, + "ssn": { + "type": "string", + "description": "Social Security Number (USA)." + }, + "phone": { + "type": "string", + "description": "Phone number for phone-verification services." + }, + "landline": { + "type": "string", + "description": "Landline number for phone-verification services." + }, + "email": { + "type": "string", + "format": "email", + "description": "Email address for identity-verification services." + }, + "country_of_residence": { + "type": "string", + "description": "ISO 3166-1 alpha-2 country of residence. Used by global identity-enrichment services (e.g. `glb_identity_enrichment`) to focus the lookup.", + "example": "US" + }, + "partial_match_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "NO_ACTION", + "description": "What a `partial_match` does to `database_validation.status`. Defaults to `NO_ACTION` (status stays `Approved`, with a warning)." + }, + "no_match_action": { + "type": "string", + "enum": [ + "DECLINE", + "NO_ACTION" + ], + "default": "DECLINE", + "description": "What a `no_match` does to `database_validation.status`. Defaults to `DECLINE`." + }, + "save_api_request": { + "type": "boolean", + "default": true, + "description": "Persist the validation as a session (console visibility, decision endpoint, `status.updated` webhook). Also changes the `validations` response shape: per-service objects when `true` (default), a merged field map when `false`." + }, + "vendor_data": { + "type": "string", + "description": "Your identifier for the validated user; echoed back and stored with the session." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Free-form JSON stored with the request and echoed back." + }, + "selfie": { + "type": "string", + "format": "binary", + "description": "Selfie image (`jpg`, `jpeg`, `png`, `webp`; max 2 MB — images are compressed to ~1.5 MB before upload to the registry) for biometric validation services, e.g. Argentina RENAPER (`arg_renaper`) and Panama SIB (`pan_cedula_sib`, `pan_cedula_sib_plus`). Required whenever a selected service lists it as a required field. Only available via `multipart/form-data`." + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/database-validation/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"issuing_state\": \"BRA\",\n \"services\": [\"bra_cpf\"],\n \"identification_number\": \"12345678900\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"date_of_birth\": \"1980-01-01\",\n \"vendor_data\": \"user-1234\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/database-validation/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\n \"issuing_state\": \"BRA\",\n \"services\": [\"bra_cpf\"],\n \"identification_number\": \"12345678900\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"date_of_birth\": \"1980-01-01\",\n \"vendor_data\": \"user-1234\",\n },\n timeout=45,\n)\nresp.raise_for_status()\ndv = resp.json()[\"database_validation\"]\nprint(dv[\"status\"], dv[\"match_type\"]) # e.g. Approved full_match\n\n# Biometric services need a selfie via multipart/form-data:\n# requests.post(..., data={\"issuing_state\": \"ARG\", \"services\": '[\"arg_renaper\"]',\n# \"identification_number\": \"12345678\", \"gender\": \"M\", ...},\n# files={\"selfie\": open(\"selfie.jpg\", \"rb\")})" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/database-validation/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n issuing_state: 'BRA',\n services: ['bra_cpf'],\n identification_number: '12345678900',\n first_name: 'John',\n last_name: 'Doe',\n date_of_birth: '1980-01-01',\n vendor_data: 'user-1234',\n }),\n});\nconst data = await res.json();\nconsole.log(data.database_validation.status, data.database_validation.match_type);" + } + ], + "responses": { + "200": { + "description": "Validation completed — at least one selected service returned a usable result. Inspect `database_validation.match_type` for the aggregate outcome, `validations` for the per-service field comparisons and `outcome_code`s, and `errors` (when present) for services that failed.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + }, + "database_validation": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "In Review" + ], + "description": "`Declined` when the overall `match_type` triggers a `DECLINE` action (`no_match_action` defaults to `DECLINE`, `partial_match_action` to `NO_ACTION`); `In Review` when no service produced a usable comparison, for example when the only selected service came back `REGISTRY_UNAVAILABLE` or `INCONCLUSIVE`; `Approved` otherwise. A request that selected several services can be `Approved` on one service's match while another was unavailable, so check `errors` and the per-service `outcome_code`s too." + }, + "issuing_state": { + "type": "string", + "description": "Echo of the ISO 3166-1 alpha-3 country validated against.", + "example": "BRA" + }, + "validation_type": { + "type": "string", + "enum": [ + "one_by_one", + "two_by_two" + ], + "description": "Derived from the results: `two_by_two` when ≥2 distinct services produced a full match (on the identification number, on full name + date of birth together, or on the address without contradictions); `one_by_one` otherwise. (The request field of the same name is deprecated and ignored.)" + }, + "screened_data": { + "type": "object", + "description": "Echo of the input fields that were sent to the selected services (dates normalized to `YYYY-MM-DD`; a `selfie`, when sent, is returned as a pre-signed URL; `consent_obtained: true` is added when consent was given)." + }, + "match_type": { + "type": "string", + "nullable": true, + "enum": [ + "full_match", + "partial_match", + "no_match" + ], + "description": "Overall result aggregated across services with field-level comparisons: any `full_match` → `full_match`; else any `partial_match` → `partial_match`; else `no_match`. Outcome-only services, such as biometric criminal screening, can return `null`; inspect each item in `validations` for its `outcome_code`." + }, + "validations": { + "description": "Shape depends on `save_api_request`. **`true` (default):** an array of per-service objects (`service_id`, `service_name`, `validation`, `outcome_code`, `outcome_detail`, `source_data`). **`false`:** a single flat object merging the field-level results of all services (`{\"full_name\": \"full_match\", ...}`).", + "oneOf": [ + { + "type": "array", + "items": { + "type": "object", + "description": "Per-service validation result (returned when `save_api_request=true`, the default).", + "properties": { + "service_id": { + "type": "string", + "description": "Catalog id of the service that produced this result (e.g. `bra_cpf`).", + "example": "bra_cpf" + }, + "service_name": { + "type": "string", + "description": "Human-readable catalog name of the service.", + "example": "Brazil - CPF status check" + }, + "validation": { + "type": "object", + "description": "Field-by-field comparison between your input and the registry record. Keys are canonical field names (`full_name`, `date_of_birth`, `identification_number`, `address`, …); values are `full_match`, `partial_match`, or `no_match`.", + "additionalProperties": { + "type": "string", + "enum": [ + "full_match", + "partial_match", + "no_match" + ] + } + }, + "outcome_code": { + "type": "string", + "enum": [ + "MATCH", + "PARTIAL_MATCH", + "NO_MATCH", + "DOCUMENT_NOT_FOUND", + "INVALID_DOCUMENT_FORMAT", + "INVALID_INPUT", + "MINOR_BLOCKED", + "DECEASED", + "BIOMETRIC_NO_MATCH", + "BIOMETRIC_IMAGE_UNUSABLE", + "INCONCLUSIVE", + "DOCUMENT_SUPERSEDED", + "REGISTRY_UNAVAILABLE", + "REGISTRY_ERROR" + ], + "description": "Vendor-neutral outcome of the registry lookup. `MATCH`/`PARTIAL_MATCH`/`NO_MATCH` describe the comparison; `DOCUMENT_NOT_FOUND` means the identifier is not in the registry; `INCONCLUSIVE` means the registry could not confirm either way (routed to review — NOT a no-match); `BIOMETRIC_NO_MATCH` is a definitive face mismatch while `BIOMETRIC_IMAGE_UNUSABLE` is a technical image problem (retake the selfie); `DECEASED`/`MINOR_BLOCKED` are registry-policy outcomes; `DOCUMENT_SUPERSEDED` means the identity matched but the edition of the document presented is not the one on file (a newer edition has been issued); `INVALID_DOCUMENT_FORMAT`/`INVALID_INPUT` mean the registry itself rejected the data it was sent; `REGISTRY_UNAVAILABLE`/`REGISTRY_ERROR` indicate upstream failures." + }, + "outcome_detail": { + "type": "string", + "nullable": true, + "description": "Raw upstream status detail backing the outcome code (e.g. an HTTP or registry status code)." + }, + "source_data": { + "type": "object", + "nullable": true, + "description": "Cleaned, normalized view of the registry record using canonical field names (`identification_number`, `first_name`, `last_name`, `date_of_birth`, plus registry-specific flags). Image fields, when present, are returned as pre-signed URLs." + } + } + } + }, + { + "type": "object", + "additionalProperties": { + "type": "string", + "enum": [ + "full_match", + "partial_match", + "no_match" + ] + }, + "description": "Merged field-level results (stateless `save_api_request=false` shape)." + } + ] + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "description": "Feature that raised the warning." }, - "issuing_authority": { + "risk": { "type": "string", + "description": "Machine-readable risk identifier." + }, + "additional_data": { + "type": "object", "nullable": true, - "description": "The authority behind the wallet, e.g. `Danish Agency for Digital Government`." + "description": "Extra context for the warning, when available." }, - "issuing_country": { + "log_type": { "type": "string", - "description": "ISO 3166-1 alpha-3 country the wallet was offered in." + "description": "`warning`, `information`, or `error` — how the risk affected the outcome." }, - "credential_type": { + "short_description": { "type": "string", - "nullable": true, - "description": "Credential the wallet presented, e.g. `person_identification`." + "description": "One-line human-readable summary." }, - "level_of_assurance": { + "long_description": { "type": "string", - "enum": [ - "low", - "substantial", - "high" - ], - "nullable": true, - "description": "The level of assurance the wallet asserted. A sign-in that asserts less than the level requested fails instead of downgrading." + "description": "Full human-readable explanation." + } + } + }, + "description": "`DATABASE_VALIDATION_NO_MATCH` or `DATABASE_VALIDATION_PARTIAL_MATCH` when applicable; empty on a full match." + }, + "errors": { + "type": "array", + "description": "Present only when some (but not all) selected services failed. Each entry carries `service_id`, `code`, `message`, and `retryable`. A service reported here is absent from `validations` and `services_used`, and is not billed. This array is the only place it appears, so inspect it even when `status` is `Approved`.", + "items": { + "type": "object", + "properties": { + "service_id": { + "type": "string" }, - "verified_at": { + "code": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "The wallet's authentication instant." + "example": "empty_provider_response" }, - "signature_valid": { - "type": "boolean", - "nullable": true, - "description": "Whether the signed assertion the wallet returned validated." + "message": { + "type": "string" }, - "attributes": { - "type": "object", - "additionalProperties": true, - "description": "The attributes the wallet shared, keyed as the wallet's catalog entry lists them." + "retryable": { + "type": "boolean", + "description": "`true` when the service produced no answer and retrying it later can succeed; `false` when the source refused the data it was sent and the input must be corrected first.", + "example": true + } + } + } + }, + "services_used": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Catalog ids of the services that returned a billable result. **Only present when the request explicitly sent `services`.**" + }, + "match_score": { + "type": "number", + "description": "Fraction (0.0–1.0) of attempted services that produced a full match (on the identification number, on full name + date of birth together, or on the address without contradictions). **Only present when the request explicitly sent `services`.**" + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Echo of the `metadata` you sent." + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "examples": { + "Full match (Approved)": { + "summary": "Default save_api_request=true — per-service `validations` objects", + "value": { + "request_id": "7f9a1c0e-1f2b-4f6e-9d3a-2b1c0e7f9a1c", + "database_validation": { + "status": "Approved", + "issuing_state": "BRA", + "validation_type": "one_by_one", + "screened_data": { + "tax_number": "12345678900", + "first_name": "John", + "last_name": "Doe", + "date_of_birth": "1980-01-01" + }, + "match_type": "full_match", + "validations": [ + { + "validation": { + "full_name": "full_match", + "date_of_birth": "full_match", + "identification_number": "full_match" + }, + "outcome_code": "MATCH", + "outcome_detail": "200", + "service_id": "bra_cpf", + "service_name": "Brazil - CPF status check", + "source_data": { + "identification_number": "12345678900", + "first_name": "JOHN", + "last_name": "DOE", + "date_of_birth": "1980-01-01", + "lgpd_minor": false, + "minor_under_16": false, + "minor_under_18": false + } + } + ], + "warnings": [] + }, + "vendor_data": "user-1234", + "metadata": null, + "created_at": "2026-06-11T10:30:00.000000+00:00" + } + }, + "Explicit services (extended shape)": { + "summary": "Request sent `services` — response adds `services_used` and `match_score`", + "value": { + "request_id": "b3e7c1a2-9d4f-4f0b-8a6c-5e2d1f0a9b3e", + "database_validation": { + "status": "Approved", + "issuing_state": "BRA", + "validation_type": "one_by_one", + "screened_data": { + "tax_number": "12345678900", + "first_name": "John", + "last_name": "Doe", + "date_of_birth": "1980-01-01" + }, + "match_type": "full_match", + "validations": [ + { + "validation": { + "full_name": "full_match", + "date_of_birth": "full_match", + "identification_number": "full_match" + }, + "outcome_code": "MATCH", + "outcome_detail": "200", + "service_id": "bra_cpf", + "service_name": "Brazil - CPF status check", + "source_data": { + "identification_number": "12345678900", + "first_name": "JOHN", + "last_name": "DOE", + "date_of_birth": "1980-01-01", + "lgpd_minor": false, + "minor_under_16": false, + "minor_under_18": false + } + } + ], + "warnings": [], + "services_used": [ + "bra_cpf" + ], + "match_score": 1.0 + }, + "vendor_data": "user-1234", + "metadata": null, + "created_at": "2026-06-11T10:30:00.000000+00:00" + } + }, + "No match (Declined)": { + "summary": "Registry record does not match — default `no_match_action=DECLINE`", + "value": { + "request_id": "0a1b2c3d-4e5f-6789-abcd-ef0123456789", + "database_validation": { + "status": "Declined", + "issuing_state": "BRA", + "validation_type": "one_by_one", + "screened_data": { + "tax_number": "12345678900", + "first_name": "John", + "last_name": "Doe", + "date_of_birth": "1980-01-01" + }, + "match_type": "no_match", + "validations": [ + { + "validation": { + "full_name": "no_match", + "date_of_birth": "no_match", + "identification_number": "no_match" + }, + "outcome_code": "NO_MATCH", + "outcome_detail": "200", + "service_id": "bra_cpf", + "service_name": "Brazil - CPF status check", + "source_data": { + "identification_number": "00987654321", + "first_name": "TOTALLY", + "last_name": "DIFFERENT NAME", + "date_of_birth": "1999-01-01", + "lgpd_minor": false, + "minor_under_16": false, + "minor_under_18": false + } + } + ], + "warnings": [ + { + "feature": "DATABASE_VALIDATION", + "risk": "DATABASE_VALIDATION_NO_MATCH", + "additional_data": null, + "log_type": "error", + "short_description": "Database validation no match", + "long_description": "The system identified a no match in the database validation, requiring further investigation." + } + ] + }, + "vendor_data": null, + "metadata": null, + "created_at": "2026-06-11T10:31:00.000000+00:00" + } + }, + "Stateless (save_api_request=false)": { + "summary": "`validations` collapses to a merged field map; nothing is stored", + "value": { + "request_id": "c4d5e6f7-8901-2345-6789-abcdef012345", + "database_validation": { + "status": "Approved", + "issuing_state": "BRA", + "validation_type": "one_by_one", + "screened_data": { + "tax_number": "12345678900", + "first_name": "John", + "last_name": "Doe", + "date_of_birth": "1980-01-01" + }, + "match_type": "full_match", + "validations": { + "full_name": "full_match", + "date_of_birth": "full_match", + "identification_number": "full_match" + }, + "warnings": [] + }, + "vendor_data": null, + "metadata": null, + "created_at": "2026-06-11T10:32:00.000000+00:00" + } + } + } + } + } + }, + "400": { + "description": "Validation error - nothing is billed. Preflight field errors occur before any external call. If every unanswered attempt explicitly refuses the input, the response instead includes `validation_errors` with `code: provider_rejected_input` and `retryable: false`; correct the input before retrying. Field-level problems use DRF's standard envelope; catalog problems return a `services` array (plus `requires_onboarding` or `services_requiring_consent` when relevant); missing-field errors include `fields_required_by` mapping each missing field to the services that demand it; services that need at least one extra identifier (e.g. `usa_states_credit_bureau`, which cannot verify a name alone) return `any_field_required_by` mapping each service to the fields of which at least one must be present; format problems checked per service return `invalid_fields_by_service`.", + "content": { + "application/json": { + "examples": { + "Invalid issuing_state": { + "summary": "Country not supported — message lists every valid alpha-3 code", + "value": { + "error": [ + "Invalid issuing state. Valid options are: ARE, ARG, AUS, AUT, BEL, BGD, BOL, BRA, CAN, CHE, CHL, CHN, CIV, COL, CRI, CZE, DEU, DNK, DOM, ECU, ESP, FIN, FRA, GBR, GHA, GLB, GRC, GTM, HKG, HND, IDN, IND, IRL, ITA, KEN, KHM, MAR, MEX, MYS, NGA, NLD, NOR, NZL, OMN, PAN, PER, PHL, POL, PRT, PRY, QAT, SGP, SLV, SVK, SWE, THA, UGA, URY, USA, VEN, ZAF, ZMB, ZWE" + ] + } + }, + "Missing required fields": { + "summary": "Union of required fields across selected services, with the universal-field hint", + "value": { + "error": [ + "Missing required fields: tax_number (or use 'identification_number' which maps to 'tax_number' for BRA)" + ], + "fields_required_by": { + "tax_number": [ + "bra_cpf" + ] + } + } + }, + "Unknown service id": { + "summary": "`services` contains an id that is not in the catalog", + "value": { + "services": [ + "Unknown service_id(s): ['bogus_service']" + ] + } + }, + "Service not live for country": { + "summary": "`services` names a service from another country (or a stub entry)", + "value": { + "services": [ + "Services not currently live for PER: ['bra_cpf']. Available services: ['per_dni', 'per_residential', 'per_tax_registration']" + ] + } + }, + "Service requires onboarding": { + "summary": "Service exists but is not activated for your organization yet", + "value": { + "services": [ + "Services require onboarding before they can be enabled for AUS: ['aus_australia_driver_licence']. Contact Didit support to activate them." + ], + "requires_onboarding": [ + "aus_australia_driver_licence" + ] + } + }, + "Consent missing": { + "summary": "A selected service is flagged requires_consent=true and `consent` was not `true`", + "value": { + "consent": [ + "Explicit end-user consent is required for these database validation services: ['chn_national_id']. Send `consent=true`." + ], + "services_requiring_consent": [ + "chn_national_id" + ] + } + }, + "Country format rule violation": { + "summary": "Identifier fails the country's format rule (length/digits/regex)", + "value": { + "tax_number": [ + "Invalid tax number for BRA: Brazilian CPF (exactly 11 digits). Must be exactly 11 characters long (got 10)." + ] + } + }, + "Service-specific format violation": { + "summary": "A field fails the selected service's own format requirements", + "value": { + "error": [ + "One or more fields do not match the format required by the selected service." + ], + "invalid_fields_by_service": { + "chn_national_id": [ + "national_id_format" + ] + } + } + }, + "Input refused by every attempt": { + "summary": "Correct the input before retrying", + "value": { + "error": "The source refused to run this query with the supplied input.", + "validation_errors": [ + { + "service_id": "bra_cpf", + "code": "provider_rejected_input", + "message": "The source refused to run this query with the supplied input.", + "retryable": false + } + ] + } + } + }, + "schema": { + "type": "object", + "additionalProperties": true, + "properties": { + "error": { + "description": "Request-level explanation or preflight field errors.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "validation_errors": { + "type": "array", + "description": "Present when every unanswered attempt refused the input after preflight.", + "items": { + "type": "object", + "properties": { + "service_id": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "retryable": { + "type": "boolean", + "description": "Whether retrying later can resolve this service failure. False means correct the supplied input first." + } + } + } + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + }, + "502": { + "description": "No selected service returned a usable result. Inspect each validation_errors entry before retrying; HTTP 502 alone does not establish a transient failure. An empty_provider_response with retryable: true indicates unavailable or unexplained results, including a refused primary followed by an unavailable fallback. Legacy provider_invalid_input and provider_validation_error entries instead require corrected input and may omit retryable. Nothing is billed. When save_api_request=true, the failed attempt is recorded as a session with status Not Finished. Partial failures return 200 with database_validation.errors.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "validation_errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "service_id": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "retryable": { + "type": "boolean", + "description": "Whether retrying later can resolve this service failure. False means correct the supplied input first." + } + } + } + } + } + }, + "examples": { + "Provider returned no result": { + "summary": "Every selected service failed", + "value": { + "error": "Database validation could not be performed. The external validation service did not return a result. Please try again later.", + "validation_errors": [ + { + "service_id": "bra_cpf", + "code": "empty_provider_response", + "message": "The provider did not return a database validation result.", + "retryable": true + } + ] + } + } + } + } + } + } + } + } + }, + "/v3/phone/send/": { + "post": { + "summary": "Send Phone Code", + "description": "Send a one-time passcode (OTP) to a phone number over WhatsApp, SMS, or another supported channel, then verify it with [`POST /v3/phone/check/`](/standalone-apis/phone-check).\n\n**How send and check pair up.** Verification state is keyed by your application plus the E.164 `phone_number` — 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 number and the code the user received before the window closes. Calling send again for the same number while a verification is pending re-sends a code for that same verification (`status: \"Retry\"`, same `request_id`). 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.\n\n**Delivery channels.** `options.preferred_channel` selects the channel (`whatsapp` by default; `sms`, `telegram`, `voice`, `rcs`, `viber`, and `zalo` are also supported). When the preferred channel is unavailable for the destination country or number, delivery automatically falls back to SMS — the send still reports `Success`, and the channel actually used is reported by the check response (`phone.verification_method` and the `lifecycle` events).\n\n**Branding.** SMS codes carry your application's SMS sender name, set in the Business Console, at no extra cost. Pass `options.sms_sender_name` to sign one code with another name, or `options.use_white_label_customization: true` to fall back to your white-label public name when no sender name is set. WhatsApp codes use a fixed message; connect your own WhatsApp Business number in the Business Console to send them from your business name and number, billed at the Didit fee only.\n\n**Send statuses.** `Success` — OTP sent for a new verification; `Retry` — OTP re-sent for the pending verification; `Blocked` — the anti-fraud layer refused to send (see `reason`, e.g. `repeated_attempts`, `suspicious`, `spam`). A `Blocked` send immediately finalizes the verification as `Declined` with a high-risk warning, so a follow-up check returns `Expired or Not Found`.\n\n**Billing.** The price depends on the destination country and the channel that actually delivers the message — see [Phone Verification Pricing](/getting-started/phone-verification-pricing). Only `Blocked` sends are guaranteed free: any other unbilled send attempt is charged at verification finalization or expiration, even if delivery was never confirmed or the delivery provider reported the message undeliverable. Checks are always free. As an anti-abuse measure, phone verification is disabled until your organization's first top-up (`403`).\n\n**Session persistence.** Every new verification is persisted as an API-type session: `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, the verification appears in the Business Console, and `status.updated` webhooks fire as it progresses.\n\n**Sandbox.** Sandbox API keys skip delivery and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Success` payload with a random `request_id`; no message is sent and nothing is persisted. Use code `123456` on the sandbox check.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limits.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints, plus an anti-abuse cap of 4 send attempts per phone number per hour; exceeding either returns `429`.", + "operationId": "post_v3phone_send", + "x-didit-sdk": { + "resource": "phone", + "method": "send_code", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/phone/send/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"phone_number\": \"+14155552671\",\n \"options\": {\n \"code_size\": 6,\n \"preferred_channel\": \"whatsapp\",\n \"locale\": \"en-US\"\n },\n \"vendor_data\": \"user-1234\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/phone/send/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"phone_number\": \"+14155552671\",\n \"options\": {\n \"code_size\": 6,\n \"preferred_channel\": \"whatsapp\",\n \"locale\": \"en-US\",\n },\n \"vendor_data\": \"user-1234\",\n },\n timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json()) # {request_id, status, reason, vendor_data, metadata}\n# Then verify with POST /v3/phone/check/ using the same phone_number" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/phone/send/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n phone_number: '+14155552671',\n options: { code_size: 6, preferred_channel: 'whatsapp', locale: 'en-US' },\n vendor_data: 'user-1234',\n }),\n});\nif (!res.ok) throw new Error(`Phone send failed: ${res.status}`);\nconst data = await res.json();\nconsole.log(data); // { request_id, status, reason, vendor_data, metadata }\n// Then verify with POST /v3/phone/check/ using the same phone_number" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "phone_number": { + "type": "string", + "maxLength": 20, + "description": "Recipient phone number in E.164 format — leading `+` and country code (e.g., `+14155552671`). Numbers are validated and normalized to E.164; unparseable or invalid numbers return `400` with a `phone_number` field error.", + "example": "+14155552671" + }, + "options": { + "type": "object", + "description": "OTP delivery options. All fields are optional.", + "properties": { + "code_size": { + "type": "integer", + "minimum": 4, + "maximum": 8, + "default": 6, + "description": "Number of digits in the OTP." + }, + "locale": { + "type": "string", + "maxLength": 5, + "pattern": "^[a-z]{2,3}(-[A-Z]{2,3})?$", + "description": "BCP-47 locale used to localize the verification message (e.g., `en-US`, `fr`, `es`). Invalid formats return `400`.", + "example": "en-US" + }, + "preferred_channel": { + "type": "string", + "enum": [ + "whatsapp", + "sms", + "telegram", + "voice", + "rcs", + "viber", + "zalo" + ], + "default": "whatsapp", + "description": "Preferred delivery channel. If the channel is unavailable for the destination country or number, the message automatically falls back to SMS — the send still reports `Success`." + }, + "use_white_label_customization": { + "type": "boolean", + "default": false, + "description": "When `true`, the send is white-labelled: if your application has no SMS sender name, the SMS names your white-label public name instead (\"123456 is your verification code for Acme Bank\"). Your SMS sender name, when set, is used with or without this flag. Applies to SMS messages, including an SMS fallback from another channel; WhatsApp codes are sent with a fixed message and are not branded. Defaults to `false`." + }, + "sms_sender_name": { + "type": "string", + "nullable": true, + "maxLength": 30, + "example": "Acme Bank", + "description": "The name this code's SMS carries (\"123456 is your verification code for Acme Bank\"), instead of your application's SMS sender name. Free. 1 to 30 characters: letters, digits, spaces and ' & - ! only, not only digits, with single spaces between words; any other value returns `400` and no code is sent. Omitted, `null` or blank, your application's SMS sender name is used. Each application can start using up to two new names per day (UTC); past that, the code is sent with your application's name. Applies to SMS messages, including an SMS fallback from another channel; WhatsApp codes are not branded.", + "pattern": "^$|^(?![\\d ]+$)[\\p{L}\\p{N}'&!-]+( [\\p{L}\\p{N}'&!-]+)*$" + } + } + }, + "signals": { + "type": "object", + "description": "Optional device and network signals about the end user, forwarded to the anti-fraud layer to improve detection of abusive or automated traffic. All fields are optional.", + "properties": { + "ip": { + "type": "string", + "description": "IP address of the end user's device. IPv4 or IPv6.", + "example": "192.0.2.1" + }, + "device_id": { + "type": "string", + "maxLength": 255, + "description": "Unique identifier of the end user's device. Android: `ANDROID_ID`; iOS: `identifierForVendor`.", + "example": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2" + }, + "device_platform": { + "type": "string", + "enum": [ + "android", + "ios", + "ipados", + "tvos", + "web" + ], + "description": "Platform of the end user's device." + }, + "device_model": { + "type": "string", + "maxLength": 255, + "description": "Model of the end user's device.", + "example": "iPhone17,2" + }, + "os_version": { + "type": "string", + "maxLength": 64, + "description": "Operating-system version of the end user's device.", + "example": "18.0.1" + }, + "app_version": { + "type": "string", + "maxLength": 64, + "description": "Version of your application.", + "example": "1.2.34" + }, + "user_agent": { + "type": "string", + "maxLength": 512, + "description": "User agent of the end user's browser or app.", + "example": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1" + } + } + }, + "vendor_data": { + "type": "string", + "description": "Optional caller-controlled identifier (your internal user id, an email, a UUID, etc.) persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console. Use it to correlate Didit's `request_id` with your user record." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Optional free-form JSON object persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console." + } + }, + "required": [ + "phone_number" + ] + }, + "examples": { + "WhatsApp (default)": { + "summary": "6-digit OTP via WhatsApp, falling back to SMS automatically", + "value": { + "phone_number": "+14155552671", + "options": { + "preferred_channel": "whatsapp", + "locale": "en-US" + }, + "vendor_data": "user-1234" + } + }, + "SMS with custom code length": { + "summary": "Force SMS delivery with a 4-digit code", + "value": { + "phone_number": "+34699999999", + "options": { + "preferred_channel": "sms", + "code_size": 4, + "locale": "es" + } + } + }, + "With fraud signals": { + "summary": "Forward device signals to strengthen anti-fraud screening", + "value": { + "phone_number": "+14155552671", + "signals": { + "ip": "192.0.2.1", + "device_id": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2", + "device_platform": "ios", + "device_model": "iPhone17,2", + "os_version": "18.0.1", + "app_version": "1.2.34" + }, + "vendor_data": "user-1234" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Send acknowledged. Inspect `status`: `Success` and `Retry` mean a code is on its way; `Blocked` means the anti-fraud layer refused and the verification is already finalized as `Declined`. `request_id` is the persisted session id (same id on a `Retry`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PhoneVerificationSendResponse" + }, + "examples": { + "Success": { + "summary": "OTP sent for a new verification", + "value": { + "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", + "status": "Success", + "reason": null, + "vendor_data": "user-1234", + "metadata": null + } + }, + "Retry": { + "summary": "Second send for the same number inside the 5-minute window — same request_id", + "value": { + "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", + "status": "Retry", + "reason": null, + "vendor_data": "user-1234", + "metadata": null + } + }, + "Blocked": { + "summary": "Anti-fraud refusal — verification finalized as Declined, nothing billed", + "value": { + "request_id": "0d8af1f4-0c43-46d7-b75c-6e4e22c0a7b3", + "status": "Blocked", + "reason": "repeated_attempts", + "vendor_data": "user-1234", + "metadata": null + } + } + } + } + } + }, + "400": { + "description": "Validation error. Malformed input returns DRF's field-error envelope (one array of messages per offending field, nested under `options` for option errors). A number that parses but is rejected at send time (unreachable, or an ineligible line type such as a short code) returns a `{\"detail\": ...}` envelope instead.", + "content": { + "application/json": { + "examples": { + "Invalid phone number (field error)": { + "summary": "Number is not valid E.164", + "value": { + "phone_number": [ + "Invalid phone number provided." + ] + } + }, + "Missing phone number": { + "summary": "`phone_number` not included in the body", + "value": { + "phone_number": [ + "This field is required." + ] + } + }, + "Invalid options": { + "summary": "Out-of-range `code_size`, malformed `locale`, unknown `preferred_channel`", + "value": { + "options": { + "code_size": [ + "Ensure this value is less than or equal to 8." + ], + "locale": [ + "Ensure this field has no more than 5 characters." + ], + "preferred_channel": [ + "\"carrier_pigeon\" is not a valid choice." + ] + } + } + }, + "Number rejected at send time": { + "summary": "Valid E.164 shape but the number cannot receive an OTP", + "value": { + "detail": "Invalid phone number provided." + } + }, + "Ineligible line type": { + "summary": "Line type cannot receive an OTP (e.g., short code)", + "value": { + "detail": "Invalid phone line type provided." + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment (`{\"detail\": ...}`) — this API never returns `401`. Also returned before any send when the organization has never topped up (anti-abuse gate) or when its balance cannot cover the destination's send price (`{\"error\": ...}`).", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Top-up required": { + "summary": "Phone verification stays disabled until the organization's first top-up", + "value": { + "detail": "To protect against abuse, phone verification is disabled until your first top-up." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the send price", + "value": { + "error": "You don't have enough credits to perform this request." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. Additionally, an anti-abuse cap of 4 verification attempts per phone number per hour applies upstream. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers. Note: the `X-RateLimit-*`/`Retry-After` headers accompany only the 300/min write-limit 429; the per-number cap 429 carries no rate-limit headers.", + "content": { + "application/json": { + "examples": { + "Per-number cap": { + "summary": "More than 4 verification attempts for the same number in the last hour", + "value": { + "detail": "Maximum verification attempts reached for this phone number. Only 4 authentication attempts are allowed per hour. Try again later or use a different number." + } + }, + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + }, + "502": { + "description": "The phone verification provider is temporarily unavailable - a 5xx response, timeout, or connection failure that survived the built-in retries. Safe to retry; nothing is billed and no attempt is consumed.", + "content": { + "application/json": { + "examples": { + "Provider outage": { + "summary": "The provider did not answer the send request", + "value": { + "detail": "Phone verification is temporarily unavailable. Please try again shortly.", + "code": "phone_provider_unavailable" + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + } + } + } + } + } + }, + "500": { + "description": "Unexpected failure while creating the verification that is not a known provider outage. Safe to retry.", + "content": { + "application/json": { + "examples": { + "Provider error": { + "summary": "OTP delivery could not be initiated", + "value": { + "detail": "Error creating phone verification" + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/phone/check/": { + "post": { + "summary": "Check Phone Code", + "description": "Verify the OTP delivered by [`POST /v3/phone/send/`](/standalone-apis/phone-send) and get the final verification result plus phone intelligence: carrier name and line type, virtual (VoIP) and disposable flags, and duplicate usage of the number across your sessions.\n\n**How the check finds the verification.** Matching is by your application plus the E.164 `phone_number` — `request_id` is not an input. The most recent pending verification created within the last **5 minutes** is checked. If there is none (never sent, already finalized, or older than 5 minutes) the endpoint returns `200` with `status: \"Expired or Not Found\"`.\n\n**Attempt budget.** Each verification allows **3 code attempts**. The first two wrong codes return `status: \"Failed\"` with the attempts remaining and `phone: null`; the third wrong code finalizes the verification as `Declined` with a `VERIFICATION_CODE_ATTEMPTS_EXCEEDED` warning and returns the full phone report.\n\n**Outcomes.** `Approved` — correct code and no declining risk. `Declined` — terminal: the code was correct but a declining risk matched (a `DECLINE` action below, a blocklisted or high-risk number), or the attempt budget was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — nothing to check. On `Approved`/`Declined` the `request_id` equals the send's `request_id` (the session id) and `phone` carries the full report (`carrier`, `is_virtual`, `is_disposable`, `warnings`, `lifecycle`, `matches`); on `Failed` and `Expired or Not Found` the `request_id` is a one-off random UUID.\n\n**Risk actions.** `duplicated_phone_number_action`, `disposable_number_action`, and `voip_number_action` decide what happens when the corresponding risk is detected on a correct code: `DECLINE` flips the final status to `Declined`; `NO_ACTION` (default) records the risk in `phone.warnings` without affecting the status.\n\n**Billing.** Checks are free — delivery is billed on the send side (see [Phone Verification Pricing](/getting-started/phone-verification-pricing)).\n\n**Session persistence.** A finalized check updates the session created by the send (visible in the Business Console, queryable via `GET /v3/session/{sessionId}/decision/`) and fires a `status.updated` webhook.\n\n**Sandbox.** Sandbox API keys skip all processing: any well-formed code (4–8 digits, `123456` included) returns a static `Approved` payload with a simplified flat `phone` object (`status`, `phone_number`, `country_code`, `carrier_name`, `line_type`, `flags`); malformed input still returns `400`. Nothing is persisted.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limits.** Shared write budget of 300 requests/min per API key, plus the upstream anti-abuse cap of 4 attempts per number per hour; exceeding either returns `429`.\n\n**Social footprint.** Set `enable_social` to `true` to add a `phone_social` block: the online platforms this number is registered on, with per-category counts and any profile details available. `profiles_registered: 0` means the platforms were checked and the number was found on none of them - a valid, billable answer. The block is absent when the add-on was not requested or the check could not be completed, and it is not billed in that case. Billing adds one `phone_social` unit per request on top of this endpoint's own price.", + "operationId": "post_v3phone_check", + "x-didit-sdk": { + "resource": "phone", + "method": "check_code", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/phone/check/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"phone_number\": \"+14155552671\",\n \"code\": \"123456\",\n \"voip_number_action\": \"DECLINE\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/phone/check/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"phone_number\": \"+14155552671\", # same number as the send call\n \"code\": \"123456\",\n \"voip_number_action\": \"DECLINE\",\n },\n timeout=15,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result[\"status\"]) # Approved / Declined / Failed / Expired or Not Found\nif result[\"status\"] in (\"Approved\", \"Declined\"):\n print(result[\"phone\"][\"carrier\"], result[\"phone\"][\"is_virtual\"])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/phone/check/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n phone_number: '+14155552671', // same number as the send call\n code: '123456',\n voip_number_action: 'DECLINE',\n }),\n});\nconst data = await res.json();\nif (data.status === 'Approved') {\n // full phone report is in data.phone (carrier, is_virtual, matches, ...)\n}" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "phone_number": { + "type": "string", + "maxLength": 20, + "description": "The same phone number used in the matching [`POST /v3/phone/send/`](/standalone-apis/phone-send) call, in E.164 format. This is what links the check to the send.", + "example": "+14155552671" + }, + "code": { + "type": "string", + "minLength": 4, + "maxLength": 8, + "pattern": "^[0-9]{4,8}$", + "description": "The OTP the end user received. Must be 4–8 numeric digits — anything else returns `400` without consuming an attempt. A well-formed but wrong code returns `200` with `status: \"Failed\"` and consumes one of the 3 attempts.", + "example": "123456" + }, + "duplicated_phone_number_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "What to do when the same number was already used by a different user (different `vendor_data`) of your application. `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `phone.warnings` and fills `phone.matches`." + }, + "disposable_number_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "What to do when the carrier lookup flags the number as a temporary/burner line (`phone.is_disposable`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `phone.warnings`." + }, + "voip_number_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "What to do when the carrier lookup reports a virtual line type — `voip`, `isp`, or `vpn` (`phone.is_virtual`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `phone.warnings`." + }, + "enable_social": { + "type": "boolean", + "default": false, + "description": "When true, also look up the social footprint of the number - the messaging, social, e-commerce and professional platforms it is registered on. Billed as one `phone_social` unit per request in addition to this endpoint's own price.", + "example": true + }, + "phone_no_social_presence_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "Action when the social footprint check finds the number registered on none of the platforms it covers. Only NO_ACTION and DECLINE are accepted on this endpoint; REVIEW is available on the workflow configuration key of the same name." + } + }, + "required": [ + "phone_number", + "code" + ] + }, + "examples": { + "Default": { + "summary": "Record any risks but never auto-decline", + "value": { + "phone_number": "+14155552671", + "code": "123456" + } + }, + "Strict risk policy": { + "summary": "Auto-decline VoIP and disposable lines", + "value": { + "phone_number": "+14155552671", + "code": "123456", + "voip_number_action": "DECLINE", + "disposable_number_action": "DECLINE" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Check completed — wrong codes and missing verifications also return `200`; inspect `status`, not the HTTP code. `phone` is populated only on finalized outcomes (`Approved`/`Declined`), `null` on `Failed`, and absent on `Expired or Not Found`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PhoneVerificationCheckResponse" + }, + "examples": { + "Approved": { + "summary": "Correct code — full phone report with carrier, lifecycle, and matches", + "value": { + "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", + "status": "Approved", + "message": "The verification code is correct.", + "phone": { + "status": "Approved", + "phone_number_prefix": "+1", + "phone_number": "4155552671", + "full_number": "+14155552671", + "country_code": "US", + "country_name": "United States", + "carrier": { + "name": "AT&T", + "type": "mobile" + }, + "is_disposable": false, + "is_virtual": false, + "verification_method": "whatsapp", + "verification_attempts": 1, + "verified_at": "2026-06-12T01:24:47.311323Z", + "warnings": [], + "lifecycle": [ + { + "type": "PHONE_VERIFICATION_MESSAGE_SENT", + "timestamp": "2026-06-12T01:23:39.580554+00:00", + "details": { + "status": "Success", + "reason": null, + "channel": "whatsapp", + "actual_channel": "whatsapp" + }, + "fee": 0.0709 + }, + { + "type": "PHONE_DELIVERY_DELIVERED", + "timestamp": "2026-06-12T01:23:43.118220+00:00", + "details": { + "channel": "whatsapp", + "status": "delivered" + }, + "fee": 0 + }, + { + "type": "VALID_CODE_ENTERED", + "timestamp": "2026-06-12T01:24:47.311201+00:00", + "details": { + "code_tried": "123456", + "status": "Approved" + }, + "fee": 0 + }, + { + "type": "PHONE_VERIFICATION_APPROVED", + "timestamp": "2026-06-12T01:24:47.384292+00:00", + "details": null, + "fee": 0 + } + ], + "matches": [] + }, + "vendor_data": "user-1234", + "metadata": null, + "created_at": "2026-06-12T01:24:47.401719+00:00" + } + }, + "Failed (attempts remaining)": { + "summary": "Wrong code — the user can retry; request_id here is a one-off UUID", + "value": { + "request_id": "11111111-2222-3333-4444-555555555555", + "status": "Failed", + "message": "The verification code is incorrect. Attempts remaining: 2", + "phone": null, + "vendor_data": "user-1234", + "metadata": null, + "created_at": "2026-06-12T01:24:21.512340+00:00" + } + }, + "Declined (max attempts)": { + "summary": "Third wrong code — verification finalized as Declined", + "value": { + "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", + "status": "Declined", + "message": "The verification code is incorrect. Maximum attempts reached.", + "phone": { + "status": "Declined", + "phone_number_prefix": "+1", + "phone_number": "4155552671", + "full_number": "+14155552671", + "country_code": "US", + "country_name": "United States", + "carrier": { + "name": "AT&T", + "type": "mobile" + }, + "is_disposable": false, + "is_virtual": false, + "verification_method": "whatsapp", + "verification_attempts": 1, + "verified_at": null, + "warnings": [ + { + "feature": "PHONE", + "risk": "VERIFICATION_CODE_ATTEMPTS_EXCEEDED", + "additional_data": null, + "log_type": "error", + "short_description": "Verification code attempts exceeded", + "long_description": "The system detected that the phone number has exceeded the maximum number of verification code attempts." + } + ], + "lifecycle": [ + { + "type": "PHONE_VERIFICATION_MESSAGE_SENT", + "timestamp": "2026-06-12T01:23:39.580554+00:00", + "details": { + "status": "Success", + "reason": null, + "channel": "whatsapp", + "actual_channel": "whatsapp" + }, + "fee": 0.0709 + }, + { + "type": "INVALID_CODE_ENTERED", + "timestamp": "2026-06-12T01:24:01.118220+00:00", + "details": { + "code_tried": "111111", + "status": "Failed" + }, + "fee": 0 + }, + { + "type": "INVALID_CODE_ENTERED", + "timestamp": "2026-06-12T01:24:21.512340+00:00", + "details": { + "code_tried": "222222", + "status": "Failed" + }, + "fee": 0 + }, + { + "type": "INVALID_CODE_ENTERED", + "timestamp": "2026-06-12T01:24:47.311201+00:00", + "details": { + "code_tried": "333333", + "status": "Failed" + }, + "fee": 0 + }, + { + "type": "PHONE_VERIFICATION_DECLINED", + "timestamp": "2026-06-12T01:24:47.384292+00:00", + "details": { + "reason": "VERIFICATION_CODE_ATTEMPTS_EXCEEDED" + }, + "fee": 0 + } + ], + "matches": [] + }, + "vendor_data": "user-1234", + "metadata": null, + "created_at": "2026-06-12T01:24:47.401719+00:00" + } + }, + "Declined (risk policy)": { + "summary": "Correct code, but the line is VoIP and voip_number_action=DECLINE", + "value": { + "request_id": "7f1c9d2e-44a1-4b6e-9a3e-5b8e6f1c2d3a", + "status": "Declined", + "message": "The verification code is correct.", + "phone": { + "status": "Declined", + "phone_number_prefix": "+1", + "phone_number": "5005550006", + "full_number": "+15005550006", + "country_code": "US", + "country_name": "United States", + "carrier": { + "name": "CLEC LLC", + "type": "voip" + }, + "is_disposable": false, + "is_virtual": true, + "verification_method": "sms", + "verification_attempts": 1, + "verified_at": "2026-06-12T01:24:47.311323Z", + "warnings": [ + { + "feature": "PHONE", + "risk": "VOIP_NUMBER_DETECTED", + "additional_data": null, + "log_type": "error", + "short_description": "VoIP number detected", + "long_description": "The system detected that the phone number is a VoIP number, which is not allowed." + } + ], + "lifecycle": [ + { + "type": "PHONE_VERIFICATION_MESSAGE_SENT", + "timestamp": "2026-06-12T01:23:39.580554+00:00", + "details": { + "status": "Success", + "reason": null, + "channel": "whatsapp", + "actual_channel": "sms" + }, + "fee": 0.0709 + }, + { + "type": "VALID_CODE_ENTERED", + "timestamp": "2026-06-12T01:24:47.311201+00:00", + "details": { + "code_tried": "123456", + "status": "Approved" + }, + "fee": 0 + }, + { + "type": "PHONE_VERIFICATION_DECLINED", + "timestamp": "2026-06-12T01:24:47.384292+00:00", + "details": { + "reason": "VOIP_NUMBER_DETECTED" + }, + "fee": 0 + } + ], + "matches": [] + }, + "vendor_data": "user-1234", + "metadata": null, + "created_at": "2026-06-12T01:24:47.401719+00:00" + } + }, + "Expired or Not Found": { + "summary": "No pending verification for this number in the last 5 minutes", + "value": { + "request_id": "69cc35c6-280a-47a6-ab8c-4f32abc02ed0", + "status": "Expired or Not Found", + "message": "No pending phone verification found in the last 5 minutes.", + "vendor_data": null, + "metadata": null, + "created_at": "2026-06-12T01:24:47.311323+00:00" + } + } + } + } + } + }, + "400": { + "description": "Validation error — DRF's field-error envelope: one array of messages per offending field.", + "content": { + "application/json": { + "examples": { + "Invalid code format": { + "summary": "`code` is not 4–8 numeric digits", + "value": { + "code": [ + "Invalid code format. Enter 4 to 8 numeric digits." + ] + } + }, + "Invalid phone number": { + "summary": "`phone_number` is not valid E.164", + "value": { + "phone_number": [ + "Invalid phone number provided." + ] + } + }, + "Invalid action": { + "summary": "Risk actions accept only NO_ACTION or DECLINE", + "value": { + "voip_number_action": [ + "\"REVIEW\" is not a valid choice." + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — this API never returns `401`. Checks are free, so there is no credit check here.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. Additionally, an anti-abuse cap of 4 verification attempts per phone number per hour applies upstream. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers. Note: the `X-RateLimit-*`/`Retry-After` headers accompany only the 300/min write-limit 429; the per-number cap 429 carries no rate-limit headers.", + "content": { + "application/json": { + "examples": { + "Per-number cap": { + "summary": "More than 4 verification attempts for the same number in the last hour", + "value": { + "detail": "Maximum verification attempts reached for this phone number. Only 4 authentication attempts are allowed per hour. Try again later or use a different number." + } + }, + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + }, + "502": { + "description": "The phone verification provider is temporarily unavailable - a 5xx response, timeout, or connection failure that survived the built-in retries. Safe to retry; the attempt budget is not consumed.", + "content": { + "application/json": { + "examples": { + "Provider outage": { + "summary": "The provider did not answer the check request", + "value": { + "detail": "Phone verification is temporarily unavailable. Please try again shortly.", + "code": "phone_provider_unavailable" + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + } + } + } + } + } + }, + "500": { + "description": "Unexpected failure while verifying the code upstream that is not a known provider outage. Safe to retry; the attempt budget is only consumed by completed checks.", + "content": { + "application/json": { + "examples": { + "Provider error": { + "summary": "Code verification could not be completed", + "value": { + "detail": "Error checking phone verification" + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/email/send/": { + "post": { + "summary": "Send Email Code", + "description": "Send a one-time passcode (OTP) to an email address, then verify it with [`POST /v3/email/check/`](/standalone-apis/email-check).\n\n**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.\n\n**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.\n\n**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`.\n\n**Billing.** One Email Verification API credit per successful send (`status: \"Success\"`), charged at send time. `Retry` and `Undeliverable` sends are free, and checks are free.\n\n**Session persistence.** Every new verification is persisted as an API-type session: `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, the verification appears in the Business Console, and `status.updated` webhooks fire as it progresses.\n\n**Sandbox.** Sandbox API keys skip delivery and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Success` payload with a random `request_id`; no email is sent and nothing is persisted. Use code `123456` on the sandbox check.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", + "operationId": "post_v3email_send", + "x-didit-sdk": { + "resource": "email", + "method": "send_code", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/email/send/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"email\": \"alice@example.com\",\n \"options\": { \"code_size\": 6, \"locale\": \"en\" },\n \"vendor_data\": \"user-1234\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/email/send/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"email\": \"alice@example.com\",\n \"options\": {\"code_size\": 6, \"locale\": \"en\"},\n \"vendor_data\": \"user-1234\",\n },\n timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json()) # {request_id, status, reason, vendor_data, metadata}\n# Then verify with POST /v3/email/check/ using the same email" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/email/send/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n email: 'alice@example.com',\n options: { code_size: 6, locale: 'en' },\n vendor_data: 'user-1234',\n }),\n});\nif (!res.ok) throw new Error(`Email send failed: ${res.status}`);\nconst data = await res.json();\nconsole.log(data); // { request_id, status, reason, vendor_data, metadata }\n// Then verify with POST /v3/email/check/ using the same email" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email", + "description": "Recipient email address. Malformed addresses return `400`; syntactically valid addresses that cannot receive mail (failed DNS/MX validation) return `200` with `status: \"Undeliverable\"`.", + "example": "alice@example.com" + }, + "options": { + "type": "object", + "description": "OTP format, localization, and branding options. All fields are optional.", + "properties": { + "code_size": { + "type": "integer", + "minimum": 4, + "maximum": 8, + "default": 6, + "description": "Number of characters in the OTP (digits by default; uppercase letters and digits when `alphanumeric_code` is `true`)." + }, + "alphanumeric_code": { + "type": "boolean", + "default": false, + "description": "When `true`, the OTP is composed of uppercase letters `A–Z` and digits `0–9` instead of digits only. The `/v3/email/check/` comparison is case-insensitive." + }, + "locale": { + "type": "string", + "maxLength": 5, + "enum": [ + "en", + "ar", + "bn", + "bg", + "bs", + "ca", + "cs", + "da", + "de", + "el", + "es", + "et", + "fa", + "fi", + "fr", + "he", + "hi", + "hr", + "hu", + "hy", + "id", + "it", + "ja", + "ka", + "kk", + "ko", + "ky", + "lt", + "lv", + "cnr", + "mk", + "mn", + "ms", + "nl", + "no", + "pl", + "pt-BR", + "pt", + "ro", + "ru", + "sk", + "sl", + "so", + "sq", + "sr", + "sv", + "th", + "tr", + "uk", + "uz", + "vi", + "zh-CN", + "zh-TW", + "zh" + ], + "description": "Language of the OTP email template. Defaults to `en`. Unsupported values return `400` listing all supported locales.", + "example": "en" + }, + "use_white_label_customization": { + "type": "boolean", + "default": false, + "description": "When `true`, the OTP email uses your application's white-label customization (logo, colors, sender branding) instead of the default Didit template." + } + } + }, + "signals": { + "type": "object", + "description": "Optional device and network signals about the end user, forwarded to the anti-fraud layer to improve detection of abusive or automated traffic. All fields are optional.", + "properties": { + "ip": { + "type": "string", + "description": "IP address of the end user's device. IPv4 or IPv6.", + "example": "192.0.2.1" + }, + "device_id": { + "type": "string", + "maxLength": 255, + "description": "Unique identifier of the end user's device. Android: `ANDROID_ID`; iOS: `identifierForVendor`.", + "example": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2" + }, + "device_platform": { + "type": "string", + "enum": [ + "android", + "ios", + "ipados", + "tvos", + "web" + ], + "description": "Platform of the end user's device." + }, + "device_model": { + "type": "string", + "maxLength": 255, + "description": "Model of the end user's device.", + "example": "iPhone17,2" + }, + "os_version": { + "type": "string", + "maxLength": 64, + "description": "Operating-system version of the end user's device.", + "example": "18.0.1" + }, + "app_version": { + "type": "string", + "maxLength": 64, + "description": "Version of your application.", + "example": "1.2.34" + }, + "user_agent": { + "type": "string", + "maxLength": 512, + "description": "User agent of the end user's browser or app.", + "example": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1" + } + } + }, + "vendor_data": { + "type": "string", + "description": "Optional caller-controlled identifier (your internal user id, an email, a UUID, etc.) persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console. Use it to correlate Didit's `request_id` with your user record." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Optional free-form JSON object persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console." + } + }, + "required": [ + "email" + ] + }, + "examples": { + "Numeric 6-digit code": { + "summary": "Default — 6-digit numeric code in English", + "value": { + "email": "alice@example.com", + "options": { + "locale": "en" + }, + "vendor_data": "user-1234" + } + }, + "Alphanumeric 8-character code": { + "summary": "Uppercase letters + digits, Spanish template", + "value": { + "email": "bob@example.com", + "options": { + "code_size": 8, + "alphanumeric_code": true, + "locale": "es" + } + } + }, + "White-label branding": { + "summary": "Send the OTP email with your application's white-label customization", + "value": { + "email": "carol@example.com", + "options": { + "locale": "en", + "use_white_label_customization": true + }, + "vendor_data": "user-5678" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Send acknowledged. Inspect `status`: `Success` and `Retry` mean a code is on its way; `Undeliverable` means the address cannot receive mail and the verification is already finalized as `Declined`. `request_id` is the persisted session id (same id on a `Retry`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmailVerificationSendResponse" + }, + "examples": { + "Success": { + "summary": "OTP emailed for a new verification (billed)", + "value": { + "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", + "status": "Success", + "reason": null, + "vendor_data": "user-1234", + "metadata": null + } + }, + "Retry": { + "summary": "Second send inside the 5-minute window — fresh code, same request_id, free", + "value": { + "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", + "status": "Retry", + "reason": null, + "vendor_data": "user-1234", + "metadata": null + } + }, + "Undeliverable": { + "summary": "Address failed DNS/MX validation — verification finalized as Declined, nothing billed", + "value": { + "request_id": "cb1a5011-344d-444d-bdce-218904745d7f", + "status": "Undeliverable", + "reason": "email_can_not_be_delivered", + "vendor_data": "user-1234", + "metadata": null + } + } + } + } + } + }, + "400": { + "description": "Validation error — DRF's field-error envelope: one array of messages per offending field, nested under `options` for option errors.", + "content": { + "application/json": { + "examples": { + "Invalid email": { + "summary": "Malformed email address", + "value": { + "email": [ + "Enter a valid email address." + ] + } + }, + "Unsupported locale": { + "summary": "`options.locale` outside the supported set", + "value": { + "options": { + "locale": [ + "Invalid locale. Supported locales are en, ar, bn, bg, bs, ca, cs, da, de, el, es, et, fa, fi, fr, he, hi, hr, hu, hy, id, it, ja, ka, kk, ko, ky, lt, lv, cnr, mk, mn, ms, nl, no, pl, pt-BR, pt, ro, ru, sk, sl, so, sq, sr, sv, th, tr, uk, uz, vi, zh-CN, zh-TW, zh." + ] + } + } + }, + "Code size out of range": { + "summary": "`options.code_size` outside 4–8", + "value": { + "options": { + "code_size": [ + "Ensure this value is less than or equal to 8." + ] + } + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment (`{\"detail\": ...}`) — this API never returns `401`. Also returned before any send when the organization's balance cannot cover one Email Verification API credit (`{\"error\": ...}`).", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the send price", + "value": { + "error": "You don't have enough credits to perform this request." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + }, + "503": { + "description": "The email provider could not be reached (an outage or a timeout), so no code was sent and nothing was billed or counted as a send attempt. Repeat the same request.", + "content": { + "application/json": { + "examples": { + "Email delivery unavailable": { + "summary": "The email provider failed after one automatic retry", + "value": { + "detail": "Email delivery is temporarily unavailable. Please try again." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/email/check/": { + "post": { + "summary": "Check Email Code", + "description": "Verify the OTP delivered by [`POST /v3/email/send/`](/standalone-apis/email-send) and get the final verification result plus email intelligence: deliverability, breach exposure (`is_breached` and the `breaches` list from a breach-intelligence database), disposable-provider detection, and duplicate usage of the address across your sessions.\n\n**How the check finds the verification.** Matching is by your application plus the `email` address — `request_id` is not an input. The most recent pending verification created within the last **5 minutes** is checked. If there is none (never sent, already finalized, undeliverable at send time, or older than 5 minutes) the endpoint returns `200` with `status: \"Expired or Not Found\"`.\n\n**Attempt budget.** Each verification allows **3 code attempts**. The first two wrong codes return `status: \"Failed\"` with the attempts remaining and `email: null`; the third wrong code finalizes the verification as `Declined` with an `EMAIL_CODE_ATTEMPTS_EXCEEDED` warning and returns the full email report. Codes are compared case-insensitively (relevant for `alphanumeric_code` sends); only the most recently sent code is valid.\n\n**Outcomes.** `Approved` — correct code and no declining risk. `Declined` — terminal: the code was correct but a declining risk matched (a `DECLINE` action below, a blocklisted address, or the address found undeliverable at finalization), or the attempt budget was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — nothing to check. On `Approved`/`Declined` the `request_id` equals the send's `request_id` (the session id) and `email` carries the full report (`is_breached`, `breaches`, `is_disposable`, `is_undeliverable`, `warnings`, `lifecycle`, `matches`); on `Failed` and `Expired or Not Found` the `request_id` is a one-off random UUID.\n\n**Risk actions.** `duplicated_email_action`, `breached_email_action`, and `disposable_email_action` decide what happens when the corresponding risk is detected on a correct code: `DECLINE` flips the final status to `Declined`; `NO_ACTION` (default) records the risk in `email.warnings` without affecting the status.\n\n**Billing.** Checks are free — the credit is consumed by the successful send.\n\n**Session persistence.** A finalized check updates the session created by the send (visible in the Business Console, queryable via `GET /v3/session/{sessionId}/decision/`) and fires a `status.updated` webhook.\n\n**Sandbox.** Sandbox API keys skip all processing: any well-formed code (`123456` included) returns a static `Approved` payload with a simplified `email` object (`status`, `email`, `is_breached`, `is_disposable`, `is_undeliverable`); malformed input still returns `400`. Nothing is persisted.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\n**Social footprint.** Set `enable_social` to `true` to add a `email_social` block: the online platforms this address is registered on, with per-category counts and any profile details available. `profiles_registered: 0` means the platforms were checked and the address was found on none of them - a valid, billable answer. The block is absent when the add-on was not requested or the check could not be completed, and it is not billed in that case. Billing adds one `email_social` unit per request on top of this endpoint's own price.", + "operationId": "post_v3email_check", + "x-didit-sdk": { + "resource": "email", + "method": "check_code", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/email/check/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"email\": \"alice@example.com\",\n \"code\": \"123456\",\n \"disposable_email_action\": \"DECLINE\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/email/check/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"email\": \"alice@example.com\", # same address as the send call\n \"code\": \"123456\",\n \"disposable_email_action\": \"DECLINE\",\n },\n timeout=15,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result[\"status\"]) # Approved / Declined / Failed / Expired or Not Found\nif result[\"status\"] in (\"Approved\", \"Declined\"):\n print(result[\"email\"][\"is_breached\"], result[\"email\"][\"is_disposable\"])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/email/check/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n email: 'alice@example.com', // same address as the send call\n code: '123456',\n disposable_email_action: 'DECLINE',\n }),\n});\nconst data = await res.json();\nif (data.status === 'Approved') {\n // full email report is in data.email (breaches, is_disposable, matches, ...)\n}" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email", + "description": "The same email address used in the matching [`POST /v3/email/send/`](/standalone-apis/email-send) call. This is what links the check to the send.", + "example": "alice@example.com" + }, + "code": { + "type": "string", + "maxLength": 10, + "description": "The OTP the end user received: 4–8 digits, or 4–8 letters/digits when `alphanumeric_code: true` was used at send time. Comparison is case-insensitive.", + "example": "123456" + }, + "duplicated_email_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "What to do when the same address was already used and approved by a different user (only previously Approved verifications count) (different `vendor_data`) of your application. `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `email.warnings` and fills `email.matches`." + }, + "breached_email_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "What to do when the address appears in known data breaches (`email.is_breached`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `email.warnings`." + }, + "disposable_email_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "What to do when the domain belongs to a disposable/temporary-mail provider (`email.is_disposable`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `email.warnings`." + }, + "enable_social": { + "type": "boolean", + "default": false, + "description": "When true, also look up the social footprint of the address - the email, social, professional, e-commerce and entertainment platforms it is registered on. Billed as one `email_social` unit per request in addition to this endpoint's own price.", + "example": true + }, + "email_no_social_presence_action": { + "type": "string", + "enum": [ + "NO_ACTION", + "DECLINE" + ], + "default": "NO_ACTION", + "description": "Action when the social footprint check finds the address registered on none of the platforms it covers. Only NO_ACTION and DECLINE are accepted on this endpoint; REVIEW is available on the workflow configuration key of the same name." + } + }, + "required": [ + "email", + "code" + ] + }, + "examples": { + "Default": { + "summary": "Record any risks but never auto-decline", + "value": { + "email": "alice@example.com", + "code": "123456" + } + }, + "Strict risk policy": { + "summary": "Auto-decline disposable and breached inboxes", + "value": { + "email": "alice@example.com", + "code": "123456", + "disposable_email_action": "DECLINE", + "breached_email_action": "DECLINE" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Check completed — wrong codes and missing verifications also return `200`; inspect `status`, not the HTTP code. `email` is populated only on finalized outcomes (`Approved`/`Declined`), `null` on `Failed`, and absent on `Expired or Not Found`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmailVerificationCheckResponse" + }, + "examples": { + "Approved": { + "summary": "Correct code — clean address, full email report", + "value": { + "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", + "status": "Approved", + "message": "The verification code is correct.", + "email": { + "status": "Approved", + "email": "alice@example.com", + "is_breached": false, + "breaches": [], + "is_disposable": false, + "is_undeliverable": false, + "verification_attempts": 1, + "verified_at": "2026-06-12T01:24:47.311323Z", + "warnings": [], + "lifecycle": [ + { + "type": "EMAIL_VERIFICATION_MESSAGE_SENT", + "timestamp": "2026-06-12T01:23:39.580554+00:00", + "details": { + "status": "Success", + "reason": null + }, + "fee": 0.03 + }, + { + "type": "VALID_CODE_ENTERED", + "timestamp": "2026-06-12T01:24:47.311201+00:00", + "details": { + "code_tried": "123456", + "status": "Approved" + }, + "fee": 0 + }, + { + "type": "EMAIL_VERIFICATION_APPROVED", + "timestamp": "2026-06-12T01:24:47.384292+00:00", + "details": null, + "fee": 0 + } + ], + "matches": [] + }, + "vendor_data": "user-1234", + "metadata": null, + "created_at": "2026-06-12T01:24:47.401719+00:00" + } + }, + "Approved with breach exposure": { + "summary": "Correct code; breach exposure recorded as a warning (breached_email_action=NO_ACTION)", + "value": { + "request_id": "3a1f2b6c-9d4e-4f7a-8b2c-1e5d6f7a8b9c", + "status": "Approved", + "message": "The verification code is correct.", + "email": { + "status": "Approved", + "email": "bob@example.com", + "is_breached": true, + "breaches": [ + { + "name": "ExampleApp", + "domain": "exampleapp.com", + "breach_date": "2019-03-21", + "breach_emails_count": 763117, + "description": "In March 2019, the social platform ExampleApp suffered a data breach that exposed email addresses and passwords.", + "logo_path": "https:///logos/ExampleApp.png", + "data_classes": [ + "email_addresses", + "passwords" + ], + "is_verified": true + } + ], + "is_disposable": false, + "is_undeliverable": false, + "verification_attempts": 1, + "verified_at": "2026-06-12T01:24:47.311323Z", + "warnings": [ + { + "feature": "EMAIL", + "risk": "BREACHED_EMAIL_DETECTED", + "additional_data": null, + "log_type": "information", + "short_description": "Breached email detected", + "long_description": "This email address was found in one or more known data breaches." + } + ], + "lifecycle": [ + { + "type": "EMAIL_VERIFICATION_MESSAGE_SENT", + "timestamp": "2026-06-12T01:23:39.580554+00:00", + "details": { + "status": "Success", + "reason": null + }, + "fee": 0.03 + }, + { + "type": "VALID_CODE_ENTERED", + "timestamp": "2026-06-12T01:24:47.311201+00:00", + "details": { + "code_tried": "835126", + "status": "Approved" + }, + "fee": 0 + }, + { + "type": "EMAIL_VERIFICATION_APPROVED", + "timestamp": "2026-06-12T01:24:47.384292+00:00", + "details": null, + "fee": 0 + } + ], + "matches": [] + }, + "vendor_data": "user-5678", + "metadata": null, + "created_at": "2026-06-12T01:24:47.401719+00:00" + } + }, + "Failed (attempts remaining)": { + "summary": "Wrong code — the user can retry; request_id here is a one-off UUID", + "value": { + "request_id": "11111111-2222-3333-4444-555555555555", + "status": "Failed", + "message": "The verification code is incorrect. Attempts remaining: 2", + "email": null, + "vendor_data": "user-1234", + "metadata": null, + "created_at": "2026-06-12T01:24:21.512340+00:00" + } + }, + "Declined (risk policy)": { + "summary": "Correct code, but the address is disposable and disposable_email_action=DECLINE", + "value": { + "request_id": "8c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f", + "status": "Declined", + "message": "The verification code is correct.", + "email": { + "status": "Declined", + "email": "tempuser42@mailinator.com", + "is_breached": false, + "breaches": [], + "is_disposable": true, + "is_undeliverable": false, + "verification_attempts": 1, + "verified_at": "2026-06-12T01:24:47.311323Z", + "warnings": [ + { + "feature": "EMAIL", + "risk": "DISPOSABLE_EMAIL_DETECTED", + "additional_data": null, + "log_type": "error", + "short_description": "Disposable email detected", + "long_description": "The system detected that the email is disposable, which is not allowed." + } + ], + "lifecycle": [ + { + "type": "EMAIL_VERIFICATION_MESSAGE_SENT", + "timestamp": "2026-06-12T01:23:39.580554+00:00", + "details": { + "status": "Success", + "reason": null + }, + "fee": 0.03 + }, + { + "type": "VALID_CODE_ENTERED", + "timestamp": "2026-06-12T01:24:47.311201+00:00", + "details": { + "code_tried": "123456", + "status": "Approved" + }, + "fee": 0 + }, + { + "type": "EMAIL_VERIFICATION_DECLINED", + "timestamp": "2026-06-12T01:24:47.384292+00:00", + "details": { + "reason": "DISPOSABLE_EMAIL_DETECTED" + }, + "fee": 0 + } + ], + "matches": [] + }, + "vendor_data": "user-1234", + "metadata": null, + "created_at": "2026-06-12T01:24:47.401719+00:00" + } + }, + "Expired or Not Found": { + "summary": "No pending verification for this address in the last 5 minutes", + "value": { + "request_id": "88808a77-06e9-4cb0-85f5-9c052ddfd987", + "status": "Expired or Not Found", + "message": "No pending email verification found in the last 5 minutes.", + "vendor_data": null, + "metadata": null, + "created_at": "2026-06-12T01:24:59.887904+00:00" + } + } + } + } + } + }, + "400": { + "description": "Validation error — DRF's field-error envelope: one array of messages per offending field.", + "content": { + "application/json": { + "examples": { + "Invalid email": { + "summary": "Malformed email address", + "value": { + "email": [ + "Enter a valid email address." + ] + } + }, + "Missing code": { + "summary": "`code` not included in the body", + "value": { + "code": [ + "This field is required." + ] + } + } + } + } + } + }, + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — this API never returns `401`. Checks are free, so there is no credit check here.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/workflows/": { + "get": { + "summary": "List workflows", + "description": "List your workflows, newest first, in a limit/offset pagination envelope (`{count, next, previous, results}`, default page size 50). Each row is one version; `workflow_id` is the stable group ID and new sessions use the latest `published` version.", + "operationId": "list_workflows", + "x-didit-sdk": { + "resource": "workflows", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Workflows" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 50 + }, + "description": "Page size (number of workflows per page)." + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0 + }, + "description": "Number of rows to skip before the first returned workflow." + } + ], + "responses": { + "200": { + "description": "One page of workflows ordered by `created_at` descending, wrapped in the pagination envelope. Each item in `results` is a `WorkflowListItem`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Total number of workflow versions for the application." + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the next page, or `null` on the last page." + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the previous page, or `null` on the first page." + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WorkflowListItem" + } + } + } + }, + "examples": { + "Two workflows": { + "summary": "A default KYC workflow and a draft KYC + AML workflow", + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111", + "workflow_id": "a1b2c3d4-5678-90ab-cdef-111111111111", + "workflow_label": "Standard KYC", + "workflow_type": "kyc", + "is_default": true, + "is_archived": false, + "is_white_label_enabled": false, + "total_price": 0.15, + "min_price": 0.15, + "max_price": 0.15, + "features": "OCR + LIVENESS + FACE_MATCH", + "is_simple_workflow": true, + "is_editable": false, + "workflow_url": "https://verify.didit.me/u/_yljPOx8RWG3UAz5ZRQGVQ", + "max_retry_attempts": 3, + "retry_window_days": 7, + "session_expiration_time": 604800, + "version": 2, + "status": "published", + "has_draft": false, + "created_by": { + "name": "Alex Mateo", + "email": "alex@example.com", + "type": "user" + }, + "updated_at": "2026-04-12T10:30:00Z" + }, + { + "uuid": "b2c3d4e5-6789-01bc-defa-222222222222", + "workflow_id": "b2c3d4e5-6789-01bc-defa-222222222222", + "workflow_label": "Full Verification + AML", + "workflow_type": "kyc", + "is_default": false, + "is_archived": false, + "is_white_label_enabled": false, + "total_price": 0.25, + "min_price": 0.25, + "max_price": 0.25, + "features": "OCR + LIVENESS + FACE_MATCH + AML", + "is_simple_workflow": true, + "is_editable": true, + "workflow_url": "https://verify.didit.me/u/Aq3kPOx8RWG3UAz5ZRQGVQ", + "max_retry_attempts": 3, + "retry_window_days": 7, + "session_expiration_time": 604800, + "version": 1, + "status": "draft", + "has_draft": true, + "created_by": null, + "updated_at": "2026-04-15T18:05:21Z" + } + ] + } + } + } + } + } + }, + "403": { + "description": "The API key is missing, malformed, expired, or belongs to a different application. Body: `{\"detail\": \"You do not have permission to perform this action.\"}`. Refresh the token via the [Auth API](/auth-api/get-credentials) and retry.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff and honour any `Retry-After` header." + }, + "500": { + "description": "Unexpected server error while building the list. The request can be safely retried." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X GET 'https://verification.didit.me/v3/workflows/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Accept: application/json'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\nresp = requests.get(\n \"https://verification.didit.me/v3/workflows/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status()\npage = resp.json()\nfor workflow in page[\"results\"]:\n print(workflow[\"uuid\"], workflow[\"workflow_label\"], workflow[\"status\"])" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(\"https://verification.didit.me/v3/workflows/\", {\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst page = await res.json();\npage.results.forEach((w) => console.log(w.uuid, w.workflow_label, w.status));" + } + ] + }, + "post": { + "summary": "Create workflow", + "description": "Create a simple workflow. Send `features` in execution order (ordering rules apply); defaults to `published` v1. The body accepts ONLY the fields listed in `SimpleWorkflowRequest` — any other key (including `workflow_type`) is rejected with a 400. Not idempotent — each call creates a new workflow. Maximum 50 workflows per application.", + "operationId": "create_workflow", + "x-didit-sdk": { + "resource": "workflows", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Workflows" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SimpleWorkflowRequest" + }, + "examples": { + "Simple KYC": { + "summary": "Linear ID, liveness, and face match workflow", + "value": { + "workflow_label": "Standard KYC", + "features": [ + { + "feature": "OCR", + "config": { + "duplicated_user_action": "review" + } + }, + { + "feature": "LIVENESS", + "config": { + "face_liveness_method": "passive" + } + }, + { + "feature": "FACE_MATCH", + "config": { + "face_match_score_decline_threshold": 40, + "face_match_score_review_threshold": 60 + } + } + ] + } + }, + "KYC + AML": { + "summary": "Run AML after ID verification", + "value": { + "workflow_label": "KYC with AML", + "features": [ + { + "feature": "OCR" + }, + { + "feature": "AML", + "config": { + "aml_score_review_threshold": 70, + "aml_score_approve_threshold": 30 + } + } + ] + } + }, + "Questionnaire workflow": { + "summary": "Run a questionnaire created with POST /v3/questionnaires/", + "value": { + "workflow_label": "Source of Funds", + "features": [ + { + "feature": "QUESTIONNAIRE", + "config": { + "questionnaire_uuid": "11111111-2222-3333-4444-555555555555", + "review_questionnaire_manually": true + } + } + ] + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Workflow created. The response body is the FULL workflow configuration — the same raw `VerificationSettings` serialization returned by `GET /v3/workflows/{settings_uuid}/` (~190 fields: every feature toggle, threshold, action and the generated `workflow_graph`), not the compact list item. Store the `uuid` (also exposed as `workflow_id`) so you can reference it from later session-creation calls. Note: `features` is a `\" + \"`-joined string and prices are JSON numbers.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkflowDetail" + }, + "examples": { + "Created (abbreviated)": { + "summary": "Key fields of the full configuration object", + "value": { + "uuid": "c3d4e5f6-7890-12cd-ef34-333333333333", + "workflow_id": "c3d4e5f6-7890-12cd-ef34-333333333333", + "workflow_label": "Standard KYC", + "workflow_type": null, + "is_default": false, + "is_archived": false, + "total_price": 0.15, + "min_price": 0.15, + "max_price": 0.15, + "features": "OCR + LIVENESS + FACE_MATCH", + "is_simple_workflow": false, + "is_editable": false, + "workflow_url": "https://verify.didit.me/u/w9TkPOx8RWG3UAz5ZRQGVQ", + "workflow_graph": { + "start_node": "ocr", + "nodes": "... one node per feature plus a final status node ..." + }, + "max_retry_attempts": 3, + "retry_window_days": 7, + "session_expiration_time": 604800, + "version": 1, + "status": "published", + "published_at": "2026-05-17T10:30:00Z", + "created_at": "2026-05-17T10:30:00Z", + "updated_at": "2026-05-17T10:30:00Z" + } + } + } + } + } + }, + "400": { + "description": "Validation error. Common causes: a top-level key outside the accepted whitelist (e.g. `workflow_type`), an unknown `feature` value, a feature placed before its dependency (e.g., `FACE_MATCH` before `OCR`), a passive feature (`AML`, `IP_ANALYSIS`, `DATABASE_VALIDATION`, `KYB_KEY_PEOPLE`) used as the first step, a `QUESTIONNAIRE` feature whose `config.questionnaire_uuid` does not exist for this application, or an out-of-range threshold. Body is a DRF validation error object — keys are field names, values are arrays of human-readable messages.", + "content": { + "application/json": { + "examples": { + "Missing feature": { + "value": { + "features": [ + "At least one feature is required." + ] + } + }, + "Unknown top-level field": { + "value": { + "workflow_type": [ + "The v3 workflow API only accepts `workflow_label`, `is_default`, `status`, `features`, `is_white_label_enabled`, `is_desktop_allowed`, `max_retry_attempts`, `retry_window_days`, `session_expiration_time`, `face_liveness_max_attempts`, `face_match_max_attempts`." + ] + } + }, + "Unknown feature": { + "value": { + "features": [ + "Invalid feature 'FOO'. Allowed values: AGE_ESTIMATION, AML, DATABASE_VALIDATION, EMAIL_VERIFICATION, FACE_MATCH, IP_ANALYSIS, KYB_DOCUMENTS, KYB_KEY_PEOPLE, KYB_REGISTRY, LIVENESS, NFC, OCR, PHONE_VERIFICATION, PROOF_OF_ADDRESS, QUESTIONNAIRE." + ] + } + }, + "Ordering": { + "value": { + "features": [ + "Feature 'FACE_MATCH' must appear after one of: OCR." + ] + } + }, + "Questionnaire not found": { + "value": { + "features": "Questionnaire '11111111-2222-3333-4444-555555555555' was not found for this application. Create the questionnaire first, then use its questionnaire_id." + } + } + } + } + } + }, + "403": { + "description": "The API key is missing, malformed, expired, or scoped to a different application. Refresh via the [Auth API](/auth-api/get-credentials) and retry.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "415": { + "description": "Unsupported media type. Set `Content-Type: application/json` and send a JSON body." + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "500": { + "description": "Unexpected server error while creating the workflow. Safe to retry." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X POST 'https://verification.didit.me/v3/workflows/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"workflow_label\": \"Standard KYC\",\n \"features\": [\n {\"feature\": \"OCR\"},\n {\"feature\": \"LIVENESS\", \"config\": {\"face_liveness_method\": \"passive\"}},\n {\"feature\": \"FACE_MATCH\", \"config\": {\"face_match_score_decline_threshold\": 40, \"face_match_score_review_threshold\": 60}}\n ]\n }'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\npayload = {\n \"workflow_label\": \"Standard KYC\",\n \"features\": [\n {\"feature\": \"OCR\"},\n {\"feature\": \"LIVENESS\", \"config\": {\"face_liveness_method\": \"passive\"}},\n {\"feature\": \"FACE_MATCH\"},\n ],\n}\nresp = requests.post(\n \"https://verification.didit.me/v3/workflows/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json=payload,\n timeout=10,\n)\nresp.raise_for_status()\nworkflow = resp.json()\nprint(\"Workflow id:\", workflow[\"uuid\"])" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(\"https://verification.didit.me/v3/workflows/\", {\n method: \"POST\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n workflow_label: \"Standard KYC\",\n features: [\n { feature: \"OCR\" },\n { feature: \"LIVENESS\", config: { face_liveness_method: \"passive\" } },\n { feature: \"FACE_MATCH\" },\n ],\n }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}: ${await res.text()}`);\nconst workflow = await res.json();\nconsole.log(\"Workflow id:\", workflow.uuid);" + } + ] + } + }, + "/v3/workflows/{settings_uuid}/": { + "get": { + "summary": "Get workflow", + "description": "Fetch full configuration for one workflow version by `settings_uuid`: feature toggles, thresholds, allowed documents, graph, retry policy, callbacks, and pricing.", + "operationId": "get_workflow", + "x-didit-sdk": { + "resource": "workflows", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Workflows" + ], + "parameters": [ + { + "name": "settings_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Per-version UUID of the workflow (the `uuid` value returned by the list endpoint). Each version of a workflow has its own `settings_uuid`." + } + ], + "responses": { + "200": { + "description": "Full workflow configuration. The shape is the raw `VerificationSettings` serialization (~190 fields: every feature toggle, threshold, warning action, allowed-documents config, the `workflow_graph`, retry policy, callbacks and pricing). The example below is abbreviated. Notes: `features` is a `\" + \"`-joined string (e.g. `\"OCR + LIVENESS\"`), prices are JSON numbers, and `is_editable` is `true` only for `draft` versions.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkflowDetail" + }, + "examples": { + "KYC workflow with face match (abbreviated)": { + "value": { + "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111", + "workflow_id": "a1b2c3d4-5678-90ab-cdef-111111111111", + "workflow_label": "Standard KYC", + "workflow_type": "kyc", + "is_default": true, + "is_archived": false, + "is_liveness_enabled": true, + "face_liveness_method": "passive", + "face_liveness_score_decline_threshold": 50, + "face_liveness_score_review_threshold": 70, + "is_face_match_enabled": true, + "face_match_score_decline_threshold": 40, + "face_match_score_review_threshold": 60, + "is_aml_enabled": false, + "is_phone_verification_enabled": false, + "is_email_verification_enabled": false, + "is_database_validation_enabled": false, + "is_ip_analysis_enabled": false, + "is_nfc_enabled": false, + "duplicated_user_action": "no_action", + "documents_allowed": { + "ESP": { + "P": { + "sides": 1, + "enabled": 1, + "subtypes": [ + "EPASSPORT", + "PASSPORT_GENERIC" + ], + "preferred_characters": "latin", + "expiration_check_mode": "strict" + } + } + }, + "max_retry_attempts": 3, + "retry_window_days": 7, + "session_expiration_time": 604800, + "version": 2, + "status": "published", + "published_at": "2026-04-12T10:30:00Z", + "is_simple_workflow": false, + "is_editable": false, + "workflow_url": "https://verify.didit.me/u/_yljPOx8RWG3UAz5ZRQGVQ", + "total_price": 0.15, + "min_price": 0.15, + "max_price": 0.15, + "features": "OCR + LIVENESS + FACE_MATCH", + "created_at": "2025-01-15T10:30:00Z", + "updated_at": "2026-04-12T10:30:00Z" + } + } + } + } + } + }, + "403": { + "description": "API key is missing, malformed, expired, or scoped to a different application.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No workflow with this `settings_uuid` exists for the authenticated application. Either the UUID is wrong or it belongs to another application — Didit deliberately does not distinguish the two cases to avoid leaking the existence of cross-application records.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "500": { + "description": "Unexpected server error. Safe to retry." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X GET 'https://verification.didit.me/v3/workflows/a1b2c3d4-5678-90ab-cdef-111111111111/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\nresp = requests.get(\n f\"https://verification.didit.me/v3/workflows/{settings_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status()\nworkflow = resp.json()\nprint(workflow[\"workflow_label\"], workflow[\"status\"])" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(`https://verification.didit.me/v3/workflows/${settingsUuid}/`, {\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst workflow = await res.json();\nconsole.log(workflow.workflow_label, workflow.status);" + } + ] + }, + "patch": { + "summary": "Update workflow", + "description": "Update a workflow. Versioning depends on the `status` field you send and the current state of the version: (1) no `status` on a **published** version → the published version is updated in place (same `uuid`, same `version`); (2) no `status` on a **draft** → the draft is updated and immediately published; (3) `status: \"published\"` on a **published** version → a NEW published version is created under the same `workflow_id` (new `uuid`, `version` + 1) and returned, and any existing draft in the group is deleted; (4) `status: \"draft\"` keeps a draft as a draft (autosave) but is rejected with 400 on a published version (`\"Cannot revert a published version to draft.\"`). The body accepts the same strict field whitelist as create — unknown keys are rejected with 400.", + "operationId": "update_workflow", + "x-didit-sdk": { + "resource": "workflows", + "method": "update", + "auth": "api_key", + "server": true + }, + "tags": [ + "Workflows" + ], + "parameters": [ + { + "name": "settings_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Per-version UUID of the workflow to update." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SimpleWorkflowUpdateRequest" + }, + "examples": { + "Replace features": { + "summary": "Send the full desired feature order", + "value": { + "features": [ + { + "feature": "OCR" + }, + { + "feature": "AML" + } + ] + } + }, + "Rename": { + "summary": "Rename a workflow", + "value": { + "workflow_label": "Premium KYC" + } + }, + "Set as default": { + "summary": "Make this workflow the application's default", + "value": { + "is_default": true + } + }, + "Update config": { + "summary": "Change feature configuration", + "value": { + "features": [ + { + "feature": "OCR" + }, + { + "feature": "LIVENESS", + "config": { + "face_liveness_method": "passive" + } + } + ] + } + }, + "Publish a draft": { + "summary": "Promote a draft to published", + "value": { + "status": "published" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Workflow updated. The body is the FULL configuration of the resulting version (same raw `VerificationSettings` shape as `GET /v3/workflows/{settings_uuid}/`). When the update created a new version (`status: \"published\"` sent on a published row) the returned `uuid` and `version` differ from the ones you patched — use the returned `uuid` for subsequent calls.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkflowDetail" + } + } + } + }, + "400": { + "description": "Validation error — a top-level key outside the accepted whitelist, an unknown feature, invalid ordering, out-of-range threshold, referencing a non-existent questionnaire, or `status: \"draft\"` sent for a published version (`\"Cannot revert a published version to draft.\"`). The response body is a DRF validation error keyed by field name." + }, + "403": { + "description": "API key missing or invalid.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No workflow with this `settings_uuid` exists for the authenticated application." + }, + "415": { + "description": "Unsupported media type. Set `Content-Type: application/json`." + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "500": { + "description": "Unexpected server error. Safe to retry." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X PATCH 'https://verification.didit.me/v3/workflows/a1b2c3d4-5678-90ab-cdef-111111111111/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"workflow_label\": \"Premium KYC\"}'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\nresp = requests.patch(\n f\"https://verification.didit.me/v3/workflows/{settings_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json={\"workflow_label\": \"Premium KYC\"},\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()[\"workflow_label\"])" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(`https://verification.didit.me/v3/workflows/${settingsUuid}/`, {\n method: \"PATCH\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ workflow_label: \"Premium KYC\" }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst workflow = await res.json();\nconsole.log(workflow.workflow_label);" + } + ] + }, + "delete": { + "summary": "Delete workflow", + "description": "Permanently delete one workflow version (no undo). Existing sessions keep their verification results but their workflow reference is cleared; other versions in the same `workflow_id` group are not deleted.", + "operationId": "delete_workflow", + "x-didit-sdk": { + "resource": "workflows", + "method": "delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Workflows" + ], + "parameters": [ + { + "name": "settings_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Per-version UUID of the workflow to delete." + } + ], + "responses": { + "204": { + "description": "Workflow deleted. The response body is empty." + }, + "403": { + "description": "API key missing or invalid.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No workflow with this `settings_uuid` exists for the authenticated application.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "500": { + "description": "Unexpected server error. Safe to retry." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X DELETE 'https://verification.didit.me/v3/workflows/a1b2c3d4-5678-90ab-cdef-111111111111/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\nresp = requests.delete(\n f\"https://verification.didit.me/v3/workflows/{settings_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status() # raises on 4xx/5xx; 204 is success" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(`https://verification.didit.me/v3/workflows/${settingsUuid}/`, {\n method: \"DELETE\",\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (res.status !== 204) throw new Error(`Didit ${res.status}`);" + } + ] + } + }, + "/v3/questionnaires/": { + "get": { + "summary": "List questionnaires", + "description": "List your questionnaires, newest first, in a limit/offset pagination envelope (`{count, next, previous, results}`, default page size 50). Each row is one version; `questionnaire_group_id` is the stable group ID resolved to the latest `published` version.", + "operationId": "list_questionnaires", + "x-didit-sdk": { + "resource": "questionnaires", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Questionnaires" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 50 + }, + "description": "Page size (number of questionnaires per page)." + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0 + }, + "description": "Number of rows to skip before the first returned questionnaire." + } + ], + "responses": { + "200": { + "description": "One page of questionnaires ordered by `created_at` descending, wrapped in the pagination envelope. Each item in `results` is a `QuestionnaireListItem`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Total number of questionnaire versions for the application." + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the next page, or `null` on the last page." + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the previous page, or `null` on the first page." + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionnaireListItem" + } + } + } + }, + "examples": { + "One questionnaire": { + "summary": "A simple bilingual questionnaire", + "value": { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "uuid": "11111111-2222-3333-4444-555555555555", + "title": "KYC Questionnaire", + "description": "Additional verification questions", + "languages": [ + "en", + "es" + ], + "default_language": "en", + "is_active": true, + "is_simple_questionnaire": true, + "created_at": "2026-04-23T09:55:00Z", + "updated_at": "2026-04-23T10:00:00Z", + "question_types": [ + "SHORT_TEXT", + "MULTIPLE_CHOICE", + "FILE_UPLOAD" + ], + "workflow_names": [], + "questionnaire_group_id": "11111111-2222-3333-4444-555555555555", + "version": 1, + "status": "published", + "published_at": "2026-04-23T10:00:00Z", + "is_editable": false + } + ] + } + } + } + } + } + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "500": { + "description": "Unexpected server error while building the list. Safe to retry." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X GET 'https://verification.didit.me/v3/questionnaires/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\nresp = requests.get(\n \"https://verification.didit.me/v3/questionnaires/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status()\npage = resp.json()\nfor q in page[\"results\"]:\n print(q[\"uuid\"], q[\"title\"], q[\"status\"])" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(\"https://verification.didit.me/v3/questionnaires/\", {\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst page = await res.json();\npage.results.forEach((q) => console.log(q.uuid, q.title, q.status));" + } + ] + }, + "post": { + "summary": "Create questionnaire", + "description": "Create a simple linear questionnaire. Send `form_elements` in display order; branching and repeatable groups require the Console. Defaults to `published` v1.", + "operationId": "create_questionnaire", + "x-didit-sdk": { + "resource": "questionnaires", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Questionnaires" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SimpleQuestionnaireRequest" + }, + "examples": { + "Simple": { + "summary": "Basic questionnaire", + "value": { + "title": "Customer Onboarding", + "description": "Additional information needed for verification", + "default_language": "en", + "languages": [ + "en" + ], + "is_active": true, + "form_elements": [ + { + "id": "occupation", + "element_type": "short_text", + "label": { + "en": "What is your occupation?" + }, + "is_required": true, + "placeholder": { + "en": "Software engineer" + } + }, + { + "id": "source_of_funds", + "element_type": "multiple_choice", + "label": { + "en": "Source of funds" + }, + "is_required": true, + "options": [ + { + "value": "employment", + "label": { + "en": "Employment" + } + }, + { + "value": "business", + "label": { + "en": "Business" + } + }, + { + "value": "investments", + "label": { + "en": "Investments" + } + }, + { + "value": "other", + "label": { + "en": "Other" + } + } + ] + } + ] + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Questionnaire created. The response includes the `questionnaire_id` — store it to reference this questionnaire from workflows or future update/delete calls.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QuestionnaireDetail" + }, + "examples": { + "Created": { + "value": { + "questionnaire_id": "11111111-2222-3333-4444-555555555555", + "title": "Customer Onboarding", + "description": "Additional information needed for verification", + "languages": [ + "en" + ], + "default_language": "en", + "is_active": true, + "is_simple_questionnaire": true, + "graph": { + "start_node": "occupation", + "nodes": { + "occupation": { + "element_type": "SHORT_TEXT", + "is_required": true, + "title": { + "en": "What is your occupation?" + }, + "next": "source_of_funds" + }, + "source_of_funds": { + "element_type": "MULTIPLE_CHOICE", + "is_required": true, + "title": { + "en": "Source of funds" + }, + "choices": [ + { + "value": "employment", + "label": { + "en": "Employment" + } }, - "portrait": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Short-lived presigned URL to the portrait the wallet shared, where it shares one." + { + "value": "business", + "label": { + "en": "Business" + } + } + ], + "next": null + } + } + }, + "sections": [], + "questionnaire_group_id": "11111111-2222-3333-4444-555555555555", + "version": 1, + "status": "published", + "published_at": "2026-04-23T10:00:00Z", + "is_editable": false + } + } + } + } + } + }, + "400": { + "description": "Validation error. Common causes: an unknown `element_type`, a translatable field missing a translation for one of `languages`, `languages` missing `en`, `default_language` not in `languages`, an option without a `label`, a question that has `branches`/`required_if`/`repeatable_config` (not supported by this endpoint), or sending a raw `graph` instead of `form_elements`. The body is a DRF validation error keyed by field name.", + "content": { + "application/json": { + "examples": { + "Graph not accepted": { + "value": { + "graph": "The v3 questionnaire API only accepts simple `form_elements`, not `graph`." + } + }, + "Missing label": { + "value": { + "form_elements": [ + { + "label": [ + "This field is required." + ] + } + ] + } + } + } + } + } + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "415": { + "description": "Unsupported media type. Set `Content-Type: application/json` and send a JSON body." + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "500": { + "description": "Unexpected server error while creating the questionnaire. Safe to retry." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X POST 'https://verification.didit.me/v3/questionnaires/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"title\": \"Customer Onboarding\",\n \"default_language\": \"en\",\n \"languages\": [\"en\"],\n \"form_elements\": [\n {\n \"id\": \"occupation\",\n \"element_type\": \"short_text\",\n \"label\": {\"en\": \"What is your occupation?\"},\n \"is_required\": true\n }\n ]\n }'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\npayload = {\n \"title\": \"Customer Onboarding\",\n \"default_language\": \"en\",\n \"languages\": [\"en\"],\n \"form_elements\": [\n {\n \"id\": \"occupation\",\n \"element_type\": \"short_text\",\n \"label\": {\"en\": \"What is your occupation?\"},\n \"is_required\": True,\n },\n ],\n}\nresp = requests.post(\n \"https://verification.didit.me/v3/questionnaires/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json=payload,\n timeout=10,\n)\nresp.raise_for_status()\nquestionnaire = resp.json()\nprint(\"Questionnaire id:\", questionnaire[\"questionnaire_id\"])" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(\"https://verification.didit.me/v3/questionnaires/\", {\n method: \"POST\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n title: \"Customer Onboarding\",\n default_language: \"en\",\n languages: [\"en\"],\n form_elements: [\n {\n id: \"occupation\",\n element_type: \"short_text\",\n label: { en: \"What is your occupation?\" },\n is_required: true,\n },\n ],\n }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}: ${await res.text()}`);\nconst questionnaire = await res.json();\nconsole.log(\"Questionnaire id:\", questionnaire.questionnaire_id);" + } + ] + } + }, + "/v3/questionnaires/{questionnaire_uuid}/": { + "get": { + "summary": "Get questionnaire", + "description": "Fetch full questionnaire detail for one version by `questionnaire_uuid`: title, `languages`, canonical `graph`, derived `sections`, and version metadata.", + "operationId": "get_questionnaire", + "x-didit-sdk": { + "resource": "questionnaires", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Questionnaires" + ], + "parameters": [ + { + "name": "questionnaire_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Per-version UUID of the questionnaire (the `uuid` value returned by the list endpoint, also returned as `questionnaire_id` on detail endpoints)." + } + ], + "responses": { + "200": { + "description": "Full questionnaire detail including the canonical `graph`, the derived `sections` view, version metadata, and `is_editable`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QuestionnaireDetail" + }, + "examples": { + "Published questionnaire": { + "value": { + "questionnaire_id": "11111111-2222-3333-4444-555555555555", + "title": "Customer Onboarding", + "description": "Additional information needed for verification", + "languages": [ + "en" + ], + "default_language": "en", + "is_active": true, + "is_simple_questionnaire": true, + "graph": { + "start_node": "occupation", + "nodes": { + "occupation": { + "element_type": "SHORT_TEXT", + "is_required": true, + "title": { + "en": "What is your occupation?" + }, + "next": "source_of_funds" + }, + "source_of_funds": { + "element_type": "MULTIPLE_CHOICE", + "is_required": true, + "title": { + "en": "Source of funds" + }, + "choices": [ + { + "value": "employment", + "label": { + "en": "Employment" + } }, - "face_match_score": { - "type": "number", - "nullable": true, - "description": "Selfie against the wallet portrait, 0-100, where the wallet shares one." + { + "value": "business", + "label": { + "en": "Business" + } + } + ], + "next": null + } + } + }, + "sections": [ + { + "title": {}, + "description": {}, + "items": [ + { + "uuid": "11111111-2222-3333-4444-666666666666", + "value": "occupation", + "element_type": "SHORT_TEXT", + "is_required": true, + "title": { + "en": "What is your occupation?" } } + ] + } + ], + "questionnaire_group_id": "11111111-2222-3333-4444-555555555555", + "version": 1, + "status": "published", + "published_at": "2026-04-23T10:00:00Z", + "is_editable": false + } + } + } + } + } + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No questionnaire with this `questionnaire_uuid` exists for the authenticated application. Either the UUID is wrong or it belongs to another application — the API deliberately does not distinguish the two cases.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "500": { + "description": "Unexpected server error. Safe to retry." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X GET 'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\nresp = requests.get(\n f\"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status()\nquestionnaire = resp.json()\nprint(questionnaire[\"title\"], questionnaire[\"status\"])" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`, {\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst questionnaire = await res.json();\nconsole.log(questionnaire.title, questionnaire.status);" + } + ] + }, + "patch": { + "summary": "Update questionnaire", + "description": "Update a questionnaire. Versioning depends on the `status` field you send and the current state of the version: (1) no `status` on a **published** version → the published version is updated in place (same `questionnaire_id`, same `version`); (2) no `status` on a **draft** → the draft is updated and immediately published; (3) `status: \"published\"` on a **published** version → a NEW published version is created under the same `questionnaire_group_id` (new `questionnaire_id`, `version` + 1) and returned, and any existing draft in the group is deleted; (4) `status: \"draft\"` keeps a draft as a draft (autosave) but is rejected with 400 on a published version (`\"Cannot revert a published version to draft.\"`).", + "operationId": "update_questionnaire", + "x-didit-sdk": { + "resource": "questionnaires", + "method": "update", + "auth": "api_key", + "server": true + }, + "tags": [ + "Questionnaires" + ], + "parameters": [ + { + "name": "questionnaire_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Per-version UUID of the questionnaire to update." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SimpleQuestionnaireUpdateRequest" + }, + "examples": { + "Rename": { + "summary": "Rename a questionnaire", + "value": { + "title": "Updated Customer Onboarding" + } + }, + "Replace questions": { + "summary": "Replace the full linear question list", + "value": { + "form_elements": [ + { + "id": "occupation", + "element_type": "short_text", + "label": { + "en": "What is your occupation?" + }, + "is_required": true + }, + { + "id": "source_of_funds", + "element_type": "multiple_choice", + "label": { + "en": "Source of funds" + }, + "options": [ + { + "value": "employment", + "label": { + "en": "Employment" + } + }, + { + "value": "business", + "label": { + "en": "Business" + } + } + ] + } + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Questionnaire updated. The body is the latest state of the resulting version — the in-place updated version, or the newly created published version when you sent `status: \"published\"` on a published row (in that case `questionnaire_id` and `version` differ from the ones you patched). `questionnaire_id` in the response is the per-version UUID; use `questionnaire_group_id` if you need the stable group identifier.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QuestionnaireDetail" + } + } + } + }, + "400": { + "description": "Validation error — unknown `element_type`, missing translation, unsupported branching/conditional fields, `languages` missing `en`, `default_language` not in `languages`, option without a label, sending a raw `graph`, or `status: \"draft\"` on a published version (`\"Cannot revert a published version to draft.\"`). Response body is a DRF validation error keyed by field name." + }, + "403": { + "description": "API key missing or invalid.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No questionnaire with this `questionnaire_uuid` exists for the authenticated application." + }, + "415": { + "description": "Unsupported media type. Set `Content-Type: application/json`." + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "500": { + "description": "Unexpected server error. Safe to retry." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X PATCH 'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"title\": \"Updated Customer Onboarding\"}'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\nresp = requests.patch(\n f\"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json={\"title\": \"Updated Customer Onboarding\"},\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()[\"questionnaire_id\"], resp.json()[\"status\"])" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`, {\n method: \"PATCH\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ title: \"Updated Customer Onboarding\" }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst q = await res.json();\nconsole.log(q.questionnaire_id, q.status);" + } + ] + }, + "delete": { + "summary": "Delete questionnaire", + "description": "Permanently delete one questionnaire version (no undo). Stored questionnaire responses linked to this version are deleted with it, and sessions referencing it lose the link; other versions in the same group are not deleted. Workflows that referenced it keep running but the questionnaire step can no longer load it.", + "operationId": "delete_questionnaire", + "x-didit-sdk": { + "resource": "questionnaires", + "method": "delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Questionnaires" + ], + "parameters": [ + { + "name": "questionnaire_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Per-version UUID of the questionnaire to delete." + } + ], + "responses": { + "204": { + "description": "Questionnaire deleted. The response body is empty." + }, + "403": { + "description": "API key missing or invalid.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No questionnaire with this `questionnaire_uuid` exists for the authenticated application.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "500": { + "description": "Unexpected server error. Safe to retry." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl -X DELETE 'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import requests\n\nresp = requests.delete(\n f\"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status() # raises on 4xx/5xx; 204 is success" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const res = await fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`, {\n method: \"DELETE\",\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (res.status !== 204) throw new Error(`Didit ${res.status}`);" + } + ] + } + }, + "/v3/users/": { + "get": { + "summary": "List users", + "description": "List [users](/entities/users) Didit assembles from KYC sessions sharing the same `vendor_data` (free-form string, NOT a UUID), plus users created via the API. Paginated, ordered by `last_session_at` desc. This endpoint does not support filtering or search parameters — fetch pages and filter client-side.", + "operationId": "list_users", + "x-didit-sdk": { + "resource": "users", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "description": "Page size. Defaults to 50 when omitted. There is no enforced maximum, but keep pages small for latency." + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset of the first record to return. Prefer following the `next` URL from the previous page over computing this by hand." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/users/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/users/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n params={'limit': 50},\n)\nresp.raise_for_status()\npage = resp.json()\nfor user in page['results']:\n print(user['vendor_data'], user['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const url = new URL('https://verification.didit.me/v3/users/');\nurl.searchParams.set('limit', '50');\n\nconst page = await fetch(url, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n}).then((r) => r.json());\n\nconsole.log(page.count, page.results.length);" + } + ], + "responses": { + "200": { + "description": "Paginated list of users with verification status, session counts, and feature breakdown.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Number of matching rows — exact up to 100, capped at 100 beyond that for performance. Do not use it to detect the last page; rely on `next` being `null` instead." + }, + "next": { + "type": "string", + "nullable": true, + "description": "Absolute URL of the next page, or `null` on the last page." + }, + "previous": { + "type": "string", + "nullable": true, + "description": "Absolute URL of the previous page, or `null` on the first page." + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserListItem" + } + } + } + }, + "examples": { + "Example": { + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", + "vendor_data": "user-abc-123", + "display_name": null, + "full_name": "John Michael Doe", + "date_of_birth": "1990-05-15", + "effective_name": "John Michael Doe", + "status": "ACTIVE", + "portrait_image_url": "https:///...", + "session_count": 3, + "approved_count": 2, + "declined_count": 0, + "in_review_count": 1, + "issuing_states": [ + "USA" + ], + "approved_emails": [ + "john@example.com" + ], + "approved_phones": [ + "+14155551234" + ], + "features": { + "ID_VERIFICATION": "Approved", + "LIVENESS": "Approved", + "FACE_MATCH": "Approved", + "AML": "Approved" + }, + "features_list": [ + { + "feature": "ID_VERIFICATION", + "status": "Approved" + }, + { + "feature": "LIVENESS", + "status": "Approved" + }, + { + "feature": "FACE_MATCH", + "status": "Approved" + }, + { + "feature": "AML", + "status": "Approved" + } + ], + "last_session_at": "2025-06-15T10:30:00Z", + "last_activity_at": "2025-06-15T10:30:00Z", + "first_session_at": "2025-06-01T08:00:00Z", + "tags": [ + { + "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "name": "VIP", + "color": "#2567FF" + } + ], + "created_at": "2025-06-01T08:00:00Z" + }, + { + "didit_internal_id": "6ed99d53-e8f5-4cf8-9ac1-4a506cb40f6b", + "vendor_data": null, + "display_name": "Jane S.", + "full_name": "Jane Elizabeth Smith", + "date_of_birth": "1985-11-22", + "effective_name": "Jane S.", + "status": "FLAGGED", + "portrait_image_url": null, + "session_count": 1, + "approved_count": 0, + "declined_count": 0, + "in_review_count": 1, + "issuing_states": [], + "approved_emails": [], + "approved_phones": [], + "features": { + "ID_VERIFICATION": "In Review" + }, + "features_list": [ + { + "feature": "ID_VERIFICATION", + "status": "In Review" + } + ], + "last_session_at": "2025-06-20T14:00:00Z", + "last_activity_at": "2025-06-20T14:05:00Z", + "first_session_at": "2025-06-20T14:00:00Z", + "tags": [], + "created_at": "2025-06-20T14:00:00Z" + } + ] + } + } + } + } + } + }, + "401": { + "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "content": { + "application/json": { + "examples": { + "Missing credentials": { + "value": { + "detail": "You must be authenticated with a valid access token to access this endpoint." + } + } + } + } + } + }, + "403": { + "description": "Invalid or revoked API key, or the key cannot list users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/users/{vendor_data}/": { + "get": { + "summary": "Get user", + "description": "Fetch one [user](/entities/users) by `vendor_data` (exact match). Adds `metadata`, `comments`, and `updated_at` to the list view, and switches `tags` to the detailed tag-link shape.", + "operationId": "get_user", + "x-didit-sdk": { + "resource": "users", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "vendor_data", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Your unique identifier for the user — a free-form string (NOT a UUID). This is the same value you passed as `vendor_data` when creating the session that first introduced this user, matched exactly as sent.", + "example": "user-abc-123" + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/users/user-abc-123/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/users/user-abc-123/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n)\nresp.raise_for_status()\nuser = resp.json()\nprint(user['didit_internal_id'], user['status'], user['session_count'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const vendorData = encodeURIComponent('user-abc-123');\nconst user = await fetch(\n `https://verification.didit.me/v3/users/${vendorData}/`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n).then((r) => r.json());\n\nconsole.log(user.didit_internal_id, user.status);" + } + ], + "responses": { + "200": { + "description": "Full user detail including metadata, comments/activity log, and aggregated session data.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserDetailItem" + }, + "examples": { + "Active user": { + "value": { + "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", + "vendor_data": "user-abc-123", + "display_name": null, + "full_name": "John Michael Doe", + "date_of_birth": "1990-05-15", + "effective_name": "John Michael Doe", + "status": "ACTIVE", + "metadata": { + "tier": "premium", + "source": "website" + }, + "portrait_image_url": "https:///...", + "session_count": 3, + "approved_count": 2, + "declined_count": 0, + "in_review_count": 1, + "issuing_states": [ + "USA" + ], + "approved_emails": [ + "john@example.com" + ], + "approved_phones": [ + "+14155551234" + ], + "features": { + "ID_VERIFICATION": "Approved", + "LIVENESS": "Approved", + "FACE_MATCH": "Approved", + "AML": "Approved" + }, + "features_list": [ + { + "feature": "ID_VERIFICATION", + "status": "Approved" + }, + { + "feature": "LIVENESS", + "status": "Approved" + }, + { + "feature": "FACE_MATCH", + "status": "Approved" + }, + { + "feature": "AML", + "status": "Approved" + } + ], + "last_session_at": "2025-06-15T10:30:00Z", + "last_activity_at": "2025-06-15T10:30:00Z", + "first_session_at": "2025-06-01T08:00:00Z", + "tags": [ + { + "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "tag": { + "uuid": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b", + "name": "VIP", + "color": "#2567FF", + "description": null, + "source": "custom", + "created_at": "2025-05-01T09:00:00Z", + "updated_at": "2025-05-01T09:00:00Z" + }, + "added_by_email": "analyst@acme.com", + "added_by_name": "Jane Analyst", + "created_at": "2025-06-02T11:00:00Z" + } + ], + "comments": [ + { + "uuid": "c1111111-2222-4333-8444-555555555555", + "comment_type": "STATUS_CHANGED", + "comment": null, + "actor_email": "analyst@acme.com", + "actor_name": "Jane Analyst", + "previous_status": "FLAGGED", + "new_status": "ACTIVE", + "previous_value": null, + "new_value": null, + "changed_fields": [], + "metadata": null, + "mentioned_emails": [], + "created_at": "2025-06-01T10:30:00Z" + } + ], + "created_at": "2025-06-01T08:00:00Z", + "updated_at": "2025-06-15T10:30:00Z" + } + } + } + } + } + }, + "401": { + "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "content": { + "application/json": { + "examples": { + "Missing credentials": { + "value": { + "detail": "You must be authenticated with a valid access token to access this endpoint." + } + } + } + } + } + }, + "403": { + "description": "Invalid or revoked API key, or the key cannot read this application's users. Note: on this endpoint an *invalid* key returns `403` (not `401`).", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No user with the supplied `vendor_data` exists for this application.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "patch": { + "summary": "Update user", + "description": "Partial update of a user: `display_name`, `full_name`, `date_of_birth`, `status`, `metadata`, `approved_emails`/`approved_phones`/`issuing_states`. `metadata` fully replaces. Changing `status` here works like [Update User Status](#patch-/v3/users/-vendor_data-/update-status/): it records a `STATUS_CHANGED` activity with the optional `reason`, and `BLOCKED` adds the `vendor_data` to the system blocklist (moving away from `BLOCKED` removes that entry). Changes to `display_name`, `full_name`, `date_of_birth` or `metadata` are recorded as a `PROFILE_UPDATED` activity.", + "operationId": "update_user", + "x-didit-sdk": { + "resource": "users", + "method": "update", + "auth": "api_key", + "server": true + }, + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "vendor_data", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Your unique identifier for the user (free-form string, NOT a UUID).", + "example": "user-abc-123" + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/users/user-abc-123/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"display_name\": \"Jane S.\", \"status\": \"FLAGGED\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/users/user-abc-123/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'display_name': 'Jane S.', 'status': 'FLAGGED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/users/user-abc-123/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ display_name: 'Jane S.', status: 'FLAGGED' }),\n});\nconst user = await resp.json();\nconsole.log(user.status);" + } + ], + "responses": { + "200": { + "description": "User updated. The full updated user record (same shape as Get User) is returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserDetailItem" + }, + "examples": { + "Updated": { + "value": { + "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", + "vendor_data": "user-abc-123", + "display_name": "Jane S.", + "full_name": "Jane Elizabeth Smith", + "date_of_birth": "1985-11-22", + "effective_name": "Jane S.", + "status": "FLAGGED", + "metadata": { + "tier": "premium" + }, + "portrait_image_url": "https:///...", + "session_count": 3, + "approved_count": 2, + "declined_count": 0, + "in_review_count": 1, + "issuing_states": [ + "USA" + ], + "approved_emails": [ + "john@example.com" + ], + "approved_phones": [ + "+14155551234" + ], + "features": { + "ID_VERIFICATION": "Approved", + "LIVENESS": "Approved", + "FACE_MATCH": "Approved", + "AML": "Approved" + }, + "features_list": [ + { + "feature": "ID_VERIFICATION", + "status": "Approved" + }, + { + "feature": "LIVENESS", + "status": "Approved" + }, + { + "feature": "FACE_MATCH", + "status": "Approved" + }, + { + "feature": "AML", + "status": "Approved" + } + ], + "last_session_at": "2025-06-15T10:30:00Z", + "last_activity_at": "2025-06-15T10:30:00Z", + "first_session_at": "2025-06-01T08:00:00Z", + "tags": [ + { + "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "tag": { + "uuid": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b", + "name": "VIP", + "color": "#2567FF", + "description": null, + "source": "custom", + "created_at": "2025-05-01T09:00:00Z", + "updated_at": "2025-05-01T09:00:00Z" + }, + "added_by_email": "analyst@acme.com", + "added_by_name": "Jane Analyst", + "created_at": "2025-06-02T11:00:00Z" + } + ], + "comments": [], + "created_at": "2025-06-01T08:00:00Z", + "updated_at": "2025-06-15T10:30:00Z" + } + } + } + } + } + }, + "400": { + "description": "Invalid body — typically an unknown `status` or a `date_of_birth` not in `YYYY-MM-DD`.", + "content": { + "application/json": { + "examples": { + "Bad status": { + "value": { + "status": [ + "\"INVALID\" is not a valid choice." + ] + } + }, + "Bad date_of_birth": { + "value": { + "date_of_birth": [ + "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." + ] + } + }, + "Reason too long": { + "value": { + "reason": [ + "Ensure this field has no more than 1000 characters." + ] + } + } + } + } + } + }, + "401": { + "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "content": { + "application/json": { + "examples": { + "Missing credentials": { + "value": { + "detail": "You must be authenticated with a valid access token to access this endpoint." + } + } + } + } + } + }, + "403": { + "description": "Invalid or revoked API key, or the key cannot update users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No user with the supplied `vendor_data` exists for this application.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "full_name": { + "type": "string", + "nullable": true, + "maxLength": 512, + "description": "Full name of the user" + }, + "display_name": { + "type": "string", + "nullable": true, + "description": "Custom display name for this user" + }, + "date_of_birth": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Date of birth in YYYY-MM-DD format" + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "FLAGGED", + "BLOCKED" + ], + "description": "User lifecycle status. A change is recorded as a `STATUS_CHANGED` activity, and `BLOCKED` adds the `vendor_data` to the system blocklist (moving away from `BLOCKED` removes that entry)." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Arbitrary JSON object you attach to the user. **Fully replaces** the existing metadata on update — merge client-side if you only want to add keys." + }, + "approved_emails": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Verified email addresses for this user, e.g. `[\"john@example.com\"]`. Fully replaces the stored list." + }, + "approved_phones": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Verified phone numbers for this user. Fully replaces the stored list." + }, + "issuing_states": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Issuing countries (ISO 3166-1 alpha-3), e.g. `[\"USA\"]`. Fully replaces the stored list." + }, + "reason": { + "type": "string", + "nullable": true, + "maxLength": 1000, + "description": "Why the status changed. Recorded on the status-change entry of the user's activity (`comments[].comment`, and **Activity** in the Business Console), only when the status actually changes. Webhooks do not carry it." + } + } + }, + "examples": { + "Rename": { + "summary": "Set display name", + "value": { + "display_name": "Jane S." + } + }, + "Override status": { + "summary": "Flag the user for review", + "value": { + "status": "FLAGGED" + } + }, + "Add metadata": { + "summary": "Replace metadata JSON", + "value": { + "metadata": { + "tier": "premium", + "risk_level": "low" + } + } + }, + "Fix extracted DOB": { + "summary": "Correct extracted profile fields", + "value": { + "full_name": "Jane Elizabeth Smith", + "date_of_birth": "1985-11-22" + } + }, + "Flag with reason": { + "summary": "Flag the user and record why", + "value": { + "status": "FLAGGED", + "reason": "Address does not match the billing country" + } + } + } + } + } + } + } + }, + "/v3/users/{vendor_data}/generate-pdf/": { + "get": { + "summary": "Download the full user history PDF report", + "description": "Render and download one compliance-ready PDF containing the full verification history of the user identified by `vendor_data`. It is the same document the Console produces from **User detail → Actions → Download PDF**, so periodic compliance exports no longer need a person clicking in the Console.\n\n**What the report contains.** A cover page with the profile summary (name, date of birth, issuing states, approved emails and phones), the latest status of every feature, the session counters and an index of the sessions included — followed by the standard report of every included session, in chronological order (oldest first). Each session report is the same document `GET /v3/session/{sessionId}/generate-pdf/` returns for that session.\n\n**Eligible sessions.** Only User Verification (KYC) sessions in `Approved`, `Declined`, `In Review` or `Kyc Expired` are included; sessions in any other status are skipped. If the user has no session in one of those statuses the endpoint returns `403` — see below.\n\n**Cap.** The report is capped at the **20 most recent** reportable sessions. When a user has more, the newest 20 are included and the cover page states how many older sessions were left out. Use `GET /v3/sessions/?vendor_data=…` for the complete list as JSON.\n\n**Branding.** If your application has white-label customization, the cover and every session report are rendered with your logo and privacy-policy URL; otherwise they carry Didit branding. Reports are rendered in English — there is no language parameter.\n\n**No caching.** Every call re-renders the PDF from the current data, so a report generated after a manual review reflects the reviewer's decision. Two calls for the same user can produce byte-different files — archive the downloaded file if you need an immutable copy.\n\n**Latency.** Rendering is synchronous and downloads every stored image of every included session, so a user with many media-heavy sessions can take tens of seconds. Use a generous client read timeout (120 s recommended) and stream the body to disk. Generation stops at a server-side time budget instead of running until your client gives up: if that budget is reached, the response is still a valid PDF containing the most recent sessions, and the cover states how many were omitted.\n\n**Scope.** The user is resolved inside the application that owns the API key. A key can never export a user of another application — such a user does not exist for it and the endpoint returns `404`.\n\n**Trailing slash.** The canonical route ends with a trailing slash (`…/generate-pdf/`). A request without it is served directly with the same response — there is no `301` redirect, so no client has to follow redirects and `curl` does not need `-L`. Use the slashed URL as in the samples.", + "operationId": "get_user_generate_pdf", + "x-didit-sdk": { + "resource": "users", + "method": "generate_pdf", + "auth": "api_key", + "server": true + }, + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "vendor_data", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Your unique identifier for the user — a free-form string (NOT a UUID). This is the same value you passed as `vendor_data` when creating the user's sessions, matched exactly as sent. URL-encode it; a value containing `/` cannot be addressed by this route.", + "example": "user-abc-123" + } + ], + "responses": { + "200": { + "description": "The rendered PDF document, returned directly as binary `application/pdf` — there is no JSON wrapper and no download-URL indirection. The body starts with the `%PDF` magic bytes. Save it to a `.pdf` file or stream it through to your caller.", + "headers": { + "Content-Disposition": { + "description": "`attachment; filename=user_{didit_internal_id}.pdf` — the file is named after the user's stable Didit identifier (`didit_internal_id` on `GET /v3/users/{vendor_data}/`), not after `vendor_data`.", + "schema": { + "type": "string", + "example": "attachment; filename=user_f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418.pdf" + } + }, + "Content-Length": { + "description": "Total size of the PDF in bytes; the body is not chunked.", + "schema": { + "type": "integer", + "example": 148213 + } + } + }, + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "401": { + "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "content": { + "application/json": { + "examples": { + "Missing credentials": { + "value": { + "detail": "You must be authenticated with a valid access token to access this endpoint." + } + } + } + } + } + }, + "403": { + "description": "Authorization or eligibility failure. Distinguish the two by the `detail` string:\n\n- `\"You do not have permission to perform this action.\"` — the API key is invalid or revoked, or the request carries a Console user token instead of an application API key.\n- `\"This user has no sessions in review, declined, approved or kyc expired to report.\"` — the user exists in your application but none of its sessions is in a reportable status yet (for example every session is still `In Progress`, or the user was created through `POST /v3/users/create/` and has never verified). Wait for a `status.updated` webhook before requesting the report.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "This user has no sessions in review, declined, approved or kyc expired to report." + } + } + }, + "examples": { + "Invalid credentials": { + "summary": "Revoked or invalid API key, or a Console user token", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "No reportable sessions": { + "summary": "User exists but has no Approved / Declined / In Review / Kyc Expired session", + "value": { + "detail": "This user has no sessions in review, declined, approved or kyc expired to report." + } + } + } + } + } + }, + "404": { + "description": "No user with the supplied `vendor_data` exists in the application behind the API key. Users of other applications are invisible to this key and return the same `404`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "User not found." + } + } + }, + "examples": { + "Unknown user": { + "summary": "vendor_data does not match any user of this application", + "value": { + "detail": "User not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. PDF generation has a dedicated limit of **50 requests per minute per credential** (API key, Bearer token, or source IP when unauthenticated), shared between this endpoint and `GET /v3/session/{sessionId}/generate-pdf/`, in addition to the global 600/min GET limit. Wait `Retry-After` seconds (also reflected in the `X-RateLimit-*` headers), then retry. Reports are not cached server-side, so download each report once and store the file instead of re-fetching it.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying the request.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Maximum number of PDF generations allowed in the current window (50).", + "schema": { + "type": "integer", + "example": 50 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests remaining in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix timestamp (seconds) at which the current window resets.", + "schema": { + "type": "integer", + "example": 1750000000 + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable explanation of the rate-limit breach.", + "example": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute." + } + } + }, + "examples": { + "Throttled": { + "summary": "More than 50 PDF generations in one minute from the same credential", + "value": { + "detail": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl --fail \\\n 'https://verification.didit.me/v3/users/user-abc-123/generate-pdf/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n --output user-history.pdf" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.get(\n \"https://verification.didit.me/v3/users/user-abc-123/generate-pdf/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n stream=True,\n timeout=120,\n)\nresponse.raise_for_status()\nwith open(\"user-history.pdf\", \"wb\") as fh:\n for chunk in response.iter_content(chunk_size=8192):\n fh.write(chunk)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import { writeFile } from 'node:fs/promises';\n\nconst vendorData = encodeURIComponent('user-abc-123');\nconst response = await fetch(\n `https://verification.didit.me/v3/users/${vendorData}/generate-pdf/`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);\nawait writeFile('user-history.pdf', Buffer.from(await response.arrayBuffer()));" + } + ] + } + }, + "/v3/users/delete/": { + "post": { + "summary": "Batch delete users", + "description": "**Permanently delete** users: the user records are removed permanently. For everyday blocking prefer [Update User Status](#patch-/v3/users/-vendor_data-/update-status/) with `BLOCKED`. Select users with `vendor_data_list`, with `didit_internal_id_list`, or with both at once; or pass `delete_all: true`. This endpoint never narrows `delete_all` with filters. The Console's People filters are refused with a `400` (see the `400` response), so a narrowed intent does not turn into a full wipe. Two exceptions: `filter_has_approved_sessions` and `filter_has_declined_sessions` are not refused and not applied, so with `delete_all: true` they still delete every user; do not send them.\n\n**Biometric templates:** every image-free biometric template retained for these users after session deletion (see `/v3/biometric-templates/`) is purged before the user is deleted, so the person stops matching in duplicate detection and Face Search. If a template cannot be purged because a biometric store is unavailable, the call returns `503`, no user in the request is deleted, and repeating the request finishes the job.", + "operationId": "batch_delete_users", + "x-didit-sdk": { + "resource": "users", + "method": "batch_delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Users" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Provide `vendor_data_list`, `didit_internal_id_list`, or `delete_all: true`. The two lists can be combined and are OR'd. When `delete_all` is `true` it takes precedence and both lists are ignored.", + "properties": { + "vendor_data_list": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Your own identifiers to delete, matched exactly as sent." + }, + "didit_internal_id_list": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "Didit internal user IDs to delete - the `didit_internal_id` returned by the create and get endpoints." + }, + "delete_all": { + "type": "boolean", + "default": false, + "description": "If `true`, deletes every user in the application." + } + } + }, + "examples": { + "Specific users": { + "summary": "Delete users by vendor_data", + "value": { + "vendor_data_list": [ + "user-123", + "user-456" + ] + } + }, + "By Didit internal ID": { + "summary": "Delete users by didit_internal_id", + "value": { + "didit_internal_id_list": [ + "3f1c0d3e-8f0a-4c2e-9a3d-9b1f5f4a2c11" + ] + } + }, + "All users": { + "summary": "Wipe every user in this application", + "value": { + "delete_all": true + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/users/delete/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"vendor_data_list\": [\"user-123\", \"user-456\"]}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/users/delete/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'vendor_data_list': ['user-123', 'user-456']},\n)\nresp.raise_for_status()\nprint('deleted', resp.json()['deleted'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/users/delete/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ vendor_data_list: ['user-123', 'user-456'] }),\n});\nconst body = await resp.json();\nconsole.log('deleted', body.deleted);" + } + ], + "responses": { + "200": { + "description": "Users deleted. Returns the number of users actually deleted.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "deleted": { + "type": "integer", + "description": "Number of users deleted (excludes vendor_data values that didn't match anything)." + } + } + }, + "examples": { + "Deleted some": { + "value": { + "deleted": 2 + } + }, + "Nothing matched": { + "value": { + "deleted": 0 + } + } + } + } + } + }, + "400": { + "description": "Validation error; nothing is deleted. Errors are keyed by field name. With no selector (neither list and `delete_all` not `true`) the body is `{\"vendor_data_list\": [\"This field is required unless delete_all is true.\"]}`; an empty list is refused under its own key with `\"This list may not be empty.\"`. A Console-only filter (`filter_search`, `filter_status`, `filter_country`, `filter_date_from`, `filter_date_to`, `filter_tag`, `filter_latest_verification_status`, `filter_risk`, `filter_has_sessions`, `filter_has_open_alerts`, `filter_aml_monitoring`, `filter_can_sign_in`, `filter_auth_status`, `filter_auth_method` or `filter_auth_mfa_enabled`) is refused whenever it carries a value, even `false`, with an object keyed by each such field. Any other unknown `filter_*` key is refused with `\"Unknown filter.\"`.", + "content": { + "application/json": { + "examples": { + "Missing selector": { + "summary": "Neither list nor delete_all provided", + "value": { + "vendor_data_list": [ + "This field is required unless delete_all is true." + ] + } + }, + "Console-only filter": { + "value": { + "filter_has_sessions": "This filter is Console-only and is not supported by this endpoint." + } + } + } + } + } + }, + "401": { + "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "content": { + "application/json": { + "examples": { + "Missing credentials": { + "value": { + "detail": "You must be authenticated with a valid access token to access this endpoint." + } + } + } + } + } + }, + "403": { + "description": "Invalid or revoked API key, or the key cannot delete users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + }, + "503": { + "description": "A biometric store was unavailable while retaining or purging a biometric template. Nothing is deleted; repeat the same request. Retries are idempotent and converge on a single template.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Retryable": { + "value": { + "detail": "The biometric-template operation could not finish because an external store is unavailable. The operation is retryable." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/sessions/delete/": { + "post": { + "summary": "Bulk delete KYC sessions by session_number", + "description": "Bulk delete User Verification (KYC) sessions by their numeric `session_number`, or every KYC session in your application with `delete_all: true`.\n\n`session_number` is the human-friendly incrementing counter shown in the Console session list and returned as `session_number` by the create-session and decision endpoints; it is **not** the `session_id` UUID. To delete a session by UUID, or to delete a Business Verification (KYB) session, which this endpoint never touches, use `DELETE /v3/session/{sessionId}/delete/` instead.\n\n**Up to 20 sessions are deleted in the request, more run as a job.** The number of matching sessions decides, whether they are selected by `session_numbers` or with `delete_all`:\n- Up to 20 matching sessions: they are deleted before the response, which is `200` with one entry per session in `results[]`.\n- More than 20: the response is `202` with a deletion job, and the sessions are deleted in the background. Poll `GET /v3/sessions/delete/{job_id}/` until `status` is `COMPLETED` or `FAILED`. A job reports counts per outcome, never which sessions it deleted. A `delete_all` job deletes only the sessions created before the request; sessions started while it runs are not deleted.\n\nEach deleted session gets exactly the same treatment as the single-session delete endpoint: the session, every verification attempt it holds, its decision and extracted data, its related feature records (ID verification, face, liveness, face match, and so on) and all of its stored files are deleted, it disappears from every read endpoint, and previously issued media URLs stop resolving. Webhook deliveries still queued for a deleted session, and their retries, are dropped; webhooks already delivered are not affected. Blocklist entries are **not** removed, credits already consumed are **not** refunded, and the parent User entities are **not** deleted (use `POST /v3/users/delete/` for those); no webhook is emitted for the deletion. Deletion is permanent and irreversible.\n\n**Selection semantics:**\n- With `delete_all: true` you can narrow the selection with the filters of `GET /v3/sessions/`, each prefixed with `filter_` (for example `filter_status`, `filter_workflow_id`, `filter_date_from`). Filters that take several values in List Sessions (`status`, `workflow_id`, `country`, `region`, `document_type`, `document_name`, `session_type`, `warning`, `organization_name`) accept a comma-separated string or an array; the others take one value. An empty value is ignored. Filters apply only together with `delete_all`, and a deletion job applies the same filters.\n- An unknown `filter_*` key, or a filter value that cannot be read, rejects the whole request with `400` before anything is deleted, so a mistyped filter never widens the deletion to every session.\n- Entries in `session_numbers` that do not match a live KYC session in your application (unknown numbers, already-deleted sessions, or sessions owned by another application) are **silently skipped**: they do not appear in `results` and are not counted in a job's `total`.\n- Validation failures (a non-numeric entry, an empty list, neither `session_numbers` nor `delete_all` provided, or a template retention the application cannot honour) reject the **whole request** with `400` and delete nothing.\n- Sessions are deleted in groups. If a group cannot be deleted (for example because a storage service is unavailable), every session in that group is reported as `face_retention_outcome: failed_retryable` and stays in place; the other groups are unaffected. Repeat the request for those sessions. A job retries such a group by itself.\n- There is no documented cap on the list length; the shared write rate limit (below) is the practical bound.\n\n**Face biometric data:** the optional `retain_face_embeddings`, `face_retention_days`, `face_retention_deadline`, `deletion_instruction`, and `instruction_id` fields have the same meaning as on `DELETE /v3/session/{sessionId}/delete/` and apply to every matched session. Each result reports its own `face_retention_outcome`.\n\n**Response:** `200 OK` with `results[]` for up to 20 matching sessions, or `202 Accepted` with a deletion job for more. Every result confirms deletion except `face_retention_outcome: failed_retryable`, which reports a session that was NOT deleted and should be retried. Earlier versions of this endpoint returned `204 No Content`; update clients that assert on `204`.\n\n**Idempotency:** repeating the same request deletes nothing more: numbers that no longer match a session are skipped.\n\n**`delete_all: true` (destructive):** deletes **every** remaining KYC session in the application; `session_numbers` is ignored when set. There is no confirmation step and no undo.\n\n**Authentication:** API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`); this API never returns `401`.\n\n**Rate limit:** shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\nThis endpoint accepts API keys only. Business Console user Bearer tokens are rejected with `403` (unlike the single-session delete, which accepts both).", + "operationId": "batch_delete_sessions", + "x-didit-sdk": { + "resource": "sessions", + "method": "batch_delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "session_numbers": { + "type": "array", + "description": "Numeric `session_number` values of the KYC sessions to delete, as digit-only strings (JSON numbers are also accepted and coerced). Required unless `delete_all` is `true`; an empty list returns `400`. Any non-digit entry rejects the whole request with `400` (unless `delete_all` is `true`, in which case validation is skipped and the entries are ignored). Numbers that do not match a live session in your application are silently skipped.", + "items": { + "type": "string", + "pattern": "^[0-9]+$", + "example": "1001" + }, + "minItems": 1, + "example": [ + "1001", + "1002", + "1003" + ] + }, + "delete_all": { + "type": "boolean", + "description": "When `true`, every remaining KYC session in the calling application is deleted and `session_numbers` is ignored, unless you narrow the selection with `filter_*` keys. More than 20 matching sessions run as a deletion job (`202`) that covers only the sessions created before the request. Defaults to `false`. Irreversible; there is no confirmation step.", + "default": false, + "example": false + }, + "retain_face_embeddings": { + "type": "boolean", + "nullable": true, + "description": "Override the application's `face_retention_policy` for this deletion. `true` keeps one image-free face biometric template anchored to the session's User after the session is deleted; `false` deletes the face embedding with the session; omit or `null` to follow the application policy. Ignored for KYB sessions and for sessions without a face embedding.", + "example": true + }, + "face_retention_days": { + "type": "integer", + "minimum": 1, + "maximum": 3650, + "description": "Finite retention duration, in days, for a retained template. Required when a template is retained unless the application already sets `face_retention_days`. The template's `expires_at` is the earliest of this duration, the application's general data-retention window, and `face_retention_deadline`.", + "example": 365 + }, + "face_retention_deadline": { + "type": "string", + "format": "date-time", + "description": "Optional hard expiry for a retained template. Must be in the future.", + "example": "2027-08-28T00:00:00Z" + }, + "deletion_instruction": { + "type": "string", + "enum": [ + "operational_session_delete", + "privacy_erasure" + ], + "default": "operational_session_delete", + "description": "The class of instruction you are giving. `operational_session_delete` deletes the session and retains a template only when the policy or `retain_face_embeddings` says so. `privacy_erasure` purges every retained biometric template anchored to the session's User before deleting the session, cannot be overridden by the application policy, and cannot be combined with `retain_face_embeddings: true` (`400`).", + "example": "operational_session_delete" + }, + "instruction_id": { + "type": "string", + "maxLength": 255, + "description": "Your durable reference for this deletion instruction (for example an erasure-ticket id). Recorded on the audit trail and on any retained template. Generated by Didit when omitted.", + "example": "dsar-2026-0142" + }, + "filter_status": { + "description": "Narrows a `delete_all` to the sessions that match the `status` filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "filter_vendor_data": { + "description": "Narrows a `delete_all` to the sessions that match the `vendor_data` filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated text, so several values are not matched as alternatives. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "string" + }, + "filter_search": { + "description": "Narrows a `delete_all` to the sessions that match the `search` filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated text, so several values are not matched as alternatives. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "string" + }, + "filter_workflow_id": { + "description": "Narrows a `delete_all` to the sessions that match the `workflow_id` filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "filter_date_from": { + "description": "Narrows a `delete_all` to the sessions that match the `date_from` filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with `400`. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "string", + "format": "date" + }, + "filter_date_to": { + "description": "Narrows a `delete_all` to the sessions that match the `date_to` filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with `400`. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "string", + "format": "date" + }, + "filter_country": { + "description": "Narrows a `delete_all` to the sessions that match the `country` filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "filter_document_type": { + "description": "Narrows a `delete_all` to the sessions that match the `document_type` filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "filter_document_name": { + "description": "Narrows a `delete_all` to the sessions that match the `document_name` filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "filter_region": { + "description": "Narrows a `delete_all` to the sessions that match the `region` filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "filter_didit_internal_id": { + "description": "Narrows a `delete_all` to the sessions that match the `didit_internal_id` filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with `400`. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "string", + "format": "uuid" + }, + "filter_session_type": { + "description": "Narrows a `delete_all` to the sessions that match the `session_type` filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "filter_api_service": { + "description": "Narrows a `delete_all` to the sessions that match the `api_service` filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with `400`. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "string" + }, + "filter_screened_full_name": { + "description": "Narrows a `delete_all` to the sessions that match the `screened_full_name` filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated text, so several values are not matched as alternatives. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "string" + }, + "filter_warning": { + "description": "Narrows a `delete_all` to the sessions that match the `warning` filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "filter_status_initialized_as_in_review": { + "description": "Narrows a `delete_all` to the sessions that match the `status_initialized_as_in_review` filter of [List Sessions](/sessions-api/list-sessions), with the same values. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "boolean" + }, + "filter_is_kyc_to_be_reviewed": { + "description": "Narrows a `delete_all` to the sessions that match the `is_kyc_to_be_reviewed` filter of [List Sessions](/sessions-api/list-sessions), with the same values. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "boolean" + }, + "filter_euuid": { + "description": "Narrows a `delete_all` to the sessions that match the `euuid` filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with `400`. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "type": "string", + "format": "uuid" + }, + "filter_organization_name": { + "description": "Narrows a `delete_all` to the sessions that match the `organization_name` filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. With an API key it can only match your own organization's name, so it selects all of your sessions or none. Ignored without `delete_all`. An empty value is ignored and does not narrow the selection.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + }, + "examples": { + "Specific sessions": { + "summary": "Delete specific sessions by number", + "value": { + "session_numbers": [ + "1001", + "1002", + "1003" + ] + } + }, + "All sessions": { + "summary": "Delete every KYC session in the application", + "value": { + "delete_all": true + } + }, + "Delete and retain templates": { + "summary": "Delete sessions but keep image-free biometric templates for 365 days", + "value": { + "session_numbers": [ + "1001", + "1002" + ], + "retain_face_embeddings": true, + "face_retention_days": 365, + "instruction_id": "post-approval-cleanup-2026-08" + } + }, + "Privacy erasure": { + "summary": "Delete sessions and purge every retained template for their Users", + "value": { + "session_numbers": [ + "1001" + ], + "deletion_instruction": "privacy_erasure", + "instruction_id": "dsar-2026-0142" + } + }, + "Filtered delete_all": { + "summary": "Delete every Declined session of one workflow created in September 2026", + "value": { + "delete_all": true, + "filter_status": [ + "Declined" + ], + "filter_workflow_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "filter_date_from": "2026-09-01", + "filter_date_to": "2026-09-30" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Up to 20 sessions matched and were processed in the request. One result per matched session; numbers that matched nothing (unknown or already deleted) are skipped and do not appear. A result confirms deletion except when `face_retention_outcome` is `failed_retryable`: that session was NOT deleted and should be retried.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "description": "The deleted session." + }, + "session_number": { + "type": "integer", + "nullable": true, + "description": "The deleted session's number." + }, + "face_retention_outcome": { + "type": "string", + "enum": [ + "retained_with_user", + "deleted", + "none", + "ineligible_no_vendor_user", + "failed_retryable" + ], + "description": "What happened to the session's face biometric data. `deleted`: the face embedding was deleted with the session (default). `retained_with_user`: the session is deleted and one image-free biometric template stays anchored to the User; see `biometric_template_uuid`. `none`: the session had no face embedding (for example a document-only KYC session without a liveness selfie; KYB sessions are never selected by this endpoint). `ineligible_no_vendor_user`: retention was requested but the session has no linked User, so the embedding was deleted with the session. `failed_retryable` (batch endpoint only): the group this session was deleted with could not be deleted (a storage service, a biometric store, or a monitoring provider was unavailable); this session is still in place, repeat the request for it." + }, + "biometric_template_uuid": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Id of the retained biometric template when `face_retention_outcome` is `retained_with_user`; `null` otherwise. Manage it through `/v3/biometric-templates/{template_uuid}/`." + }, + "biometric_template_expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the retained biometric template is deleted, when `face_retention_outcome` is `retained_with_user`; `null` when no template is retained." + }, + "deleted_child_verifications": { + "type": "integer", + "description": "Always `0` on this endpoint, which deletes KYC sessions only. Deleting a Business Verification (KYB) session with `DELETE /v3/session/{sessionId}/delete/` reports the verifications it created that were deleted with it." + } + } + } + } + } + }, + "examples": { + "Mixed outcomes": { + "value": { + "results": [ + { + "session_id": "4c5c7f3a-3b1d-4b7e-9c39-2b4f7e6a1d02", + "session_number": 1001, + "face_retention_outcome": "deleted", + "biometric_template_uuid": null, + "biometric_template_expires_at": null, + "deleted_child_verifications": 0 + }, + { + "session_id": "b8e1d2c3-4f5a-4b6c-8d7e-9f0a1b2c3d4e", + "session_number": 1002, + "face_retention_outcome": "retained_with_user", + "biometric_template_uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", + "biometric_template_expires_at": "2027-10-09T10:15:00Z", + "deleted_child_verifications": 0 + }, + { + "session_id": "7a1e9c2d-5f4b-4c3a-8e2d-1b0a9f8e7d6c", + "session_number": 1003, + "face_retention_outcome": "ineligible_no_vendor_user", + "biometric_template_uuid": null, + "biometric_template_expires_at": null, + "deleted_child_verifications": 0 + }, + { + "session_id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f", + "session_number": 1004, + "face_retention_outcome": "failed_retryable", + "biometric_template_uuid": null, + "biometric_template_expires_at": null, + "deleted_child_verifications": 0 + } + ] + } + } + } + } + } + }, + "202": { + "description": "More than 20 sessions matched. They are deleted in the background; poll `GET /v3/sessions/delete/{job_id}/` until `status` is `COMPLETED` or `FAILED`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionDeletionJob" + }, + "examples": { + "Job accepted": { + "value": { + "job_id": "5b8f2c1e-7d3a-4f6b-9e2c-1a0d8f7e6c5b", + "status": "PENDING", + "total": 250, + "processed": 0, + "outcomes": { + "deleted": 0, + "none": 0, + "retained_with_user": 0, + "ineligible_no_vendor_user": 0, + "failed_retryable": 0, + "already_deleted": 0 + }, + "deleted_child_verifications": 0, + "error": null, + "created_at": "2026-10-09T10:15:00Z", + "completed_at": null + } + } + } + } + } + }, + "400": { + "description": "Validation error — nothing was deleted. Error values are arrays of message strings keyed by field name.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "session_numbers": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": { + "description": "Other keys: a `filter_*` key that is not a List Sessions filter (`\"Unknown filter.\"`), or a `filter_*` key whose value cannot be read (keyed by that `filter_*` key, with an array of messages)." + } + }, + "examples": { + "Missing selector": { + "summary": "Neither session_numbers nor delete_all provided", + "value": { + "session_numbers": [ + "This field is required unless delete_all is true." + ] + } + }, + "Empty list": { + "summary": "session_numbers is an empty array", + "value": { + "session_numbers": [ + "This list may not be empty." + ] + } + }, + "Non-numeric entry": { + "summary": "session_numbers contains a non-digit string", + "value": { + "session_numbers": [ + "All session_numbers must be numeric." + ] + } + }, + "Privacy erasure cannot retain": { + "summary": "deletion_instruction privacy_erasure combined with retain_face_embeddings true", + "value": { + "retain_face_embeddings": [ + "Privacy erasure cannot retain biometric templates." + ] + } + }, + "No finite retention duration": { + "summary": "Retention requested without face_retention_days on the request or the application", + "value": { + "detail": "A finite biometric-template retention duration is required." + } + }, + "Unknown filter": { + "summary": "A filter_* key that List Sessions does not have", + "value": { + "filter_reviewer": "Unknown filter." + } + }, + "Unreadable filter value": { + "summary": "A filter value that cannot be read", + "value": { + "filter_date_from": [ + "Enter a valid date." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing or invalid API key. This API returns `403` for authentication failures — never `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Missing or invalid API key": { + "summary": "No x-api-key header, or the key is invalid", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Rate limited": { + "summary": "Write budget exhausted", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST \\\n https://verification.didit.me/v3/sessions/delete/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"session_numbers\": [\"1001\", \"1002\", \"1003\"]\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/sessions/delete/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\"session_numbers\": [\"1001\", \"1002\", \"1003\"]},\n)\nresponse.raise_for_status()\nif response.status_code == 202: # more than 20 sessions: poll the job\n job_id = response.json()[\"job_id\"]\nelse: # 200: one result per matched session\n print(response.json()[\"results\"])\n\n# Destructive: delete every KYC session in the application\n# requests.post(\n# \"https://verification.didit.me/v3/sessions/delete/\",\n# headers={\"x-api-key\": \"YOUR_API_KEY\"},\n# json={\"delete_all\": True},\n# )" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const response = await fetch(\n 'https://verification.didit.me/v3/sessions/delete/',\n {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ session_numbers: ['1001', '1002', '1003'] }),\n },\n);\nif (response.status !== 200 && response.status !== 202) throw new Error(`HTTP ${response.status}`);\nconst body = await response.json();\n// 200: body.results, one per matched session. 202: poll /v3/sessions/delete/${body.job_id}/" + } + ] + } + }, + "/v3/sessions/delete/{job_id}/": { + "get": { + "summary": "Get a bulk session deletion job", + "description": "Progress of a bulk session deletion that `POST /v3/sessions/delete/` answered with `202` (more than 20 matching sessions, selected by `session_numbers` or with `delete_all`). Poll every few seconds while `status` is `PENDING` or `PROCESSING`; stop when it is `COMPLETED` or `FAILED`.\n\nThe job reports how many sessions ended in each outcome and how many it has processed, never which sessions it deleted. A `FAILED` job carries the reason in `error`; the sessions it had not reached are still in place, so repeat the deletion request to delete them.\n\n**Authentication:** API key in the `x-api-key` header. A job is only visible to the application that created it; any other `job_id` returns `404`. Missing or invalid credentials return `403`; this API never returns `401`.", + "operationId": "get_session_deletion_job", + "x-didit-sdk": { + "resource": "sessions", + "method": "retrieve_deletion_job", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The `job_id` returned with the `202` from `POST /v3/sessions/delete/`." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl 'https://verification.didit.me/v3/sessions/delete/JOB_ID/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import time\nimport requests\n\nurl = 'https://verification.didit.me/v3/sessions/delete/JOB_ID/'\nwhile True:\n job = requests.get(url, headers={'x-api-key': 'YOUR_API_KEY'}).json()\n if job['status'] in ('COMPLETED', 'FAILED'):\n break\n time.sleep(3)\nprint(job['status'], job['outcomes'], job['error'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const url = 'https://verification.didit.me/v3/sessions/delete/JOB_ID/';\nlet job;\ndo {\n await new Promise((r) => setTimeout(r, 3000));\n job = await (await fetch(url, { headers: { 'x-api-key': 'YOUR_API_KEY' } })).json();\n} while (job.status === 'PENDING' || job.status === 'PROCESSING');\nconsole.log(job.status, job.outcomes, job.error);" + } + ], + "responses": { + "200": { + "description": "The job.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionDeletionJob" + }, + "examples": { + "Processing": { + "value": { + "job_id": "5b8f2c1e-7d3a-4f6b-9e2c-1a0d8f7e6c5b", + "status": "PROCESSING", + "total": 250, + "processed": 100, + "outcomes": { + "deleted": 82, + "none": 18, + "retained_with_user": 0, + "ineligible_no_vendor_user": 0, + "failed_retryable": 0, + "already_deleted": 0 + }, + "deleted_child_verifications": 0, + "error": null, + "created_at": "2026-10-09T10:15:00Z", + "completed_at": null + } + }, + "Completed": { + "value": { + "job_id": "5b8f2c1e-7d3a-4f6b-9e2c-1a0d8f7e6c5b", + "status": "COMPLETED", + "total": 250, + "processed": 250, + "outcomes": { + "deleted": 204, + "none": 44, + "retained_with_user": 0, + "ineligible_no_vendor_user": 0, + "failed_retryable": 0, + "already_deleted": 2 + }, + "deleted_child_verifications": 0, + "error": null, + "created_at": "2026-10-09T10:15:00Z", + "completed_at": "2026-10-09T10:16:40Z" + } + }, + "Failed": { + "value": { + "job_id": "5b8f2c1e-7d3a-4f6b-9e2c-1a0d8f7e6c5b", + "status": "FAILED", + "total": 250, + "processed": 100, + "outcomes": { + "deleted": 82, + "none": 18, + "retained_with_user": 0, + "ineligible_no_vendor_user": 0, + "failed_retryable": 0, + "already_deleted": 0 + }, + "deleted_child_verifications": 0, + "error": "The deletion stopped before it finished. The sessions it had not reached are still in place; repeat the request to delete them.", + "created_at": "2026-10-09T10:15:00Z", + "completed_at": "2026-10-09T10:15:52Z" + } + } + } + } + } + }, + "403": { + "description": "Missing or invalid API key. This API returns `403` for authentication failures, never `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Missing or invalid API key": { + "summary": "No x-api-key header, or the key is invalid", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No deletion job with this `job_id` exists for the calling application. Jobs are only visible to the application that created them." + } + } + } + }, + "/v3/sessions/{session_id}/reviews/": { + "get": { + "summary": "List session reviews and activity log", + "description": "Return the audit trail / activity feed for a User Verification (KYC) session, newest first, in a paginated envelope (`count` / `next` / `previous` / `results`, 50 entries per page by default).\n\nFeed entries are written by every actor that touches the session: manual decisions made through `PATCH /v3/session/{session_id}/update-status/` (which records `previous_status` → `new_status`), Console reviewer activity (comments with `@email` mentions, KYC/POA data edits, file uploads/removals, tag changes, detail-page views), AML ongoing-monitoring matches, and notes appended via `POST` on this same path. Reading this feed is the API-side equivalent of the **Activity** panel in the Didit Console session view.\n\nOnly User Verification (KYC) sessions are addressable here; Business Verification (KYB) session IDs return `404`.", + "operationId": "list_session_reviews", + "x-didit-sdk": { + "resource": "sessions.reviews", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "description": "UUID of the User Verification (KYC) session. Must belong to the same application as the API key — cross-application UUIDs, deleted sessions, and Business Verification (KYB) session IDs all return `404`.", + "schema": { + "type": "string", + "format": "uuid", + "example": "8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "Page size. Defaults to `50`.", + "schema": { + "type": "integer", + "default": 50, + "example": 50 + } + }, + { + "name": "offset", + "in": "query", + "required": false, + "description": "Number of entries to skip from the start of the (ordered) feed. Defaults to `0`. The `next` / `previous` URLs in the response carry the right values for walking the feed.", + "schema": { + "type": "integer", + "default": 0, + "example": 0 + } + }, + { + "name": "ordering", + "in": "query", + "required": false, + "description": "Sort order. The feed defaults to newest first (`-created_at`); pass `created_at` to read it oldest first. Unknown values are silently ignored (default order is used); other serializer field names are also accepted.", + "schema": { + "type": "string", + "default": "-created_at", + "example": "created_at" + } + } + ], + "responses": { + "200": { + "description": "Paginated activity feed, newest first by default. `count` is the total number of entries; `next` / `previous` are ready-made page URLs (`null` at the ends). A `count` of `0` with empty `results` means no activity has been recorded yet.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Total number of feed entries for this session (across all pages).", + "example": 3 + }, + "next": { + "type": "string", + "nullable": true, + "description": "URL of the next page, or `null` on the last page.", + "example": null + }, + "previous": { + "type": "string", + "nullable": true, + "description": "URL of the previous page, or `null` on the first page.", + "example": null + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReviewItem" + }, + "description": "Feed entries for this page." + } + } + }, + "examples": { + "DecisionTrail": { + "summary": "Manual approval plus API-added notes", + "value": { + "count": 3, + "next": null, + "previous": null, + "results": [ + { + "uuid": "de0561bc-5583-4409-9171-1a31a4c8a5f4", + "activity_type": "STATUS_UPDATED", + "actor_display": "API Client", + "actor_email": null, + "actor_type": "API_KEY", + "new_status": "Approved", + "previous_status": "In Review", + "comment": "Document re-checked manually; address matches.", + "mentioned_emails": [], + "previous_value": { + "status": "In Review" + }, + "new_value": { + "status": "Approved" + }, + "changed_fields": [ + "status" + ], + "metadata": null, + "created_at": "2026-06-12T00:19:31.918735Z" + }, + { + "uuid": "35370f58-5098-44ee-8a06-8c9af80b2455", + "activity_type": "STATUS_UPDATED", + "actor_display": "Unknown", + "actor_email": null, + "actor_type": "CONSOLE_USER", + "new_status": null, + "previous_status": null, + "comment": "Note without status.", + "mentioned_emails": [], + "previous_value": null, + "new_value": null, + "changed_fields": [], + "metadata": null, + "created_at": "2026-06-12T00:18:44.504157Z" + }, + { + "uuid": "00338f82-f1c5-4f57-8614-c49bb258af1a", + "activity_type": "STATUS_UPDATED", + "actor_display": "Unknown", + "actor_email": null, + "actor_type": "CONSOLE_USER", + "new_status": "In Review", + "previous_status": null, + "comment": "Flagging for manual document re-check.", + "mentioned_emails": [], + "previous_value": null, + "new_value": null, + "changed_fields": [], + "metadata": null, + "created_at": "2026-06-12T00:18:44.466537Z" + } + ] + } + }, + "PaginatedFirstPage": { + "summary": "First page with limit=2 — next holds the follow-up URL", + "value": { + "count": 4, + "next": "https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/?limit=2&offset=2", + "previous": null, + "results": [ + { + "uuid": "de0561bc-5583-4409-9171-1a31a4c8a5f4", + "activity_type": "STATUS_UPDATED", + "actor_display": "API Client", + "actor_email": null, + "actor_type": "API_KEY", + "new_status": "Approved", + "previous_status": "In Review", + "comment": "Document re-checked manually; address matches.", + "mentioned_emails": [], + "previous_value": { + "status": "In Review" + }, + "new_value": { + "status": "Approved" + }, + "changed_fields": [ + "status" + ], + "metadata": null, + "created_at": "2026-06-12T00:19:31.918735Z" + }, + { + "uuid": "bd4e6227-bdd2-42b2-a726-1d12f785a457", + "activity_type": "STATUS_UPDATED", + "actor_display": "Unknown", + "actor_email": null, + "actor_type": "CONSOLE_USER", + "new_status": null, + "previous_status": null, + "comment": null, + "mentioned_emails": [], + "previous_value": null, + "new_value": null, + "changed_fields": [], + "metadata": null, + "created_at": "2026-06-12T00:18:44.540238Z" + } + ] + } + }, + "Empty": { + "summary": "Session with no recorded activity yet", + "value": { + "count": 0, + "next": null, + "previous": null, + "results": [] + } + } + } + } + } + }, + "403": { + "description": "Missing, malformed, expired, or revoked `x-api-key`. Authentication is checked before the session lookup, so an invalid key returns `403` even when the session UUID does not exist.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "You do not have permission to perform this action." + } + } + }, + "examples": { + "InvalidKey": { + "summary": "Missing or invalid x-api-key header", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No User Verification (KYC) session with this UUID is visible to your application: the UUID does not exist, the session was deleted, or it belongs to a different application. Business Verification (KYB) session IDs also return `404` here — the reviews feed is only addressable for KYC sessions.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "Session not found." + } + } + }, + "examples": { + "NotFound": { + "summary": "Unknown, deleted, or cross-application session UUID", + "value": { + "detail": "Session not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Inspect `Retry-After` and `X-RateLimit-*` response headers. See [Rate limiting](/integration/rate-limiting)." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -s 'https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import os, requests\n\nsession_id = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\"\nurl = f\"https://verification.didit.me/v3/sessions/{session_id}/reviews/\"\n\nwhile url: # walk every page of the feed\n resp = requests.get(url, headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]}, timeout=10)\n resp.raise_for_status()\n page = resp.json()\n for entry in page[\"results\"]:\n print(entry[\"created_at\"], entry[\"activity_type\"], entry[\"actor_display\"], entry.get(\"new_status\"))\n url = page[\"next\"]" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "const sessionId = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\";\nconst res = await fetch(\n `https://verification.didit.me/v3/sessions/${sessionId}/reviews/`,\n { headers: { 'x-api-key': 'YOUR_API_KEY' } },\n);\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst { count, results } = await res.json();\nconsole.log(`${count} feed entries`);\nfor (const entry of results) {\n console.log(entry.created_at, entry.activity_type, entry.actor_display, entry.new_status ?? \"\");\n}" + } + ] + }, + "post": { + "summary": "Add a review note to a session's audit trail", + "description": "Append a note to a User Verification (KYC) session's review / activity feed. **This endpoint only records an entry — it never changes the session.** The session's `status` is left untouched, no status-transition validation runs, and no webhook fires. To actually approve, decline, or re-open a session (which also writes a fully-attributed `STATUS_UPDATED` entry to this feed), use `PATCH /v3/session/{session_id}/update-status/` instead.\n\nBoth body fields are optional and an empty JSON object is accepted. `new_status` is stored purely as a label on the entry and accepts any of the ten session status values (case-sensitive). `@email` mentions inside `comment` are stored as plain text only — they trigger no notifications on this endpoint, and the entry's `mentioned_emails` stays empty.\n\nNote on attribution: entries created here carry no actor information. In the feed they show `actor_display: \"Unknown\"`, `actor_type: \"CONSOLE_USER\"`, and `activity_type: \"STATUS_UPDATED\"` (storage defaults), and the `201` body's `user` field is `\"Unknown\"`.", + "operationId": "create_session_review", + "x-didit-sdk": { + "resource": "sessions.reviews", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "name": "Content-Type", + "in": "header", + "required": false, + "description": "Set to `application/json` when sending a JSON body.", + "schema": { + "type": "string", + "example": "application/json" + } + }, + { + "name": "session_id", + "in": "path", + "required": true, + "description": "UUID of the User Verification (KYC) session. Must belong to the same application as the API key — cross-application UUIDs, deleted sessions, and Business Verification (KYB) session IDs all return `404`.", + "schema": { + "type": "string", + "format": "uuid", + "example": "8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d" + } + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "new_status": { + "type": "string", + "nullable": true, + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "description": "Optional status label to record on the entry. Accepts any session status value, case-sensitive (`\"approved\"` is rejected with `400`). Recording a status here does **not** change the session's actual status — use the update-status endpoint for that.", + "example": "In Review" + }, + "comment": { + "type": "string", + "nullable": true, + "description": "Optional free-text note (no enforced length limit). Stored verbatim; `@email` mentions are not processed on this endpoint.", + "example": "Flagging for manual document re-check." + } + } + }, + "examples": { + "NoteWithStatusLabel": { + "summary": "Note carrying a status label (session status unchanged)", + "value": { + "new_status": "In Review", + "comment": "Flagging for manual document re-check." + } + }, + "CommentOnly": { + "summary": "Plain note without a status label", + "value": { + "comment": "Customer emailed an updated utility bill; awaiting upload." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Entry recorded on the session's audit trail. The session itself is unchanged — its `status` keeps its previous value and no webhook is sent. The new entry also appears at the top of `GET …/reviews/` (with `actor_display: \"Unknown\"`).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "user": { + "type": "string", + "description": "Display name of the entry's author. Always `\"Unknown\"` for entries created through this endpoint, because it records no actor attribution.", + "example": "Unknown" + }, + "new_status": { + "type": "string", + "nullable": true, + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "description": "The status label recorded on the entry, or `null` if none was sent. The session's actual status is not affected.", + "example": "In Review" + }, + "comment": { + "type": "string", + "nullable": true, + "description": "The note recorded on the entry, or `null` if none was sent.", + "example": "Flagging for manual document re-check." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the entry was written, ISO 8601 UTC.", + "example": "2026-06-12T00:18:44.466537Z" + } + } + }, + "examples": { + "NoteWithStatusLabel": { + "summary": "Note with a status label", + "value": { + "user": "Unknown", + "new_status": "In Review", + "comment": "Flagging for manual document re-check.", + "created_at": "2026-06-12T00:18:44.466537Z" + } + }, + "CommentOnly": { + "summary": "Plain note", + "value": { + "user": "Unknown", + "new_status": null, + "comment": "Note without status.", + "created_at": "2026-06-12T00:18:44.504157Z" + } + } + } + } + } + }, + "400": { + "description": "Validation error, returned as a per-field map of messages. The usual cause is a `new_status` value that is not one of the ten allowed statuses — the check is case-sensitive.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "new_status": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "\"approved\" is not a valid choice." + ] + } + } + }, + "examples": { + "InvalidStatusChoice": { + "summary": "Lowercase / unknown status value", + "value": { + "new_status": [ + "\"approved\" is not a valid choice." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing, malformed, expired, or revoked `x-api-key`. Authentication is checked before the session lookup, so an invalid key returns `403` even when the session UUID does not exist.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "You do not have permission to perform this action." + } + } + }, + "examples": { + "InvalidKey": { + "summary": "Missing or invalid x-api-key header", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No User Verification (KYC) session with this UUID is visible to your application: the UUID does not exist, the session was deleted, or it belongs to a different application. Business Verification (KYB) session IDs also return `404` here — the reviews feed is only addressable for KYC sessions.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "example": "Session not found." + } + } + }, + "examples": { + "NotFound": { + "summary": "Unknown, deleted, or cross-application session UUID", + "value": { + "detail": "Session not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Inspect `Retry-After` and `X-RateLimit-*` response headers. See [Rate limiting](/integration/rate-limiting)." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "# Records an audit-trail note only -- the session's status does NOT change.\ncurl -s -X POST https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"new_status\": \"In Review\",\n \"comment\": \"Flagging for manual document re-check.\"\n }'" + }, + { + "lang": "Python", + "label": "Python (requests)", + "source": "import os, requests\n\n# Records an audit-trail note only -- the session's status does NOT change.\n# To approve/decline a session use PATCH /v3/session/{session_id}/update-status/.\nsession_id = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\"\nresp = requests.post(\n f\"https://verification.didit.me/v3/sessions/{session_id}/reviews/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"new_status\": \"In Review\",\n \"comment\": \"Flagging for manual document re-check.\",\n },\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()) # {'user': 'Unknown', 'new_status': 'In Review', ...}" + }, + { + "lang": "JavaScript", + "label": "Node.js (fetch)", + "source": "// Records an audit-trail note only -- the session's status does NOT change.\n// To approve/decline a session use PATCH /v3/session/{session_id}/update-status/.\nconst sessionId = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\";\nconst res = await fetch(\n `https://verification.didit.me/v3/sessions/${sessionId}/reviews/`,\n {\n method: \"POST\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n new_status: \"In Review\",\n comment: \"Flagging for manual document re-check.\",\n }),\n },\n);\nif (!res.ok) throw new Error(`Didit ${res.status}: ${await res.text()}`);\nconsole.log(await res.json()); // { user: 'Unknown', new_status: 'In Review', ... }" + } + ] + } + }, + "/v3/billing/balance/": { + "get": { + "summary": "Get credit balance", + "description": "Get current pre-paid credit balance in USD (organization-wide) plus auto-refill config. Amounts are decimal strings — parse with a decimal library.", + "operationId": "get_billing_balance", + "x-didit-sdk": { + "resource": "billing", + "method": "retrieve_balance", + "auth": "api_key", + "server": true + }, + "tags": [ + "Billing" + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/billing/balance/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nfrom decimal import Decimal\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/billing/balance/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\ndata = resp.json()\nbalance = Decimal(data['balance'])\nif balance < Decimal('50'):\n print(f'Low balance: ${balance} - top up before launching new sessions.')\nelse:\n print(f'Balance OK: ${balance}')" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/billing/balance/', {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Billing balance failed: ${resp.status}`);\nconst { balance, auto_refill_enabled, auto_refill_threshold } = await resp.json();\nconsole.log({ balance, auto_refill_enabled, auto_refill_threshold });" + } + ], + "responses": { + "200": { + "description": "Current credit balance plus the organization's auto-refill configuration.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "balance", + "auto_refill_enabled" + ], + "properties": { + "balance": { + "type": "string", + "description": "Current pre-paid credit balance in USD, as a fixed-point decimal string. Decreases as verifications are billed and increases when a top-up settles." + }, + "auto_refill_enabled": { + "type": "boolean", + "description": "When `true`, Didit will automatically charge the saved payment method to add credits once the balance crosses `auto_refill_threshold`. Configured from Console → Billing." + }, + "auto_refill_amount": { + "type": "string", + "nullable": true, + "description": "USD amount that will be charged on each auto-refill. `null` when auto-refill is disabled or unset." + }, + "auto_refill_threshold": { + "type": "string", + "nullable": true, + "description": "Trigger threshold in USD. Auto-refill fires when `balance` falls below this value. `null` when auto-refill is disabled or unset." + } + } + }, + "examples": { + "Healthy balance with auto-refill": { + "value": { + "balance": "1234.5600", + "auto_refill_enabled": true, + "auto_refill_amount": "500.00", + "auto_refill_threshold": "100.00" + } + }, + "Low balance, manual top-up only": { + "value": { + "balance": "12.0000", + "auto_refill_enabled": false, + "auto_refill_amount": null, + "auto_refill_threshold": null + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot read billing data for this organization. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/billing/top-up/": { + "post": { + "summary": "Top up credits", + "description": "Add credits via a hosted checkout session (**$50 minimum**); not available on annual plans. Redirect the customer to the returned `checkout_session_url`.", + "operationId": "billing_top_up", + "x-didit-sdk": { + "resource": "billing", + "method": "top_up", + "auth": "api_key", + "server": true + }, + "tags": [ + "Billing" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "amount_in_dollars" + ], + "properties": { + "amount_in_dollars": { + "type": "number", + "minimum": 50, + "description": "Amount to charge, in USD. **Must be at least 50.** Tax is added automatically at checkout based on the customer's billing address." + }, + "success_url": { + "type": "string", + "format": "uri", + "description": "URL the hosted checkout redirects the customer to after a successful payment. Currently defaults to `https://console.didit.me`. Pass a working URL explicitly; the default host is unavailable. Append the literal placeholder `{CHECKOUT_SESSION_ID}` if you want the hosted checkout to substitute the session ID." + }, + "cancel_url": { + "type": "string", + "format": "uri", + "description": "URL the hosted checkout redirects the customer to when they abort or close the Checkout window. Currently defaults to `https://console.didit.me`. Pass a working URL explicitly; the default host is unavailable." + } + } + }, + "examples": { + "Minimum top-up": { + "value": { + "amount_in_dollars": 50 + } + }, + "Custom amount with redirects": { + "value": { + "amount_in_dollars": 500, + "success_url": "https://yourapp.com/billing/top-up/success", + "cancel_url": "https://yourapp.com/billing/top-up/cancel" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/billing/top-up/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"amount_in_dollars\": 500,\n \"success_url\": \"https://yourapp.com/billing/success\",\n \"cancel_url\": \"https://yourapp.com/billing/cancel\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/billing/top-up/',\n headers={\n 'x-api-key': os.environ['DIDIT_API_KEY'],\n 'Content-Type': 'application/json',\n },\n json={\n 'amount_in_dollars': 500,\n 'success_url': 'https://yourapp.com/billing/success',\n 'cancel_url': 'https://yourapp.com/billing/cancel',\n },\n timeout=10,\n)\nresp.raise_for_status()\nsession = resp.json()\nprint('Redirect customer to:', session['checkout_session_url'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/billing/top-up/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n amount_in_dollars: 500,\n success_url: 'https://yourapp.com/billing/success',\n cancel_url: 'https://yourapp.com/billing/cancel',\n }),\n});\nif (!resp.ok) throw new Error(`Top-up failed: ${resp.status}`);\nconst { checkout_session_url } = await resp.json();\nwindow.location.assign(checkout_session_url);" + } + ], + "responses": { + "200": { + "description": "Hosted checkout session created. Redirect the user to `checkout_session_url` to complete payment.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "checkout_session_id", + "checkout_session_url" + ], + "properties": { + "checkout_session_id": { + "type": "string", + "description": "Hosted checkout session identifier (e.g. `cs_test_a1B2c3...`). Useful for server-side reconciliation." + }, + "checkout_session_url": { + "type": "string", + "format": "uri", + "description": "Single-use hosted payment page URL. Redirect the customer here to complete payment." + } + } + }, + "examples": { + "Created": { + "value": { + "checkout_session_id": "cs_test_a1B2c3D4e5F6g7H8i9J0", + "checkout_session_url": "https://checkout.example.com/c/pay/cs_test_a1B2c3D4e5F6g7H8i9J0" + } + } + } + } + } + }, + "400": { + "description": "Invalid payload — the amount is missing, not a number, below the $50 minimum, or the organization is on an annual plan. The error body is a JSON array.", + "content": { + "application/json": { + "examples": { + "Missing amount": { + "value": [ + "amount_in_dollars is required." + ] + }, + "Not a number": { + "value": [ + "amount_in_dollars must be a number." + ] + }, + "Below minimum": { + "value": [ + "Amount must be at least $50." + ] + }, + "Annual plan": { + "value": [ + "You have an annual plan. Contact your account manager to top up." + ] + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot create top-up sessions for this organization. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/webhook/destinations/": { + "get": { + "summary": "List webhook destinations", + "description": "List all webhook destinations (unpaginated array, oldest first). Each item carries the destination's configuration, its delivery metrics for the last 30 days (`metrics_window_days`) with one `daily_points` entry per day, and its health (`health_status`, `failing_streak`, `failing_since`, `last_success_at`). The list has no `summary` object and never includes the signing secret: each item shows only `secret_last4`. Fetch a single destination (`GET /v3/webhook/destinations/{destination_uuid}/`) to read `secret_shared_key`.", + "operationId": "list_webhook_destinations", + "x-didit-sdk": { + "resource": "webhooks.destinations", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Webhook" + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/webhook/destinations/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/webhook/destinations/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nfor dest in resp.json():\n print(f\"{dest['label']:30s} {dest['url']:50s} {dest['health_status']}\")" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/webhook/destinations/', {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nconst destinations = await resp.json();\nfor (const d of destinations) {\n console.log(d.label, d.url, d.health_status, `${d.error_rate_percentage}% errors`);\n}" + } + ], + "responses": { + "200": { + "description": "List of webhook destinations (unpaginated, oldest first).", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Stable destination identifier; use as `{destination_uuid}` for update/delete." + }, + "label": { + "type": "string", + "description": "Human-readable name shown in the Console." + }, + "url": { + "type": "string", + "format": "uri", + "description": "Destination URL that receives `POST` requests with the webhook payload." + }, + "enabled": { + "type": "boolean", + "description": "When `false`, Didit silently skips delivery to this destination." + }, + "webhook_version": { + "type": "string", + "enum": [ + "v1", + "v2", + "v3" + ], + "description": "Payload schema version sent to this destination. `v3` is the current/recommended format." + }, + "subscribed_events": { + "$ref": "#/components/schemas/WebhookSubscribedEvents" + }, + "subscribed_events_count": { + "type": "integer", + "description": "Cached length of `subscribed_events`, useful for UI rendering." + }, + "deliveries_count": { + "type": "integer", + "description": "Delivery attempts (retries included) made to this destination in the last 30 days." + }, + "failed_deliveries_count": { + "type": "integer", + "description": "Delivery attempts in the last 30 days that the destination answered with HTTP 400 or above, or did not answer (timeout, refused connection or blocked address)." + }, + "average_response_time_ms": { + "type": "integer", + "nullable": true, + "description": "Average response time in milliseconds over the last 30 days, rounded to an integer. `null` when there were no deliveries in that window." + }, + "error_rate_percentage": { + "type": "integer", + "description": "`round(failed_deliveries_count / deliveries_count * 100)` over the last 30 days; `0` when there are no deliveries." + }, + "last_delivery_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the most recent delivery attempt (success or failure). `null` if never delivered." + }, + "metrics_window_days": { + "type": "integer", + "description": "Length in days of the window that `deliveries_count`, `failed_deliveries_count`, `average_response_time_ms`, `error_rate_percentage` and `daily_points` cover. Always `30`." + }, + "health_status": { + "type": "string", + "enum": [ + "healthy", + "degraded", + "failing" + ], + "description": "Delivery health. `failing`: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see `failing_streak`). `degraded`: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. `healthy`: neither. Independent of `enabled`. A failed attempt is one answered with HTTP 400 or above, or not answered at all." + }, + "failing_streak": { + "type": "integer", + "description": "How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. `0` when no event has failed since the last success." + }, + "failing_since": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the first failed attempt of the current `failing_streak` was made. `null` when `failing_streak` is `0`." + }, + "last_success_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. `null` if it has not in that window." + }, + "environment": { + "type": "string", + "enum": [ + "live", + "sandbox" + ], + "description": "Environment of the application that owns the destination." + }, + "daily_points": { + "type": "array", + "description": "One entry per UTC day of the last 30 days, oldest first, including days with no deliveries. Counts every attempt, retries included.", + "items": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date", + "description": "The UTC day." + }, + "total_deliveries": { + "type": "integer", + "description": "Delivery attempts made that day." + }, + "failed_deliveries": { + "type": "integer", + "description": "Attempts made that day that were answered with HTTP 400 or above, or not answered at all." + } + } + } + }, + "secret_last4": { + "type": "string", + "description": "Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature." + }, + "created_by_email": { + "type": "string", + "nullable": true, + "description": "Email of the Business Console member who created the destination. `null` for a destination created with an API key." + }, + "created_by_name": { + "type": "string", + "nullable": true, + "description": "Name of the Business Console member who created the destination. `null` for a destination created with an API key." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + } + }, + "examples": { + "Two destinations": { + "value": [ + { + "uuid": "11111111-2222-3333-4444-555555555555", + "label": "Production session webhooks", + "url": "https://yourapp.com/webhooks/didit", + "enabled": true, + "webhook_version": "v3", + "subscribed_events": [ + "status.updated", + "data.updated" + ], + "subscribed_events_count": 2, + "deliveries_count": 12453, + "failed_deliveries_count": 14, + "average_response_time_ms": 187, + "error_rate_percentage": 0, + "last_delivery_at": "2026-05-17T09:21:18Z", + "metrics_window_days": 30, + "health_status": "healthy", + "failing_streak": 0, + "failing_since": null, + "last_success_at": "2026-05-17T09:21:18Z", + "environment": "live", + "daily_points": [ + { + "date": "2026-04-18", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-19", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-20", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-21", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-22", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-23", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-24", + "total_deliveries": 415, + "failed_deliveries": 9 + }, + { + "date": "2026-04-25", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-26", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-27", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-28", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-29", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-04-30", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-01", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-02", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-03", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-04", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-05", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-06", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-07", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-08", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-09", + "total_deliveries": 415, + "failed_deliveries": 5 + }, + { + "date": "2026-05-10", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-11", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-12", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-13", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-14", + "total_deliveries": 415, + "failed_deliveries": 0 + }, + { + "date": "2026-05-15", + "total_deliveries": 416, + "failed_deliveries": 0 + }, + { + "date": "2026-05-16", + "total_deliveries": 416, + "failed_deliveries": 0 + }, + { + "date": "2026-05-17", + "total_deliveries": 416, + "failed_deliveries": 0 + } + ], + "secret_last4": "0xnQ", + "created_by_email": null, + "created_by_name": null, + "created_at": "2026-01-04T08:00:00Z", + "updated_at": "2026-04-22T14:09:11Z" + }, + { + "uuid": "22222222-3333-4444-5555-666666666666", + "label": "Staging mirror", + "url": "https://staging.yourapp.com/webhooks/didit", + "enabled": false, + "webhook_version": "v3", + "subscribed_events": [ + "status.updated" + ], + "subscribed_events_count": 1, + "deliveries_count": 0, + "failed_deliveries_count": 0, + "average_response_time_ms": null, + "error_rate_percentage": 0, + "last_delivery_at": null, + "metrics_window_days": 30, + "health_status": "healthy", + "failing_streak": 0, + "failing_since": null, + "last_success_at": null, + "environment": "live", + "daily_points": [ + { + "date": "2026-04-18", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-19", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-20", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-21", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-22", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-23", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-24", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-25", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-26", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-27", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-28", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-29", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-04-30", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-01", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-02", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-03", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-04", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-05", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-06", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-07", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-08", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-09", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-10", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-11", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-12", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-13", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-14", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-15", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-16", + "total_deliveries": 0, + "failed_deliveries": 0 + }, + { + "date": "2026-05-17", + "total_deliveries": 0, + "failed_deliveries": 0 + } + ], + "secret_last4": "k9Qe", + "created_by_email": "reviewer@example.com", + "created_by_name": "Example Reviewer", + "created_at": "2026-03-12T10:15:02Z", + "updated_at": "2026-03-12T10:15:02Z" + } + ] + } + } + } + } + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "post": { + "summary": "Create webhook destination", + "description": "Create a webhook destination. The response includes the auto-generated `secret_shared_key` HMAC signing secret (also readable later via `GET /v3/webhook/destinations/{destination_uuid}/`). Always send `subscribed_events` with at least one event (no wildcards): an explicit empty list is rejected with 400, and omitting the field creates a destination subscribed to nothing, which never receives a webhook. The `(application, url)` pair must be unique among the application's destinations.", + "operationId": "create_webhook_destination", + "x-didit-sdk": { + "resource": "webhooks.destinations", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Webhook" + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/webhook/destinations/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"label\": \"Production session webhooks\",\n \"url\": \"https://yourapp.com/webhooks/didit\",\n \"webhook_version\": \"v3\",\n \"subscribed_events\": [\"status.updated\", \"data.updated\"]\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/webhook/destinations/',\n headers={\n 'x-api-key': os.environ['DIDIT_API_KEY'],\n 'Content-Type': 'application/json',\n },\n json={\n 'label': 'Production session webhooks',\n 'url': 'https://yourapp.com/webhooks/didit',\n 'webhook_version': 'v3',\n 'subscribed_events': ['status.updated', 'data.updated'],\n },\n timeout=10,\n)\nresp.raise_for_status()\ndestination = resp.json()\n# Store secret_shared_key in your secret manager.\nprint('Destination UUID:', destination['uuid'])\nprint('Signing secret:', destination['secret_shared_key'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/webhook/destinations/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n label: 'Production session webhooks',\n url: 'https://yourapp.com/webhooks/didit',\n webhook_version: 'v3',\n subscribed_events: ['status.updated', 'data.updated'],\n }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst destination = await resp.json();\nconsole.log('Store this secret securely:', destination.secret_shared_key);" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "label", + "url" + ], + "properties": { + "label": { + "type": "string", + "maxLength": 255, + "description": "Human-readable name displayed in Console → Webhooks. Helps distinguish destinations when you have several (e.g. \"Prod\", \"Staging mirror\")." + }, + "url": { + "type": "string", + "format": "uri", + "description": "HTTPS URL that will receive `POST` requests. Must be unique among the application's destinations. Use a stable, hardened endpoint: Didit retries failed deliveries on a backoff schedule." + }, + "enabled": { + "type": "boolean", + "default": true, + "description": "When `false`, the destination is saved but Didit will not push any webhooks to it. Useful for staging configs you want to keep parked." + }, + "webhook_version": { + "type": "string", + "enum": [ + "v1", + "v2", + "v3" + ], + "description": "Payload schema version Didit will send. `v3` is the current/recommended format; older versions are kept for backward compatibility with legacy integrations." + }, + "subscribed_events": { + "allOf": [ + { + "$ref": "#/components/schemas/WebhookSubscribedEvents" + } + ], + "description": "Events to deliver. Effectively required: an explicit `[]` is rejected with 400, and omitting the field creates a destination with no subscriptions that receives nothing." + } + } + }, + "examples": { + "session_updates": { + "summary": "Session status and data updates", + "value": { + "label": "Production session webhooks", + "url": "https://yourapp.com/webhooks/didit", + "webhook_version": "v3", + "subscribed_events": [ + "status.updated", + "data.updated" + ] + } + }, + "transaction_updates": { + "summary": "Transaction monitoring updates", + "value": { + "label": "Transaction monitoring webhooks", + "url": "https://yourapp.com/webhooks/didit-transactions", + "webhook_version": "v3", + "subscribed_events": [ + "transaction.created", + "transaction.status.updated" + ] + } + }, + "entity_updates": { + "summary": "User and Business entity updates", + "value": { + "label": "Entity lifecycle webhooks", + "url": "https://yourapp.com/webhooks/didit-entities", + "webhook_version": "v3", + "subscribed_events": [ + "user.status.updated", + "user.data.updated", + "business.status.updated", + "business.data.updated", + "activity.created" + ] + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Destination created. `secret_shared_key` is the HMAC signing secret — store it now.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Stable destination identifier. Use as `{destination_uuid}` in subsequent calls." + }, + "label": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "enabled": { + "type": "boolean" + }, + "webhook_version": { + "type": "string", + "enum": [ + "v1", + "v2", + "v3" + ] + }, + "secret_shared_key": { + "type": "string", + "description": "Auto-generated HMAC signing secret bound to this destination (43-character URL-safe token). Use it to verify the signature headers on incoming webhooks. Always returned in full to API-key callers; Console users need the `read:webhooks` permission, otherwise they see an empty string." + }, + "secret_last4": { + "type": "string", + "description": "Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature." + }, + "previous_secret_expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries `X-Signature-Previous`, `X-Signature-V2-Previous` and `X-Signature-Simple-Previous`, made with the replaced secret (see [Signature verification](/integration/webhooks#signature-verification)). `null` when no rotation happened in the last 24 hours." + }, + "subscribed_events": { + "$ref": "#/components/schemas/WebhookSubscribedEvents" + }, + "created_by_email": { + "type": "string", + "nullable": true, + "description": "Email of the Business Console member who created the destination. `null` for a destination created with an API key." + }, + "created_by_name": { + "type": "string", + "nullable": true, + "description": "Name of the Business Console member who created the destination. `null` for a destination created with an API key." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "health_status": { + "type": "string", + "enum": [ + "healthy", + "degraded", + "failing" + ], + "description": "Delivery health. `failing`: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see `failing_streak`). `degraded`: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. `healthy`: neither. Independent of `enabled`. A failed attempt is one answered with HTTP 400 or above, or not answered at all." + }, + "failing_streak": { + "type": "integer", + "description": "How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. `0` when no event has failed since the last success." + }, + "failing_since": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the first failed attempt of the current `failing_streak` was made. `null` when `failing_streak` is `0`." + }, + "last_success_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. `null` if it has not in that window." + }, + "environment": { + "type": "string", + "enum": [ + "live", + "sandbox" + ], + "description": "Environment of the application that owns the destination." + }, + "summary": { + "type": "object", + "description": "Delivery metrics for the last `metrics_window_days` (30) days. `last_delivery_at` is the most recent attempt, whenever it was made.", + "properties": { + "total_deliveries": { + "type": "integer" + }, + "failed_deliveries": { + "type": "integer" + }, + "error_rate_percentage": { + "type": "integer" + }, + "min_response_time_ms": { + "type": "integer", + "nullable": true + }, + "avg_response_time_ms": { + "type": "integer", + "nullable": true + }, + "max_response_time_ms": { + "type": "integer", + "nullable": true + }, + "last_delivery_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "metrics_window_days": { + "type": "integer", + "description": "Length in days of the window the other summary metrics cover. Always `30`." + } + } + } + } + }, + "examples": { + "Created": { + "value": { + "uuid": "11111111-2222-3333-4444-555555555555", + "label": "Production session webhooks", + "url": "https://yourapp.com/webhooks/didit", + "enabled": true, + "webhook_version": "v3", + "secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ", + "secret_last4": "0xnQ", + "previous_secret_expires_at": null, + "subscribed_events": [ + "status.updated", + "data.updated" + ], + "created_by_email": null, + "created_by_name": null, + "created_at": "2026-05-17T10:00:00Z", + "updated_at": "2026-05-17T10:00:00Z", + "health_status": "healthy", + "failing_streak": 0, + "failing_since": null, + "last_success_at": null, + "environment": "live", + "summary": { + "total_deliveries": 0, + "failed_deliveries": 0, + "error_rate_percentage": 0, + "min_response_time_ms": null, + "avg_response_time_ms": null, + "max_response_time_ms": null, + "last_delivery_at": null, + "metrics_window_days": 30 + } + } + } + } + } + } + }, + "400": { + "description": "Validation failed — usually a duplicate URL, missing/invalid `subscribed_events`, or unknown event name.", + "content": { + "application/json": { + "examples": { + "Duplicate URL": { + "value": { + "url": [ + "This webhook destination already exists for the application." + ] + } + }, + "Empty events list": { + "value": { + "subscribed_events": [ + "At least one subscribed event is required." + ] + } + }, + "Unknown event": { + "value": { + "subscribed_events": [ + "Unsupported webhook events: foo.bar" + ] + } + } + } + } + } + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/webhook/destinations/{destination_uuid}/": { + "get": { + "summary": "Get webhook destination", + "description": "Retrieve one webhook destination with its `secret_shared_key` signing secret, its health (`health_status`, `failing_streak`, `failing_since`, `last_success_at`) and a delivery `summary` for the last 30 days. API-key callers always receive the secret in full; Console users without `read:webhooks` see an empty string.", + "operationId": "get_webhook_destination", + "x-didit-sdk": { + "resource": "webhooks.destinations", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Webhook" + ], + "parameters": [ + { + "name": "destination_uuid", + "in": "path", + "required": true, + "description": "UUID of the destination (the `uuid` returned from create / list).", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "11111111-2222-3333-4444-555555555555" + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nuuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n f'https://verification.didit.me/v3/webhook/destinations/{uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json())" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const uuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${uuid}/`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Lookup failed: ${resp.status}`);\nconst destination = await resp.json();\nconsole.log(destination);" + } + ], + "responses": { + "200": { + "description": "Webhook destination details.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "label": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "enabled": { + "type": "boolean" + }, + "webhook_version": { + "type": "string", + "enum": [ + "v1", + "v2", + "v3" + ] + }, + "secret_shared_key": { + "type": "string", + "description": "HMAC signing secret (43-character URL-safe token). Always returned in full to API-key callers; Console users need `read:webhooks`, otherwise an empty string." + }, + "secret_last4": { + "type": "string", + "description": "Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature." + }, + "previous_secret_expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries `X-Signature-Previous`, `X-Signature-V2-Previous` and `X-Signature-Simple-Previous`, made with the replaced secret (see [Signature verification](/integration/webhooks#signature-verification)). `null` when no rotation happened in the last 24 hours." + }, + "subscribed_events": { + "$ref": "#/components/schemas/WebhookSubscribedEvents" + }, + "created_by_email": { + "type": "string", + "nullable": true, + "description": "Email of the Business Console member who created the destination. `null` for a destination created with an API key." + }, + "created_by_name": { + "type": "string", + "nullable": true, + "description": "Name of the Business Console member who created the destination. `null` for a destination created with an API key." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "health_status": { + "type": "string", + "enum": [ + "healthy", + "degraded", + "failing" + ], + "description": "Delivery health. `failing`: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see `failing_streak`). `degraded`: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. `healthy`: neither. Independent of `enabled`. A failed attempt is one answered with HTTP 400 or above, or not answered at all." + }, + "failing_streak": { + "type": "integer", + "description": "How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. `0` when no event has failed since the last success." + }, + "failing_since": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the first failed attempt of the current `failing_streak` was made. `null` when `failing_streak` is `0`." + }, + "last_success_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. `null` if it has not in that window." + }, + "environment": { + "type": "string", + "enum": [ + "live", + "sandbox" + ], + "description": "Environment of the application that owns the destination." + }, + "summary": { + "type": "object", + "description": "Delivery metrics for the last `metrics_window_days` (30) days. `last_delivery_at` is the most recent attempt, whenever it was made.", + "properties": { + "total_deliveries": { + "type": "integer" + }, + "failed_deliveries": { + "type": "integer" + }, + "error_rate_percentage": { + "type": "integer" + }, + "min_response_time_ms": { + "type": "integer", + "nullable": true + }, + "avg_response_time_ms": { + "type": "integer", + "nullable": true + }, + "max_response_time_ms": { + "type": "integer", + "nullable": true + }, + "last_delivery_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "metrics_window_days": { + "type": "integer", + "description": "Length in days of the window the other summary metrics cover. Always `30`." + } + } + } + } + }, + "examples": { + "Healthy destination": { + "value": { + "uuid": "11111111-2222-3333-4444-555555555555", + "label": "Production session webhooks", + "url": "https://yourapp.com/webhooks/didit", + "enabled": true, + "webhook_version": "v3", + "secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ", + "secret_last4": "0xnQ", + "previous_secret_expires_at": null, + "subscribed_events": [ + "status.updated", + "data.updated" + ], + "created_by_email": null, + "created_by_name": null, + "created_at": "2026-01-04T08:00:00Z", + "updated_at": "2026-04-22T14:09:11Z", + "health_status": "healthy", + "failing_streak": 0, + "failing_since": null, + "last_success_at": "2026-05-17T09:21:18Z", + "environment": "live", + "summary": { + "total_deliveries": 12453, + "failed_deliveries": 14, + "error_rate_percentage": 0, + "min_response_time_ms": 41, + "avg_response_time_ms": 187, + "max_response_time_ms": 2103, + "last_delivery_at": "2026-05-17T09:21:18Z", + "metrics_window_days": 30 + } + } + } + } + } + } + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No destination with this UUID exists, or it belongs to a different application, or it has been deleted.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Webhook destination not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "patch": { + "summary": "Update webhook destination", + "description": "Update a webhook destination. **`subscribed_events` is REPLACED wholesale, not merged**: fetch first, append, resend. Secret is not rotated. A `uuid` in the body is ignored: the path names the destination.", + "operationId": "update_webhook_destination", + "x-didit-sdk": { + "resource": "webhooks.destinations", + "method": "update", + "auth": "api_key", + "server": true + }, + "tags": [ + "Webhook" + ], + "parameters": [ + { + "name": "destination_uuid", + "in": "path", + "required": true, + "description": "UUID of the destination to update.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "11111111-2222-3333-4444-555555555555" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "New human-readable name." + }, + "url": { + "type": "string", + "format": "uri", + "description": "New target URL. Must be unique within the application." + }, + "enabled": { + "type": "boolean", + "description": "Set to `false` to pause deliveries to this destination without deleting it." + }, + "webhook_version": { + "type": "string", + "enum": [ + "v1", + "v2", + "v3" + ], + "description": "New payload schema version for future deliveries." + }, + "subscribed_events": { + "$ref": "#/components/schemas/WebhookSubscribedEvents" + } + } + }, + "examples": { + "Pause destination": { + "value": { + "enabled": false + } + }, + "Add transaction events": { + "value": { + "subscribed_events": [ + "status.updated", + "data.updated", + "transaction.created", + "transaction.status.updated" + ] + } + }, + "Move target URL": { + "value": { + "label": "Production session webhooks v2", + "url": "https://api.yourapp.com/webhooks/didit/v2" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"subscribed_events\": [\n \"status.updated\",\n \"data.updated\",\n \"transaction.created\"\n ]\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nuuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.patch(\n f'https://verification.didit.me/v3/webhook/destinations/{uuid}/',\n headers={\n 'x-api-key': os.environ['DIDIT_API_KEY'],\n 'Content-Type': 'application/json',\n },\n json={'enabled': False},\n timeout=10,\n)\nresp.raise_for_status()\nprint('Paused destination:', resp.json()['label'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const uuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${uuid}/`, {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ enabled: false }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconsole.log(await resp.json());" + } + ], + "responses": { + "200": { + "description": "Updated webhook destination. Body has the same shape as `GET /v3/webhook/destinations/{destination_uuid}/`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "label": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "enabled": { + "type": "boolean" + }, + "webhook_version": { + "type": "string", + "enum": [ + "v1", + "v2", + "v3" + ] + }, + "secret_shared_key": { + "type": "string" + }, + "secret_last4": { + "type": "string", + "description": "Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature." + }, + "previous_secret_expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries `X-Signature-Previous`, `X-Signature-V2-Previous` and `X-Signature-Simple-Previous`, made with the replaced secret (see [Signature verification](/integration/webhooks#signature-verification)). `null` when no rotation happened in the last 24 hours." + }, + "subscribed_events": { + "$ref": "#/components/schemas/WebhookSubscribedEvents" + }, + "created_by_email": { + "type": "string", + "nullable": true, + "description": "Email of the Business Console member who created the destination. `null` for a destination created with an API key." + }, + "created_by_name": { + "type": "string", + "nullable": true, + "description": "Name of the Business Console member who created the destination. `null` for a destination created with an API key." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "health_status": { + "type": "string", + "enum": [ + "healthy", + "degraded", + "failing" + ], + "description": "Delivery health. `failing`: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see `failing_streak`). `degraded`: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. `healthy`: neither. Independent of `enabled`. A failed attempt is one answered with HTTP 400 or above, or not answered at all." + }, + "failing_streak": { + "type": "integer", + "description": "How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. `0` when no event has failed since the last success." + }, + "failing_since": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the first failed attempt of the current `failing_streak` was made. `null` when `failing_streak` is `0`." + }, + "last_success_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. `null` if it has not in that window." + }, + "environment": { + "type": "string", + "enum": [ + "live", + "sandbox" + ], + "description": "Environment of the application that owns the destination." + }, + "summary": { + "type": "object", + "properties": { + "total_deliveries": { + "type": "integer" + }, + "failed_deliveries": { + "type": "integer" + }, + "error_rate_percentage": { + "type": "integer" + }, + "min_response_time_ms": { + "type": "integer", + "nullable": true + }, + "avg_response_time_ms": { + "type": "integer", + "nullable": true + }, + "max_response_time_ms": { + "type": "integer", + "nullable": true + }, + "last_delivery_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "metrics_window_days": { + "type": "integer", + "description": "Length in days of the window the other summary metrics cover. Always `30`." + } + }, + "description": "Delivery metrics for the last `metrics_window_days` (30) days. `last_delivery_at` is the most recent attempt, whenever it was made." + } + } + }, + "examples": { + "Paused": { + "value": { + "uuid": "11111111-2222-3333-4444-555555555555", + "label": "Production session webhooks", + "url": "https://yourapp.com/webhooks/didit", + "enabled": false, + "webhook_version": "v3", + "secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ", + "secret_last4": "0xnQ", + "previous_secret_expires_at": null, + "subscribed_events": [ + "status.updated", + "data.updated" + ], + "created_by_email": null, + "created_by_name": null, + "created_at": "2026-01-04T08:00:00Z", + "updated_at": "2026-05-17T11:02:55Z", + "health_status": "healthy", + "failing_streak": 0, + "failing_since": null, + "last_success_at": "2026-05-17T09:21:18Z", + "environment": "live", + "summary": { + "total_deliveries": 12453, + "failed_deliveries": 14, + "error_rate_percentage": 0, + "min_response_time_ms": 41, + "avg_response_time_ms": 187, + "max_response_time_ms": 2103, + "last_delivery_at": "2026-05-17T09:21:18Z", + "metrics_window_days": 30 + } + } + } + } + } + } + }, + "400": { + "description": "Validation failed (duplicate URL, empty/unknown events list, malformed body).", + "content": { + "application/json": { + "examples": { + "Duplicate URL": { + "value": { + "url": [ + "This webhook destination already exists for the application." + ] + } + }, + "Empty events": { + "value": { + "subscribed_events": [ + "At least one subscribed event is required." + ] + } + } + } + } + } + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "Destination with this UUID does not exist (or is deleted, or belongs to another application).", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Webhook destination not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "delete": { + "summary": "Delete webhook destination", + "description": "Permanently delete a webhook destination, its signing secret and its entire delivery log. This cannot be undone. No new webhook is sent to the destination's URL; other destinations subscribed to the same events keep receiving them. Afterwards the destination no longer appears in `GET /v3/webhook/destinations/`, and every route under `/v3/webhook/destinations/{destination_uuid}/`, including its deliveries, a delivery's detail and its resend, answers 404. The application's legacy `webhook_url` is set to the URL of the oldest remaining enabled destination, or cleared when no enabled destination is left. Calling DELETE again returns 404.", + "operationId": "delete_webhook_destination", + "x-didit-sdk": { + "resource": "webhooks.destinations", + "method": "delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Webhook" + ], + "parameters": [ + { + "name": "destination_uuid", + "in": "path", + "required": true, + "description": "UUID of the destination to delete.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "11111111-2222-3333-4444-555555555555" + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nuuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.delete(\n f'https://verification.didit.me/v3/webhook/destinations/{uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nassert resp.status_code == 204" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const uuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${uuid}/`, {\n method: 'DELETE',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (resp.status !== 204) throw new Error(`Delete failed: ${resp.status}`);" + } + ], + "responses": { + "204": { + "description": "Destination, signing secret and delivery log deleted permanently. No response body." + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No destination with this UUID exists for the application (already deleted or never existed).", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Webhook destination not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/wallet-screening/": { + "post": { + "summary": "Screen a wallet address", + "description": "Run on-demand AML screening for a single crypto wallet/address without creating a transaction. Resolves your application's configured blockchain analytics provider, screens the address synchronously, and returns the normalised risk result. LIGHTNING is available only with an Elliptic or Chainalysis BYOK connection added through Marketplace. Nothing is written to the transactions table. One AML monitoring usage is billed per successful screening (sandbox applications are not billed).", + "operationId": "screenWallet", + "x-didit-sdk": { + "resource": "wallet_screening", + "method": "screen", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "wallet_address", + "blockchain" + ], + "properties": { + "wallet_address": { + "type": "string", + "description": "The crypto address or network identifier to screen. Must match the selected network (e.g. a `0x`-prefixed 40-hex-char address for EVM chains). Alias: `address`.", + "example": "0x94f1b9b64e2932f6a2db338f616844400cd58e8a" + }, + "blockchain": { + "type": "string", + "description": "Network identifier. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism), and ARB (Arbitrum One). USDT and USDC are legacy Ethereum aliases, not multi-network searches. Connected-provider coverage varies. LIGHTNING requires a supported connected provider. CELO (Celo), MODE (Mode) and PLUME (Plume) are screened through a connected TRM Labs account; managed screening answers 400 for them. Alias: `currency`.", + "enum": [ + "BTC", + "LIGHTNING", + "ETH", + "LTC", + "XRP", + "BCH", + "DOGE", + "TRX", + "SOL", + "MATIC", + "BNB", + "USDT", + "USDC", + "BASE", + "OP", + "ARB", + "CELO", + "MODE", + "PLUME" + ], + "example": "ETH" + }, + "direction": { + "type": "string", + "description": "Optional. Whether the address is being screened as an inbound (deposit) or outbound (withdrawal) counterparty. Accepts `inbound`, `outbound`, `deposit`, or `withdrawal`. Defaults to a neutral pre-transfer screen when omitted. This is passed to the provider as context only; it does not change the returned `risk_score`, `severity`, or the `source_of_funds`/`destination_of_funds` breakdowns, which are always both returned regardless of `direction`. Changing or omitting `direction` therefore does not require re-screening an address.", + "enum": [ + "inbound", + "outbound", + "deposit", + "withdrawal" + ] + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/wallet-screening/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"wallet_address\": \"0x94f1b9b64e2932f6a2db338f616844400cd58e8a\",\n \"blockchain\": \"ETH\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/wallet-screening/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'wallet_address': '0x94f1b9b64e2932f6a2db338f616844400cd58e8a',\n 'blockchain': 'ETH',\n },\n timeout=20,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result['risk_score'], result['severity'], result['sanctions_hit'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const result = await fetch('https://verification.didit.me/v3/wallet-screening/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n wallet_address: '0x94f1b9b64e2932f6a2db338f616844400cd58e8a',\n blockchain: 'ETH',\n }),\n}).then((r) => r.json());\nconsole.log(result.risk_score, result.severity, result.sanctions_hit);" + } + ], + "responses": { + "200": { + "description": "The normalised wallet screening result.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WalletScreeningResult" + }, + "example": { + "provider": "merklescience", + "screening_type": "WALLET_SCREENING", + "risk_score": 95, + "severity": "CRITICAL", + "status": "SCREENED", + "summary": "Merkle Science risk: Critical (5/5). SANCTIONS HIT. Dominant risk: sanctioned. Driven by: the address is attributed to Sanctions / OFAC (Example Sanctions)", + "wallet_address": "0x94f1b9b64e2932f6a2db338f616844400cd58e8a", + "transaction_hash": null, + "blockchain": "ETH", + "block_timestamp": null, + "total_value_usd": null, + "fee_usd": null, + "sender_rows": [], + "receiver_rows": [], + "transaction_flows": [], + "transaction_flows_status": null, + "transaction_flows_error": null, + "source_of_funds": [ + { + "category": "sanctioned", + "percentage": 100.0, + "amount_usd": 9813695.0, + "entity_name": "Example Sanctions", + "entity_type": "Sanctions", + "entity_subtype": "OFAC", + "hops": null, + "description": "Sanctions / OFAC", + "is_direct": true, + "exposure_direction": "incoming", + "exposure_type": "direct", + "provider_entity_id": "118885", + "provider_entity_type_id": "30", + "provider_entity_subtype_id": "125", + "country": "", + "raw_amount": null, + "raw_currency": null + } + ], + "destination_of_funds": [ + { + "category": "defi", + "percentage": 84.824453, + "amount_usd": 7783414.38, + "entity_name": "Example DeFi 7", + "entity_type": "DeFi", + "entity_subtype": "Bridges", + "hops": null, + "description": "DeFi / Bridges", + "is_direct": false, + "exposure_direction": "outgoing", + "exposure_type": "indirect", + "provider_entity_id": "118459", + "provider_entity_type_id": "33", + "provider_entity_subtype_id": "143", + "country": "", + "raw_amount": null, + "raw_currency": null + }, + { + "category": "defi", + "percentage": 15.175547, + "amount_usd": 1392494.37, + "entity_name": "Example DeFi 2", + "entity_type": "DeFi", + "entity_subtype": "Yield Aggregator", + "hops": null, + "description": "DeFi / Yield Aggregator", + "is_direct": false, + "exposure_direction": "outgoing", + "exposure_type": "indirect", + "provider_entity_id": "90192", + "provider_entity_type_id": "33", + "provider_entity_subtype_id": "142", + "country": "", + "raw_amount": null, + "raw_currency": null + } + ], + "counterparty_connections": [ + { + "entity_name": "Example Sanctions", + "entity_type": "Sanctions", + "entity_subtype": "OFAC", + "risk_level": "critical", + "categories": [ + "sanctioned" + ], + "received_usd": null, + "sent_usd": null, + "received_hops": null, + "sent_hops": null, + "percentage": 0.0, + "is_direct": true, + "country": null, + "provider_entity_id": null + }, + { + "entity_name": "Example Sanctions", + "entity_type": "Sanctions", + "entity_subtype": "OFAC", + "risk_level": "critical", + "categories": [ + "sanctioned" + ], + "received_usd": null, + "sent_usd": null, + "received_hops": null, + "sent_hops": null, + "percentage": 0.0, + "is_direct": true, + "country": null, + "provider_entity_id": null + } + ], + "network_nodes": [], + "network_edges": [], + "sanctions_hit": true, + "pep_counterparty": null, + "dominant_risk_category": "sanctioned", + "risk_factors": [ + { + "source": "attribution", + "category": "sanctioned", + "label": "Sanctions / OFAC", + "entity_name": "Example Sanctions", + "direction": "attribution", + "exposure_type": "attribution", + "amount_usd": null, + "percentage": 0.0, + "is_high_risk": true, + "description": "the address is attributed to Sanctions / OFAC (Example Sanctions)" + }, + { + "source": "exposure", + "category": "sanctioned", + "label": "Sanctions / OFAC", + "entity_name": "Example Sanctions", + "direction": "incoming", + "exposure_type": "direct", + "amount_usd": 9813695.0, + "percentage": 51.679304, + "is_high_risk": true, + "description": "51.6793% direct incoming exposure to Sanctions / OFAC (Example Sanctions)" + }, + { + "source": "exposure", + "category": "defi", + "label": "DeFi / Bridges", + "entity_name": "Example DeFi 7", + "direction": "outgoing", + "exposure_type": "indirect", + "amount_usd": 7783414.38, + "percentage": 40.987766, + "is_high_risk": false, + "description": "40.9878% indirect outgoing exposure to DeFi / Bridges (Example DeFi 7)" + }, + { + "source": "exposure", + "category": "defi", + "label": "DeFi / Yield Aggregator", + "entity_name": "Example DeFi 2", + "direction": "outgoing", + "exposure_type": "indirect", + "amount_usd": 1392494.37, + "percentage": 7.33293, + "is_high_risk": false, + "description": "7.33293% indirect outgoing exposure to DeFi / Yield Aggregator (Example DeFi 2)" + } + ], + "raw_response": { + "...": "provider-specific details" + }, + "report_signature": "" + } + } + } + }, + "400": { + "description": "Validation error — malformed wallet address, missing `wallet_address`, unsupported `blockchain`, or LIGHTNING without an Elliptic or Chainalysis BYOK Marketplace connection." + }, + "401": { + "description": "Missing or invalid `x-api-key`." + }, + "409": { + "description": "No AML provider is configured for the application, or the configured provider does not support wallet screening yet. Configure transaction monitoring first." + }, + "502": { + "description": "The AML provider could not complete the screening. Retry later." + } + } + } + }, + "/v3/wallet-screening/pdf/": { + "post": { + "summary": "Render a wallet-screening result as a PDF", + "description": "Stateless companion to `POST /v3/wallet-screening/`: repost the exact JSON body that endpoint returned — including the `report_signature` it added — and get back a Didit-branded PDF of the same result. No provider is called and nothing is billed or persisted; this only re-renders a result Didit already produced. The signature ties the PDF to an unmodified result: editing `risk_score`, `severity`, `sanctions_hit`, or any other signed field before reposting fails with `400`.", + "operationId": "screenWalletPdf", + "x-didit-sdk": { + "resource": "wallet_screening", + "method": "screen_pdf", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "description": "The unmodified JSON object returned by `POST /v3/wallet-screening/`, including `report_signature`.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/WalletScreeningResult" + } + ], + "required": [ + "report_signature" + ], + "description": "Same shape as the `POST /v3/wallet-screening/` response — pass that response body back verbatim, including `report_signature`." + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "# $RESULT is the saved JSON body from POST /v3/wallet-screening/\ncurl -X POST 'https://verification.didit.me/v3/wallet-screening/pdf/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d \"$RESULT\" \\\n -o wallet_screening_report.pdf" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\n# `result` is the dict returned by POST /v3/wallet-screening/, unmodified\nresponse = requests.post(\n 'https://verification.didit.me/v3/wallet-screening/pdf/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n json=result,\n timeout=30,\n)\nresponse.raise_for_status()\nwith open('wallet_screening_report.pdf', 'wb') as fh:\n fh.write(response.content)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "// `result` is the object returned by POST /v3/wallet-screening/, unmodified\nconst response = await fetch('https://verification.didit.me/v3/wallet-screening/pdf/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(result),\n});\nif (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);\nconst pdfBuffer = Buffer.from(await response.arrayBuffer());" + } + ], + "responses": { + "200": { + "description": "The rendered PDF document, returned directly as binary `application/pdf` — there is no JSON wrapper.", + "headers": { + "Content-Disposition": { + "description": "`attachment; filename=wallet_screening_{address}.pdf`, derived from the `wallet_address` (or `transaction_hash`) in the reposted result.", + "schema": { + "type": "string", + "example": "attachment; filename=wallet_screening_0x28c6c06298d514db0899.pdf" + } + } + }, + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "The body is not a JSON object matching the wallet-screening result shape, or `report_signature` is missing or does not match the reposted fields (edited or truncated before repost).", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "examples": { + "Not an object": { + "value": { + "non_field_errors": [ + "Expected a JSON object matching a wallet-screening result." + ] + } + }, + "Missing or invalid signature": { + "value": { + "report_signature": [ + "This wallet-screening result is missing or fails its report signature - repost the exact JSON returned by the wallet-screening endpoint, unmodified." + ] + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid `x-api-key`." + } + } + } + }, + "/v3/transactions/": { + "get": { + "summary": "List transactions", + "description": "Paginated list of monitored transactions, newest first (`txn_date` desc). `count` is capped at 100; use `total_transactions` for the true row count. This endpoint does not support filtering or search parameters.", + "operationId": "listTransactions", + "x-didit-sdk": { + "resource": "transactions", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "description": "Page size. Defaults to 50 when omitted. There is no enforced maximum, but keep pages small for latency." + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset of the first record to return. Prefer following the `next` URL from the previous page." + }, + { + "name": "travel_rule_status", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Return only transactions whose Travel Rule transfer is in this exact status (for example `ON_HOLD` or `AWAITING_COUNTERPARTY`). `count` follows this filter; `total_transactions` still counts every transaction in the application." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/transactions/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/transactions/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'limit': 50},\n timeout=10,\n)\nresp.raise_for_status()\npage = resp.json()\nfor tx in page['results']:\n print(\n tx['transaction_number'],\n tx['status'],\n tx['severity'],\n f\"{tx['amount']} {tx['currency']}\",\n tx.get('subject_name') or tx.get('subject_vendor_data'),\n )" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const url = new URL('https://verification.didit.me/v3/transactions/');\nurl.searchParams.set('limit', '50');\nconst page = await fetch(url, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n}).then((r) => r.json());\nfor (const tx of page.results) {\n console.log(tx.transaction_number, tx.status, tx.severity, tx.amount, tx.currency);\n}" + } + ], + "responses": { + "200": { + "description": "Paginated list of transaction summaries, newest first.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Capped count of matching transactions (exact up to 100, capped thereafter). Use `total_transactions` for the true count when paginating." + }, + "next": { + "type": "string", + "nullable": true, + "description": "Absolute URL of the next page, or `null` on the last page." + }, + "previous": { + "type": "string", + "nullable": true, + "description": "Absolute URL of the previous page, or `null` on the first page." + }, + "total_transactions": { + "type": "integer", + "description": "Exact total count of (non-deleted) transactions for the application. Present on paginated responses." + }, + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Stable Didit transaction identifier. Use as `{transaction_id}` in detail/notes/related calls." + }, + "transaction_number": { + "type": "integer", + "description": "Application-scoped sequential transaction number shown in Console (e.g. `4123`)." + }, + "txn_id": { + "type": "string", + "description": "Customer-provided identifier from the create call (max 128 chars). Unique per application." + }, + "transaction_type": { + "type": "string", + "description": "Top-level category as stored: `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`, `gamblingLimitChange`, `gamblingBonusChange`, `auditTrailEvent`. Note multi-word values are echoed back in camelCase even when submitted in snake_case." + }, + "action_type": { + "type": "string", + "description": "Sub-type within the category (e.g. `deposit`, `withdrawal`, `transfer`)." }, "status": { "type": "string", "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Expired" + "APPROVED", + "IN_REVIEW", + "DECLINED", + "AWAITING_USER" ], - "description": "Feature-level status of this ID verification step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`." + "description": "Current monitoring verdict." }, - "document_type": { + "direction": { + "type": "string", + "enum": [ + "INBOUND", + "OUTBOUND" + ], + "description": "Direction relative to the subject. Stored uppercase regardless of the casing submitted." + }, + "amount": { + "type": "string", + "description": "Transaction amount as a decimal string with trailing zeros stripped (e.g. `\"1500\"`, `\"0.5\"`, `\"0.123456789012345678\"`). Up to 18 decimal places." + }, + "currency": { + "type": "string", + "description": "Currency code of `amount`. Any ISO 4217 alphabetic code is accepted (e.g. `EUR`, `USD`, `NGN`), as is a crypto asset symbol (e.g. `BTC`). Prefer a currently-circulating code. Not every accepted code has an exchange rate: when none is available for the code (for example the ISO accounting unit `XSU`, or a crypto symbol Didit does not price), automatic conversion fails and `preferred_currency_amount` is returned null unless the request supplied a pre-converted `amount_in_default_currency`." + }, + "preferred_currency_amount": { + "type": "string", + "nullable": true, + "description": "`amount` converted to the application's preferred currency, when available." + }, + "preferred_currency_code": { + "type": "string", + "nullable": true, + "description": "Preferred currency code used for `preferred_currency_amount` - the application's base currency, any currently-circulating ISO 4217 code. A code with no available exchange rate leaves `preferred_currency_amount` null unless the request supplied a pre-converted `amount_in_default_currency`." + }, + "payment_details": { + "type": "string", + "nullable": true, + "description": "Free-text payment reference or memo from the submission." + }, + "score": { + "type": "integer", + "description": "Risk score (typically 0–100). Higher = riskier." + }, + "severity": { + "type": "string", + "nullable": true, + "enum": [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL" + ], + "description": "Categorical risk severity set by rules/providers (`UNKNOWN`, `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`). `null` until something sets it." + }, + "subject_name": { + "type": "string", + "nullable": true, + "description": "Full name of the `APPLICANT` party." + }, + "subject_vendor_data": { + "type": "string", + "nullable": true, + "description": "Your internal user/business identifier for the applicant." + }, + "subject_country": { + "type": "string", + "nullable": true, + "description": "Country code of the applicant, from the submitted `address.country` (typically ISO 3166-1 alpha-3)." + }, + "counterparty_name": { + "type": "string", + "nullable": true, + "description": "Full name of the `COUNTERPARTY` party, if any." + }, + "counterparty_vendor_data": { + "type": "string", + "nullable": true, + "description": "Your internal identifier for the counterparty." + }, + "counterparty_country": { + "type": "string", + "nullable": true, + "description": "Country code of the counterparty, from the submitted `address.country`." + }, + "tags": { + "type": "array", + "description": "Tags attached to the transaction (manually or by rules).", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "color": { + "type": "string" + }, + "description": { + "type": "string", + "nullable": true + }, + "source": { + "type": "string", + "enum": [ + "didit", + "custom" + ], + "description": "Whether the tag is a Didit preset or a custom tag." + } + } + } + }, + "txn_date": { + "type": "string", + "format": "date-time", + "description": "When the transaction occurred (from `transaction_at` at create time)." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When Didit recorded the submission." + }, + "decision_reason_code": { + "type": "string", + "nullable": true, + "description": "Machine-readable reason for the current `status` (e.g. `WALLET_HIGH_RISK`)." + }, + "decision_reason_label": { + "type": "string", + "nullable": true, + "description": "Human-readable label for `decision_reason_code`." + }, + "assigned_to_name": { + "type": "string", + "nullable": true, + "description": "Display name of the Console reviewer assigned to this transaction." + }, + "travel_rule_status": { + "type": "string", + "nullable": true, + "description": "Status of the transaction's Travel Rule transfer (for example `COMPLETED`), or `null` when it has none." + }, + "travel_rule": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/TransactionTravelRule" + } + ], + "description": "The transaction's Travel Rule transfer, the same block as `travel_rule` on `GET /v3/transactions/{transaction_id}/`, or `null` when the transaction has none." + } + } + } + } + } + }, + "examples": { + "Single transaction": { + "value": { + "count": 1, + "next": null, + "previous": null, + "total_transactions": 1, + "results": [ + { + "uuid": "abcdef12-3456-4890-abcd-ef1234567890", + "transaction_number": 4123, + "txn_id": "your-txn-2026-05-17-001", + "transaction_type": "finance", + "action_type": "withdrawal", + "status": "IN_REVIEW", + "direction": "OUTBOUND", + "amount": "1500", + "currency": "EUR", + "preferred_currency_amount": "1620.45", + "preferred_currency_code": "USD", + "payment_details": "Withdrawal to crypto wallet", + "txn_date": "2026-05-17T08:42:00Z", + "created_at": "2026-05-17T08:42:11Z", + "score": 78, + "severity": "HIGH", + "decision_reason_code": "WALLET_HIGH_RISK", + "decision_reason_label": "Destination wallet flagged as high risk", + "tags": [ + { + "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "name": "Manual review", + "color": "#F59E0B", + "description": null, + "source": "custom" + } + ], + "subject_name": "Maria Garcia", + "subject_vendor_data": "user-12345", + "counterparty_name": null, + "counterparty_vendor_data": null, + "counterparty_country": null, + "subject_country": null, + "assigned_to_name": "Alex Reviewer", + "travel_rule_status": null, + "travel_rule": null + } + ] + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot list transactions for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "post": { + "summary": "Create transaction", + "description": "Submit a transaction for synchronous AML/risk monitoring. The transaction is created `APPROVED`, then your rules (and crypto screening, when applicable) run inline and may flip the returned `status` to `IN_REVIEW` or `DECLINED`. **`transaction_id` is your idempotency key** (max 128 chars, unique per app); reusing it returns 400. A `transaction.created` webhook is dispatched on success for live applications. Sandbox applications are not billed and do not emit `transaction.created` or `transaction.status.updated` webhooks. Sandbox Travel Rule exchanges do emit `travel_rule.status.updated` webhooks with `environment: sandbox`. A Travel Rule transfer is stored and returned before any counterparty is contacted: `travel_rule.status` comes back `AWAITING_COUNTERPARTY` (or `NOT_APPLICABLE` below your threshold, `UNCONFIRMED_OWNERSHIP` for a declared self-hosted wallet, `COMPLETED` when the other wallet is your own verified wallet) and `travel_rule.protocol` is null. Didit then chooses the rail and sends the exchange, and every later status change of the transfer, including `COUNTERPARTY_VASP_NOT_FOUND` when no rail reaches the counterparty, arrives as a `travel_rule.status.updated` webhook. Your rules run again when the rail or the status changes.", + "operationId": "createTransaction", + "tags": [ + "Transactions" + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/transactions/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"transaction_id\": \"your-txn-2026-05-17-001\",\n \"transaction_category\": \"finance\",\n \"transaction_details\": {\n \"direction\": \"outbound\",\n \"amount\": 1500,\n \"currency\": \"EUR\",\n \"action_type\": \"withdrawal\"\n },\n \"subject\": {\n \"entity_type\": \"individual\",\n \"vendor_data\": \"user-12345\",\n \"full_name\": \"Maria Garcia\"\n }\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/transactions/',\n headers={\n 'x-api-key': os.environ['DIDIT_API_KEY'],\n 'Content-Type': 'application/json',\n },\n json={\n 'transaction_id': 'your-txn-2026-05-17-001',\n 'transaction_category': 'finance',\n 'transaction_details': {\n 'direction': 'outbound',\n 'amount': 1500,\n 'currency': 'EUR',\n 'action_type': 'withdrawal',\n },\n 'subject': {\n 'entity_type': 'individual',\n 'vendor_data': 'user-12345',\n 'full_name': 'Maria Garcia',\n },\n },\n timeout=15,\n)\nresp.raise_for_status()\ntx = resp.json()\nprint(tx['txn_id'], '->', tx['status'], tx['severity'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/transactions/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n transaction_id: 'your-txn-2026-05-17-001',\n transaction_category: 'finance',\n transaction_details: {\n direction: 'outbound',\n amount: 1500,\n currency: 'EUR',\n action_type: 'withdrawal',\n },\n subject: {\n entity_type: 'individual',\n vendor_data: 'user-12345',\n full_name: 'Maria Garcia',\n },\n }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst tx = await resp.json();\nconsole.log(tx.txn_id, tx.status, tx.severity);" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "transaction_id": { + "type": "string", + "description": "Your unique identifier for this transaction (max 128 characters).", + "example": "your-txn-2026-05-17-001" + }, + "transaction_at": { + "type": "string", + "format": "date-time", + "description": "When the transaction occurred. Defaults to now if omitted." + }, + "time_zone": { + "type": "string", + "description": "IANA time zone identifier, e.g. `Europe/Madrid`." + }, + "transaction_category": { + "type": "string", + "description": "Category of the transaction. CamelCase aliases (`travelRule`, `userPlatformEvent`, `gamblingBet`, …) are also accepted; the stored/echoed value is camelCase for multi-word categories.", + "example": "finance", + "enum": [ + "finance", + "kyc", + "travel_rule", + "user_event", + "audit_trail_event", + "gambling_bet", + "gambling_limit_change", + "gambling_bonus_change", + "travelRule" + ] + }, + "transaction_details": { + "type": "object", + "description": "Core financial details of the transaction.", + "required": [ + "direction", + "amount", + "currency" + ], + "properties": { + "direction": { + "type": "string", + "description": "Direction of the transaction relative to the subject. Case-insensitive; `in`/`out` are accepted as aliases. Echoed back uppercase (`INBOUND`/`OUTBOUND`).", + "example": "outbound", + "enum": [ + "inbound", + "outbound", + "in", + "out" + ] + }, + "amount": { + "type": "number", + "description": "Transaction amount (up to 18 decimal places). Send it as a decimal string (e.g. `\"0.123456789012345678\"`) when exact precision matters: a JSON number is parsed as a binary float first, so digits beyond about 15 significant digits are rounded before the amount is stored.", + "example": 1500 + }, + "currency": { + "type": "string", + "description": "Currency code. Any ISO 4217 alphabetic code is accepted (e.g. `EUR`, `USD`, `NGN`), as is a crypto asset symbol (e.g. `BTC`). Prefer a currently-circulating code. Not every accepted code has an exchange rate: when none is available for the code (for example the ISO accounting unit `XSU`, or a crypto symbol Didit does not price), automatic conversion fails and `preferred_currency_amount` is returned null unless the request supplied a pre-converted `amount_in_default_currency`.", + "example": "EUR" + }, + "currency_kind": { + "type": "string", + "description": "Whether the currency is `fiat` or `crypto`." + }, + "amount_in_default_currency": { + "type": "number", + "nullable": true, + "description": "Pre-converted amount in the default currency. If omitted, automatic FX conversion is attempted." + }, + "default_currency": { + "type": "string", + "description": "The default currency code for the converted amount. Any currently-circulating ISO 4217 code." + }, + "payment_details": { + "type": "string", + "nullable": true, + "description": "Free-text payment reference or memo." + }, + "payment_reference_id": { + "type": "string", + "nullable": true, + "description": "External payment system reference. For crypto AML transaction-hash screening, send the blockchain transaction hash and also provide `subject.payment_method.account_id` with the related address." + }, + "action_type": { + "type": "string", + "nullable": true, + "description": "Sub-type of the transaction, e.g. `deposit`, `withdrawal`, `transfer`." + }, + "crypto_params": { + "type": "object", + "description": "Crypto network selection, separate from currency (the asset symbol). Recording a custom token symbol does not guarantee transaction-hash coverage for its contract.", + "properties": { + "crypto_chain": { "type": "string", - "description": "Type of identity document" - }, - "document_subtype": { + "description": "Actual network used for screening. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism) and ARB (Arbitrum One). Always specify this for multi-network tokens. When it is missing for USDT, USDC or ETH sent with a 0x transaction hash, Didit looks the hash up on Ethereum, BNB Chain, Polygon, Base, Arbitrum and Optimism and screens it on the network where it is found; if no network has it, the transaction is not screened and custom_values.aml_screening_status is FAILED with the reason in custom_values.aml_screening_error. When it is missing for USDT or USDC screened by wallet (no transaction hash), a Tron address is screened on Tron, a Solana address on Solana, and a 0x address on the first of those six networks where it has sent a transaction or holds the token, or on Ethereum when it is active on none of them. Connected-provider coverage varies; LIGHTNING requires a supported connected provider.", + "example": "BASE" + } + } + } + } + }, + "subject": { + "type": "object", + "description": "The subject (applicant) of the transaction.", + "required": [ + "vendor_data", + "full_name" + ], + "properties": { + "entity_type": { + "type": "string", + "description": "Entity type: `individual` or `company`." + }, + "vendor_data": { + "type": "string", + "description": "Your internal user/business identifier.", + "example": "user-12345" + }, + "full_name": { + "type": "string", + "description": "Full name of the subject.", + "example": "Maria Garcia" + }, + "first_name": { + "type": "string", + "description": "First name of the subject." + }, + "last_name": { + "type": "string", + "description": "Last name of the subject." + }, + "date_of_birth": { + "type": "string", + "format": "date", + "description": "Date of birth (YYYY-MM-DD)." + }, + "address": { + "type": "object", + "description": "Address object.", + "properties": { + "country": { "type": "string", - "nullable": true, - "description": "Accepted document subtype code from the workflow document settings, including the document region when applicable. Examples: `ID_CARD_GENERIC`, `QUEENSLAND_DRIVER_LICENSE_GENERIC`." + "description": "Country code (ISO 3166-1 alpha-3 recommended). Copied to the party's `country_code`." }, - "document_number": { - "type": "string", - "description": "Document identification number" + "town": { + "type": "string" }, - "personal_number": { - "type": "string", - "description": "Personal identification number" + "state": { + "type": "string" }, - "portrait_image": { - "type": "string", - "format": "uri", - "description": "URL of the portrait image" + "street": { + "type": "string" }, - "front_image": { - "type": "string", - "format": "uri", - "description": "URL of the front document image" + "post_code": { + "type": "string" + } + } + }, + "institution_details": { + "type": "object", + "description": "Institution details (for institutional subjects).", + "properties": { + "name": { + "type": "string" }, - "front_video": { - "type": "string", - "format": "uri", - "description": "URL of the front document video" + "code": { + "type": "string" }, - "back_image": { + "address": { + "type": "object" + } + } + }, + "device_context": { + "type": "object", + "description": "Device context captured at transaction time. browser_family and platform must be sent as explicit top-level keys — they are not derived from user_agent. The camelCase alias device is also accepted on the request.", + "properties": { + "fingerprint": { "type": "string", - "format": "uri", - "description": "URL of the back document image" + "description": "Unique device fingerprint." }, - "back_video": { + "persistent_device_id": { "type": "string", - "format": "uri", - "description": "URL of the back document video" + "description": "Stable cross-session device identifier (v2 fingerprint schema)." }, - "full_front_image": { + "user_agent": { "type": "string", - "format": "uri", - "description": "URL of the full front document image" + "description": "Browser or app user agent string. CamelCase alias: userAgent." }, - "full_back_image": { + "browser_family": { "type": "string", - "format": "uri", - "description": "URL of the full back document image" + "description": "Browser family (e.g. Chrome, Firefox, Safari). Must be sent explicitly — it is not derived from user_agent. CamelCase alias: browserFamily." }, - "front_image_camera_front": { + "browser_version": { "type": "string", - "format": "uri", - "description": "URL of the front document image captured from the front-facing camera" + "description": "Browser version string. Must be sent explicitly. CamelCase alias: browserVersion." }, - "back_image_camera_front": { + "platform": { "type": "string", - "format": "uri", - "description": "URL of the back document image captured from the front-facing camera" + "description": "Platform identifier (web, ios, android, macos, windows, linux). Must be sent explicitly — it is not derived from user_agent." }, - "date_of_birth": { + "os_family": { "type": "string", - "format": "date", - "description": "Date of birth from document" - }, - "age": { - "type": "integer", - "description": "Calculated age from date of birth" + "description": "Operating system family (e.g. iOS, Android, Windows). CamelCase alias: osFamily." }, - "expiration_date": { + "os_version": { "type": "string", - "format": "date", - "description": "Document expiration date" + "description": "Operating system version string. CamelCase alias: osVersion." }, - "date_of_issue": { + "accept_language": { "type": "string", - "format": "date", - "description": "Document issue date" + "description": "Accept-Language header value. CamelCase alias: acceptLang." }, - "issuing_state": { + "session_id": { "type": "string", - "description": "ISO code of issuing state" + "description": "Session identifier of the user current session. Stored in metadata. CamelCase alias: sessionId." }, - "issuing_state_name": { - "type": "string", - "description": "Full name of issuing state" + "session_age_ms": { + "type": "number", + "description": "Age of the user session in milliseconds at transaction time. CamelCase alias: sessionAgeMs." }, - "first_name": { + "network_context": { + "type": "object", + "description": "Network context with IP address and geo-location. A top-level ipInfo block is also folded into this object.", + "properties": { + "ip_address": { + "type": "string", + "description": "Client IP address. CamelCase alias: ip." + }, + "ip_country_code": { + "type": "string", + "description": "Geo-resolved country of the IP (ISO 3166-1 alpha-3). CamelCase alias: countryCode3." + }, + "latitude": { + "type": "number", + "description": "Geographic latitude of the IP. CamelCase alias: lat." + }, + "longitude": { + "type": "number", + "description": "Geographic longitude of the IP. CamelCase alias: lon." + } + } + } + } + }, + "payment_method": { + "type": "object", + "description": "Payment method used by the subject.", + "properties": { + "method_type": { "type": "string", - "description": "First name from document" + "description": "Type of payment method: `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`." }, - "last_name": { + "account_id": { "type": "string", - "description": "Last name from document" + "description": "Account identifier (e.g. IBAN, wallet address). For crypto screening, this should contain the wallet address for this participant. Inbound transaction-hash screening requires the service/customer deposit address on the subject. Outbound screening uses the destination wallet on the counterparty. On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account (\"\" when it names none) and what the request says on the Travel Rule transfer's `chain_details.lightning`. At most 255 characters once stored. On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account (\"\" when it names none) and what the request says on the Travel Rule transfer's `chain_details.lightning`. At most 255 characters once stored." }, - "full_name": { + "memo": { "type": "string", - "description": "Full name from document" + "maxLength": 256, + "description": "The destination tag or memo that is part of the wallet on a memo network (XRP, XLM, EOS, ATOM, HBAR). On one shared exchange address each memo is its own payment method. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a `400`. Alias: `destinationTag` or `destination_tag`." }, - "gender": { + "issuing_country": { "type": "string", - "description": "Gender from document" - }, - "address": { + "description": "ISO 3166-1 alpha-3 country code of the issuer." + } + } + } + } + }, + "counterparty": { + "type": "object", + "description": "The counterparty of the transaction (optional).", + "properties": { + "entity_type": { + "type": "string", + "description": "Entity type: `individual` or `company`." + }, + "vendor_data": { + "type": "string", + "description": "Your internal identifier for the counterparty." + }, + "full_name": { + "type": "string", + "description": "Full name of the counterparty." + }, + "first_name": { + "type": "string" + }, + "last_name": { + "type": "string" + }, + "date_of_birth": { + "type": "string", + "format": "date" + }, + "address": { + "type": "object", + "description": "Address object with `country` (ISO 3166-1 alpha-3)." + }, + "institution_details": { + "type": "object" + }, + "device_context": { + "type": "object", + "description": "Device context for the counterparty. browser_family and platform must be sent as explicit top-level keys — they are not derived from user_agent.", + "properties": { + "fingerprint": { "type": "string", - "description": "Raw address from document" + "description": "Unique device fingerprint." }, - "formatted_address": { + "persistent_device_id": { "type": "string", - "description": "Formatted full address" + "description": "Stable cross-session device identifier (v2 fingerprint schema)." }, - "place_of_birth": { + "user_agent": { "type": "string", - "description": "Place of birth from document" + "description": "Browser or app user agent string. CamelCase alias: userAgent." }, - "marital_status": { + "browser_family": { "type": "string", - "description": "Marital status from document" + "description": "Browser family (e.g. Chrome, Firefox, Safari). Must be sent explicitly — it is not derived from user_agent. CamelCase alias: browserFamily." }, - "nationality": { + "browser_version": { "type": "string", - "description": "ISO nationality code" - }, - "extra_fields": { - "type": "object", - "description": "Additional document fields. Includes document-specific attributes (e.g. `dl_categories`, `blood_group`, `sponsor`, `employer`, `dl_class_code_b_from`, `dl_class_code_b_to`). Driver license class validity dates are returned as `dl_class_code__from` and `dl_class_code__to`, and per-class restriction/information codes (e.g. column 12 of UK licences) as `dl_class_code__notes`. When the document carries names, address, or place of birth in multiple scripts, this object also includes alternate-script versions of those fields. The workflow setting `preferred_characters` (`latin` or `non_latin`) controls which script populates the top-level fields (`first_name`, `last_name`, `full_name`, `address`, `place_of_birth`); the opposite script lands here as `first_name_non_latin` / `last_name_non_latin` / `full_name_non_latin` / `middle_name_non_latin` / `address_non_latin` / `place_of_birth_non_latin` (when `preferred_characters` is `latin`) or with a `_latin` suffix (when `preferred_characters` is `non_latin`). Keys appear only when the OCR actually extracts a real alternate-script value — a field whose alternate value is in the same script as the preferred set (for example, a Cyrillic-labelled field whose OCR value came back in Latin homoglyphs) is skipped. When `preferred_characters` is `latin` but the document only carries non-Latin text, Didit transliterates the extracted values and exposes `first_name_latin` / `last_name_latin` / etc. as a fallback.", - "additionalProperties": true, - "properties": { - "dl_categories": { - "type": "array", - "items": { - "type": "string" - } - }, - "blood_group": { - "type": "string", - "nullable": true - }, - "first_name_non_latin": { - "type": "string", - "description": "First name in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document (e.g. `Айна` for a Kyrgyz ID)." - }, - "last_name_non_latin": { - "type": "string", - "description": "Surname in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document." - }, - "middle_name_non_latin": { - "type": "string", - "description": "Middle name / patronymic in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document." - }, - "full_name_non_latin": { - "type": "string", - "description": "Composed full name in the non-Latin script. Reconstructed from the individual components when both first and last names are available." - }, - "address_non_latin": { - "type": "string", - "description": "Address in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document." - }, - "place_of_birth_non_latin": { - "type": "string", - "description": "Place of birth in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document." - }, - "first_name_latin": { - "type": "string", - "description": "First name in Latin script. Populated when `preferred_characters=non_latin` and a Latin version is present on the document, or — as a transliteration fallback — when `preferred_characters=latin` but the document only carries non-Latin text." - }, - "last_name_latin": { - "type": "string", - "description": "Surname in Latin script. Same rules as `first_name_latin`." - }, - "middle_name_latin": { - "type": "string", - "description": "Middle name / patronymic in Latin script. Same rules as `first_name_latin`." - }, - "full_name_latin": { - "type": "string", - "description": "Composed full name in Latin script. Always reconstructed from the individual components when both first and last names are available." - }, - "address_latin": { - "type": "string", - "description": "Address in Latin script. Same rules as `first_name_latin`." - }, - "place_of_birth_latin": { - "type": "string", - "description": "Place of birth in Latin script. Same rules as `first_name_latin`." - } - } - }, - "mrz": { - "type": "object", - "nullable": true, - "description": "Parsed Machine-Readable Zone (MRZ) fields extracted from the document. `null` if no MRZ was detected.", - "properties": { - "surname": { - "type": "string", - "example": "MARTINEZ`.", + "additionalProperties": true, + "example": { + "merchant_id": "test", + "bank_name": "test_2" + } + }, + "travel_rule_details": { + "type": "object", + "description": "Travel Rule data. When Travel Rule is enabled and transaction_category is travel_rule, Didit runs the exchange itself: it ignores status, protocol, required and obligations_count, and uses originator_data, beneficiary_data and metadata. Otherwise Didit stores these values as a passive Travel Rule record. While Travel Rule is on, an outbound transfer at or above your threshold must carry what its regime requires (EU TFR or FATF Recommendation 16), checked on the sides you send: a missing field answers 400 under `travel_rule_details.originator_data` or `travel_rule_details.beneficiary_data`, for example \"EU TFR requires the originator name.\"", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Required whenever you send travel_rule_details. One of the Travel Rule transfer statuses (for example AWAITING_COUNTERPARTY, COMPLETED, NOT_APPLICABLE); lowercase and camelCase spellings and the legacy values UNKNOWN, COMPLIANT, PENDING_ACTION, PENDING_COUNTERPARTY, FAILED and EXEMPT are also accepted and mapped. Any other value returns 400." + }, + "protocol": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "obligations_count": { + "type": "integer" + }, + "originator_data": { + "type": "object", + "description": "Originator party, as flat fields or one IVMS 101 person. For your user with an approved KYC verification, Didit replaces the fields the verification holds with the verified values (see `metadata.originator_provenance`). It must be an object: anything else is a `400`. On a memo network a `memo` key names the originator wallet's memo or tag." + }, + "beneficiary_data": { + "type": "object", + "description": "Beneficiary party, as flat fields or one IVMS 101 person. An empty `wallet_address` is filled from `counterparty.payment_method.account_id`. It must be an object: anything else is a `400`. On a memo network `memo` names the beneficiary wallet's memo or tag; `wallet_address` and `memo` win over the counterparty's payment method." + }, + "metadata": { + "type": "object" + }, + "utxo": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleUtxo" + } + ], + "description": "Optional, UTXO chains only (BTC, LTC, BCH, DOGE). The transaction's inputs and outputs, from which Didit infers the originator wallet and the beneficiary output when you do not declare them." + }, + "originator": { + "type": "object", + "description": "An IVMS 101 originator block: `originatorPersons` (one `naturalPerson` or `legalPerson`; a legal person makes the party a company) and `accountNumber` (wallet addresses). Used for `originator_data` when that object is empty. A malformed block answers 400 keyed by its path, for example `originator.originatorPersons`." + }, + "beneficiary": { + "type": "object", + "description": "An IVMS 101 beneficiary block: `beneficiaryPersons` (one `naturalPerson` or `legalPerson`; a legal person makes the party a company) and `accountNumber` (wallet addresses). Used for `beneficiary_data` when that object is empty. A malformed block answers 400 keyed by its path, for example `beneficiary.beneficiaryPersons`." + }, + "ivms101": { + "type": "object", + "description": "The `originator` and `beneficiary` blocks wrapped in one IVMS 101 object (`IVMS101` is accepted too). Top-level blocks win over the wrapper's." + } + } + }, + "network_snapshot": { + "type": "object", + "description": "Pre-computed network graph snapshot.", + "properties": { + "nodes": { + "type": "array", + "items": { + "type": "object" + } + }, + "edges": { + "type": "array", + "items": { + "type": "object" + } + }, + "metrics": { + "type": "object" + } + } + }, + "include_crypto_screening": { + "type": "boolean", + "nullable": true, + "description": "Per-transaction override for crypto blockchain analytics screening. When `true`, crypto screening is performed regardless of the application default. When `false`, crypto screening is skipped even if enabled in the application settings. When omitted or `null`, the application-level default configured in the Console is used. Crypto screening requires `transaction_details.direction`, `currency_kind: \"crypto\"`, a chain/currency, and the wallet address selected by the direction: inbound pre-transfer screening uses `counterparty.payment_method.account_id`, inbound transaction-hash screening uses `subject.payment_method.account_id`, and outbound screening uses `counterparty.payment_method.account_id`. When `transaction_details.payment_reference_id` contains a blockchain transaction hash, Didit performs transaction screening and returns transaction summary enrichment when available." + } + }, + "required": [ + "transaction_id", + "transaction_category", + "transaction_details", + "subject" + ] + }, + "examples": { + "Minimal fiat withdrawal": { + "summary": "Smallest acceptable payload for a fiat outbound transfer", + "value": { + "transaction_id": "your-txn-2026-05-17-001", + "transaction_category": "finance", + "transaction_details": { + "direction": "outbound", + "amount": 1500, + "currency": "EUR", + "action_type": "withdrawal" + }, + "subject": { + "entity_type": "individual", + "vendor_data": "user-12345", + "full_name": "Maria Garcia" + } + } + }, + "Crypto outbound with screening": { + "summary": "Outbound crypto withdrawal to an unhosted wallet, opt-in screening", + "value": { + "transaction_id": "your-txn-2026-05-17-002", + "transaction_category": "finance", + "include_crypto_screening": true, + "transaction_details": { + "direction": "outbound", + "amount": 0.5, + "currency": "BTC", + "currency_kind": "crypto", + "amount_in_default_currency": 32500, + "default_currency": "USD", + "action_type": "withdrawal" + }, + "subject": { + "entity_type": "individual", + "vendor_data": "user-12345", + "full_name": "Maria Garcia", + "payment_method": { + "method_type": "crypto_wallet", + "account_id": "bc1qexampleexchangewalletaddressxyz", + "issuing_country": "ESP" + } + }, + "counterparty": { + "entity_type": "individual", + "payment_method": { + "method_type": "unhosted_wallet", + "account_id": "bc1qexampleexternalwalletaddressabc" + } + } + } + }, + "Travel Rule transfer": { + "summary": "Outbound crypto transfer with Travel Rule originator/beneficiary data", + "value": { + "transaction_id": "your-txn-2026-05-17-003", + "transaction_category": "travel_rule", + "transaction_details": { + "direction": "outbound", + "amount": 25000, + "currency": "USDT", + "currency_kind": "crypto", + "action_type": "transfer" + }, + "subject": { + "entity_type": "individual", + "vendor_data": "user-12345", + "full_name": "Maria Garcia", + "date_of_birth": "1992-03-14", + "address": { + "country": "ESP", + "town": "Madrid", + "street": "Calle Mayor 1", + "post_code": "28013" + } + }, + "counterparty": { + "entity_type": "individual", + "full_name": "John Beneficiary", + "institution_details": { + "name": "Beneficiary VASP, Inc.", + "code": "BVASP-US" + } + }, + "travel_rule_details": { + "status": "AWAITING_COUNTERPARTY", + "protocol": "IVMS101", + "required": true, + "originator_data": { + "name": "Maria Garcia" + }, + "beneficiary_data": { + "name": "John Beneficiary" + } + } + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Transaction submitted and monitored. The body is the full transaction detail (same shape as `GET /v3/transactions/{transaction_id}/`), reflecting any rule outcomes already applied.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionDetail" + }, + "examples": { + "Approved with no rule hits": { + "value": { + "uuid": "abcdef12-3456-4890-abcd-ef1234567890", + "environment": "live", + "transaction_number": 4123, + "txn_id": "your-txn-2026-05-17-001", + "txn_date": "2026-05-17T08:42:00Z", + "zone_id": "Europe/Madrid", + "transaction_type": "finance", + "action_type": "withdrawal", + "direction": "OUTBOUND", + "status": "APPROVED", + "source": "LIVE", + "amount": "1500", + "currency": "EUR", + "currency_type": "fiat", + "amount_in_default_currency": "1620.45", + "default_currency_code": "USD", + "preferred_currency_amount": "1620.45", + "preferred_currency_code": "USD", + "payment_details": "Withdrawal to crypto wallet", + "payment_txn_id": null, + "score": 0, + "severity": null, + "decision_reason_code": null, + "decision_reason_label": null, + "vendor_data": "user-12345", + "metadata": { + "subject": { + "entity_type": "individual", + "vendor_data": "user-12345", + "full_name": "Maria Garcia", + "address": {}, + "institution_details": {}, + "device_context": {} + }, + "counterparty": {} + }, + "props": {}, + "tags": [], + "parties": [ + { + "uuid": "b1111111-2222-4333-8444-555555555555", + "role": "APPLICANT", + "entity_type": "individual", + "kind": "USER", + "vendor_data": "user-12345", + "full_name": "Maria Garcia", + "first_name": null, + "last_name": null, + "country_code": null, + "dob": null, + "address": {}, + "institution_info": {}, + "device": {}, + "external_party_snapshot": null + } + ], + "payment_methods": [], + "documents": [], + "activities": [ + { + "uuid": "d4e5f6a7-8901-4bcd-9ef0-123456789abc", + "activity_type": "TRANSACTION_CREATED", + "source": "TRANSACTION", + "status": "APPROVED", + "title": "withdrawal", + "description": "Transaction recorded.", + "metadata": {}, + "occurred_at": "2026-05-17T08:42:11Z" + } + ], + "alerts": [], + "rule_runs": [], + "provider_results": [], + "travel_rule": null, + "network_snapshot": null, + "remediation": null, + "action_required": null, + "cost_breakdown": null + } + }, + "Flagged for review": { + "value": { + "uuid": "abcdef12-3456-4890-abcd-ef1234567891", + "environment": "live", + "transaction_number": 4124, + "txn_id": "your-txn-2026-05-17-002", + "txn_date": "2026-05-17T08:42:00Z", + "zone_id": "Europe/Madrid", + "transaction_type": "finance", + "action_type": "withdrawal", + "direction": "OUTBOUND", + "status": "IN_REVIEW", + "source": "LIVE", + "amount": "0.5", + "currency": "BTC", + "currency_type": "crypto", + "amount_in_default_currency": "32500", + "default_currency_code": "USD", + "preferred_currency_amount": "32500", + "preferred_currency_code": "USD", + "payment_details": "Withdrawal to crypto wallet", + "payment_txn_id": null, + "score": 78, + "severity": "HIGH", + "decision_reason_code": "WALLET_HIGH_RISK", + "decision_reason_label": "Destination wallet flagged as high risk", + "vendor_data": "user-12345", + "metadata": { + "subject": { + "entity_type": "individual", + "vendor_data": "user-12345", + "full_name": "Maria Garcia", + "address": {}, + "institution_details": {}, + "device_context": {} + }, + "counterparty": {} + }, + "props": { + "order_id": "ord-9988" + }, + "tags": [], + "parties": [ + { + "uuid": "b1111111-2222-4333-8444-555555555555", + "role": "APPLICANT", + "entity_type": "individual", + "kind": "USER", + "vendor_data": "user-12345", + "full_name": "Maria Garcia", + "first_name": null, + "last_name": null, + "country_code": null, + "dob": null, + "address": {}, + "institution_info": {}, + "device": {}, + "external_party_snapshot": null + } + ], + "payment_methods": [], + "documents": [], + "activities": [], + "alerts": [ + { + "uuid": "a7b8c9d0-1234-4abc-9def-567890abcdef", + "title": "Destination wallet flagged as high risk", + "description": "Provider risk score exceeded the configured threshold.", + "severity": "HIGH", + "status": "OPEN", + "source": "RULE", + "metadata": {}, + "created_at": "2026-05-17T08:42:12Z", + "due_at": null + } + ], + "rule_runs": [ + { + "uuid": "e5f6a7b8-9012-4cde-af01-23456789abcd", + "rule": "f6a7b8c9-0123-4def-b012-3456789abcde", + "rule_title": "High-risk wallet screening", + "rule_description": null, + "rule_category": "crypto", + "matched": true, + "is_test": false, + "mode": "ACTIVE", + "severity": "HIGH", + "score_delta": 78, + "status_target": "IN_REVIEW", + "action_summary": "score +78; change_status:IN_REVIEW", + "metadata": {}, + "created_at": "2026-05-17T08:42:12Z" + } + ], + "provider_results": [], + "travel_rule": null, + "network_snapshot": null, + "remediation": null, + "action_required": null, + "cost_breakdown": null + } + } + } + } + } + }, + "400": { + "description": "Validation failed (missing required fields, duplicate `transaction_id`, unknown enum value, invalid currency, etc.).", + "content": { + "application/json": { + "examples": { + "Missing required field": { + "value": { + "transaction_details": { + "amount": [ + "This field is required." + ] + } + } + }, + "Duplicate transaction_id": { + "value": { + "transaction_id": [ + "A transaction with this transaction_id already exists." + ] + } + }, + "Invalid category": { + "value": { + "non_field_errors": [ + "Unsupported transaction category." + ] + } + }, + "Missing subject identity": { + "value": { + "subject": [ + "subject.vendor_data and subject.full_name are required." + ] + } + }, + "Blocklisted participant": { + "value": { + "vendor_data": [ + "The individual 'user-12345' is in the blocklist and cannot participate in transactions." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot submit transactions for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "409": { + "description": "Another request with the same `transaction_id` created the transaction while this one was being processed, so only one of two simultaneous submissions succeeds. Read the existing transaction instead of resubmitting.", + "content": { + "application/json": { + "examples": { + "Concurrent duplicate": { + "value": { + "detail": "A transaction with this transaction_id already exists." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-didit-sdk": { + "resource": "transactions", + "method": "create", + "auth": "api_key", + "server": true + } + } + }, + "/v3/transactions/{transaction_id}/": { + "get": { + "summary": "Get transaction", + "description": "Retrieve the full monitoring record for one transaction: parties, score/severity, rule runs, alerts, Travel Rule outcome, remediation session.", + "operationId": "getTransaction", + "x-didit-sdk": { + "resource": "transactions", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "parameters": [ + { + "name": "transaction_id", + "in": "path", + "required": true, + "description": "Didit-stable transaction UUID (the `uuid` returned from create / list, **not** your `txn_id`).", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "abcdef12-3456-7890-abcd-ef1234567890" + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/transactions/abcdef12-3456-7890-abcd-ef1234567890/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nuuid = 'abcdef12-3456-7890-abcd-ef1234567890'\nresp = requests.get(\n f'https://verification.didit.me/v3/transactions/{uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\ntx = resp.json()\nfor alert in tx['alerts']:\n print(alert.get('code'), alert.get('severity'))" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const uuid = 'abcdef12-3456-7890-abcd-ef1234567890';\nconst resp = await fetch(`https://verification.didit.me/v3/transactions/${uuid}/`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Lookup failed: ${resp.status}`);\nconst tx = await resp.json();\nconsole.log(tx.status, tx.severity, tx.alerts.length, 'alerts');" + } + ], + "responses": { + "200": { + "description": "Full transaction detail.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionDetail" + }, + "examples": { + "In review with an alert": { + "value": { + "uuid": "abcdef12-3456-4890-abcd-ef1234567890", + "environment": "live", + "transaction_number": 4123, + "txn_id": "your-txn-2026-05-17-001", + "txn_date": "2026-05-17T08:42:00Z", + "zone_id": "Europe/Madrid", + "transaction_type": "finance", + "action_type": "withdrawal", + "direction": "OUTBOUND", + "status": "IN_REVIEW", + "source": "LIVE", + "amount": "1500", + "currency": "EUR", + "currency_type": "fiat", + "amount_in_default_currency": "1620.45", + "default_currency_code": "USD", + "preferred_currency_amount": "1620.45", + "preferred_currency_code": "USD", + "payment_details": "Withdrawal to crypto wallet", + "payment_txn_id": null, + "score": 78, + "severity": "HIGH", + "decision_reason_code": "WALLET_HIGH_RISK", + "decision_reason_label": "Destination wallet flagged as high risk", + "vendor_data": "user-12345", + "metadata": { + "subject": { + "entity_type": "individual", + "vendor_data": "user-12345", + "full_name": "Maria Garcia", + "address": {}, + "institution_details": {}, + "device_context": {} + }, + "counterparty": {} + }, + "props": { + "order_id": "ord-9988" + }, + "tags": [], + "parties": [ + { + "uuid": "b1111111-2222-4333-8444-555555555555", + "role": "APPLICANT", + "entity_type": "individual", + "kind": "USER", + "vendor_data": "user-12345", + "full_name": "Maria Garcia", + "first_name": null, + "last_name": null, + "country_code": null, + "dob": null, + "address": {}, + "institution_info": {}, + "device": {}, + "external_party_snapshot": null + } + ], + "payment_methods": [], + "documents": [], + "activities": [], + "alerts": [ + { + "uuid": "a7b8c9d0-1234-4abc-9def-567890abcdef", + "title": "Destination wallet flagged as high risk", + "description": "Provider risk score exceeded the configured threshold.", + "severity": "HIGH", + "status": "OPEN", + "source": "RULE", + "metadata": {}, + "created_at": "2026-05-17T08:42:12Z", + "due_at": null + } + ], + "rule_runs": [], + "provider_results": [], + "travel_rule": null, + "network_snapshot": null, + "remediation": null, + "action_required": null, + "cost_breakdown": null + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot read this application's transactions. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No transaction with this UUID exists for the application (deleted, never created, belongs to another application, or the path segment is not a valid UUID).", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/transactions/sdk-token/": { + "post": { + "summary": "Mint an SDK transaction token", + "description": "Mint a short-lived scoped token your app passes to the Didit SDKs so they can submit transactions directly from the end-user's device via `POST /v1/transactions/`. The token is bound to one `vendor_data` (your user id): every transaction submitted with it has its subject identity enforced server-side from that binding, so a tampered client cannot submit on behalf of another user. Mint tokens from your backend - never ship the API key itself in an app. camelCase aliases (`vendorData`, `ttlSeconds`, `maxUses`) are also accepted.", + "operationId": "createTransactionSdkToken", + "x-didit-sdk": { + "resource": "transactions", + "method": "create_sdk_token", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "vendor_data": { + "type": "string", + "description": "Your internal identifier for the end user this token is scoped to. Enforced as the subject identity of every transaction submitted with the token.", + "example": "user-042" + }, + "ttl_seconds": { + "type": "integer", + "default": 900, + "maximum": 86400, + "description": "Token lifetime in seconds. Defaults to 900 (15 minutes); maximum 86400 (24 hours)." + }, + "max_uses": { + "type": "integer", + "nullable": true, + "description": "Maximum number of successful submissions allowed with this token. Omit or null for unlimited uses within the TTL." + } + }, + "required": [ + "vendor_data" + ] + }, + "example": { + "vendor_data": "user-042", + "ttl_seconds": 900, + "max_uses": 3 + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/transactions/sdk-token/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"vendor_data\": \"user-042\", \"ttl_seconds\": 900, \"max_uses\": 3}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/transactions/sdk-token/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'vendor_data': 'user-042', 'ttl_seconds': 900, 'max_uses': 3},\n timeout=10,\n)\nresp.raise_for_status()\ntoken = resp.json()\nprint(token['sdk_token'], token['expires_at'])" + } + ], + "responses": { + "201": { + "description": "The minted token. Hand `sdk_token` to your app; it authenticates the device-facing `/v1/transactions/` endpoints via the `X-Transaction-Token` header until `expires_at`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sdk_token": { + "type": "string", + "description": "The scoped transaction token to pass to the SDK." + }, + "expires_at": { + "type": "string", + "format": "date-time", + "description": "When the token stops being accepted." + } + } + }, + "example": { + "sdk_token": "q7Zt...urlsafe", + "expires_at": "2026-07-07T12:15:00Z" + } + } + } + }, + "400": { + "description": "Validation failed: missing `vendor_data`, or `ttl_seconds` outside the allowed range.", + "content": { + "application/json": { + "examples": { + "Missing vendor_data": { + "value": { + "vendor_data": [ + "This field is required." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key.", + "content": { + "application/json": { + "examples": { + "Invalid API Key": { + "value": { + "detail": "Invalid API Key." + } + } + } + } + } + } + } + } + }, + "/v1/transactions/": { + "post": { + "summary": "Submit a transaction from an SDK", + "description": "Device-facing endpoint the Didit SDKs call to submit a transaction directly from the end-user's device. Authentication is a scoped token from `POST /v3/transactions/sdk-token/` sent in the `X-Transaction-Token` header instead of an API key, and the endpoint is CORS-enabled so browsers can call it cross-origin. The payload uses the camelCase wire aliases shown below (the SDK `submitTransaction` methods build it for you). Three server-side guarantees: the application is always taken from the token; the subject identity (`externalUserId`) is always enforced from the token's `vendor_data`, so spoofed payload values are ignored; and device intelligence (the SDK device fingerprint plus server-derived IP and user agent) is attached to the subject automatically.", + "operationId": "submitTransactionFromSdk", + "x-didit-sdk": { + "resource": "transactions", + "method": "submit_from_device", + "auth": "device_token", + "server": false + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "TransactionTokenAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "txnId": { + "type": "string", + "description": "Your unique identifier for this transaction (maps to `transaction_id`, max 128 characters).", + "example": "wd-2026-07-07-0042" + }, + "txnDate": { + "type": "string", + "format": "date-time", + "description": "When the transaction occurred (maps to `transaction_at`). Defaults to now if omitted." + }, + "zoneId": { + "type": "string", + "description": "IANA time zone identifier (maps to `time_zone`), e.g. `Europe/Madrid`." + }, + "type": { + "type": "string", + "description": "Transaction category (maps to `transaction_category`), e.g. `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`.", + "example": "travelRule" + }, + "info": { + "type": "object", + "description": "Core financial details of the transaction (maps to `transaction_details`).", + "required": [ + "direction", + "amount", + "currency" + ], + "properties": { + "direction": { + "type": "string", + "description": "Direction relative to the subject. Case-insensitive; `in`/`out` are accepted as aliases. Echoed back uppercase (`INBOUND`/`OUTBOUND`).", + "example": "out" + }, + "amount": { + "type": "number", + "description": "Transaction amount (up to 18 decimal places). A decimal string is also accepted and is the only way to preserve more than about 15 significant digits: a JSON number is parsed as a binary float first, so `0.123456789012345678` is stored as `0.12345678901234568`.", + "example": 0.25 + }, + "currency": { + "type": "string", + "description": "Currency code. Any ISO 4217 alphabetic code is accepted (e.g. `EUR`, `USD`, `NGN`), as is a crypto asset symbol (e.g. `BTC`). Prefer a currently-circulating code. Not every accepted code has an exchange rate: when none is available for the code (for example the ISO accounting unit `XSU`, or a crypto symbol Didit does not price), automatic conversion fails and `preferred_currency_amount` is returned null unless the request supplied a pre-converted `amountInDefaultCurrency`.", + "example": "ETH" + }, + "currencyType": { + "type": "string", + "description": "Whether the currency is `fiat` or `crypto` (maps to `currency_kind`)." + }, + "amountInDefaultCurrency": { + "type": "number", + "nullable": true, + "description": "Pre-converted amount in the default currency (maps to `amount_in_default_currency`). If omitted, automatic FX conversion is attempted." + }, + "defaultCurrencyCode": { + "type": "string", + "description": "The default currency code for the converted amount (maps to `default_currency`). Any currently-circulating ISO 4217 code." + }, + "paymentDetails": { + "type": "string", + "nullable": true, + "description": "Free-text payment reference or memo (maps to `payment_details`)." + }, + "paymentTxnId": { + "type": "string", + "nullable": true, + "description": "External payment reference (maps to `payment_reference_id`); the on-chain transaction hash for crypto transactions." + }, + "type": { + "type": "string", + "nullable": true, + "description": "Sub-type of the transaction (maps to `action_type`), e.g. `deposit`, `withdrawal`, `transfer`." + }, + "cryptoParams": { + "type": "object", + "description": "Crypto-specific parameters, e.g. `{\"crypto_chain\": \"ETH\"}`." + } + } + }, + "subject": { + "type": "object", + "description": "The subject (applicant) of the transaction. `externalUserId` is ignored here: the subject identity is always enforced from the token's `vendor_data`.", + "required": [ + "fullName" + ], + "properties": { + "type": { + "type": "string", + "description": "Entity type (maps to `entity_type`): `individual` (default) or `company`." + }, + "externalUserId": { + "type": "string", + "description": "Ignored on the subject - enforced server-side from the token's `vendor_data`." + }, + "fullName": { + "type": "string", + "example": "Jane Doe" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "dob": { + "type": "string", + "format": "date", + "description": "Date of birth (YYYY-MM-DD; maps to `date_of_birth`)." + }, + "address": { + "type": "object", + "description": "Address object with `country` (ISO 3166-1 alpha-3), `town`, `state`, `street`, `post_code`." + }, + "institutionInfo": { + "type": "object", + "description": "Institution details for institutional participants (maps to `institution_details`)." + }, + "device": { + "type": "object", + "description": "Optional explicit device context. Merged with the automatically captured device intelligence; server-derived fields win on conflict." + }, + "paymentMethod": { + "type": "object", + "description": "Payment method used by the subject (maps to `payment_method`).", + "properties": { + "type": { + "type": "string", + "description": "Payment method kind (maps to `method_type`), e.g. `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`." }, - "matches": { - "type": "array", - "description": "Matching documents found during duplicate/blocklist detection (up to 5 matches)", - "items": { - "type": "object", - "properties": { - "session_id": { - "type": "string", - "format": "uuid", - "description": "Session ID of the matching document" - }, - "session_number": { - "type": "integer", - "description": "Session number of the matching document" - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Vendor data associated with the matching session" - }, - "verification_date": { - "type": "string", - "format": "date-time", - "description": "Date when the matching session was verified" - }, - "user_details": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Full name from the matching document" - }, - "document_type": { - "type": "string", - "description": "Type of the matching document" - }, - "document_number": { - "type": "string", - "description": "Document number of the matching document" - } - } - }, - "status": { - "type": "string", - "description": "Status of the matching session" - }, - "is_blocklisted": { - "type": "boolean", - "description": "Whether the matching document is blocklisted" - }, - "api_service": { - "type": "string", - "nullable": true, - "description": "API service type of the matching session" - }, - "front_image_url": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "URL of the front document image from the matching document" - } - } - } + "accountId": { + "type": "string", + "description": "Account identifier (maps to `account_id`): IBAN, card fingerprint, or wallet address." }, - "document_liveness": { - "type": "object", - "nullable": true, - "description": "Document-liveness scores per fraud type, reported whether or not a warning fired, so an approved document can still be audited. `null` when the check did not run.", - "properties": { - "portrait_replace": { - "type": "object", - "nullable": true, - "properties": { - "score": { - "type": "number", - "nullable": true, - "description": "Likelihood that the document is genuine for this fraud type, 0-100 (higher = more likely genuine). The lowest of the two sides - the value the decision used. `null` when neither side produced a usable score." - }, - "front": { - "type": "number", - "nullable": true, - "description": "Score for the front side; `null` when not captured or when the score was dropped." - }, - "back": { - "type": "number", - "nullable": true, - "description": "Score for the back side; `null` when not captured or when the score was dropped." - }, - "decline_threshold": { - "type": "number", - "nullable": true, - "description": "The decline threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." - }, - "review_threshold": { - "type": "number", - "nullable": true, - "description": "The review threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." - }, - "bucket": { - "type": "string", - "nullable": true, - "enum": [ - "approve", - "review", - "decline", - null - ], - "description": "The fraud-type bucket saved at evaluation time. Null when there is no usable score, the check was not applied, or historical evaluation evidence is unavailable. Later workflow edits do not change it." - } - } - }, - "printed_copy": { - "type": "object", - "nullable": true, - "properties": { - "score": { - "type": "number", - "nullable": true, - "description": "Likelihood that the document is genuine for this fraud type, 0-100 (higher = more likely genuine). The lowest of the two sides - the value the decision used. `null` when neither side produced a usable score." - }, - "front": { - "type": "number", - "nullable": true, - "description": "Score for the front side; `null` when not captured or when the score was dropped." - }, - "back": { - "type": "number", - "nullable": true, - "description": "Score for the back side; `null` when not captured or when the score was dropped." - }, - "decline_threshold": { - "type": "number", - "nullable": true, - "description": "The decline threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." - }, - "review_threshold": { - "type": "number", - "nullable": true, - "description": "The review threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." - }, - "bucket": { - "type": "string", - "nullable": true, - "enum": [ - "approve", - "review", - "decline", - null - ], - "description": "The fraud-type bucket saved at evaluation time. Null when there is no usable score, the check was not applied, or historical evaluation evidence is unavailable. Later workflow edits do not change it." - } - } - }, - "screen_replay": { - "type": "object", - "nullable": true, - "properties": { - "score": { - "type": "number", - "nullable": true, - "description": "Likelihood that the document is genuine for this fraud type, 0-100 (higher = more likely genuine). The lowest of the two sides - the value the decision used. `null` when neither side produced a usable score." - }, - "front": { - "type": "number", - "nullable": true, - "description": "Score for the front side; `null` when not captured or when the score was dropped." - }, - "back": { - "type": "number", - "nullable": true, - "description": "Score for the back side; `null` when not captured or when the score was dropped." - }, - "decline_threshold": { - "type": "number", - "nullable": true, - "description": "The decline threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." - }, - "review_threshold": { - "type": "number", - "nullable": true, - "description": "The review threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." - }, - "bucket": { - "type": "string", - "nullable": true, - "enum": [ - "approve", - "review", - "decline", - null - ], - "description": "The fraud-type bucket saved at evaluation time. Null when there is no usable score, the check was not applied, or historical evaluation evidence is unavailable. Later workflow edits do not change it." - } - } - } + "issuingCountry": { + "type": "string", + "description": "ISO 3166-1 alpha-3 country code of the issuer (maps to `issuing_country`)." + } + } + } + } + }, + "counterparty": { + "type": "object", + "description": "The counterparty of the transaction (optional). Same shape as `subject`; `externalUserId` is meaningful here.", + "properties": { + "type": { + "type": "string", + "description": "Entity type (maps to `entity_type`): `individual` or `company`." + }, + "externalUserId": { + "type": "string", + "description": "Your internal identifier for the counterparty (maps to `vendor_data`)." + }, + "fullName": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "dob": { + "type": "string", + "format": "date" + }, + "address": { + "type": "object" + }, + "institutionInfo": { + "type": "object" + }, + "device": { + "type": "object" + }, + "paymentMethod": { + "type": "object", + "description": "Payment method used by the counterparty (maps to `payment_method`). On travelRule transactions, `type: \"unhosted_wallet\"` declares the counterparty wallet as self-hosted: the transfer skips counterparty-VASP routing, is created directly in `UNCONFIRMED_OWNERSHIP`, and (when `auto_wallet_verification` is enabled) a wallet-ownership widget session is auto-minted and returned in `action_required` so the SDK can launch it.", + "properties": { + "type": { + "type": "string", + "description": "Payment method kind (maps to `method_type`), e.g. `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`. Use `unhosted_wallet` to declare a self-hosted wallet for the Travel Rule." + }, + "accountId": { + "type": "string", + "description": "Account identifier (maps to `account_id`): IBAN, card fingerprint, or wallet address." + }, + "issuingCountry": { + "type": "string" + } + } + } + } + }, + "props": { + "type": "object", + "additionalProperties": true, + "description": "Custom key-value pairs (maps to `custom_properties`). Each key can be referenced in rule conditions as `custom_values.`." + }, + "travelRule": { + "type": "object", + "description": "Travel Rule details (maps to `travel_rule_details`). When Travel Rule is enabled for the application, Didit ignores any `status` you send and drives the exchange itself.", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string" + }, + "protocol": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "obligationsCount": { + "type": "integer" + }, + "originatorData": { + "type": "object" + }, + "beneficiaryData": { + "type": "object" + }, + "metadata": { + "type": "object" + } + } + }, + "includeCryptoScreening": { + "type": "boolean", + "nullable": true, + "description": "Per-transaction override for crypto blockchain analytics screening (maps to `include_crypto_screening`)." + }, + "fingerprint_v2": { + "type": "object", + "description": "The `didit-fp-v2` device fingerprint payload. Injected automatically by the SDKs; only relevant when calling the wire API directly." + } + }, + "required": [ + "txnId", + "type", + "info", + "subject" + ] + }, + "example": { + "txnId": "wd-2026-07-07-0042", + "type": "travelRule", + "info": { + "direction": "out", + "amount": "0.25", + "currency": "ETH", + "currencyType": "crypto", + "cryptoParams": { + "crypto_chain": "ETH" + } + }, + "subject": { + "fullName": "Jane Doe" + }, + "counterparty": { + "fullName": "Ana Diaz", + "paymentMethod": { + "type": "unhosted_wallet", + "accountId": "0xBeneficiaryWallet01" + } + }, + "travelRule": { + "status": "AWAITING_COUNTERPARTY", + "beneficiaryData": { + "wallet_address": "0xBeneficiaryWallet01", + "name": "Ana Diaz" + } + }, + "includeCryptoScreening": true + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v1/transactions/ \\\n -H 'X-Transaction-Token: SDK_TOKEN' -H 'Content-Type: application/json' \\\n -d '{\"txnId\": \"wd-2026-07-07-0042\", \"type\": \"travelRule\", \"info\": {\"direction\": \"out\", \"amount\": \"0.25\", \"currency\": \"ETH\", \"currencyType\": \"crypto\"}, \"subject\": {\"fullName\": \"Jane Doe\"}, \"counterparty\": {\"fullName\": \"Ana Diaz\", \"paymentMethod\": {\"type\": \"unhosted_wallet\", \"accountId\": \"0xBeneficiaryWallet01\"}}, \"travelRule\": {\"status\": \"AWAITING_COUNTERPARTY\", \"beneficiaryData\": {\"wallet_address\": \"0xBeneficiaryWallet01\", \"name\": \"Ana Diaz\"}}, \"includeCryptoScreening\": true}'" + }, + { + "lang": "javascript", + "label": "Web SDK", + "source": "import { DiditSdk } from '@didit-protocol/sdk-web';\n\nconst result = await DiditSdk.shared.submitTransaction({\n transactionToken: sdkToken,\n transaction: {\n txnId: 'wd-2026-07-07-0042',\n category: 'travelRule',\n details: { direction: 'out', amount: '0.25', currency: 'ETH', currencyType: 'crypto' },\n subject: { fullName: 'Jane Doe' },\n counterparty: { fullName: 'Ana Diaz', paymentMethod: { type: 'unhosted_wallet', accountId: '0xBeneficiaryWallet01' } },\n travelRule: { status: 'AWAITING_COUNTERPARTY', beneficiaryData: { wallet_address: '0xBeneficiaryWallet01', name: 'Ana Diaz' } },\n includeCryptoScreening: true\n },\n onActionCompleted: (refreshed) => console.log(refreshed.status)\n});" + } + ], + "responses": { + "201": { + "description": "Transaction submitted and monitored. Same detail shape as `GET /v3/transactions/{transaction_id}/`, including the `action_required` block when the end user must complete a follow-up action (the SDKs auto-launch it by default).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionDetail" + }, + "examples": { + "Wallet ownership required": { + "value": { + "uuid": "22222222-3333-4444-5555-666666666666", + "txn_id": "wd-2026-07-07-0042", + "transaction_type": "travelRule", + "direction": "OUTBOUND", + "status": "APPROVED", + "amount": "0.25", + "currency": "ETH", + "travel_rule": { + "uuid": "33333333-4444-5555-6666-777777777777", + "status": "UNCONFIRMED_OWNERSHIP", + "required": true + }, + "action_required": { + "type": "wallet_ownership", + "url": "https://verify.didit.me/wallet-ownership/3s9x...urlsafe", + "widget_session_id": "77777777-8888-9999-aaaa-bbbbbbbbbbbb", + "expires_at": "2026-07-07T13:00:00Z" + } + } + } + } + } + } + }, + "400": { + "description": "Validation failed (missing required fields, duplicate `txnId`, unknown category).", + "content": { + "application/json": { + "examples": { + "Missing fields": { + "value": { + "transaction_details": { + "currency": [ + "This field is required." + ] + } + } + }, + "Duplicate txnId": { + "value": { + "transaction_id": [ + "A transaction with this transaction_id already exists." + ] + } + } + } + } + } + }, + "401": { + "description": "The transaction token was rejected: missing, unknown, revoked, over its `max_uses`, or expired. Expired-token responses always contain the word `expired` in the detail, which is how the SDKs distinguish `expired_token` from `invalid_token` errors.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "Invalid transaction token." + } + }, + "Expired token": { + "value": { + "detail": "Transaction token expired." + } + } + } + } + } + }, + "409": { + "description": "Another request with the same `txnId` created the transaction while this one was being processed, so only one of two simultaneous submissions succeeds. Read the existing transaction instead of resubmitting.", + "content": { + "application/json": { + "examples": { + "Concurrent duplicate": { + "value": { + "detail": "A transaction with this transaction_id already exists." + } + } + } + } + } + } + } + } + }, + "/v1/transactions/{transaction_id}/": { + "get": { + "summary": "Get a transaction from an SDK", + "description": "Device-facing read endpoint, authenticated with the same `X-Transaction-Token` used to submit. The SDKs poll it after an auto-launched action flow completes or its window closes, so the final state never depends on a single notification. Returns the same transaction detail as the submit response, including the current `action_required` block (`null` once the action is resolved).", + "operationId": "getTransactionFromSdk", + "x-didit-sdk": { + "resource": "transactions", + "method": "retrieve_from_device", + "auth": "device_token", + "server": false + }, + "tags": [ + "Transactions" + ], + "parameters": [ + { + "name": "transaction_id", + "in": "path", + "required": true, + "description": "Didit-stable transaction UUID (the `uuid` from the submit response, **not** your `txnId`).", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "22222222-3333-4444-5555-666666666666" + } + ], + "security": [ + { + "TransactionTokenAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v1/transactions/22222222-3333-4444-5555-666666666666/' \\\n -H 'X-Transaction-Token: SDK_TOKEN'" + } + ], + "responses": { + "200": { + "description": "The full transaction detail, including `action_required` (`null` once the pending action is resolved).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionDetail" + } + } + } + }, + "401": { + "description": "The transaction token was rejected: missing, unknown, revoked, over its `max_uses`, or expired.", + "content": { + "application/json": { + "examples": { + "Expired token": { + "value": { + "detail": "Transaction token expired." + } + } + } + } + } + }, + "404": { + "description": "No transaction with this UUID exists for the token's application, or the path segment is not a valid UUID.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + } + } + } + }, + "/v3/businesses/": { + "get": { + "summary": "List businesses", + "description": "List [businesses](/entities/businesses) Didit assembles from KYB sessions sharing the same `vendor_data` (free-form string, NOT a UUID), plus businesses created via the API. Paginated, ordered by `last_session_at` desc. This endpoint does not support filtering or search parameters — fetch pages and filter client-side.", + "operationId": "list_businesses", + "x-didit-sdk": { + "resource": "businesses", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Businesses" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "description": "Page size. Defaults to 50 when omitted. There is no enforced maximum, but keep pages small for latency." + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset of the first record to return. Prefer following the `next` URL from the previous page over computing this by hand." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/businesses/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/businesses/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n params={'limit': 50},\n)\nresp.raise_for_status()\npage = resp.json()\nfor biz in page['results']:\n print(biz['vendor_data'], biz['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const url = new URL('https://verification.didit.me/v3/businesses/');\nurl.searchParams.set('limit', '50');\n\nconst page = await fetch(url, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n}).then((r) => r.json());\nconsole.log(page.count, page.results.length);" + } + ], + "responses": { + "200": { + "description": "Paginated list of businesses with status, session counts, and feature breakdown.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Number of matching rows — exact up to 100, capped at 100 beyond that for performance. Do not use it to detect the last page; rely on `next` being `null` instead." + }, + "next": { + "type": "string", + "nullable": true, + "description": "Absolute URL of the next page, or `null` on the last page." + }, + "previous": { + "type": "string", + "nullable": true, + "description": "Absolute URL of the previous page, or `null` on the first page." + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BusinessListItem" + } + } + } + }, + "examples": { + "Example": { + "value": { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", + "vendor_data": "company-123", + "display_name": null, + "legal_name": "Acme Trading Ltd", + "registration_number": "12345678", + "country_code": "GB", + "effective_name": "Acme Trading Ltd", + "status": "ACTIVE", + "session_count": 2, + "approved_count": 1, + "declined_count": 0, + "in_review_count": 1, + "features": { + "KYB": "Approved" + }, + "features_list": [ + { + "feature": "KYB", + "status": "Approved" } - } + ], + "last_session_at": "2026-03-15T10:30:00Z", + "last_activity_at": "2026-03-15T10:30:00Z", + "first_session_at": "2026-01-10T08:00:00Z", + "tags": [ + { + "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "name": "VIP", + "color": "#2567FF" + } + ], + "created_at": "2026-01-10T08:00:00Z" + } + ] + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot list businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/customization/": { + "get": { + "summary": "Get branding customization", + "description": "Returns the white-label branding for the application resolved from the API key: colors, fonts, border radii, logo URLs and UI/UX options. Custom domain and white-label email settings are managed only in the Business Console and are not part of this response.", + "operationId": "get_customization", + "x-didit-sdk": { + "resource": "customization", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Customization" + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/customization/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/customization/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n)\nresp.raise_for_status()\nbranding = resp.json()\nprint(branding['font_family'], branding['color_primary'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const branding = await fetch(\n 'https://verification.didit.me/v3/customization/',\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n).then((r) => r.json());\nconsole.log(branding.font_family, branding.color_primary);" + } + ], + "responses": { + "200": { + "description": "Current branding customization for the application.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BrandingCustomization" + }, + "examples": { + "Default branding": { + "value": { + "color_primary": "#000000", + "color_secondary": "#9DA1A1", + "color_background": "#FFFFFF", + "color_panel": "#2567FF", + "color_panel_10": "#1A1A1A", + "color_on_panel_1": "#9DA1A1", + "color_on_panel_2": "#F4F4F6", + "color_on_background": "#000000", + "color_button_1": "#2567FF", + "color_button_2": "#F8F8F8", + "color_button_text_1": "#FFFFFF", + "color_button_text_2": "#000000", + "color_pill_text": "#111827", + "border_radius_panel": 10, + "border_radius_buttons": 12, + "font_family": "Inter", + "font_weight": "regular", + "font_url": "https://fonts.googleapis.com/css2?family=Inter", + "logo_square": null, + "logo_rectangular": null, + "logo_rectangular_dark": null, + "favicon": null, + "app_public_name": "Acme Verify", + "privacy_policy_url": "https://acme.com/privacy", + "skip_welcome_screen": false, + "disable_login_with_didit": false, + "hide_progress_bar": false, + "callback_seconds": 3 + } + } + } + } + } + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "patch": { + "summary": "Update branding customization", + "description": "Partial update of the application's white-label branding. Send `application/json` to change colors, fonts, radii and UI options, or `multipart/form-data` to upload logo images. Only the fields you include are changed. Custom domain and white-label email cannot be set through this endpoint — use the Business Console for those.", + "operationId": "update_customization", + "x-didit-sdk": { + "resource": "customization", + "method": "update", + "auth": "api_key", + "server": true + }, + "tags": [ + "Customization" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BrandingCustomizationUpdate" + }, + "examples": { + "Update colors and font": { + "summary": "Change branding colors and font", + "value": { + "color_primary": "#123456", + "color_button_1": "#2567FF", + "font_family": "AR One Sans", + "border_radius_buttons": 16 + } + }, + "Update UI options": { + "summary": "Change public name and flow options", + "value": { + "app_public_name": "Acme Verify", + "privacy_policy_url": "https://acme.com/privacy", + "skip_welcome_screen": true, + "hide_progress_bar": true, + "callback_seconds": 5 + } + }, + "Remove a logo": { + "summary": "Delete the current rectangular logo", + "value": { + "delete_logo_rectangular": true + } + }, + "Remove the dark-mode logo": { + "summary": "Delete the current dark-mode logo", + "value": { + "delete_logo_rectangular_dark": true + } + } + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "description": "Use multipart/form-data only to upload logo images. Branding fields above may also be sent as form fields in the same request.", + "properties": { + "image_square": { + "type": "string", + "format": "binary", + "description": "Square logo (1:1 aspect ratio). PNG, JPG or WebP, at least 512 px on the long side, at most **1 MB**." + }, + "image_rectangular": { + "type": "string", + "format": "binary", + "description": "Rectangular logo (2:1 aspect ratio). PNG, JPG or WebP, at least 512 px on the long side, at most **1 MB**." + }, + "image_rectangular_dark": { + "type": "string", + "format": "binary", + "description": "Rectangular logo for dark mode (2:1 aspect ratio), shown on a dark background. PNG, JPG or WebP, at least 512 px on the long side, at most **1 MB**. Returned as `logo_rectangular_dark`." + }, + "image_favicon": { + "type": "string", + "format": "binary", + "description": "Favicon image. Max **500 KB**." + } + } + }, + "example": { + "image_rectangular": "(binary PNG logo, 2:1)", + "image_rectangular_dark": "(binary PNG logo for dark mode, 2:1)", + "image_favicon": "(binary favicon)" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/customization/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"color_primary\": \"#123456\", \"font_family\": \"AR One Sans\"}'" + }, + { + "lang": "curl", + "label": "curl (logo upload)", + "source": "curl -X PATCH 'https://verification.didit.me/v3/customization/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'image_rectangular=@logo.png' \\\n -F 'image_rectangular_dark=@logo-dark.png' \\\n -F 'image_favicon=@favicon.ico'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/customization/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'color_primary': '#123456', 'font_family': 'AR One Sans'},\n)\nresp.raise_for_status()\nprint(resp.json()['color_primary'])\n\n# Upload a logo (multipart):\nwith open('logo.png', 'rb') as logo:\n requests.patch(\n 'https://verification.didit.me/v3/customization/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n files={'image_rectangular': logo},\n )" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/customization/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ color_primary: '#123456', font_family: 'AR One Sans' }),\n});\nconst branding = await resp.json();\nconsole.log(branding.color_primary);" + } + ], + "responses": { + "200": { + "description": "Branding updated. The FULL branding object is returned (all fields, not just the ones you changed).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BrandingCustomization" + }, + "examples": { + "Updated": { + "value": { + "color_primary": "#123456", + "color_secondary": "#9DA1A1", + "color_background": "#FFFFFF", + "color_panel": "#2567FF", + "color_panel_10": "#1A1A1A", + "color_on_panel_1": "#9DA1A1", + "color_on_panel_2": "#F4F4F6", + "color_on_background": "#000000", + "color_button_1": "#2567FF", + "color_button_2": "#F8F8F8", + "color_button_text_1": "#FFFFFF", + "color_button_text_2": "#000000", + "color_pill_text": "#111827", + "border_radius_panel": 10, + "border_radius_buttons": 16, + "font_family": "AR One Sans", + "font_weight": "400", + "font_url": "https://fonts.googleapis.com/css2?family=AR+One+Sans", + "logo_square": null, + "logo_rectangular": "https://cdn.didit.me/white_label/logo_rectangular/...", + "logo_rectangular_dark": null, + "favicon": null, + "app_public_name": "Acme Verify", + "privacy_policy_url": "https://acme.com/privacy", + "skip_welcome_screen": true, + "disable_login_with_didit": false, + "hide_progress_bar": false, + "callback_seconds": 3 + } + } + } + } + } + }, + "400": { + "description": "Invalid body. For example: a malformed hex color, an unknown font family or weight, a Business-Console-only field (`domain`, `from_email`, `verify_domain`, `force_remove_domain`, `verify_email_domain`, `force_remove_email_domain`, and their read-only companions), or a logo upload that is not a PNG, JPG or WebP image, is under 512 px on its long side, is over 1 MB, or has the wrong aspect ratio. Also returned when setting `disable_login_with_didit` to `false` while a custom domain is configured.", + "content": { + "application/json": { + "examples": { + "Bad color": { + "value": { + "color_primary": [ + "Invalid color format. Use '#RRGGBB' or '#RRGGBBAA' format." + ] + } + }, + "Bad logo": { + "value": { + "image_rectangular": [ + "Image aspect ratio must be 2:1. Got 1024x1024 image dimensions" + ] + } + }, + "Unsupported logo format": { + "summary": "Not a PNG, JPG or WebP image (an SVG, for example)", + "value": { + "image_rectangular_dark": [ + "Upload a PNG, JPG or WebP image." + ] + } + }, + "Logo too small": { + "summary": "Under 512 px on the long side", + "value": { + "image_rectangular": [ + "Image must be at least 512 px on its long side. Got 400x200 image dimensions" + ] + } + }, + "Logo too large": { + "summary": "Over 1 MB", + "value": { + "image_square": [ + "File size should not exceed 1 MB" + ] + } + }, + "Console-only field": { + "value": { + "domain": [ + "Manage this field in the Business Console." + ] + } + }, + "Bad font weight": { + "value": { + "font_weight": [ + "Font weight 950 is not available for Inter. Available weights are: 100, 200, 300, 400, 500, 600, 700, 800, 900" + ] + } + } + } + } + } + }, + "403": { + "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "content": { + "application/json": { + "examples": { + "Invalid token": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Retry with exponential backoff." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/businesses/{vendor_data}/": { + "get": { + "summary": "Get business", + "description": "Fetch one [business](/entities/businesses) by `vendor_data` (exact match). Adds `metadata` and `updated_at` to the list view. Deleted businesses return 404.", + "operationId": "get_business", + "x-didit-sdk": { + "resource": "businesses", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Businesses" + ], + "parameters": [ + { + "name": "vendor_data", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Your unique identifier for the business — a free-form string (NOT a UUID), matched exactly as sent.", + "example": "company-123" + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/businesses/company-123/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/businesses/company-123/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n)\nresp.raise_for_status()\nbiz = resp.json()\nprint(biz['didit_internal_id'], biz['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const vendorData = encodeURIComponent('company-123');\nconst biz = await fetch(\n `https://verification.didit.me/v3/businesses/${vendorData}/`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n).then((r) => r.json());\nconsole.log(biz.didit_internal_id, biz.status);" + } + ], + "responses": { + "200": { + "description": "Full business detail including metadata and aggregated session data.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessDetailItem" + }, + "examples": { + "Active business": { + "value": { + "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", + "vendor_data": "company-123", + "display_name": null, + "legal_name": "Acme Trading Ltd", + "registration_number": "12345678", + "country_code": "GB", + "effective_name": "Acme Trading Ltd", + "status": "ACTIVE", + "session_count": 2, + "approved_count": 1, + "declined_count": 0, + "in_review_count": 1, + "features": { + "KYB": "Approved" + }, + "features_list": [ + { + "feature": "KYB", + "status": "Approved" + } + ], + "last_session_at": "2026-03-15T10:30:00Z", + "last_activity_at": "2026-03-15T10:30:00Z", + "first_session_at": "2026-01-10T08:00:00Z", + "tags": [ + { + "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "name": "VIP", + "color": "#2567FF" + } + ], + "created_at": "2026-01-10T08:00:00Z", + "metadata": { + "industry": "fintech" + }, + "updated_at": "2026-03-15T10:30:00Z" + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot read this application's businesses. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No (non-deleted) business with the supplied `vendor_data` exists for this application.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "patch": { + "summary": "Update business", + "description": "Partial update of a business: `display_name`, `legal_name`, `registration_number`, `country_code`, `status`, `metadata`. `metadata` fully replaces. Setting `status` to `BLOCKED` adds the `vendor_data` to the system blocklist; moving away from `BLOCKED` removes that entry. Every edit is recorded in the company's activity: a status change with the optional `reason`, and a change to `display_name`, `legal_name`, `registration_number`, `country_code` or `metadata` as a profile update.", + "operationId": "update_business", + "x-didit-sdk": { + "resource": "businesses", + "method": "update", + "auth": "api_key", + "server": true + }, + "tags": [ + "Businesses" + ], + "parameters": [ + { + "name": "vendor_data", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Your unique identifier for the business (free-form string, NOT a UUID).", + "example": "company-123" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Partial-update body. Provide only the fields you want to change.", + "properties": { + "display_name": { + "type": "string", + "nullable": true, + "description": "Friendly display name shown in the console (takes precedence over `legal_name` for UI display)." + }, + "legal_name": { + "type": "string", + "nullable": true, + "description": "Official legal name from registry or manual entry." + }, + "registration_number": { + "type": "string", + "nullable": true, + "description": "Company registration or incorporation number." + }, + "country_code": { + "type": "string", + "nullable": true, + "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `US`)." + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "FLAGGED", + "BLOCKED" + ], + "description": "Business lifecycle status. Setting `BLOCKED` automatically adds the `vendor_data` to the system blocklist; flipping away from `BLOCKED` removes that blocklist entry." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Arbitrary JSON object you attach to the business. **Fully replaces** the existing metadata on update — merge client-side if you only want to add keys." + }, + "reason": { + "type": "string", + "nullable": true, + "maxLength": 1000, + "description": "Why the status changed. Recorded on the status-change entry of the company's **Activity** in the Business Console, only when the status actually changes. Webhooks do not carry it." + } + } + }, + "examples": { + "Update name": { + "summary": "Update display name", + "value": { + "display_name": "Acme Corp" + } + }, + "Flag for review": { + "summary": "Flip status", + "value": { + "status": "FLAGGED" + } + }, + "Add metadata": { + "summary": "Replace metadata JSON", + "value": { + "metadata": { + "industry": "fintech", + "risk_level": "low" + } + } + }, + "Fix registry data": { + "summary": "Correct extracted fields", + "value": { + "legal_name": "Acme Trading International Ltd", + "registration_number": "12345678", + "country_code": "GB" + } + }, + "Flag with reason": { + "summary": "Flag the business and record why", + "value": { + "status": "FLAGGED", + "reason": "Ownership structure under review" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/businesses/company-123/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"display_name\": \"Acme Corp\", \"status\": \"FLAGGED\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/businesses/company-123/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'display_name': 'Acme Corp', 'status': 'FLAGGED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/company-123/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ display_name: 'Acme Corp', status: 'FLAGGED' }),\n});\nconst biz = await resp.json();\nconsole.log(biz.status);" + } + ], + "responses": { + "200": { + "description": "Business updated. The full updated business record is returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessDetailItem" + }, + "examples": { + "Updated": { + "value": { + "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", + "vendor_data": "company-123", + "display_name": "Acme Corp", + "legal_name": "Acme Trading Ltd", + "registration_number": "12345678", + "country_code": "GB", + "effective_name": "Acme Corp", + "status": "FLAGGED", + "session_count": 2, + "approved_count": 1, + "declined_count": 0, + "in_review_count": 1, + "features": { + "KYB": "Approved" + }, + "features_list": [ + { + "feature": "KYB", + "status": "Approved" + } + ], + "last_session_at": "2026-03-15T10:30:00Z", + "last_activity_at": "2026-03-15T10:30:00Z", + "first_session_at": "2026-01-10T08:00:00Z", + "tags": [ + { + "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "name": "VIP", + "color": "#2567FF" } + ], + "created_at": "2026-01-10T08:00:00Z", + "metadata": { + "industry": "fintech", + "risk_level": "low" + }, + "updated_at": "2026-03-15T10:30:00Z" + } + } + } + } + } + }, + "400": { + "description": "Invalid body: typically an unknown `status`, malformed `metadata` or a `reason` over 1000 characters.", + "content": { + "application/json": { + "examples": { + "Bad status": { + "value": { + "status": [ + "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" + ] + } + }, + "Bad metadata": { + "value": { + "metadata": [ + "Metadata must be a JSON object." + ] + } + }, + "Reason too long": { + "value": { + "reason": [ + "Ensure this field has no more than 1000 characters." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot update businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No (non-deleted) business with the supplied `vendor_data` exists for this application.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/businesses/{vendor_data}/update-status/": { + "patch": { + "summary": "Update business status", + "description": "Flip only the lifecycle `status` of a business: `ACTIVE`, `FLAGGED` or `BLOCKED` (not session statuses). `BLOCKED` adds the `vendor_data` to the system blocklist; moving away from `BLOCKED` removes that entry. Each change is recorded in the company's activity, with the optional `reason`. Sending the current status changes nothing and records no `reason`. The `business.status.updated` webhook does not carry `reason`.", + "operationId": "update_business_status", + "x-didit-sdk": { + "resource": "businesses", + "method": "update_status", + "auth": "api_key", + "server": true + }, + "tags": [ + "Businesses" + ], + "parameters": [ + { + "name": "vendor_data", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Your unique identifier for the business (free-form string, NOT a UUID).", + "example": "company-123" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/EntityStatusUpdateRequest" + }, + { + "type": "object", + "properties": { + "reason": { + "type": "string", + "nullable": true, + "maxLength": 1000, + "description": "Why the status changed. Recorded on the status-change entry of the company's **Activity** in the Business Console, only when the status actually changes. Webhooks do not carry it." } + } + } + ] + }, + "examples": { + "Flag": { + "summary": "Flag the business for review", + "value": { + "status": "FLAGGED" + } + }, + "Block": { + "summary": "Block the business (adds to system blocklist)", + "value": { + "status": "BLOCKED" + } + }, + "Reactivate": { + "summary": "Reactivate the business", + "value": { + "status": "ACTIVE" + } + }, + "Block with reason": { + "summary": "Block the business and record why", + "value": { + "status": "BLOCKED", + "reason": "Registry shows the company dissolved" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/businesses/company-123/update-status/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"status\": \"BLOCKED\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/businesses/company-123/update-status/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'status': 'BLOCKED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/company-123/update-status/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'BLOCKED' }),\n});\nconst biz = await resp.json();\nconsole.log(biz.status);" + } + ], + "responses": { + "200": { + "description": "Business status updated. Full business record returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessDetailItem" + }, + "examples": { + "Blocked": { + "value": { + "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", + "vendor_data": "company-123", + "display_name": null, + "legal_name": "Acme Trading Ltd", + "registration_number": "12345678", + "country_code": "GB", + "effective_name": "Acme Trading Ltd", + "status": "BLOCKED", + "session_count": 2, + "approved_count": 1, + "declined_count": 0, + "in_review_count": 1, + "features": { + "KYB": "Approved" + }, + "features_list": [ + { + "feature": "KYB", + "status": "Approved" + } + ], + "last_session_at": "2026-03-15T10:30:00Z", + "last_activity_at": "2026-03-15T10:30:00Z", + "first_session_at": "2026-01-10T08:00:00Z", + "tags": [ + { + "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "name": "VIP", + "color": "#2567FF" + } + ], + "created_at": "2026-01-10T08:00:00Z", + "metadata": { + "industry": "fintech" + }, + "updated_at": "2026-03-15T10:30:00Z" + } + } + } + } + } + }, + "400": { + "description": "Missing or invalid `status`, or a `reason` over 1000 characters.", + "content": { + "application/json": { + "examples": { + "Missing status": { + "value": { + "status": [ + "This field is required." + ] + } + }, + "Bad status": { + "value": { + "status": [ + "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" + ] + } + }, + "Reason too long": { + "value": { + "reason": [ + "Ensure this field has no more than 1000 characters." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot update businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No (non-deleted) business with the supplied `vendor_data` exists for this application.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/businesses/delete/": { + "post": { + "summary": "Batch delete businesses", + "description": "**Permanently delete** businesses — this cannot be undone. For blocking prefer [Update Business Status](#patch-/v3/businesses/-vendor_data-/update-status/) with `BLOCKED`.", + "operationId": "batch_delete_businesses", + "x-didit-sdk": { + "resource": "businesses", + "method": "batch_delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Businesses" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Provide `vendor_data_list`, `didit_internal_id_list`, or `delete_all: true`. Precedence when several are sent: `delete_all` > `vendor_data_list` > `didit_internal_id_list` (only the highest-precedence selector is used).", + "properties": { + "vendor_data_list": { + "type": "array", + "items": { + "type": "string" }, - "nfc_skip_reason": { + "description": "Your own identifiers to delete, matched exactly as sent." + }, + "didit_internal_id_list": { + "type": "array", + "items": { "type": "string", - "nullable": true, - "enum": [ - "USER_SKIPPED", - "DOCUMENT_WITHOUT_CHIP", - "CHIP_CERTIFICATE_UNAVAILABLE", - "DEVICE_WITHOUT_NFC", - "INTEGRATION_WITHOUT_NFC_ACCESS", - "MRZ_KEY_UNAVAILABLE" - ], - "description": "Why NFC chip verification did not run, populated whenever the workflow includes an NFC step. `null` while NFC is pending or when the chip was actually read. Values: `USER_SKIPPED` - the user chose to skip on an NFC-capable device (allowed by `allow_nfc_skip`); `DOCUMENT_WITHOUT_CHIP` - the document has no readable NFC chip (not an ICAO 9303 electronic document); `CHIP_CERTIFICATE_UNAVAILABLE` - the issuing country's signing certificate (CSCA) covering the document's issue period is unavailable, so the chip signature cannot be verified; `DEVICE_WITHOUT_NFC` - the device reported no NFC reader; `INTEGRATION_WITHOUT_NFC_ACCESS` - the verification ran without a native SDK (e.g. in a web browser), which has no access to the phone's NFC reader; `MRZ_KEY_UNAVAILABLE` - the chip access key could not be derived from the document's MRZ." + "format": "uuid" }, - "nfc_verifications": { - "type": "array", - "nullable": true, - "description": "**Array of NFC / ePassport reports** produced by NFC steps in the workflow graph. **This is always a JSON array, never a singular `nfc` object** — read `nfc_verifications[0]` and iterate if your workflow can run NFC more than once. `null` until at least one NFC step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `chip_data`, `authenticity` (SOD + DG integrity), `certificate_summary`, and `warnings[]`. When the NFC step was bypassed before it could ever be offered (chipless document, missing certificate, no NFC capability reported, unreadable MRZ key), this stays `null` and `nfc_skip_reason` explains why; a skip at the step itself (user tapped skip, or the reader turned out unavailable at scan time) still produces a report with `is_nfc_skipped: true`.", - "items": { - "type": "object", - "description": "One NFC report — the full report for a single ePassport chip read.", - "properties": { - "node_id": { - "type": "string", - "description": "Workflow graph node id of the NFC step that produced this report. Use it to correlate the report with the workflow definition." - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "description": "Feature-level status of this NFC step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`." - }, - "is_nfc_skipped": { - "type": "boolean", - "description": "`true` when this NFC step was explicitly skipped instead of read; `skip_reason` says why." - }, - "skip_reason": { - "type": "string", - "nullable": true, - "enum": [ - "USER_SKIPPED", - "DOCUMENT_WITHOUT_CHIP", - "CHIP_CERTIFICATE_UNAVAILABLE", - "DEVICE_WITHOUT_NFC", - "INTEGRATION_WITHOUT_NFC_ACCESS", - "MRZ_KEY_UNAVAILABLE" - ], - "description": "Why this NFC step was skipped. Only set when `is_nfc_skipped` is `true`. Same taxonomy as the session-level `nfc_skip_reason`, which additionally covers the cases where NFC never produced a report at all." - }, - "portrait_image": { - "type": "string", - "format": "uri" - }, - "signature_image": { - "type": "string", - "format": "uri" - }, - "chip_data": { - "type": "object", - "properties": { - "document_type": { - "type": "string" - }, - "issuing_country": { - "type": "string" - }, - "document_number": { - "type": "string" - }, - "expiration_date": { - "type": "string", - "format": "date" - }, - "first_name": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "birth_date": { - "type": "string", - "format": "date" - }, - "gender": { - "type": "string" - }, - "nationality": { - "type": "string" - }, - "address": { - "type": "string" - }, - "place_of_birth": { - "type": "string" - } - } - }, - "authenticity": { - "type": "object", - "properties": { - "sod_integrity": { - "type": "boolean" - }, - "dg_integrity": { - "type": "boolean" - } - } - }, - "certificate_summary": { - "type": "object", - "properties": { - "issuer": { - "type": "string" - }, - "subject": { - "type": "string" - }, - "serial_number": { - "type": "string" - }, - "not_valid_after": { - "type": "string", - "format": "date-time" - }, - "not_valid_before": { - "type": "string", - "format": "date-time" - } - } - }, - "warnings": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACEMATCH", - "PROOF_OF_ADDRESS", - "PHONE", - "EMAIL", - "AML", - "LOCATION", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ], - "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." - }, - "risk": { - "type": "string" - }, - "additional_data": { - "type": "object", - "nullable": true - }, - "log_type": { - "type": "string" - }, - "short_description": { - "type": "string" - }, - "long_description": { - "type": "string" - }, - "node_id": { - "type": "string", - "description": "The workflow graph node ID that generated this warning" - } - } - } - } - } - } + "description": "Didit's internal UUIDs (`didit_internal_id`) to delete." + }, + "delete_all": { + "type": "boolean", + "default": false, + "description": "If `true`, deletes every business in the application. Cannot be combined with the list selectors." + } + } + }, + "examples": { + "Specific businesses": { + "summary": "Delete by vendor_data", + "value": { + "vendor_data_list": [ + "company-123", + "company-456" + ] + } + }, + "Specific internal IDs": { + "summary": "Delete by didit_internal_id", + "value": { + "didit_internal_id_list": [ + "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "b2c3d4e5-f6a7-8901-bcde-f12345678901" + ] + } + }, + "All businesses": { + "summary": "Wipe every business in this application", + "value": { + "delete_all": true + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/businesses/delete/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"vendor_data_list\": [\"company-123\", \"company-456\"]}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/businesses/delete/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'vendor_data_list': ['company-123', 'company-456']},\n)\nresp.raise_for_status()\nprint('deleted', resp.json()['deleted'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/delete/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ vendor_data_list: ['company-123', 'company-456'] }),\n});\nconst body = await resp.json();\nconsole.log('deleted', body.deleted);" + } + ], + "responses": { + "200": { + "description": "Businesses deleted. Returns the number of rows actually removed.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "deleted": { + "type": "integer", + "description": "Number of businesses deleted (excludes vendor_data values that didn't match anything)." + } + } + }, + "examples": { + "Deleted some": { + "value": { + "deleted": 2 + } + }, + "Nothing matched": { + "value": { + "deleted": 0 + } + } + } + } + } + }, + "400": { + "description": "No selector supplied (all of `vendor_data_list`, `didit_internal_id_list`, and `delete_all` missing or empty). The error body is a JSON array.", + "content": { + "application/json": { + "examples": { + "Missing selector": { + "value": [ + "Provide vendor_data_list, didit_internal_id_list, or set delete_all=true." + ] + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot delete businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/users/aml-monitoring/": { + "post": { + "summary": "Enable or disable AML ongoing monitoring for users", + "description": "Turns AML ongoing monitoring on or off for the selected users, with the same semantics as the Business Console. Exactly one monitored AML screening is kept per user, so a profile that verified several times is monitored and billed once; disabling turns monitoring off across all of the profile's screenings. Users without any AML screening are screened first (billed as a regular AML screening) when they have the full name, date of birth and country needed; otherwise they are reported as `skipped_no_identity_data`.\n\nSet `estimate_only: true` to preview counts and costs without writing anything. Selections of 20 profiles or fewer resolve inline and return `200` with the completed job; larger selections return `202` and run in the background - poll [Get AML monitoring job](#get-/v3/aml-monitoring/jobs/-job_id-/) with the returned `job_id`. Enabling is refused with `403` when the organization balance does not cover the estimate. Identifiers that match nothing are listed in `unmatched` and never fail the request: a selection that matches nothing completes as an empty job (`total` 0), the same convention as the batch delete endpoints.", + "operationId": "toggle_users_aml_monitoring", + "x-didit-sdk": { + "resource": "users", + "method": "set_aml_monitoring", + "auth": "api_key", + "server": true + }, + "tags": [ + "AML Ongoing Monitoring" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "is_enabled" + ], + "description": "Select users with `vendor_data_list`, `didit_internal_id_list`, or both (the two lists are OR'd). At most 1000 identifiers per request across both lists.", + "properties": { + "is_enabled": { + "type": "boolean", + "description": "true to enable monitoring, false to disable it." + }, + "estimate_only": { + "type": "boolean", + "default": false, + "description": "When true, returns the cost estimate and performs no writes - no screening, no billing, no job." + }, + "vendor_data_list": { + "type": "array", + "items": { + "type": "string" }, - "liveness_checks": { - "type": "array", - "nullable": true, - "description": "**Array of liveness reports** produced by liveness steps in the workflow graph. Always a JSON array — never a singular `liveness` object. `null` until at least one liveness step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `method`, `score`, `reference_image`, `video_url`, optional `age_estimation`, cross-session `matches[]`, and `warnings[]`.", - "items": { - "type": "object", - "description": "One liveness report — the full report for a single liveness pass.", - "properties": { - "node_id": { - "type": "string", - "description": "Workflow graph node id of the liveness step that produced this report." - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "description": "Feature-level status of this liveness step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`." - }, - "method": { - "type": "string", - "description": "Method used for liveness detection" - }, - "score": { - "type": "number", - "description": "Confidence score of the liveness check" - }, - "reference_image": { - "type": "string", - "format": "uri", - "description": "URL of the reference image used" - }, - "video_url": { - "type": "string", - "format": "uri", - "description": "URL of the liveness check video" - }, - "age_estimation": { - "type": "number", - "description": "Estimated age of the person" - }, - "matches": { - "type": "array", - "description": "Similar faces found by the duplicate-face search (top matches, blocklisted/allowlisted entries first). Each entry's `source` tells you where the matching face came from: `session` (another verification session), `imported` (a face uploaded against a vendor user), `list_entry` (an org-managed allow/block list face), or `retained_template` (an image-free biometric template stored on a User, listed under `/v3/biometric-templates/`). `session_id`, `session_number`, `status` and `api_service` are `null` for non-session sources. `imported` matches still populate `vendor_data`, `verification_date` and — when the vendor user has a name on file — `user_details` (with `full_name` only) from the vendor-user record; `retained_template` matches populate `vendor_user_id`, `vendor_data`, `biometric_template_id` and `verification_date`, while their image and identity fields are `null`; `list_entry` matches return all of these as `null`.", - "items": { - "type": "object", - "properties": { - "session_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Session ID of the matching face. `null` when `source` is `imported`, `list_entry`, or `retained_template`." - }, - "session_number": { - "type": "integer", - "nullable": true - }, - "similarity_percentage": { - "type": "number", - "description": "Face similarity between the two images, clamped to 0–100." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "`vendor_data` of the matching session or linked User for imported and retained-template faces." - }, - "vendor_user_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Linked User id for `imported` and `retained_template` matches and for session matches linked to a User. `null` for list entries and sessions without a User." - }, - "biometric_template_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Id of the retained biometric template. Set only when `source` is `retained_template`; `null` otherwise." - }, - "verification_date": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the matching face was captured, or when a retained template was created. `null` for list entries." - }, - "user_details": { - "type": "object", - "nullable": true, - "description": "Identity data from the matching session or imported User profile. Always `null` for `retained_template` and `list_entry` matches.", - "properties": { - "full_name": { - "type": "string", - "nullable": true - }, - "document_type": { - "type": "string", - "nullable": true - }, - "document_number": { - "type": "string", - "nullable": true - } - } - }, - "match_image_url": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Reference image for image-backed matches. Always `null` for `retained_template` matches because retained templates contain no image." - }, - "status": { - "type": "string", - "nullable": true, - "description": "Lifecycle status of the matching session. `null` for non-session sources." - }, - "is_blocklisted": { - "type": "boolean", - "description": "Whether the matching face is on a blocklist." - }, - "is_allowlisted": { - "type": "boolean", - "description": "Whether the matching face is on an allowlist." - }, - "api_service": { - "type": "string", - "nullable": true, - "description": "Standalone API service of the matching session (e.g. `PASSIVE_LIVENESS`, `FACE_MATCH`); `null` for workflow sessions and non-session sources." - }, - "source": { - "type": "string", - "enum": [ - "session", - "imported", - "list_entry", - "retained_template" - ], - "description": "Origin of the matching face: `session`, `imported` (User face upload), `list_entry` (allow/block list), or `retained_template` (an image-free biometric template stored on a User, listed under `/v3/biometric-templates/`)." - } - } - } - }, - "warnings": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACEMATCH", - "PROOF_OF_ADDRESS", - "PHONE", - "EMAIL", - "AML", - "LOCATION", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ], - "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." - }, - "risk": { - "type": "string" - }, - "additional_data": { - "type": "object", - "nullable": true - }, - "log_type": { - "type": "string" - }, - "short_description": { - "type": "string" - }, - "long_description": { - "type": "string" - }, - "node_id": { - "type": "string", - "description": "The workflow graph node ID that generated this warning" - } - } - } - }, - "face_quality": { - "type": "number", - "nullable": true, - "description": "Face image quality score (0-100%). Only available for passive liveness." - }, - "face_luminance": { - "type": "number", - "nullable": true, - "description": "Face luminance/brightness score (0-100%). Only available for passive liveness." - } - } - } + "description": "Your own identifiers of the users to toggle, matched exactly as sent." + }, + "didit_internal_id_list": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "Didit internal ids of the users to toggle - the `didit_internal_id` returned by the create, get and list endpoints." + } + } + }, + "examples": { + "Estimate first": { + "summary": "Preview the cost without writing", + "value": { + "is_enabled": true, + "estimate_only": true, + "vendor_data_list": [ + "user-123", + "user-456" + ] + } + }, + "Enable": { + "summary": "Enable monitoring for two users", + "value": { + "is_enabled": true, + "vendor_data_list": [ + "user-123", + "user-456" + ] + } + }, + "Disable": { + "summary": "Disable monitoring by Didit internal id", + "value": { + "is_enabled": false, + "didit_internal_id_list": [ + "3f1c0d3e-8f0a-4c2e-9a3d-9b1f5f4a2c11" + ] + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/users/aml-monitoring/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"is_enabled\": true, \"vendor_data_list\": [\"user-123\", \"user-456\"]}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/users/aml-monitoring/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'is_enabled': True, 'vendor_data_list': ['user-123', 'user-456']},\n)\nresp.raise_for_status()\njob = resp.json()\nprint(job['status'], job['outcomes'])\nfor row in job['results']:\n print(row['vendor_data'], row['outcome'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/users/aml-monitoring/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ is_enabled: true, vendor_data_list: ['user-123', 'user-456'] }),\n});\nconst job = await resp.json();\nconsole.log(job.status, job.outcomes);\nfor (const row of job.results) console.log(row.vendor_data, row.outcome);" + } + ], + "responses": { + "200": { + "description": "The estimate (when `estimate_only` is true) or the completed job for a selection processed inline.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AMLMonitoringToggleResult" + } + } + } + }, + "202": { + "description": "Larger selections run in the background. Poll the job endpoint with `job_id`; `results` fills in as profiles are processed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AMLMonitoringJob" + } + } + } + }, + "400": { + "description": "Validation error: `is_enabled` missing, no selector, or more than 1000 identifiers across both lists." + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "Insufficient balance to enable monitoring for this selection. The response carries the `estimate` so you can top up the right amount.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" }, - "face_matches": { - "type": "array", - "nullable": true, - "description": "**Array of face-match reports** produced by face-match steps in the workflow graph. Always a JSON array — never a singular `face_match` object. `null` until at least one face-match step has produced data. Each item is the full report, including `node_id`, feature-level `status`, similarity `score`, `source_image`, `target_image`, and `warnings[]`.", - "items": { - "type": "object", - "description": "One face-match report — the full report.", - "properties": { - "node_id": { - "type": "string", - "description": "Workflow graph node id of the face-match step that produced this report." - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "description": "Feature-level status of this face-match step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`." - }, - "score": { - "type": "number", - "description": "Confidence score of the face match" - }, - "source_image_session_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Session id under which the source (reference) portrait is stored. In biometric-authentication flows this is the session's own id (the submitted portrait is stored under the new session). `null` when the source image is the same session's document portrait." - }, - "source_image": { - "type": "string", - "format": "uri", - "description": "URL of the source image used for comparison" - }, - "target_image": { - "type": "string", - "format": "uri", - "description": "URL of the target image used for comparison" - }, - "warnings": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACEMATCH", - "PROOF_OF_ADDRESS", - "PHONE", - "EMAIL", - "AML", - "LOCATION", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ], - "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." - }, - "risk": { - "type": "string" - }, - "additional_data": { - "type": "object", - "nullable": true - }, - "log_type": { - "type": "string" - }, - "short_description": { - "type": "string" - }, - "long_description": { - "type": "string" - }, - "node_id": { - "type": "string", - "description": "The workflow graph node ID that generated this warning" - } - } - }, - "description": "Face match verification warnings" - } + "estimate": { + "type": "object", + "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", + "properties": { + "users_total": { + "type": "integer", + "description": "Profiles matched by the selection." + }, + "already_enabled": { + "type": "integer", + "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." + }, + "with_existing_aml": { + "type": "integer", + "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." + }, + "needing_screening": { + "type": "integer", + "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." + }, + "skipped_no_identity_data": { + "type": "integer", + "description": "Profiles that cannot be screened because identity data is missing." + }, + "skipped_blocked": { + "type": "integer", + "description": "Blocked users that will be skipped (always 0 for businesses)." + }, + "estimated_screening_cost_usd": { + "type": "number", + "example": 0.2, + "description": "One-time cost of the new screenings." + }, + "estimated_annual_monitoring_cost_usd": { + "type": "number", + "example": 0.07, + "description": "Yearly monitoring cost of the profiles that will become monitored." + }, + "balance_sufficient": { + "type": "boolean", + "description": "Whether the organization balance covers the screening plus the first year of monitoring." } } + } + } + } + } + } + } + } + } + }, + "/v3/businesses/aml-monitoring/": { + "post": { + "summary": "Enable or disable AML ongoing monitoring for businesses", + "description": "Turns AML ongoing monitoring on or off for the selected businesses, with the same semantics as the Business Console. Exactly one monitored AML screening is kept per business, so a profile that verified several times is monitored and billed once; disabling turns monitoring off across all of the profile's screenings. Businesses without any AML screening are screened first (billed as a regular AML screening) when they have the legal or display name needed; otherwise they are reported as `skipped_no_identity_data`.\n\nSet `estimate_only: true` to preview counts and costs without writing anything. Selections of 20 profiles or fewer resolve inline and return `200` with the completed job; larger selections return `202` and run in the background - poll [Get AML monitoring job](#get-/v3/aml-monitoring/jobs/-job_id-/) with the returned `job_id`. Enabling is refused with `403` when the organization balance does not cover the estimate. Identifiers that match nothing are listed in `unmatched` and never fail the request: a selection that matches nothing completes as an empty job (`total` 0), the same convention as the batch delete endpoints.", + "operationId": "toggle_businesses_aml_monitoring", + "x-didit-sdk": { + "resource": "businesses", + "method": "set_aml_monitoring", + "auth": "api_key", + "server": true + }, + "tags": [ + "AML Ongoing Monitoring" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "is_enabled" + ], + "description": "Select businesses with `vendor_data_list`, `didit_internal_id_list`, or both (the two lists are OR'd). At most 1000 identifiers per request across both lists.", + "properties": { + "is_enabled": { + "type": "boolean", + "description": "true to enable monitoring, false to disable it." + }, + "estimate_only": { + "type": "boolean", + "default": false, + "description": "When true, returns the cost estimate and performs no writes - no screening, no billing, no job." + }, + "vendor_data_list": { + "type": "array", + "items": { + "type": "string" }, - "phone_verifications": { - "type": "array", - "nullable": true, - "description": "**Array of phone verification reports** — always a JSON array, never a singular `phone` object. `null` until at least one phone step has produced data. Each item is the full report, including `node_id`, feature-level `status`, the phone number and country metadata, `lifecycle[]`, `matches[]`, and `warnings[]`.", - "items": { - "type": "object", - "properties": { - "node_id": { - "type": "string", - "description": "Unique identifier for this phone step in the workflow graph." - }, - "status": { - "type": "string", - "description": "Feature-level status of this phone verification step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Expired" - ] - }, - "phone_number_prefix": { - "type": "string", - "description": "Country calling code prefix" - }, - "phone_number": { - "type": "string", - "description": "Phone number without country prefix" - }, - "full_number": { - "type": "string", - "description": "Complete phone number with country prefix" - }, - "country_code": { - "type": "string", - "description": "Two-letter country code (ISO 3166-1 alpha-2)" - }, - "country_name": { - "type": "string", - "description": "Full country name" - }, - "carrier": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of the phone carrier" - }, - "type": { - "type": "string", - "description": "Type of phone line (mobile, landline, etc)" - } - } - }, - "is_disposable": { - "type": "boolean", - "description": "Whether the phone number is from a disposable service" - }, - "is_virtual": { - "type": "boolean", - "description": "Whether the phone number is virtual" - }, - "verification_method": { - "type": "string", - "description": "Method used to verify the phone number" - }, - "verification_attempts": { - "type": "integer", - "description": "Number of verification attempts made" - }, - "verified_at": { - "type": "string", - "format": "date-time", - "description": "Timestamp when the phone was verified" - }, - "lifecycle": { - "type": "array", - "description": "Chronological list of events in the phone verification lifecycle.", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "details": { - "type": "object", - "nullable": true, - "description": "Event-specific payload. Send events carry `status`, `reason`, `channel`, `actual_channel`; delivery events carry `channel`, `status`; code-check events carry `code_tried`, `status`; final status events carry `reason` — or `null` when there is no reason to report (e.g. `PHONE_VERIFICATION_APPROVED`).", - "properties": { - "status": { - "type": "string" - }, - "reason": { - "type": "string", - "nullable": true - }, - "channel": { - "type": "string", - "nullable": true - }, - "actual_channel": { - "type": "string", - "nullable": true - }, - "code_tried": { - "type": "string" - } - } - }, - "fee": { - "type": "number" - } - } - } - }, - "warnings": { - "type": "array", - "description": "List of warnings related to phone verification", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACEMATCH", - "PROOF_OF_ADDRESS", - "PHONE", - "EMAIL", - "AML", - "LOCATION", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ], - "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." - }, - "risk": { - "type": "string" - }, - "additional_data": { - "type": "object", - "nullable": true - }, - "log_type": { - "type": "string" - }, - "short_description": { - "type": "string" - }, - "long_description": { - "type": "string" - }, - "node_id": { - "type": "string", - "description": "The workflow graph node ID that generated this warning" - } - } - } - }, - "matches": { - "type": "array", - "description": "Matches found for this phone number. Each match has a `source`: `session` (another verification session in your application matched on the same number, returned with `session_id`/`session_number`/`vendor_data`/`verification_date`) or `list_entry` (the number is on an org-managed allow/block list — see `/management-api/lists/overview`). Cross-session matches use different `vendor_data` and are capped at 5.", - "items": { - "type": "object", - "properties": { - "session_id": { - "type": "string", - "format": "uuid", - "nullable": true - }, - "session_number": { - "type": "integer", - "nullable": true - }, - "vendor_data": { - "type": "string", - "nullable": true - }, - "verification_date": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "phone_number": { - "type": "string" - }, - "status": { - "type": "string", - "nullable": true - }, - "is_blocklisted": { - "type": "boolean" - }, - "api_service": { - "type": "string", - "nullable": true - }, - "source": { - "type": "string", - "enum": [ - "session", - "list_entry" - ], - "description": "Where this match came from. `session` means another verification session in your application matched on this value. `list_entry` means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at `/management-api/lists/overview` for how to manage those lists." - } - } - } - } - } - } + "description": "Your own identifiers of the businesses to toggle, matched exactly as sent." + }, + "didit_internal_id_list": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" }, - "email_verifications": { - "type": "array", - "nullable": true, - "description": "**Array of email verification reports** — always a JSON array, never a singular `email` object. `null` until at least one email step has produced data. Each item is the full report, including `node_id`, feature-level `status`, the email address, breach data, `lifecycle[]`, `matches[]`, and `warnings[]`.", - "items": { - "type": "object", - "properties": { - "node_id": { - "type": "string", - "description": "Unique identifier for this email step in the workflow graph." - }, - "status": { - "type": "string", - "description": "Feature-level status of this email verification step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Expired" - ] - }, - "email": { - "type": "string", - "description": "The email address being verified" - }, - "is_breached": { - "type": "boolean", - "description": "Indicates if the email was found in known data breaches" - }, - "breaches": { - "type": "array", - "description": "List of known breaches this email was found in", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of the breach" - }, - "domain": { - "type": "string", - "description": "Domain affected by the breach" - }, - "logo_path": { - "type": "string", - "description": "URL to the logo of the breached service" - }, - "breach_date": { - "type": "string", - "format": "date", - "description": "Date when the breach occurred" - }, - "description": { - "type": "string", - "description": "Description of the breach" - }, - "is_verified": { - "type": "boolean", - "description": "Whether the breach is verified" - }, - "data_classes": { - "type": "array", - "description": "Types of data exposed in the breach", - "items": { - "type": "string" - } - }, - "breach_emails_count": { - "type": "integer", - "description": "Number of email addresses affected in the breach" - } - } - } - }, - "is_disposable": { - "type": "boolean", - "description": "Indicates if the email is from a disposable provider" - }, - "is_undeliverable": { - "type": "boolean", - "description": "Indicates if the email address is undeliverable" - }, - "verification_attempts": { - "type": "integer", - "description": "Number of verification attempts for this email" - }, - "verified_at": { - "type": "string", - "format": "date-time", - "description": "Timestamp when the email was verified" - }, - "warnings": { - "type": "array", - "description": "List of warnings related to email verification", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACEMATCH", - "PROOF_OF_ADDRESS", - "PHONE", - "EMAIL", - "AML", - "LOCATION", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ], - "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." - }, - "risk": { - "type": "string" - }, - "additional_data": { - "type": "object", - "nullable": true - }, - "log_type": { - "type": "string" - }, - "short_description": { - "type": "string" - }, - "long_description": { - "type": "string" - }, - "node_id": { - "type": "string", - "description": "The workflow graph node ID that generated this warning" - } - } - } - }, - "lifecycle": { - "type": "array", - "description": "Chronological list of events in the email verification lifecycle.", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "details": { - "type": "object", - "nullable": true, - "description": "Event-specific payload. Send events carry `status`, `reason`; code-check events carry `code_tried`, `status`; final status events carry `reason` — or `null` when there is no reason to report (e.g. `EMAIL_VERIFICATION_APPROVED`).", - "properties": { - "status": { - "type": "string" - }, - "reason": { - "type": "string", - "nullable": true - }, - "code_tried": { - "type": "string" - } - } - }, - "fee": { - "type": "number" - } - } - } - }, - "matches": { - "type": "array", - "description": "Matches found for this email address. Each match has a `source`: `session` (another verification session in your application matched on the same email, returned with `session_id`/`session_number`/`vendor_data`/`verification_date`) or `list_entry` (the email is on an org-managed allow/block list — see `/management-api/lists/overview`). Cross-session matches use different `vendor_data` and are capped at 5.", - "items": { - "type": "object", - "properties": { - "session_id": { - "type": "string", - "format": "uuid", - "nullable": true - }, - "session_number": { - "type": "integer", - "nullable": true - }, - "vendor_data": { - "type": "string", - "nullable": true - }, - "verification_date": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "email": { - "type": "string", - "format": "email" - }, - "status": { - "type": "string", - "nullable": true - }, - "is_blocklisted": { - "type": "boolean" - }, - "api_service": { - "type": "string", - "nullable": true - }, - "source": { - "type": "string", - "enum": [ - "session", - "list_entry" - ], - "description": "Where this match came from. `session` means another verification session in your application matched on this value. `list_entry` means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at `/management-api/lists/overview` for how to manage those lists." - } - } - } - } + "description": "Didit internal ids of the businesses to toggle - the `didit_internal_id` returned by the create, get and list endpoints." + } + } + }, + "examples": { + "Estimate first": { + "summary": "Preview the cost without writing", + "value": { + "is_enabled": true, + "estimate_only": true, + "vendor_data_list": [ + "company-123", + "company-456" + ] + } + }, + "Enable": { + "summary": "Enable monitoring for two businesses", + "value": { + "is_enabled": true, + "vendor_data_list": [ + "company-123", + "company-456" + ] + } + }, + "Disable": { + "summary": "Disable monitoring by Didit internal id", + "value": { + "is_enabled": false, + "didit_internal_id_list": [ + "3f1c0d3e-8f0a-4c2e-9a3d-9b1f5f4a2c11" + ] + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/businesses/aml-monitoring/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"is_enabled\": true, \"vendor_data_list\": [\"company-123\", \"company-456\"]}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/businesses/aml-monitoring/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'is_enabled': True, 'vendor_data_list': ['company-123', 'company-456']},\n)\nresp.raise_for_status()\njob = resp.json()\nprint(job['status'], job['outcomes'])\nfor row in job['results']:\n print(row['vendor_data'], row['outcome'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/aml-monitoring/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ is_enabled: true, vendor_data_list: ['company-123', 'company-456'] }),\n});\nconst job = await resp.json();\nconsole.log(job.status, job.outcomes);\nfor (const row of job.results) console.log(row.vendor_data, row.outcome);" + } + ], + "responses": { + "200": { + "description": "The estimate (when `estimate_only` is true) or the completed job for a selection processed inline.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AMLMonitoringToggleResult" + } + } + } + }, + "202": { + "description": "Larger selections run in the background. Poll the job endpoint with `job_id`; `results` fills in as profiles are processed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AMLMonitoringJob" + } + } + } + }, + "400": { + "description": "Validation error: `is_enabled` missing, no selector, or more than 1000 identifiers across both lists." + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "Insufficient balance to enable monitoring for this selection. The response carries the `estimate` so you can top up the right amount.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "estimate": { + "type": "object", + "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", + "properties": { + "users_total": { + "type": "integer", + "description": "Profiles matched by the selection." + }, + "already_enabled": { + "type": "integer", + "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." + }, + "with_existing_aml": { + "type": "integer", + "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." + }, + "needing_screening": { + "type": "integer", + "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." + }, + "skipped_no_identity_data": { + "type": "integer", + "description": "Profiles that cannot be screened because identity data is missing." + }, + "skipped_blocked": { + "type": "integer", + "description": "Blocked users that will be skipped (always 0 for businesses)." + }, + "estimated_screening_cost_usd": { + "type": "number", + "example": 0.2, + "description": "One-time cost of the new screenings." + }, + "estimated_annual_monitoring_cost_usd": { + "type": "number", + "example": 0.07, + "description": "Yearly monitoring cost of the profiles that will become monitored." + }, + "balance_sufficient": { + "type": "boolean", + "description": "Whether the organization balance covers the screening plus the first year of monitoring." } } + } + } + } + } + } + } + } + } + }, + "/v3/aml-monitoring/jobs/{job_id}/": { + "get": { + "summary": "Get AML ongoing monitoring job", + "description": "Status, outcome counters and per-record results of a monitoring toggle job started from `POST /v3/users/aml-monitoring/` or `POST /v3/businesses/aml-monitoring/`. Poll every couple of seconds while `status` is `PENDING` or `PROCESSING`; `results` is complete once it is `COMPLETED`.", + "operationId": "get_aml_monitoring_job", + "x-didit-sdk": { + "resource": "aml_monitoring", + "method": "retrieve_job", + "auth": "api_key", + "server": true + }, + "tags": [ + "AML Ongoing Monitoring" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The `job_id` returned by the toggle endpoint." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl 'https://verification.didit.me/v3/aml-monitoring/jobs/JOB_ID/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import time\nimport requests\n\nurl = 'https://verification.didit.me/v3/aml-monitoring/jobs/JOB_ID/'\nwhile True:\n job = requests.get(url, headers={'x-api-key': 'YOUR_API_KEY'}).json()\n if job['status'] in ('COMPLETED', 'FAILED'):\n break\n time.sleep(2)\nprint(job['outcomes'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const url = 'https://verification.didit.me/v3/aml-monitoring/jobs/JOB_ID/';\nlet job;\ndo {\n await new Promise((r) => setTimeout(r, 2000));\n job = await (await fetch(url, { headers: { 'x-api-key': process.env.DIDIT_API_KEY } })).json();\n} while (job.status === 'PENDING' || job.status === 'PROCESSING');\nconsole.log(job.outcomes);" + } + ], + "responses": { + "200": { + "description": "The job.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "job_id": { + "type": "string", + "format": "uuid" }, - "poa_verifications": { - "type": "array", - "nullable": true, - "description": "**Array of Proof of Address (POA) reports** — always a JSON array, never a singular `poa` object. `null` until at least one POA step has produced data. Each item is the full report, including `node_id`, feature-level `status`, the document file URL, issuer, dates, parsed address, and `warnings[]`.", - "items": { - "type": "object", - "properties": { - "node_id": { - "type": "string", - "description": "Unique identifier for this POA step in the workflow graph." - }, - "status": { - "type": "string", - "description": "Feature-level status of this Proof of Address step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ] - }, - "document_file": { - "type": "string", - "format": "uri", - "description": "URL of the proof of address document" - }, - "issuing_state": { - "type": "string", - "description": "Two-letter country code of the issuing state" - }, - "document_type": { - "type": "string", - "description": "Type of proof of address document" - }, - "document_subtype": { - "type": "string", - "nullable": true, - "description": "More specific classification of the POA document within its `document_type`, when one was detected. `null` otherwise." - }, - "document_language": { - "type": "string", - "description": "Language code of the document" - }, - "document_metadata": { - "type": "object", - "properties": { - "file_size": { - "type": "integer", - "description": "Size of the document" - }, - "content_type": { - "type": "string", - "description": "Content type of the document" - }, - "creation_date": { - "type": "string", - "format": "date", - "description": "Date when the document was created" - }, - "modified_date": { - "type": "string", - "format": "date", - "description": "Date when the document was modified" - }, - "overlay_manipulation": { - "type": "object", - "nullable": true, - "description": "PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF.", - "properties": { - "detected": { - "type": "boolean", - "description": "Whether overlay-text manipulation evidence was detected." - }, - "analyzed": { - "type": "boolean", - "description": "Whether the PDF could be analyzed." - }, - "signals": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation." - }, - "manipulated_regions": { - "type": "array", - "description": "Suspected manipulated areas in PDF page coordinates.", - "items": { - "type": "object", - "properties": { - "page": { - "type": "integer" - }, - "x": { - "type": "number" - }, - "y": { - "type": "number" - }, - "width": { - "type": "number" - }, - "height": { - "type": "number" - }, - "page_width": { - "type": "number" - }, - "page_height": { - "type": "number" - } - } - } - } - } - }, - "creator": { - "type": "string", - "nullable": true, - "description": "Creator application recorded in the document metadata." - }, - "producer": { - "type": "string", - "nullable": true, - "description": "Producer software recorded in the PDF metadata." - }, - "software": { - "type": "string", - "nullable": true, - "description": "Editing/processing software recorded in the file metadata." - }, - "encryption": { - "type": "string", - "nullable": true, - "description": "Encryption scheme of the document, when encrypted." - }, - "is_signed": { - "type": "boolean", - "nullable": true, - "description": "Whether the PDF carries a digital signature." - }, - "is_tampered": { - "type": "boolean", - "nullable": true, - "description": "Whether signature validation indicates the file was modified after signing." - }, - "signature_info": { - "type": "object", - "nullable": true, - "description": "Details of the digital signature(s), when present." - }, - "exif_original_date": { - "type": "string", - "nullable": true, - "description": "EXIF original capture date, for image uploads." - }, - "exif_digitized_date": { - "type": "string", - "nullable": true, - "description": "EXIF digitization date, for image uploads." - }, - "processed_by_known_editor": { - "type": "boolean", - "nullable": true, - "description": "Whether the file metadata indicates processing by a known document editor." - }, - "has_different_creation_mod_date": { - "type": "boolean", - "nullable": true, - "description": "Whether the creation and modification dates differ — a common manipulation signal." - } - }, - "description": "File-forensics metadata extracted from the uploaded POA document. Always contains the keys below (values are `null` when not applicable, e.g. EXIF dates for PDFs). `null` when no metadata could be extracted.", - "nullable": true - }, - "issuer": { - "type": "string", - "description": "Name of the document issuer" - }, - "issue_date": { - "type": "string", - "format": "date", - "description": "Date when the document was issued" - }, - "expiration_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Expiration date of the document, if available" - }, - "poa_address": { - "type": "string", - "description": "Raw address from the proof of address document" - }, - "poa_formatted_address": { - "type": "string", - "description": "Formatted address from the proof of address document" - }, - "poa_parsed_address": { - "type": "object", - "properties": { - "address_type": { - "type": "string", - "description": "Type of address (Street, Avenue, etc)" - }, - "street_1": { - "type": "string", - "description": "Primary street address" - }, - "street_2": { - "type": "string", - "nullable": true, - "description": "Secondary street address" - }, - "city": { - "type": "string", - "description": "City name" - }, - "region": { - "type": "string", - "description": "Region or state name" - }, - "country": { - "type": "string", - "description": "Two-letter country code" - }, - "postal_code": { - "type": "string", - "description": "Postal code" - }, - "document_location": { - "type": "object", - "properties": { - "latitude": { - "type": "number", - "description": "Latitude coordinate" - }, - "longitude": { - "type": "number", - "description": "Longitude coordinate" - } - } - } - } - }, - "name_on_document": { - "type": "string", - "nullable": true, - "description": "Account-holder / addressee name extracted from the POA document, used for name matching. `null` when no name could be extracted." - }, - "name_match_score_expected_details": { - "type": "number", - "nullable": true, - "description": "Similarity score (0-100) between `name_on_document` and the expected name supplied at session creation. `null` when either side is missing." - }, - "name_match_score_id_verification": { - "type": "number", - "nullable": true, - "description": "Similarity score (0-100) between `name_on_document` and the name extracted by the ID verification step. `null` when either side is missing." - }, - "expected_details_address": { - "type": "string", - "nullable": true, - "description": "Expected raw address for verification" - }, - "expected_details_formatted_address": { - "type": "string", - "nullable": true, - "description": "Expected formatted address for verification" - }, - "expected_details_parsed_address": { - "type": "object", - "description": "Expected parsed address details for verification" - }, - "extra_fields": { - "type": "object", - "description": "Additional banking and contact details extracted from the document.", - "properties": { - "bank_account_number": { - "type": "string", - "nullable": true - }, - "bank_iban": { - "type": "string", - "nullable": true - }, - "bank_sort_code": { - "type": "string", - "nullable": true - }, - "bank_routing_number": { - "type": "string", - "nullable": true - }, - "bank_swift_bic": { - "type": "string", - "nullable": true - }, - "bank_branch_name": { - "type": "string", - "nullable": true - }, - "bank_branch_address": { - "type": "string", - "nullable": true - }, - "document_phone_number": { - "type": "string", - "nullable": true - }, - "additional_names": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "extra_files": { - "type": "array", - "items": { - "type": "string", - "format": "uri" - }, - "description": "Additional proof of address files" - }, - "warnings": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACEMATCH", - "PROOF_OF_ADDRESS", - "PHONE", - "EMAIL", - "AML", - "LOCATION", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ], - "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." - }, - "risk": { - "type": "string" - }, - "additional_data": { - "type": "object", - "nullable": true - }, - "log_type": { - "type": "string" - }, - "short_description": { - "type": "string" - }, - "long_description": { - "type": "string" - }, - "node_id": { - "type": "string", - "description": "The workflow graph node ID that generated this warning" - } - } - } - }, - "detected_codes": { - "type": "array", - "description": "QR codes and barcodes detected across every processed page of the document. Empty when none are found. A located but undecodable QR code has `parsed_payload: null`.", - "items": { - "$ref": "#/components/schemas/DocumentAIDetectedCode" - } - } + "status": { + "type": "string", + "enum": [ + "PENDING", + "PROCESSING", + "COMPLETED", + "FAILED" + ] + }, + "is_enabled": { + "type": "boolean", + "description": "true when the job enables monitoring, false when it disables it." + }, + "target_type": { + "type": "string", + "enum": [ + "user", + "business" + ], + "description": "Which profile family the job toggles." + }, + "total": { + "type": "integer", + "description": "Profiles the job processes." + }, + "processed": { + "type": "integer", + "description": "Profiles processed so far." + }, + "outcomes": { + "type": "object", + "description": "Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped.", + "properties": { + "enabled": { + "type": "integer", + "description": "Monitoring enabled on an existing AML screening." + }, + "screened_and_enabled": { + "type": "integer", + "description": "A new AML screening was created (billed) and monitoring enabled on it." + }, + "already_enabled": { + "type": "integer", + "description": "Monitoring was already active; nothing changed and nothing was billed." + }, + "disabled": { + "type": "integer", + "description": "Monitoring turned off across every AML screening of the profile." + }, + "skipped_no_identity_data": { + "type": "integer", + "description": "Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen." + }, + "skipped_blocked": { + "type": "integer", + "description": "Skipped: the user is blocked." + }, + "skipped_sandbox": { + "type": "integer", + "description": "Skipped: sandbox application, no screening is created." + }, + "failed_provider": { + "type": "integer", + "description": "The screening could not be completed; retry later." + }, + "failed_insufficient_balance": { + "type": "integer", + "description": "Balance ran out while the job was running." } } }, - "document_ai_documents": { + "results": { "type": "array", - "nullable": true, - "description": "**Document AI reports grouped by workflow node** — always a JSON array, never a singular object. `null` until at least one Document AI step has produced data. Present only when `session_kind = user`. Each item is a node group with an aggregate `status`, the `node_id`, an `items[]` array of the per-document results (extracted data keyed by the configured field key), and `warnings`.", + "description": "One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API; a job started from the Business Console reports counters only.", "items": { "type": "object", - "description": "Document AI results grouped by workflow node.", "properties": { - "status": { + "didit_internal_id": { "type": "string", - "description": "Aggregate Document AI status for this node, rolled up from its `items[]`.", - "enum": [ - "Approved", - "Declined", - "In Review", - "Not Finished" - ] + "format": "uuid", + "description": "Didit internal id of the profile." }, - "node_id": { + "vendor_data": { "type": "string", "nullable": true, - "description": "Unique identifier for this Document AI step in the workflow graph." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "node_id": { - "type": "string", - "nullable": true, - "description": "Workflow graph node that produced this document." - }, - "document_key": { - "type": "string", - "description": "Key of the configured Document AI document this file corresponds to." - }, - "original_filename": { - "type": "string", - "nullable": true, - "description": "Filename as uploaded by the user." - }, - "file_size": { - "type": "integer", - "nullable": true, - "description": "File size in bytes." - }, - "file_url": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Short-lived presigned link to the uploaded file." - }, - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined", - "In Review", - "Not Finished" - ], - "description": "Per-document Document AI status." - }, - "extracted_data": { - "type": "object", - "additionalProperties": true, - "nullable": true, - "description": "Values extracted by Document AI, keyed by the configured field `key`. Each value is a string, number, or `null`." - }, - "detected_codes": { - "type": "array", - "description": "QR codes and barcodes detected across every processed page. Empty when none are found. A located but undecodable QR code has `parsed_payload: null`.", - "items": { - "$ref": "#/components/schemas/DocumentAIDetectedCode" - } - }, - "fields": { - "type": "array", - "description": "The configured extraction fields for this document.", - "items": { - "type": "object", - "properties": { - "key": { - "type": "string", - "description": "Stable field key used as the key in `extracted_data`." - }, - "name": { - "type": "string", - "description": "Human-readable field name." - }, - "instruction": { - "type": "string", - "description": "Extraction instruction shown to the model." - }, - "type": { - "type": "string", - "enum": [ - "text", - "number", - "date" - ], - "description": "Expected value type of the extracted field." - }, - "required": { - "type": "boolean", - "description": "Whether this field is required." - } - } - } - }, - "document_metadata": { - "type": "object", - "nullable": true, - "description": "File-forensics metadata extracted from the uploaded document (e.g. `is_tampered`). `null` when nothing could be extracted." - }, - "cross_check_result": { - "type": "object", - "nullable": true, - "description": "Cross-reference of the extracted document fields against other session data. `null` when no cross-check was performed." - }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - } + "description": "Your own identifier for the profile." }, - "warnings": { - "type": "array", - "nullable": true, - "items": { - "type": "object" - }, - "description": "Warning logs raised for this Document AI node." + "outcome": { + "type": "string", + "enum": [ + "enabled", + "screened_and_enabled", + "already_enabled", + "disabled", + "skipped_no_identity_data", + "skipped_blocked", + "skipped_sandbox", + "failed_provider", + "failed_insufficient_balance" + ] } } } }, - "questionnaire_responses": { - "type": "array", - "nullable": true, - "description": "**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.", - "items": { - "type": "object", - "properties": { - "node_id": { - "type": "string", - "description": "Unique identifier for this questionnaire step in the workflow graph." - }, - "questionnaire_id": { - "type": "string", - "description": "Unique identifier for the questionnaire" - }, - "title": { - "type": "string", - "description": "Title of the questionnaire" - }, - "description": { - "type": "string", - "description": "Description of the questionnaire" - }, - "languages": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Supported languages" - }, - "default_language": { - "type": "string", - "description": "Default language" - }, - "is_active": { - "type": "boolean", - "description": "Whether the questionnaire is active" - }, - "is_simple_questionnaire": { - "type": "boolean", - "description": "Whether the questionnaire is a simple linear form (no branching graph)." - }, - "questionnaire_group_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Stable identifier of the questionnaire group across versions." - }, - "version": { - "type": "integer", - "nullable": true, - "description": "Version number of the questionnaire that was answered." - }, - "published_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp at which this questionnaire version was published." - }, - "sections": { - "type": "array", - "items": { - "type": "object", - "properties": { - "title": { - "type": "string", - "nullable": true, - "description": "Section title" - }, - "description": { - "type": "string", - "nullable": true, - "description": "Section description" - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "description": "Unique identifier for the item" - }, - "value": { - "type": "string", - "nullable": true, - "description": "Stable node identifier of this form element within the questionnaire graph." - }, - "element_type": { - "type": "string", - "description": "Type of the questionnaire element" - }, - "is_required": { - "type": "boolean", - "description": "Whether the item is required" - }, - "title": { - "type": "string", - "description": "Title of the item" - }, - "description": { - "type": "string", - "nullable": true, - "description": "Description of the item" - }, - "placeholder": { - "type": "string", - "nullable": true, - "description": "Placeholder text" - }, - "choices": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "properties": { - "label": { - "type": "string" - }, - "value": { - "type": "string" - }, - "requires_text_input": { - "type": "boolean", - "description": "Whether this choice requires additional text input", - "nullable": true - } - }, - "required": [ - "label", - "value" - ] - }, - "description": "Choices for dropdown or single choice elements" - }, - "max_files": { - "type": "integer", - "description": "Maximum number of files allowed", - "nullable": true - }, - "answer": { - "type": "object", - "description": "Answer provided for the item", - "properties": { - "value": { - "type": "string", - "nullable": true - }, - "text": { - "type": "string", - "nullable": true - }, - "files": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true - } - } - }, - "required_if": { - "type": "object", - "nullable": true, - "description": "Conditional-requirement rule, when the element is only required for certain answers." - }, - "repeatable_config": { - "type": "object", - "nullable": true, - "description": "Configuration for repeatable element groups, when applicable." - } - }, - "required": [ - "uuid", - "value", - "element_type", - "is_required", - "title" - ] - } - } - } - } - }, - "status": { - "type": "string", - "description": "Status of the questionnaire" - } - } - } + "error": { + "type": "string", + "nullable": true, + "description": "Generic failure message when status is FAILED; the detailed cause is kept on the platform side." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "completed_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid API key." + }, + "404": { + "description": "No such job for this application." + } + } + } + }, + "/v3/users/{vendor_data}/update-status/": { + "patch": { + "summary": "Update user status", + "description": "Flip only the lifecycle `status` of a user: `ACTIVE`, `FLAGGED` or `BLOCKED` (not session statuses). `BLOCKED` adds the `vendor_data` to the system blocklist; moving away from `BLOCKED` removes that entry. Each change is recorded as a `STATUS_CHANGED` activity on the user, with the optional `reason` as its `comment`. Sending the current status changes nothing and records no `reason`. The `user.status.updated` webhook does not carry `reason`.", + "operationId": "update_user_status", + "x-didit-sdk": { + "resource": "users", + "method": "update_status", + "auth": "api_key", + "server": true + }, + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "vendor_data", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Your unique identifier for the user." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/EntityStatusUpdateRequest" + }, + { + "type": "object", + "properties": { + "reason": { + "type": "string", + "nullable": true, + "maxLength": 1000, + "description": "Why the status changed. Recorded on the status-change entry of the user's activity (`comments[].comment`, and **Activity** in the Business Console), only when the status actually changes. Webhooks do not carry it." + } + } + } + ] + }, + "examples": { + "Activate": { + "summary": "Reactivate the user", + "value": { + "status": "ACTIVE" + } + }, + "Flag": { + "summary": "Flag the user for review", + "value": { + "status": "FLAGGED" + } + }, + "Block": { + "summary": "Block the user (adds to system blocklist)", + "value": { + "status": "BLOCKED" + } + }, + "Block with reason": { + "summary": "Block the user and record why", + "value": { + "status": "BLOCKED", + "reason": "Chargeback fraud pattern confirmed" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/users/user-abc-123/update-status/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"status\": \"BLOCKED\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/users/user-abc-123/update-status/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'status': 'BLOCKED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/users/user-abc-123/update-status/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'BLOCKED' }),\n});\nconst user = await resp.json();\nconsole.log(user.status);" + } + ], + "responses": { + "200": { + "description": "User status updated. Full user record returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserDetailItem" + }, + "examples": { + "Blocked": { + "value": { + "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", + "vendor_data": "user-abc-123", + "display_name": null, + "full_name": "John Michael Doe", + "date_of_birth": "1990-05-15", + "effective_name": "John Michael Doe", + "status": "BLOCKED", + "metadata": { + "tier": "premium", + "source": "website" + }, + "portrait_image_url": "https:///...", + "session_count": 3, + "approved_count": 2, + "declined_count": 0, + "in_review_count": 1, + "issuing_states": [ + "USA" + ], + "approved_emails": [ + "john@example.com" + ], + "approved_phones": [ + "+14155551234" + ], + "features": { + "ID_VERIFICATION": "Approved", + "LIVENESS": "Approved", + "FACE_MATCH": "Approved", + "AML": "Approved" + }, + "features_list": [ + { + "feature": "ID_VERIFICATION", + "status": "Approved" + }, + { + "feature": "LIVENESS", + "status": "Approved" + }, + { + "feature": "FACE_MATCH", + "status": "Approved" + }, + { + "feature": "AML", + "status": "Approved" + } + ], + "last_session_at": "2025-06-15T10:30:00Z", + "last_activity_at": "2025-06-15T10:30:00Z", + "first_session_at": "2025-06-01T08:00:00Z", + "tags": [ + { + "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", + "tag": { + "uuid": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b", + "name": "VIP", + "color": "#2567FF", + "description": null, + "source": "custom", + "created_at": "2025-05-01T09:00:00Z", + "updated_at": "2025-05-01T09:00:00Z" + }, + "added_by_email": "analyst@acme.com", + "added_by_name": "Jane Analyst", + "created_at": "2025-06-02T11:00:00Z" + } + ], + "comments": [ + { + "uuid": "c1111111-2222-4333-8444-555555555555", + "comment_type": "STATUS_CHANGED", + "comment": "Chargeback fraud pattern confirmed", + "actor_email": null, + "actor_name": null, + "previous_status": "ACTIVE", + "new_status": "BLOCKED", + "previous_value": null, + "new_value": null, + "changed_fields": [], + "metadata": null, + "mentioned_emails": [], + "created_at": "2025-06-15T10:30:00Z" + } + ], + "created_at": "2025-06-01T08:00:00Z", + "updated_at": "2025-06-15T10:30:00Z" + } + } + } + } + } + }, + "400": { + "description": "Missing or invalid `status`, or a `reason` over 1000 characters.", + "content": { + "application/json": { + "examples": { + "Missing status": { + "value": { + "status": [ + "This field is required." + ] + } + }, + "Bad status": { + "value": { + "status": [ + "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" + ] + } + }, + "Reason too long": { + "value": { + "reason": [ + "Ensure this field has no more than 1000 characters." + ] + } + } + } + } + } + }, + "401": { + "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "content": { + "application/json": { + "examples": { + "Missing credentials": { + "value": { + "detail": "You must be authenticated with a valid access token to access this endpoint." + } + } + } + } + } + }, + "403": { + "description": "Invalid or revoked API key, or the key cannot update users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No (non-deleted) user with the supplied `vendor_data` exists for this application.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/businesses/create/": { + "post": { + "summary": "Create business", + "description": "Pre-create a [business](/entities/businesses) *without* a KYB session. All fields are optional, but you should always send `vendor_data` — without it the record cannot be linked to sessions or transactions. When provided, `vendor_data` must be unique among non-deleted businesses (exact match; conflicts return 400). Not idempotent.", + "operationId": "create_business", + "x-didit-sdk": { + "resource": "businesses", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Businesses" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your unique identifier for this business (free-form string, NOT a UUID). Optional but strongly recommended. When provided, must be unique among non-deleted businesses for the application; matched exactly as sent." + }, + "display_name": { + "type": "string", + "nullable": true, + "maxLength": 255, + "description": "Friendly display name shown in the console (takes precedence over `legal_name` for UI display)." + }, + "legal_name": { + "type": "string", + "nullable": true, + "maxLength": 255, + "description": "Official legal name of the company." + }, + "registration_number": { + "type": "string", + "nullable": true, + "maxLength": 100, + "description": "Company registration or incorporation number." + }, + "country_code": { + "type": "string", + "nullable": true, + "maxLength": 10, + "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `US`)." + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "FLAGGED", + "BLOCKED" + ], + "default": "ACTIVE", + "description": "Initial lifecycle status. Defaults to `ACTIVE`." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Arbitrary JSON object you attach to the business. Defaults to `{}`." + } + } + }, + "examples": { + "Basic": { + "summary": "Recommended minimum", + "value": { + "vendor_data": "company-456", + "legal_name": "New Corp Ltd", + "country_code": "US" + } + }, + "Full": { + "summary": "All fields", + "value": { + "vendor_data": "company-789", + "display_name": "New Corp", + "legal_name": "New Corp International Ltd", + "registration_number": "98765432", + "country_code": "DE", + "status": "ACTIVE", + "metadata": { + "industry": "fintech", + "tier": "enterprise" + } + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/businesses/create/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"vendor_data\": \"company-456\", \"legal_name\": \"New Corp Ltd\", \"country_code\": \"US\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/businesses/create/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={\n 'vendor_data': 'company-456',\n 'legal_name': 'New Corp Ltd',\n 'country_code': 'US',\n },\n)\nresp.raise_for_status()\nbiz = resp.json()\nprint(biz['didit_internal_id'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/create/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n vendor_data: 'company-456',\n legal_name: 'New Corp Ltd',\n country_code: 'US',\n }),\n});\nconst biz = await resp.json();\nconsole.log(biz.didit_internal_id);" + } + ], + "responses": { + "201": { + "description": "Business created. Full business record returned (same shape as Get Business).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessDetailItem" + }, + "examples": { + "Created": { + "value": { + "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", + "vendor_data": "company-456", + "display_name": null, + "legal_name": "New Corp Ltd", + "registration_number": null, + "country_code": "US", + "effective_name": "New Corp Ltd", + "status": "ACTIVE", + "session_count": 0, + "approved_count": 0, + "declined_count": 0, + "in_review_count": 0, + "features": {}, + "features_list": [], + "last_session_at": null, + "last_activity_at": "2026-03-15T10:30:00Z", + "first_session_at": null, + "tags": [], + "created_at": "2026-03-15T10:30:00Z", + "metadata": {}, + "updated_at": "2026-03-15T10:30:00Z" + } + } + } + } + } + }, + "400": { + "description": "Invalid body — typically a duplicate `vendor_data`, an unknown `status`, or malformed `metadata`.", + "content": { + "application/json": { + "examples": { + "Duplicate": { + "value": { + "vendor_data": [ + "A business with this vendor_data already exists." + ] + } + }, + "Bad status": { + "value": { + "status": [ + "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" + ] + } + }, + "Bad metadata": { + "value": { + "metadata": [ + "Metadata must be a JSON object." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing, invalid, or revoked API key, or the key cannot create businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/users/create/": { + "post": { + "summary": "Create user", + "description": "Create a [user](/entities/users) by `vendor_data`, before or without a verification session. `vendor_data` must be unique among the application's users (a duplicate returns `400`). Not idempotent.\n\n**Contact details:** `email` and `phone_number` are stored in `metadata.contact`, and `country` in `metadata.address`. When you send `first_name` or `last_name` without `full_name`, Didit joins them into `full_name`.\n\n**Verification link:** with `send_verification_link: true`, Didit also creates a verification session for the person on `workflow_id` and emails them the link at `email`. The session runs the same checks as [Create Session](/sessions-api/create-session), such as the blocklist and your credit balance. The person and the session are created together or not at all: if any check fails, nothing is written and no email is sent. The response's `verification_link` describes the session, and is `null` when you don't ask for a link.\n\n**API key access:** a scoped API key needs `users` write. With `send_verification_link: true` it also needs `sessions` write, or the call answers `403` before anything is written. Full-access keys are unaffected. See [API keys](/console/api-keys).", + "operationId": "create_user", + "x-didit-sdk": { + "resource": "users", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Users" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "vendor_data" + ], + "properties": { + "vendor_data": { + "type": "string", + "description": "Your unique identifier for this user (free-form string, NOT a UUID). Must be unique among the application's users; matched exactly as sent." + }, + "full_name": { + "type": "string", + "nullable": true, + "maxLength": 512, + "description": "Full legal name of the user." + }, + "display_name": { + "type": "string", + "nullable": true, + "description": "Friendly display name shown in the console (takes precedence over `full_name` for UI display).", + "maxLength": 255 + }, + "date_of_birth": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Date of birth in `YYYY-MM-DD` format." + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "FLAGGED", + "BLOCKED" + ], + "default": "ACTIVE", + "description": "Initial lifecycle status. Defaults to `ACTIVE`." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Arbitrary JSON object you attach to the user. Defaults to `{}`." + }, + "approved_emails": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Pre-trusted email addresses for this user, e.g. `[\"john@example.com\"]`." + }, + "approved_phones": { + "type": "array", + "items": { + "type": "string" }, - "aml_screenings": { - "type": "array", - "nullable": true, - "description": "**Array of AML screening reports** — always a JSON array, never a singular `aml` object. `null` until at least one AML step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `total_hits`, `entity_type`, `hits[]`, `score`, `screened_data`, `is_ongoing_monitoring_enabled`, and `warnings[]`.", - "items": { - "type": "object", - "properties": { - "node_id": { - "type": "string", - "description": "Unique identifier for this AML step in the workflow graph." - }, - "status": { - "type": "string", - "description": "Feature-level status of this AML screening. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ] - }, - "total_hits": { - "type": "integer", - "description": "Total number of matches found" - }, - "entity_type": { - "type": "string", - "description": "Type of entity screened: person or company" - }, - "hits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique identifier of the match" - }, - "url": { - "type": "string", - "format": "uri", - "description": "Source URL for the match" - }, - "match": { - "type": "boolean", - "description": "Whether this is a confirmed match" - }, - "score": { - "type": "number", - "format": "float", - "nullable": true, - "minimum": 0, - "maximum": 1, - "description": "Deprecated weighted match score in the 0-1 range (e.g. `0.73`). Use `match_score` (0-100) instead.", - "deprecated": true - }, - "target": { - "type": "boolean", - "nullable": true, - "description": "Whether the matched entity is the direct target of the listing (as opposed to a related party). `null` when the source does not specify it." - }, - "caption": { - "type": "string", - "description": "Caption or title of the match" - }, - "datasets": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "PEP", - "PEP Level 1", - "PEP Level 2", - "PEP Level 3", - "PEP Level 4", - "Sanctions", - "Adverse Media", - "Warnings and Regulatory Enforcement", - "Fitness and Probity", - "SIP", - "SIE", - "Insolvency" - ] - }, - "description": "List of dataset categories where the match was found. Indicates whether the entity appeared in PEP lists (with level), sanctions databases, adverse media, warnings/regulatory enforcement, or other compliance datasets." - }, - "features": { - "type": "object", - "nullable": true, - "description": "Additional features of the match" - }, - "rca_name": { - "type": "string", - "description": "RCA name if applicable" - }, - "last_seen": { - "type": "string", - "format": "date-time", - "description": "Date when match was last seen" - }, - "risk_view": { - "type": "object", - "properties": { - "crimes": { - "type": "object", - "properties": { - "score": { - "type": "integer" - }, - "weightage": { - "type": "integer" - }, - "risk_level": { - "type": "string" - }, - "risk_scores": { - "type": "object" - } - } - }, - "countries": { - "type": "object", - "properties": { - "score": { - "type": "integer" - }, - "weightage": { - "type": "integer" - }, - "risk_level": { - "type": "string" - }, - "risk_scores": { - "type": "object" - } - } - }, - "categories": { - "type": "object", - "properties": { - "score": { - "type": "integer" - }, - "weightage": { - "type": "integer" - }, - "risk_level": { - "type": "string" - }, - "risk_scores": { - "type": "object" - } - } - }, - "custom_list": { - "type": "object" - } - } - }, - "first_seen": { - "type": "string", - "format": "date-time", - "description": "Date when match was first seen" - }, - "properties": { - "type": "object", - "properties": { - "name": { - "type": "array", - "items": { - "type": "string" - } - }, - "alias": { - "type": "array", - "items": { - "type": "string" - } - }, - "notes": { - "type": "array", - "items": { - "type": "string" - } - }, - "title": { - "type": "string", - "nullable": true - }, - "gender": { - "type": "array", - "items": { - "type": "string" - } - }, - "height": { - "type": "string", - "nullable": true - }, - "topics": { - "type": "string", - "nullable": true - }, - "weight": { - "type": "string", - "nullable": true - }, - "address": { - "type": "string", - "nullable": true - }, - "country": { - "type": "string", - "nullable": true - }, - "website": { - "type": "string", - "nullable": true - }, - "eyeColor": { - "type": "string", - "nullable": true - }, - "keywords": { - "type": "string", - "nullable": true - }, - "lastName": { - "type": "array", - "items": { - "type": "string" - } - }, - "position": { - "type": "string", - "nullable": true - }, - "religion": { - "type": "string", - "nullable": true - }, - "birthDate": { - "type": "array", - "items": { - "type": "string" - } - }, - "education": { - "type": "array", - "items": { - "type": "string" - } - }, - "ethnicity": { - "type": "string", - "nullable": true - }, - "firstName": { - "type": "array", - "items": { - "type": "string" - } - }, - "hairColor": { - "type": "string", - "nullable": true - }, - "weakAlias": { - "type": "string", - "nullable": true - }, - "birthPlace": { - "type": "array", - "items": { - "type": "string" - } - }, - "modifiedAt": { - "type": "string", - "nullable": true - }, - "wikidataId": { - "type": "string", - "nullable": true - }, - "citizenship": { - "type": "string", - "nullable": true - }, - "nationality": { - "type": "string", - "nullable": true - } - } - }, - "match_score": { - "type": "number", - "description": "Weighted identity confidence score (0-100) calculated from name similarity, date of birth, and country matching. Used to determine if hit is a False Positive or Possible Match.", - "format": "float", - "nullable": true - }, - "risk_score": { - "type": "number", - "description": "Entity risk score (0-100). Used to determine final AML status (Approved/In Review/Declined) based on the highest risk_score among non-False-Positive hits." - }, - "review_status": { - "type": "string", - "description": "Review status for this hit. Can be 'Unreviewed', 'Confirmed Match', 'False Positive', or 'Inconclusive'.", - "enum": [ - "Unreviewed", - "Confirmed Match", - "False Positive", - "Inconclusive" - ] - }, - "score_breakdown": { - "type": "object", - "description": "Detailed breakdown of how the match_score was calculated.", - "properties": { - "name_score": { - "type": "integer", - "description": "Raw name similarity percentage (0-100)." - }, - "name_weight": { - "type": "integer", - "description": "Original weight for name from config (0-100)." - }, - "name_weight_normalized": { - "type": "number", - "description": "Normalized weight for name after re-weighting for missing data (0-100)." - }, - "name_contribution": { - "type": "number", - "description": "Points contributed by name to the total score." - }, - "dob_score": { - "type": "integer", - "description": "DOB match score (-100 to 100)." - }, - "dob_weight": { - "type": "integer", - "description": "Original weight for DOB from config (0-100)." - }, - "dob_weight_normalized": { - "type": "number", - "description": "Normalized weight for DOB after re-weighting for missing data (0-100)." - }, - "dob_contribution": { - "type": "number", - "description": "Points contributed by DOB to the total score." - }, - "country_score": { - "type": "integer", - "description": "Country match score (-100 to 100)." - }, - "country_weight": { - "type": "integer", - "description": "Original weight for country from config (0-100)." - }, - "country_weight_normalized": { - "type": "number", - "description": "Normalized weight for country after re-weighting for missing data (0-100)." - }, - "country_contribution": { - "type": "number", - "description": "Points contributed by country to the total score." - }, - "document_number_match_type": { - "type": "string", - "description": "Result of document number 'Golden Key' evaluation.", - "enum": [ - "MATCH", - "NEUTRAL", - "HARD_MISMATCH" - ] - }, - "document_number_effect": { - "type": "string", - "description": "Human-readable description of the document number effect on the score." - }, - "total_score": { - "type": "integer", - "description": "Final weighted match score (0-100)." - } - } - }, - "pep_matches": { - "type": "array", - "items": { - "type": "object", - "properties": { - "aliases": { - "type": "array", - "items": { - "type": "string" - } - }, - "education": { - "type": "array", - "items": { - "type": "string" - } - }, - "list_name": { - "type": "string" - }, - "publisher": { - "type": "string" - }, - "source_url": { - "type": "string", - "format": "uri" - }, - "description": { - "type": "string" - }, - "matched_name": { - "type": "string" - }, - "pep_position": { - "type": "string" - }, - "date_of_birth": { - "type": "string" - }, - "other_sources": { - "type": "array", - "items": { - "type": "string" - } - }, - "place_of_birth": { - "type": "string" - } - } - } - }, - "linked_entities": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "array", - "items": { - "type": "string" - } - }, - "active": { - "type": "array", - "items": { - "type": "string" - } - }, - "status": { - "type": "array", - "items": { - "type": "string" - } - }, - "details": { - "type": "array", - "items": { - "type": "string" - } - }, - "relation": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "warning_matches": { - "type": "array", - "description": "List of warning and regulatory enforcement matches. Includes entries from categories: Warning, SIP, Warnings and Regulatory Enforcement, Fitness and Probity, SIE, and Insolvency.", - "items": { - "type": "object", - "properties": { - "list_name": { - "type": "string", - "description": "Name of the warning list publisher" - }, - "matched_name": { - "type": "string", - "description": "Name matched in the warning list" - }, - "description": { - "type": "string", - "description": "Description of the warning entry" - }, - "publisher": { - "type": "string", - "description": "Publisher or source of the warning" - }, - "source_url": { - "type": "string", - "format": "uri", - "description": "URL to the source of the warning" - }, - "other_sources": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Additional source URLs" - }, - "countries": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "description": "Countries associated with the warning" - }, - "created_at": { - "type": "string", - "nullable": true, - "description": "Date the warning entry was created" - }, - "updated_at": { - "type": "string", - "nullable": true, - "description": "Date the warning entry was last updated" - }, - "additional_data": { - "type": "object", - "description": "Additional data from the warning source. May contain a `data` object with `case_details` array.", - "properties": { - "data": { - "type": "object", - "properties": { - "case_details": { - "type": "array", - "description": "Details of warning cases associated with this match", - "items": { - "type": "object", - "properties": { - "case_type": { - "type": "string", - "description": "Type of the warning case", - "enum": [ - "Accusation", - "Arrest", - "Bribery", - "Criminal", - "Fraud", - "Illegal Fundraising", - "Non-conviction Terror", - "Proclaimed Offender", - "Regulatory Enforcement", - "Terrorism", - "Terrorism-related", - "Terrorism-related listing" - ] - } - } - } - } - } - } - } - } - } - } - }, - "sanction_matches": { - "type": "array", - "description": "List of sanction list matches found for this entity.", - "items": { - "type": "object", - "properties": { - "list_name": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "description": "Name(s) of the sanction list(s)" - }, - "matched_name": { - "type": "string", - "description": "Name matched in the sanction list" - }, - "description": { - "type": "string", - "description": "Description of the sanction entry" - }, - "reason": { - "type": "string", - "nullable": true, - "description": "Free-text reason why the entity was sanctioned. Joined from the sanction list's reason entries. Examples: 'Executive Order 13224 (Terrorism)', 'Support for actions undermining Ukraine's sovereignty', 'Listed due to national security concerns.'" - }, - "source_url": { - "type": "string", - "format": "uri", - "description": "URL to the source of the sanction" - }, - "other_sources": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Additional source URLs" - }, - "details": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Additional details about the sanction" - }, - "legal_basis": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Legal basis for the sanction" - }, - "listed_on": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "description": "Date(s) when the entity was listed" - }, - "sanction_list": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "description": "Sanction list identifier(s)" - }, - "sanction_program": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Sanction program(s) the entity is listed under" - }, - "sanctioning_authority": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Authority/authorities that imposed the sanction" - }, - "updated_on": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Date(s) when the sanction entry was last updated" - }, - "additional_data": { - "type": "object", - "description": "Additional data from the sanction source not captured in the main fields" - } - } - } - }, - "adverse_media_details": { - "type": "object", - "properties": { - "sentiment": { - "type": "string" - }, - "entity_type": { - "type": "string" - }, - "sentiment_score": { - "type": "integer" - }, - "adverse_keywords": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - } - } - }, - "adverse_media_matches": { - "type": "array", - "items": { - "type": "object", - "properties": { - "country": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "headline": { - "type": "string" - }, - "sentiment": { - "type": "string" - }, - "thumbnail": { - "type": "string", - "format": "uri" - }, - "source_url": { - "type": "string", - "format": "uri" - }, - "author_name": { - "type": "string", - "nullable": true - }, - "other_sources": { - "type": "array", - "items": { - "type": "string" - } - }, - "adverse_keywords": { - "type": "array", - "items": { - "type": "string" - } - }, - "sentiment_score": { - "type": "integer" - }, - "publication_date": { - "type": "string", - "format": "date-time" - } - } - } - }, - "additional_information": { - "type": "object" - } - } - } - }, - "score": { - "type": "number", - "format": "float", - "nullable": true, - "minimum": 0, - "maximum": 100, - "description": "Overall AML screening score — the highest risk score across all hits (float, 0-100). `null` until computed." - }, - "screened_data": { - "type": "object", - "properties": { - "full_name": { - "type": "string" - }, - "nationality": { - "type": "string" - }, - "date_of_birth": { - "type": "string", - "format": "date" - }, - "document_number": { - "type": "string", - "nullable": true - } - } - }, - "is_ongoing_monitoring_enabled": { - "type": "boolean", - "description": "Whether AML ongoing monitoring is enabled for this screening. When `true`, the entity is re-screened periodically and any new hits update the report." - }, - "next_ongoing_monitoring_bill_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Date (YYYY-MM-DD) when this ongoing-monitoring subscription will next be billed. Computed as the date of the last ongoing-monitoring billing plus 365 days when `is_ongoing_monitoring_enabled = true`; otherwise `null`. This is a billing date, not a screening date - use `last_ongoing_screening_at` for that." - }, - "last_ongoing_screening_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When ongoing monitoring last completed a re-screening of this record, whether or not the result changed. `null` when the record has never been re-screened; it is never back-filled from the enablement or billing date." - }, - "last_ongoing_screening_datasets_updated_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "The most recent AML dataset update date the provider reported at the time of that screening. `null` when the provider reported no dataset dates; never estimated." - }, - "warnings": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACEMATCH", - "PROOF_OF_ADDRESS", - "PHONE", - "EMAIL", - "AML", - "LOCATION", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ], - "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Within an AML screening this is typically `AML`, but the enum above is authoritative across all feature warnings on the decision payload." - }, - "risk": { - "type": "string" - }, - "additional_data": { - "type": "object", - "nullable": true - }, - "log_type": { - "type": "string" - }, - "short_description": { - "type": "string" - }, - "long_description": { - "type": "string" - }, - "node_id": { - "type": "string", - "description": "The workflow graph node ID that generated this warning" - } - } + "description": "Pre-trusted phone numbers for this user, e.g. `[\"+14155551234\"]`." + }, + "issuing_states": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Pre-recorded issuing countries (ISO 3166-1 alpha-3), e.g. `[\"USA\"]`." + }, + "first_name": { + "type": "string", + "maxLength": 255, + "description": "Given name. When `full_name` is not sent, Didit joins `first_name` and `last_name` with a space into `full_name`. Not returned as a separate field." + }, + "last_name": { + "type": "string", + "maxLength": 255, + "description": "Family name. Joined with `first_name` into `full_name` when `full_name` is not sent. Not returned as a separate field." + }, + "email": { + "type": "string", + "format": "email", + "description": "Contact email, stored in `metadata.contact.email` (it replaces an `email` you send inside `metadata.contact`). Required when `send_verification_link` is `true`: the link is sent to this address. If `metadata.contact` is not a JSON object, the call answers `400` on this field." + }, + "phone_number": { + "type": "string", + "maxLength": 32, + "description": "Contact phone number, stored in `metadata.contact.phone_number`. If `metadata.contact` is not a JSON object, the call answers `400` on this field." + }, + "country": { + "type": "string", + "maxLength": 3, + "description": "Country as an ISO 3166 code, alpha-2 or alpha-3 (`ES` or `ESP`). Stored uppercased in `metadata.address.country`. If `metadata.address` is not a JSON object, the call answers `400` on this field." + }, + "send_verification_link": { + "type": "boolean", + "default": false, + "description": "When `true`, Didit creates a verification session for the person on `workflow_id` and emails them the link at `email`. Requires `email` and `workflow_id`. A scoped API key also needs `sessions` write." + }, + "workflow_id": { + "type": "string", + "format": "uuid", + "description": "The workflow of the verification session. Required when `send_verification_link` is `true`. It must verify a person: a Business Verification (KYB) workflow is refused. Ignored otherwise." + }, + "language": { + "type": "string", + "maxLength": 10, + "description": "Language of the verification email, as a [supported language](/integration/supported-languages) code. English when omitted. Ignored when `send_verification_link` is not `true`." + } + } + }, + "examples": { + "Basic": { + "summary": "Create with basic info", + "value": { + "vendor_data": "user-abc-123", + "full_name": "John Doe" + } + }, + "Full": { + "summary": "All fields", + "value": { + "vendor_data": "user-abc-456", + "full_name": "Jane Smith", + "display_name": "Jane S.", + "date_of_birth": "1990-05-15", + "status": "ACTIVE", + "metadata": { + "tier": "premium", + "source": "api" + }, + "approved_emails": [ + "jane@example.com" + ] + } + }, + "Contact details": { + "summary": "Name, email, phone and country", + "value": { + "vendor_data": "user-abc-789", + "first_name": "Julia", + "last_name": "Martin", + "email": "julia.martin@example.com", + "phone_number": "+34600000000", + "country": "ES" + } + }, + "With verification link": { + "summary": "Create the person and email them a verification link", + "value": { + "vendor_data": "user-abc-789", + "first_name": "Julia", + "last_name": "Martin", + "email": "julia.martin@example.com", + "send_verification_link": true, + "workflow_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "language": "es" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/users/create/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"vendor_data\": \"user-abc-123\", \"full_name\": \"John Doe\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/users/create/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'vendor_data': 'user-abc-123', 'full_name': 'John Doe'},\n)\nresp.raise_for_status()\nuser = resp.json()\nprint(user['didit_internal_id'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/users/create/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ vendor_data: 'user-abc-123', full_name: 'John Doe' }),\n});\nconst user = await resp.json();\nconsole.log(user.didit_internal_id);" + } + ], + "responses": { + "201": { + "description": "User created. The full user record (same shape as Get User), plus `verification_link`: the verification session Didit emailed when `send_verification_link` was `true`, otherwise `null`.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/UserDetailItem" + }, + { + "type": "object", + "required": [ + "verification_link" + ], + "properties": { + "verification_link": { + "type": "object", + "nullable": true, + "description": "The verification session Didit created and emailed to the person. Always present: `null` when `send_verification_link` was not `true`.", + "required": [ + "session_id", + "session_number", + "status", + "url", + "workflow_id", + "email" + ], + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "description": "ID of the verification session." + }, + "session_number": { + "type": "integer", + "description": "Sequential number of the session inside your application." + }, + "status": { + "type": "string", + "description": "Status of the session, `Not Started` for a session that was just created." + }, + "url": { + "type": "string", + "format": "uri", + "description": "The verification link Didit emailed to the person." + }, + "workflow_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "The workflow the session runs." + }, + "email": { + "type": "string", + "format": "email", + "description": "The address the link was sent to." } } } } + } + ] + }, + "examples": { + "Created": { + "value": { + "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", + "vendor_data": "user-abc-123", + "display_name": null, + "full_name": "John Doe", + "date_of_birth": null, + "effective_name": "John Doe", + "status": "ACTIVE", + "metadata": {}, + "portrait_image_url": null, + "session_count": 0, + "approved_count": 0, + "declined_count": 0, + "in_review_count": 0, + "issuing_states": [], + "approved_emails": [], + "approved_phones": [], + "features": {}, + "features_list": [], + "last_session_at": null, + "last_activity_at": "2025-06-15T10:30:00Z", + "first_session_at": null, + "tags": [], + "comments": [], + "created_at": "2025-06-15T10:30:00Z", + "updated_at": "2025-06-15T10:30:00Z", + "verification_link": null + } + }, + "Created with verification link": { + "value": { + "didit_internal_id": "9b2f6c1e-3d4a-4e5f-8a7b-1c2d3e4f5a6b", + "vendor_data": "user-abc-789", + "display_name": null, + "full_name": "Julia Martin", + "date_of_birth": null, + "effective_name": "Julia Martin", + "status": "ACTIVE", + "metadata": { + "contact": { + "email": "julia.martin@example.com" + } + }, + "portrait_image_url": null, + "session_count": 1, + "approved_count": 0, + "declined_count": 0, + "in_review_count": 0, + "issuing_states": [], + "approved_emails": [], + "approved_phones": [], + "features": {}, + "features_list": [], + "last_session_at": "2025-06-15T10:30:00Z", + "last_activity_at": "2025-06-15T10:30:00Z", + "first_session_at": "2025-06-15T10:30:00Z", + "tags": [], + "comments": [], + "created_at": "2025-06-15T10:30:00Z", + "updated_at": "2025-06-15T10:30:00Z", + "verification_link": { + "session_id": "3fa85f64-5717-4562-b3fc-2c963f66afa7", + "session_number": 43762, + "status": "Not Started", + "url": "https://verify.didit.me/en/session/3FaJ9wLqX2Mz", + "workflow_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "email": "julia.martin@example.com" + } + } + } + } + } + } + }, + "400": { + "description": "Invalid body, keyed by field: typically a duplicate `vendor_data`, a missing required field or a malformed value. With `send_verification_link: true`, also a missing `email` or `workflow_id`, an unknown or Business Verification (KYB) workflow, an unsupported `language` (keyed `contact_details.email_lang`), a blocklisted `vendor_data`, or too few credits to start the session. Nothing is written.", + "content": { + "application/json": { + "examples": { + "Duplicate": { + "value": { + "vendor_data": [ + "A user with this vendor_data already exists." + ] + } + }, + "Missing vendor_data": { + "value": { + "vendor_data": [ + "This field is required." + ] + } + }, + "Bad metadata": { + "value": { + "metadata": [ + "Metadata must be a JSON object." + ] + } + }, + "Bad status": { + "value": { + "status": [ + "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" + ] + } + }, + "Bad country": { + "value": { + "country": [ + "Use an ISO 3166 country code, like ES or ESP." + ] + } + }, + "Contact not an object": { + "value": { + "email": [ + "metadata.contact must be a JSON object to store this value." + ] + } + }, + "Address not an object": { + "value": { + "country": [ + "metadata.address must be a JSON object to store this value." + ] + } + }, + "Link without email or workflow": { + "value": { + "email": [ + "An email address is required to send a verification link." + ], + "workflow_id": [ + "Pick the workflow to send a verification link for." + ] + } + }, + "Unknown workflow": { + "value": { + "workflow_id": [ + "Invalid workflow_id." + ] + } + }, + "Business workflow": { + "value": { + "workflow_id": [ + "This workflow verifies a business. Pick a workflow that verifies a person to send them a verification link." + ] + } + }, + "Blocklisted vendor_data": { + "value": { + "vendor_data": "This user or business has been blocked and cannot create new verification sessions." + } + }, + "Insufficient credits": { + "value": { + "detail": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + } + }, + "401": { + "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "content": { + "application/json": { + "examples": { + "Missing credentials": { + "value": { + "detail": "You must be authenticated with a valid access token to access this endpoint." + } + } + } + } + } + }, + "403": { + "description": "Invalid or revoked API key, or the key cannot create users for this application. A scoped API key needs `users` write, and also `sessions` write when `send_verification_link` is `true`; nothing is written. Note: on this endpoint an *invalid* key returns `403` (not `401`).", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "No sessions write": { + "summary": "Scoped key asks for a verification link without sessions write", + "value": { + "detail": "This API key has no write access to sessions." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/vendor-users/by-id/{didit_internal_id}/faces/upload/": { + "post": { + "summary": "Upload user face", + "description": "Attach a trusted imported face image to an existing User profile. Use this after creating or retrieving a User by `vendor_data` and reading its `didit_internal_id`. The uploaded face is stored on the User profile and indexed for duplicate detection and face search. It is not added to a face blocklist.", + "operationId": "upload_user_face", + "x-didit-sdk": { + "resource": "users", + "method": "upload_face", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID that owns the application." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID that owns the User profile." + }, + { + "name": "didit_internal_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Didit's internal User UUID, returned as `didit_internal_id` by `GET /v3/users/{vendor_data}/` and `POST /v3/users/create/`." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string", + "format": "byte", + "description": "Raw base64-encoded JPG or PNG face image. Do not include a `data:image/...;base64,` prefix. Max decoded size: 2 MB." + }, + "comment": { + "type": "string", + "description": "Optional operator-visible note for the uploaded face.", + "default": "" + } + } + }, + "examples": { + "Imported face": { + "summary": "Attach a face imported from a previous provider", + "value": { + "image": "/9j/4AAQSkZJRgABAQEA...", + "comment": "Imported from previous KYC provider" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/vendor-users/by-id/f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418/faces/upload/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"image\": \"/9j/4AAQSkZJRgABAQEA...\", \"comment\": \"Imported from previous KYC provider\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/vendor-users/by-id/f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418/faces/upload/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={\n 'image': '/9j/4AAQSkZJRgABAQEA...',\n 'comment': 'Imported from previous KYC provider',\n },\n)\nresp.raise_for_status()\nface = resp.json()\nprint(face['uuid'], face['image_url'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/vendor-users/by-id/f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418/faces/upload/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n image: '/9j/4AAQSkZJRgABAQEA...',\n comment: 'Imported from previous KYC provider',\n }),\n});\nconst face = await resp.json();\nconsole.log(face.uuid, face.image_url);" + } + ], + "responses": { + "201": { + "description": "Face uploaded and attached to the User profile.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "uuid", + "image_url", + "comment", + "uploaded_by", + "created_at" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "UUID of the uploaded User face record." + }, + "image_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Signed URL for the uploaded face image." + }, + "comment": { + "type": "string", + "nullable": true, + "description": "Comment supplied when the face was uploaded." + }, + "uploaded_by": { + "type": "string", + "nullable": true, + "description": "Email or actor identifier of the uploader when available." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "Upload timestamp." + } + } + }, + "examples": { + "Uploaded": { + "value": { + "uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "image_url": "https://signed-url.example/face.jpg", + "comment": "Imported from previous KYC provider", + "uploaded_by": "api", + "created_at": "2026-05-18T12:00:00Z" + } + } + } + } + } + }, + "400": { + "description": "Invalid request body or image. Common causes: invalid base64, invalid image, no detectable face, image over 2 MB, missing embedding, or the User already has 5 uploaded faces.", + "content": { + "application/json": { + "examples": { + "No face": { + "value": { + "image": "No face detected in the image. Please upload a clear photo with a visible face." + } + }, + "Too many faces": { + "value": { + "detail": "Maximum of 5 faces per user reached." + } + }, + "Too large": { + "value": { + "image": "Image exceeds maximum size of 2 MB." + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid API key.", + "content": { + "application/json": { + "examples": { + "Missing key": { + "value": { + "detail": "Authentication credentials were not provided." + } + } + } + } + } + }, + "403": { + "description": "The credential is valid but cannot write User profiles for this application.", + "content": { + "application/json": { + "examples": { + "Forbidden": { + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "404": { + "description": "No User exists for that `didit_internal_id` in the application.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded; back off and retry.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "Request was throttled. Expected available in 30 seconds." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/session/{sessionId}/update-data/": { + "patch": { + "summary": "Update KYC data extracted from the ID document on a verification session", + "description": "Correct the identity fields that were extracted (OCR / NFC) from the user's ID document — misread names, dates, document numbers, and so on — after the verification has finished. Typical callers are a compliance analyst working from your own back office, or an automated reconciliation job. Every field in the body is optional: only the keys you send are written, everything else is left untouched (`PATCH` semantics).\n\nThe session must belong to your application and be in `Approved`, `Declined`, `In Review`, or `Kyc Expired` status. Sessions in any other state (e.g. still `In Progress`) return `404` with `\"No Session matches the given query.\"` — distinct from the `\"Not found.\"` returned for an unknown or deleted `sessionId`. The session must also carry an ID-verification (KYC) record: sessions whose workflow never ran an ID-verification step return `404` (`\"The session does not have a KYC.\"`). For graph workflows with multiple ID-verification steps, target a specific step with `node_id` (query parameter or body field); when omitted, the most recent KYC record is patched.\n\nTwo derived fields are recomputed on every call: `full_name` (from `first_name` + `last_name`) and `issuing_state_name` (from `issuing_state`). If the free-text `address` changes, the server may also re-geocode it and refresh `parsed_address` (skipped for sandbox sessions). `extra_fields` and `parsed_address` are merged key-by-key rather than replaced — see the per-field descriptions for the `null`-to-delete semantics.\n\nSubmitting at least one valid field fires the `data.updated` [webhook](/integration/webhooks) (trigger `manual_review`) — even when the submitted values equal the stored ones. When values actually change, a `KYC_DATA_UPDATED` activity entry recording the changed fields with previous and new values is appended to the session's `reviews`, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and the console activity timeline. The session's status and decision are **not** re-evaluated — use [Update Status](#patch-/v3/session/-sessionId-/update-status/) to change the outcome. An empty JSON body (`{}`) is accepted and simply returns the current KYC data without firing the webhook.\n\nAccepts an API key or a console user token; either way the credential needs the `write:sessions` permission. Authentication and permission failures both return `403` — this API never responds `401`. Note that the session lookup runs before credential checks, so an unknown `sessionId` returns `404` even with missing credentials. This endpoint has a dedicated rate limit of 10 requests per minute per API key (invalid requests count too); the shared write budget of 300 requests per minute also applies.", + "operationId": "patch_v3_session_update_data", + "x-didit-sdk": { + "resource": "sessions", + "method": "update_data", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "description": "UUID of the User Verification (KYC) session whose extracted document data you are correcting. Business (KYB) session IDs are not valid here — for company data use [Update KYB Company Data](#patch-/v3/session/-sessionId-/kyb/-companyUuid-/update-data/).", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + }, + { + "name": "node_id", + "in": "query", + "required": false, + "description": "Workflow graph node whose KYC record to patch, for graph workflows with multiple ID-verification steps (also accepted as a `node_id` body field). When omitted, the most recent KYC record on the session is patched. If no KYC exists for the given node, the call returns `404` (`\"The session does not have a KYC for node ''.\"`).", + "schema": { + "type": "string", + "example": "feature_ocr" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "All fields optional — send only what you want to change.", + "properties": { + "document_type": { + "type": "string", + "description": "Document type code. Accepts the short codes listed in the enum, plus six long-form aliases normalized server-side before validation: `Passport` → `P`, `Identity Card` → `ID`, `Driver's License` → `DL`, `Residence Permit` → `RP`, `Health Insurance Card` → `HIC`, `Tax Card` → `TC`. Any other value (including `PASSPORT` in caps) returns `400`.", + "enum": [ + "P", + "DL", + "ID", + "RP", + "SSC", + "HIC", + "WP", + "TC", + "VISA", + "PSC", + "BC", + "OTHER" + ], + "nullable": true, + "example": "P" + }, + "document_subtype": { + "type": "string", + "maxLength": 128, + "nullable": true, + "description": "Document subtype from the document registry, including the region prefix when the registry distinguishes regional variants." + }, + "document_number": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Document number as printed on the ID.", + "example": "PAB123456" + }, + "personal_number": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Personal / national identification number where the document carries one (e.g. Spanish DNI on a passport).", + "example": "99999999R" + }, + "date_of_birth": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Date of birth in `YYYY-MM-DD`. Any other format returns `400`.", + "example": "1980-01-01" + }, + "date_of_issue": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Issue date of the document in `YYYY-MM-DD`." + }, + "expiration_date": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Expiration date of the document in `YYYY-MM-DD`." + }, + "issuing_state": { + "type": "string", + "nullable": true, + "description": "Issuing country as an ISO 3166-1 alpha-3 code. Case-insensitive (`esp` is stored as `ESP`); the human-readable `issuing_state_name` shown in [Get Decision](#get-/v3/session/-sessionId-/decision/) is recomputed from this value. Invalid codes return `400`.", + "example": "ESP" + }, + "first_name": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Given name(s). The stored `full_name` is recomputed as `first_name + ' ' + last_name` on every call to this endpoint.", + "example": "Carmen" + }, + "last_name": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Family name(s). Also feeds the recomputed `full_name`.", + "example": "Espanola Espanola" + }, + "gender": { + "type": "string", + "enum": [ + "M", + "F", + "U" + ], + "nullable": true, + "description": "Sex as printed on the document: `M` (male), `F` (female), or `U` (unknown). Any other value — including `X` — returns `400`." + }, + "address": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it synchronously before responding — the 200 body already reflects the refreshed `parsed_address`. The re-geocode also requires `issuing_state` to be present on the KYC record." + }, + "place_of_birth": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Place of birth as printed on the document." + }, + "nationality": { + "type": "string", + "maxLength": 32, + "nullable": true, + "description": "Nationality as printed on the document. Free text up to 32 characters — typically an ISO 3166-1 alpha-3 code (e.g. `ESP`), but not validated as one." + }, + "marital_status": { + "type": "string", + "enum": [ + "SINGLE", + "MARRIED", + "DIVORCED", + "WIDOWED", + "UNKNOWN" + ], + "description": "Marital status where the document carries it. Any other value returns `400`." + }, + "extra_fields": { + "type": "object", + "additionalProperties": true, + "nullable": true, + "description": "Per-key overrides for document-specific extra fields (e.g. `blood_group`, `dl_categories`, `mrz_string`). Keys are merged into the existing override map: send a key with a value to set it, or with `null` / `\"\"` to remove that key from the merged view. Send the whole field as `null` to clear all previously applied overrides. Must be a JSON object, otherwise `400`. The response always returns the merged view (OCR-extracted values with your overrides applied)." + }, + "parsed_address": { + "type": "object", + "nullable": true, + "description": "Structured address. Only these nested keys are accepted: `address_type`, `city`, `region`, `street_1`, `street_2`, `postal_code`, `country` — any other key returns `400`. Per-key semantics: a value sets the key, `null` removes it. `formatted_address` is recomputed server-side by joining `street_1`, `street_2`, `city`, `region`, `postal_code`, and `country` and is returned in the response — you cannot set it directly.", + "properties": { + "address_type": { + "type": "string", + "nullable": true, + "description": "Free-form address classification (e.g. `home`)." + }, + "city": { + "type": "string", + "nullable": true, + "example": "Madrid" + }, + "region": { + "type": "string", + "nullable": true, + "description": "State / province / autonomous community." + }, + "street_1": { + "type": "string", + "nullable": true, + "example": "Avda de Madrid 34" + }, + "street_2": { + "type": "string", + "nullable": true + }, + "postal_code": { + "type": "string", + "nullable": true, + "example": "28013" + }, + "country": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.", + "example": "ES" + } + } + } + } + }, + "examples": { + "Correct name and DOB": { + "summary": "Fix OCR-misread name, date of birth, and structured address", + "value": { + "first_name": "Carmen", + "last_name": "Espanola Espanola", + "date_of_birth": "1980-01-01", + "issuing_state": "ESP", + "marital_status": "MARRIED", + "extra_fields": { + "blood_group": "O+" }, - "ip_analyses": { - "type": "array", - "nullable": true, - "description": "**Array of Device & IP Analysis reports** — always a JSON array, never a singular `ip_analysis` object. `null` until at least one IP-analysis step has produced data. Each item is the full report, including `node_id`, feature-level `status`, IP and geolocation data, device fingerprint, and `warnings[]`. Entries with duplicate `(node_id, ip_address, device_fingerprint)` are deduplicated server-side.", - "items": { - "type": "object", - "properties": { - "node_id": { - "type": "string", - "description": "Unique identifier for this Device & IP Analysis step in the workflow graph." - }, - "status": { - "type": "string", - "description": "Feature-level status of this Device & IP Analysis entry. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ] - }, - "device_brand": { - "type": "string", - "description": "Brand of the device used" - }, - "device_model": { - "type": "string", - "description": "Model of the device used" - }, - "browser_family": { - "type": "string", - "description": "Browser family used" - }, - "os_family": { - "type": "string", - "description": "Operating system family" - }, - "platform": { - "type": "string", - "description": "Platform type (mobile, desktop, etc)" - }, - "device_fingerprint": { - "type": "string", - "nullable": true, - "description": "Unique device fingerprint identifier" - }, - "ip_country": { - "type": "string", - "description": "Country detected from IP" - }, - "ip_country_code": { - "type": "string", - "description": "Two-letter country code from IP" - }, - "ip_state": { - "type": "string", - "description": "State/region detected from IP" - }, - "ip_city": { - "type": "string", - "description": "City detected from IP" - }, - "latitude": { - "type": "number", - "description": "Latitude coordinate of IP location" - }, - "longitude": { - "type": "number", - "description": "Longitude coordinate of IP location" - }, - "ip_address": { - "type": "string", - "description": "IP address" - }, - "isp": { - "type": "string", - "nullable": true, - "description": "Internet Service Provider" - }, - "organization": { - "type": "string", - "nullable": true, - "description": "Organization associated with IP" - }, - "is_vpn_or_tor": { - "type": "boolean", - "description": "Whether IP is from VPN or Tor network" - }, - "is_data_center": { - "type": "boolean", - "description": "Whether IP is from a data center" - }, - "time_zone": { - "type": "string", - "description": "Time zone detected from IP" - }, - "time_zone_offset": { - "type": "string", - "description": "Time zone offset" - }, - "ip": { - "type": "object", - "description": "IP geolocation of this entry plus great-circle distances (km) to the ID document address and the POA document address. Distances are `null` when either side has no resolvable location.", - "properties": { - "location": { - "type": "object", - "nullable": true, - "properties": { - "latitude": { - "type": "number", - "example": 41.2706327 - }, - "longitude": { - "type": "number", - "example": 1.9770097 - } - }, - "description": "Coordinates, or `null` when unavailable." - }, - "distance_from_id_document": { - "type": "number", - "nullable": true, - "example": 23.4 - }, - "distance_from_poa_document": { - "type": "number", - "nullable": true, - "example": 12.3 - } - } - }, - "id_document": { - "type": "object", - "description": "Location extracted from the ID document address plus distances (km) to the IP geolocation and the POA document address. `location` is `null` when no document address could be geocoded.", - "properties": { - "location": { - "type": "object", - "nullable": true, - "properties": { - "latitude": { - "type": "number", - "example": 41.2706327 - }, - "longitude": { - "type": "number", - "example": 1.9770097 - } - }, - "description": "Coordinates, or `null` when unavailable." - }, - "distance_from_ip": { - "type": "number", - "nullable": true, - "example": 23.4 - }, - "distance_from_poa_document": { - "type": "number", - "nullable": true, - "example": 18.7 - } - } - }, - "poa_document": { - "type": "object", - "description": "Location extracted from the Proof of Address document plus distances (km) to the IP geolocation and the ID document address. `location` is `null` when the session has no POA step or the address could not be geocoded.", - "properties": { - "location": { - "type": "object", - "nullable": true, - "properties": { - "latitude": { - "type": "number", - "example": 41.2706327 - }, - "longitude": { - "type": "number", - "example": 1.9770097 - } - }, - "description": "Coordinates, or `null` when unavailable." - }, - "distance_from_ip": { - "type": "number", - "nullable": true, - "example": 12.3 - }, - "distance_from_id_document": { - "type": "number", - "nullable": true, - "example": 18.7 - } - } - }, - "warnings": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACEMATCH", - "PROOF_OF_ADDRESS", - "PHONE", - "EMAIL", - "AML", - "LOCATION", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ], - "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." - }, - "risk": { - "type": "string" - }, - "additional_data": { - "type": "object", - "nullable": true - }, - "log_type": { - "type": "string" - }, - "short_description": { - "type": "string" - }, - "long_description": { - "type": "string" - }, - "node_id": { - "type": "string", - "description": "The workflow graph node ID that generated this warning" - } - } - } - }, - "matches": { - "type": "array", - "description": "Cross-session matches found for this entry's IP address or device identity. Every match is another verification session in your application (`source` is always `session`) that shares the same value, always belonging to a different vendor user / `vendor_data` than the current session, capped at 5 per collector (`device_fingerprint` sums two independently capped collectors, so up to 10 entries are possible). Every item carries `match_source`, `confidence`, `match_mode`, `device_info` and `location_info`; the keys `recovery_similarity`, `tls_ja4_corroborated` and `recovery_gate_reason` are present only when `match_source` is `recovered_high`.", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "session_id": { - "type": "string", - "format": "uuid", - "description": "Session ID of the matching session." - }, - "session_number": { - "type": "integer", - "description": "Sequential number of the matching session." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "`vendor_data` of the matching session — always different from the current session's vendor user." - }, - "verification_date": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Creation date of the matching session (`YYYY-MM-DDTHH:MM:SSZ`)." - }, - "match_type": { - "type": "string", - "enum": [ - "ip_address", - "device_fingerprint" - ], - "description": "Whether the match was on IP address or device fingerprint" - }, - "match_source": { - "type": "string", - "enum": [ - "ip_address", - "persistent_id", - "legacy_fp", - "recovered_high" - ], - "description": "How this match was reached: `ip_address` (shared IP address — network co-location, not a device-identity claim), `persistent_id` (exact stored persistent device identifier), `legacy_fp` (legacy `didit-fp-*` device-fingerprint hash), or `recovered_high` (high-confidence fuzzy fingerprint-recovery hit). Lower-confidence recovery candidates surface as risk warnings only, never in this array." - }, - "matched_value": { - "type": "string", - "nullable": true, - "description": "The value that matched: the shared IP address, the persistent device identifier or legacy fingerprint hash, or — for `recovered_high` — the UUID of the recovered device." - }, - "status": { - "type": "string", - "description": "Lifecycle status of the matching session." - }, - "is_blocklisted": { - "type": "boolean", - "description": "Whether the matched value is on a blocklist. Currently always `false` for device/IP matches." - }, - "api_service": { - "type": "string", - "nullable": true, - "description": "Standalone API service of the matching session; `null` for workflow sessions." - }, - "source": { - "type": "string", - "enum": [ - "session" - ], - "description": "Always `session` for device/IP matches — every match is another verification session in your application." - }, - "device_info": { - "type": "object", - "description": "Device details observed on the matching session.", - "properties": { - "device_brand": { - "type": "string", - "nullable": true - }, - "device_model": { - "type": "string", - "nullable": true - }, - "browser_family": { - "type": "string", - "nullable": true - }, - "os_family": { - "type": "string", - "nullable": true - }, - "platform": { - "type": "string", - "nullable": true - }, - "device_fingerprint": { - "type": "string", - "nullable": true - } - } - }, - "location_info": { - "type": "object", - "description": "Network and geolocation details observed on the matching session.", - "properties": { - "ip_address": { - "type": "string", - "nullable": true - }, - "ip_country": { - "type": "string", - "nullable": true - }, - "ip_country_code": { - "type": "string", - "nullable": true - }, - "ip_state": { - "type": "string", - "nullable": true - }, - "ip_city": { - "type": "string", - "nullable": true - }, - "is_vpn_or_tor": { - "type": "boolean" - }, - "is_data_center": { - "type": "boolean" - } - } - }, - "confidence": { - "type": "number", - "minimum": 0, - "maximum": 1, - "description": "Server-side identification confidence, computed as `1 - P(false positive)`: `1.0` for deterministic matches (`persistent_id`, or hardware-rooted `recovered_high`), `0.5` for `legacy_fp`, `0.0` for `ip_address` (co-location only), and a corroboration-adjusted value below `1.0` for probabilistic recovered matches (higher when the TLS JA4 fingerprint and IP country independently agree)." - }, - "match_mode": { - "type": "string", - "enum": [ - "deterministic", - "probabilistic", - "co_occurrence" - ], - "description": "Evidence class of the match: `deterministic` (exact stored or hardware-rooted identifier that cannot collide between physical devices), `probabilistic` (fuzzy fingerprint evidence), or `co_occurrence` (shared network only, no device-identity claim)." - }, - "recovery_similarity": { - "type": "number", - "description": "Cosine similarity of the fingerprint-recovery match, rounded to 4 decimals. Present only when `match_source` is `recovered_high`." - }, - "tls_ja4_corroborated": { - "type": "boolean", - "description": "Whether the TLS JA4 fingerprints of the two observations agree — a server-observed signal the browser cannot spoof. Present only when `match_source` is `recovered_high`." - }, - "recovery_gate_reason": { - "type": "string", - "description": "Why the recovery match passed the false-positive hard gate: `hardware_root_match` (a hardware-rooted device identifier matched exactly) or, on configurations that admit probe-only evidence, `exact_composite_plus__families` / `very_high_similarity_plus__families`. Present only when `match_source` is `recovered_high`." - } - } - } - } - } - } + "parsed_address": { + "street_1": "Avda de Madrid 34", + "city": "Madrid", + "postal_code": "28013", + "country": "ES" + } + } + }, + "Remove an extra field": { + "summary": "Delete one extra-field key, keep the rest", + "value": { + "extra_fields": { + "blood_group": null + } + } + }, + "Long-form document type": { + "summary": "Long-form alias is normalized to the short code (here P)", + "value": { + "document_type": "Passport" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "KYC data updated. Returns the **full current KYC record** (every updatable field with its stored value, plus the merged `extra_fields` and the current `parsed_address`) — not just the fields you submitted. The `data.updated` webhook fires when at least one valid field was in the payload.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "document_type": { + "type": "string", + "description": "Document type code. Accepts the short codes listed in the enum, plus six long-form aliases normalized server-side before validation: `Passport` → `P`, `Identity Card` → `ID`, `Driver's License` → `DL`, `Residence Permit` → `RP`, `Health Insurance Card` → `HIC`, `Tax Card` → `TC`. Any other value (including `PASSPORT` in caps) returns `400`.", + "enum": [ + "P", + "DL", + "ID", + "RP", + "SSC", + "HIC", + "WP", + "TC", + "VISA", + "PSC", + "BC", + "OTHER" + ], + "nullable": true, + "example": "P" }, - "database_validations": { - "type": "array", + "document_subtype": { + "type": "string", + "maxLength": 128, "nullable": true, - "description": "**Array of Database Validation reports** — always a JSON array, never a singular `database_validation` object. `null` until at least one DB validation step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `screened_data`, the per-service `validations[]` (each with its registry `source_data`), and `warnings[]`. The `screened_data` and `source_data` fields vary by country:\n\n**ARG (Argentina)**: screened_data: {document_number, selfie, gender, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, date_of_death, tax_id, tax_id_type, face_match_score, sit_1_since, banks, last_position, highest_position, rejected_checks}. Uses RENAPER biometric face-match validation. Gender is auto-inferred from face analysis or given name when not present in the document (e.g., driver licenses)\n\n**BOL (Bolivia)**: screened_data: {document_number, date_of_birth, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, gender}\n\n**BRA (Brazil)**: screened_data: {tax_number, first_name, last_name, date_of_birth}. source_data always includes {identification_number, first_name, last_name, date_of_birth, lgpd_minor, minor_under_18, minor_under_16} for successful CPF matches. Successful adult lookups set those flags to false. The Receita Federal Consulta CPF service may return HTTP 422 (minor under 18) or 451 (under 16) with LGPD-withheld data; in those cases source_data sets the minor flags to true as appropriate, includes the upstream HTTP status, and field-level validation is no_match. See the Brazil section of the Database Validation Outcome Codes reference for the full code list.\n\n**CHL (Chile)**: screened_data: {personal_number, first_name, last_name, date_of_birth}. source_data: {identification_number, first_name, last_name, date_of_birth, gender}\n\n**COL (Colombia)**: screened_data: {document_number, document_type, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, document_type}\n\n**CRI (Costa Rica)**: screened_data: {personal_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**DOM (Dominican Republic)**: screened_data: {personal_number}. source_data: {identification_number} (returns valid/invalid only)\n\n**ECU (Ecuador)**: screened_data: {personal_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, street, formatted_address, gender, nationality, education, marital_status, spouse, mother_name, father_name, profession}\n\n**ESP (Spain)**: screened_data: {personal_number, document_type, expiration_date, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, document_type, expiration_date}\n\n**GTM (Guatemala)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**HND (Honduras)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**MEX (Mexico)**: screened_data: {personal_number (CURP), first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, gender, nationality, curp_status, state_of_birth, doc_probatorio, registration_year, registration_state, registration_municipality, num_acta, crip, issuing_state_code, foreign_registry_number, folio_carta, folio_certificado}. Uses the RENAPO civil registry. A separate INE validity-check service is also available for MEX as an additional one-by-one validation.\n\n**PAN (Panama)**: screened_data: {personal_number, selfie}. source_data: {identification_number}. Uses SIB biometric validation\n\n**PER (Peru)**: screened_data: {personal_number (DNI), first_name, last_name}. source_data: {identification_number, first_name, last_name, paternal_name, maternal_name, verification_number, verification_letter}\n\n**PRY (Paraguay)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**SLV (El Salvador)**: screened_data: {document_number, date_of_birth, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**URY (Uruguay)**: screened_data: {personal_number, date_of_birth, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**VEN (Venezuela)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, gender, date_of_birth, document_type}", - "items": { - "type": "object", - "properties": { - "node_id": { - "type": "string", - "description": "Unique identifier for this database validation step in the workflow graph." - }, - "issuing_state": { - "type": "string", - "description": "ISO 3166-1 alpha-3 code of the country whose government/registry sources were queried (e.g. `BRA`, `MEX`, `ESP`, `NGA`, `IDN`). Database Validation covers 60+ countries across the Americas, Europe, Africa, the Middle East and Asia-Pacific, plus global multi-country sources — coverage expands regularly, so treat the value as an open set rather than a fixed enum." - }, - "validation_type": { - "type": "string", - "enum": [ - "one_by_one", - "two_by_two", - "not_enabled" - ], - "description": "How the result was corroborated: `one_by_one` (single-service outcome), `two_by_two` (two or more distinct services independently full-matched the identification number), or `not_enabled` (persisted when no service produced a usable match). The value is never `null`. Dedicated biometric 2x2 validation modes are currently available for `ECU` and `PER`." - }, - "screened_data": { - "type": "object", - "description": "The input data used for validation. Fields vary by country — see the Database Validation docs (`/core-technology/database-validation/overview`) for country-specific formats.", - "properties": { - "last_name": { - "type": "string" - }, - "first_name": { - "type": "string" - }, - "tax_number": { - "type": "string", - "description": "Brazil: CPF (11 digits)" - }, - "personal_number": { - "type": "string", - "description": "Chile: RUT, DOM: Cédula (11 digits), ECU: Cédula (10 digits), ESP: DNI/NIE, MEX: CURP (18 chars), PER: DNI (8 digits), URY: CI" - }, - "document_number": { - "type": "string", - "description": "ARG: DNI, BOL: CI, COL: Cédula, GTM: DPI, HND: DNI, PAN: Cédula, PRY: CI, SLV: DUI, VEN: Cédula" - }, - "date_of_birth": { - "type": "string", - "format": "date" - }, - "document_type": { - "type": "string", - "description": "COL: CC/CE/TI/NIT, ESP: ID/PASSPORT" - }, - "expiration_date": { - "type": "string", - "format": "date", - "description": "Required for ESP" - } - } - }, - "validations": { - "type": "array", - "description": "One entry per registry service that returned a validation payload. Each entry identifies the service (`service_id`, `service_name`) and carries the per-field `validation` comparison, the service `outcome_code` / `outcome_detail` when reported, and the normalised registry record in `source_data`.", - "items": { - "type": "object", - "properties": { - "service_id": { - "type": "string", - "description": "Identifier of the registry service that produced this validation result.", - "example": "bra_cpf" - }, - "service_name": { - "type": "string", - "description": "Human-readable name of the registry service.", - "example": "Brazil - CPF status check" - }, - "validation": { - "type": "object", - "description": "The results of the field validations.", - "properties": { - "full_name": { - "type": "string", - "description": "Match result for full name (e.g., 'full_match', 'partial_match', 'no_match')." - }, - "date_of_birth": { - "type": "string", - "description": "Match result for date of birth." - }, - "identification_number": { - "type": "string", - "description": "Match result for identification number." - } - } - }, - "outcome_code": { - "type": "string", - "nullable": true, - "description": "Machine-readable outcome of the registry lookup (e.g. `MATCH`, `NO_MATCH`, `PARTIAL_MATCH`, `INCONCLUSIVE`, source-specific codes). Present when the service reports one." - }, - "outcome_detail": { - "type": "string", - "nullable": true, - "description": "Human-readable explanation of `outcome_code`, when provided by the service." - }, - "source_data": { - "type": "object", - "description": "The data returned from the government database. Common fields: identification_number, first_name, last_name, full_name, date_of_birth, gender. Additional fields by country: BRA always includes lgpd_minor, minor_under_18, and minor_under_16 on successful CPF matches; it also includes the upstream HTTP status when CPF data is withheld under LGPD (HTTP 422/451). ECU adds street, formatted_address, nationality, education, marital_status, spouse, mother_name, father_name, profession. PER adds paternal_name, maternal_name, verification_number, verification_letter. ESP adds document_type, expiration_date. VEN/COL add document_type.", - "additionalProperties": true - } - } - } - }, - "errors": { - "type": "array", - "description": "Errors returned by registry services while validating, each tagged with the `service_id` that produced it. **This key is omitted entirely when there are no errors.**", - "items": { - "type": "object", - "description": "The upstream service's error object spread as-is — its keys vary by service (commonly a message/code and any provider-specific details) — plus `service_id` identifying the service that errored.", - "properties": { - "service_id": { - "type": "string", - "nullable": true, - "description": "Identifier of the registry service that errored." - } - }, - "additionalProperties": true - } - }, - "match_type": { - "type": "string", - "enum": [ - "no_match", - "partial_match", - "full_match" - ], - "nullable": true, - "description": "The aggregate match outcome across the queried services. `null` until the validation has been evaluated." - }, - "status": { - "type": "string", - "description": "Feature-level status of this database validation. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ] - }, - "warnings": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACEMATCH", - "PROOF_OF_ADDRESS", - "PHONE", - "EMAIL", - "AML", - "LOCATION", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ], - "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." - }, - "risk": { - "type": "string" - }, - "additional_data": { - "type": "object", - "nullable": true - }, - "log_type": { - "type": "string" - }, - "short_description": { - "type": "string" - }, - "long_description": { - "type": "string" - }, - "node_id": { - "type": "string", - "description": "The workflow graph node ID that generated this warning" - } - } - }, - "description": "Warnings related to the database validation." - } - } - } + "description": "Document subtype from the document registry, including the region prefix when the registry distinguishes regional variants." }, - "reviews": { - "type": "array", - "items": { - "type": "object", - "properties": { - "user": { - "type": "string", - "description": "Who performed the review. Usually the console reviewer's identifier (their email), but the value is `\"API Client\"` for status updates made with an API key and `\"Unknown\"` when no actor could be resolved — so do not parse this field as an email address." - }, - "new_status": { - "type": "string" - }, - "comment": { - "type": "string" - }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - } + "document_number": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Document number as printed on the ID.", + "example": "PAB123456" }, - "created_at": { + "personal_number": { "type": "string", - "format": "date-time", - "description": "Timestamp at which the session was created (ISO 8601, UTC)." + "maxLength": 255, + "nullable": true, + "description": "Personal / national identification number where the document carries one (e.g. Spanish DNI on a passport).", + "example": "99999999R" }, - "expires_at": { + "date_of_birth": { "type": "string", - "format": "date-time", + "format": "date", "nullable": true, - "description": "Timestamp at which this session is scheduled to expire. Set on User Verification (KYC) sessions when an expiration was configured at creation time (or once `Approved` status triggers KYC expiry computation). `null` for sessions without a configured expiration, and for kinds that do not use expiry (e.g. Business Verification (KYB) sessions that never received an `expires_at`)." + "description": "Date of birth in `YYYY-MM-DD`. Any other format returns `400`.", + "example": "1980-01-01" }, - "registry_checks": { - "type": "array", + "date_of_issue": { + "type": "string", + "format": "date", "nullable": true, - "description": "Company registry checks. Present only when `session_kind = business`. Each item wraps the full company-registry payload.", - "items": { - "type": "object", - "description": "One company registry check — the full company-registry payload enriched with ownership structure and warnings. Present only when `session_kind = business`.", - "properties": { - "status": { - "type": "string", - "description": "Feature lifecycle status." - }, - "node_id": { - "type": "string", - "nullable": true - }, - "data_resolved": { - "type": "boolean" - }, - "company": { - "type": "object", - "description": "Full registry payload for the company (registry record merged with any user-provided or console-edited data).", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "node_id": { - "type": "string", - "nullable": true - }, - "status": { - "type": "string" - }, - "registry_status": { - "type": "string", - "nullable": true - }, - "data_resolved": { - "type": "boolean" - }, - "company_name": { - "type": "string" - }, - "registration_number": { - "type": "string", - "nullable": true - }, - "country_code": { - "type": "string", - "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `DE`, `US`)." - }, - "region": { - "type": "string", - "nullable": true, - "description": "ISO 3166-2 subdivision code when applicable (e.g. `CA`, `NY` for US states)." - }, - "company_type": { - "type": "string", - "nullable": true - }, - "incorporation_date": { - "type": "string", - "format": "date", - "nullable": true - }, - "registered_address": { - "type": "string", - "nullable": true - }, - "tax_number": { - "type": "string", - "nullable": true - }, - "risk_level": { - "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ], - "nullable": true - }, - "verification_status": { - "type": "string", - "nullable": true - }, - "is_from_registry": { - "type": "boolean" - }, - "fetch_status": { - "type": "string", - "nullable": true - }, - "alternative_names": { - "type": "string", - "nullable": true, - "description": "Alternative, trading, former or original-language company names as text, when reported." - }, - "nature_of_business": { - "type": "string", - "nullable": true - }, - "registered_capital": { - "type": "string", - "nullable": true - }, - "registered_capital_amount": { - "type": "string", - "format": "decimal", - "nullable": true, - "description": "Registered/share capital amount as a decimal string (e.g. `\"100.00\"`), parsed from the registry filing. `null` when not available." - }, - "registered_capital_currency": { - "type": "string", - "nullable": true, - "description": "ISO 4217 currency code of `registered_capital_amount` (e.g. `\"GBP\"`). `null` when not available." - }, - "website": { - "type": "string", - "nullable": true - }, - "email": { - "type": "string", - "format": "email", - "nullable": true - }, - "phone": { - "type": "string", - "nullable": true - }, - "legal_entity_identifier": { - "type": "string", - "nullable": true - }, - "location_of_registration": { - "type": "string", - "nullable": true - }, - "vat_number": { - "type": "string", - "nullable": true, - "description": "EU VAT number, when collected during the hosted registry step or edited afterwards." - }, - "vat_validation_status": { - "type": "string", - "enum": [ - "valid", - "invalid", - "format_valid", - "invalid_format", - "could_not_validate", - "not_applicable" - ], - "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." - }, - "vat_validated_number": { - "type": "string", - "nullable": true, - "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." - }, - "vat_validation_source": { - "type": "string", - "nullable": true, - "enum": [ - "eu_vies", - "pl_vat_white_list", - "no_brreg", - "ch_uid_register", - "br_receita_federal", - "co_rues", - "cr_hacienda", - "ec_sri", - "py_dnit", - "format_rules", - null - ], - "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." - }, - "vat_validated_name": { - "type": "string", - "nullable": true, - "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_validated_address": { - "type": "string", - "nullable": true, - "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_checked_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the check. `null` when no check ran." - }, - "financial_summary": { - "type": "object", - "nullable": true - }, - "officers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "designation": { - "type": "string", - "nullable": true - }, - "role": { - "type": "string", - "nullable": true - }, - "nationality": { - "type": "string", - "nullable": true - }, - "is_active": { - "type": "boolean" - }, - "kyc_status": { - "type": "string", - "enum": [ - "Approved", - "Declined", - "Pending" - ], - "nullable": true - }, - "kyc_session_url": { - "type": "string", - "format": "uri", - "nullable": true - }, - "first_name": { - "type": "string", - "nullable": true, - "description": "Given name when explicitly reported by the registry; not inferred from the full name." - }, - "last_name": { - "type": "string", - "nullable": true, - "description": "Family name when explicitly reported by the registry." - }, - "address": { - "type": "string", - "nullable": true, - "description": "Officer address reported by the registry." - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Date the officer was appointed.", - "format": "date" - }, - "termination_date": { - "type": "string", - "nullable": true, - "description": "Date the appointment ended, when reported.", - "format": "date" - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", - "format": "date" - }, - "date_of_birth_year": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 9999, - "description": "Registry-reported birth year, including when the full date is unavailable." - }, - "date_of_birth_month": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 12, - "description": "Registry-reported birth month. Null when only the year is known." - } - } - } - }, - "beneficial_owners": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "first_name": { - "type": "string", - "nullable": true - }, - "last_name": { - "type": "string", - "nullable": true - }, - "entity_type": { - "type": "string", - "enum": [ - "person", - "company" - ] - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "ownership_min_shares": { - "type": "string", - "nullable": true, - "description": "Lower bound of the ownership percentage as reported by the registry. Free-form string (e.g. `\"25.00\"`, `\"50\"`); use `effective_ownership_percent` for a parsed numeric value." - }, - "ownership_max_shares": { - "type": "string", - "nullable": true, - "description": "Upper bound of the ownership percentage as reported by the registry. Free-form string; use `effective_ownership_percent` for a parsed numeric value." - }, - "is_active": { - "type": "boolean" - }, - "kyc_status": { - "type": "string", - "enum": [ - "Approved", - "Declined", - "Pending" - ], - "nullable": true - }, - "kyc_session_url": { - "type": "string", - "format": "uri", - "nullable": true - }, - "effective_ownership_percent": { - "type": "number", - "nullable": true - }, - "company_name": { - "type": "string", - "nullable": true - }, - "registration_number": { - "type": "string", - "nullable": true - }, - "country_of_birth": { - "type": "string", - "nullable": true - }, - "email": { - "type": "string", - "nullable": true - }, - "nationality": { - "type": "string", - "nullable": true - }, - "designation": { - "type": "string", - "nullable": true - }, - "voting_min_shares": { - "type": "string", - "nullable": true - }, - "voting_max_shares": { - "type": "string", - "nullable": true - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete birth date when reported.", - "format": "date" - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Appointment date when reported.", - "format": "date" - }, - "shareholding": { - "$ref": "#/components/schemas/KYBRegistryShareholding" - } - } - } - }, - "addresses": { - "type": "array", - "nullable": true, - "description": "Every address the registry states, one canonical shape. Null when it states none.", - "items": { - "$ref": "#/components/schemas/KYBRegistryAddress" - } - }, - "industries": { - "type": "array", - "nullable": true, - "description": "The activities the registry classifies the company under, one canonical shape. Null when it states none.", - "items": { - "$ref": "#/components/schemas/KYBRegistryIndustry" - } - }, - "accounts": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." - }, - "registry_data": { - "type": "object", - "description": "Raw record returned by the registry source, kept for reviewers. Its shape differs by country and source and is not a stable contract: read the canonical fields (`addresses`, `industries`, `ownership`, `beneficial_owners[].shareholding`) instead." - }, - "user_provided_data": { - "type": "object", - "description": "Data confirmed or edited by the end user." - }, - "confirmed_by_user_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "last_console_edit_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "is_editable": { - "type": "boolean" - }, - "ownership": { - "$ref": "#/components/schemas/KYBRegistryOwnership" - }, - "registry_tier": { - "type": "string", - "nullable": true, - "description": "The registry tier the workflow ordered: `basic`, `shareholders` or `ubo`. Also in `ownership.tier_requested`." - }, - "registry_tier_delivered": { - "type": "string", - "nullable": true, - "description": "The tier the registry delivered and that is billed. Also in `ownership.tier_delivered`." - }, - "registry_ownership_status": { - "type": "string", - "nullable": true, - "description": "`not_requested`, `pending`, `delivered` or `unavailable`. Also in `ownership.status`." - } - } - }, - "ownership_structure": { - "type": "object", - "description": "Raw ownership snapshot kept for reviewers; its shape differs by registry source and is not a stable contract. Read `company.beneficial_owners` and `company.ownership` instead." - }, - "warnings": { - "type": "array", - "items": { - "type": "object" - } - } - } - } + "description": "Issue date of the document in `YYYY-MM-DD`." + }, + "expiration_date": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Expiration date of the document in `YYYY-MM-DD`." + }, + "issuing_state": { + "type": "string", + "nullable": true, + "description": "Issuing country as an ISO 3166-1 alpha-3 code. Case-insensitive (`esp` is stored as `ESP`); the human-readable `issuing_state_name` shown in [Get Decision](#get-/v3/session/-sessionId-/decision/) is recomputed from this value. Invalid codes return `400`.", + "example": "ESP" + }, + "first_name": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Given name(s). The stored `full_name` is recomputed as `first_name + ' ' + last_name` on every call to this endpoint.", + "example": "Carmen" + }, + "last_name": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Family name(s). Also feeds the recomputed `full_name`.", + "example": "Espanola Espanola" + }, + "gender": { + "type": "string", + "enum": [ + "M", + "F", + "U" + ], + "nullable": true, + "description": "Sex as printed on the document: `M` (male), `F` (female), or `U` (unknown). Any other value — including `X` — returns `400`." }, - "key_people_checks": { - "type": "array", + "address": { + "type": "string", + "maxLength": 255, "nullable": true, - "description": "Aggregate key-people checks. Present only when `session_kind = business`. Each item uses the two-bucket shape (`registry`, `submitted`, `ubo_kyc_summary`).", - "items": { - "type": "object", - "description": "Aggregate check over the company's officers + UBOs. Uses the `registry` / `submitted` shape. Present only when session_kind = business.", - "properties": { - "status": { - "type": "string" - }, - "node_id": { - "type": "string", - "nullable": true - }, - "registry": { - "type": "object", - "description": "Parties discovered via the registry check.", - "properties": { - "officers": { - "type": "array", - "description": "Officers discovered via the registry check, each enriched with the status of any child KYC session and AML screening spawned for them.", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string", - "description": "Officer's full name as recorded in the registry." - }, - "designation": { - "type": "string", - "nullable": true, - "description": "Free-text designation from the registry (e.g. `Director`, `Company Secretary`)." - }, - "role": { - "type": "string", - "enum": [ - "director", - "non_executive_director", - "secretary", - "chairman", - "ubo", - "shareholder", - "representative", - "founder", - "legal_advisor", - "authorized_signatory", - "trustee", - "beneficiary", - "settlor", - "protector", - "company_officer", - "investor", - "other" - ], - "description": "Normalized officer role." - }, - "nationality": { - "type": "string", - "nullable": true - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Date the officer was appointed.", - "format": "date" - }, - "termination_date": { - "type": "string", - "nullable": true, - "description": "Date the appointment ended, when reported.", - "format": "date" - }, - "is_active": { - "type": "boolean" - }, - "address": { - "type": "string", - "nullable": true, - "description": "Officer address reported by the registry." - }, - "aml_status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "nullable": true, - "description": "Status of the AML screening run for this officer. `null` when no AML screening was run." - }, - "kyc_status": { - "type": "string", - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "nullable": true, - "description": "Raw lifecycle status of the child KYC session spawned for this officer. This is the full session status enum (not a reduced Approved/Declined/Pending set). `null` when no child KYC session exists." - }, - "kyc_session_url": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Shareable URL of the child KYC session. `null` when no child session exists." - }, - "kyc_session_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Session id of the child KYC session. Pass it to this same decision endpoint to fetch the person's full KYC report. `null` when no child session exists." - }, - "verification_workflow_type": { - "type": "string", - "nullable": true, - "description": "Base workflow type of the child session's workflow (e.g. `kyc`). `null` for graph-based workflows or when no child session exists." - }, - "verification_workflow_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Workflow id used by the child session. `null` when no child session exists." - }, - "verification_workflow_label": { - "type": "string", - "nullable": true, - "description": "Human-readable label of the child session's workflow. `null` when unset." - }, - "verification_workflow_features": { - "type": "array", - "nullable": true, - "description": "Per-feature progress of the child verification session, e.g. `[{\"feature\": \"ID_VERIFICATION\", \"status\": \"Approved\"}]`. `null` when the child session has no resolvable feature list.", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "description": "Feature display name (e.g. `ID_VERIFICATION`, `LIVENESS`, `AML`)." - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "description": "Feature-level status inside the child session." - } - } - } - }, - "didit_internal_id": { - "type": "string", - "nullable": true, - "description": "Stable internal identifier of the verified individual behind the child session — useful for cross-session linking. `null` when no child session exists or the user has not been resolved yet." - }, - "kyc_document_type": { - "type": "string", - "nullable": true, - "description": "Document type presented in the child KYC session (e.g. `P`, `ID`, `DL`). `null` until the child session has an ID verification result." - }, - "kyc_nationality": { - "type": "string", - "nullable": true, - "description": "Nationality extracted from the document in the child KYC session. `null` until available." - }, - "kyc_liveness_passed": { - "type": "boolean", - "nullable": true, - "description": "Whether the child KYC session's liveness check passed. `null` when no liveness check has run." - }, - "kyc_verified_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the child KYC session reached `Approved`. `null` while the child session has any other status." - }, - "kyc_aml_categories": { - "type": "object", - "nullable": true, - "description": "Per-category summary derived from the person's AML hits. `null` when there are no hits.", - "properties": { - "sanctions": { - "type": "string", - "enum": [ - "match", - "clear" - ] - }, - "pep": { - "type": "string", - "enum": [ - "match", - "clear" - ] - }, - "adverse_media": { - "type": "string", - "enum": [ - "match", - "clear" - ] - }, - "warnings": { - "type": "string", - "enum": [ - "match", - "clear" - ] - } - } - }, - "kyc_notification_sent_at": { - "type": "string", - "nullable": true, - "description": "ISO 8601 timestamp of the last verification-invite notification sent for this person's child session. `null` when none was sent." - }, - "first_name": { - "type": "string", - "nullable": true, - "description": "Given name when explicitly reported by the registry; not inferred from the full name." - }, - "last_name": { - "type": "string", - "nullable": true, - "description": "Family name when explicitly reported by the registry." - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", - "format": "date" - }, - "date_of_birth_year": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 9999, - "description": "Registry-reported birth year, including when the full date is unavailable." - }, - "date_of_birth_month": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 12, - "description": "Registry-reported birth month. Null when only the year is known." - } - } - } - }, - "beneficial_owners": { - "type": "array", - "description": "Beneficial owners discovered via the registry check, each enriched with the status of any child KYC session and AML screening spawned for them.", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string", - "description": "Beneficial owner's full name (or company name for corporate UBOs)." - }, - "first_name": { - "type": "string", - "nullable": true - }, - "last_name": { - "type": "string", - "nullable": true - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete birth date when reported.", - "format": "date" - }, - "country_of_birth": { - "type": "string", - "nullable": true - }, - "email": { - "type": "string", - "nullable": true - }, - "company_name": { - "type": "string", - "nullable": true - }, - "registration_number": { - "type": "string", - "nullable": true - }, - "entity_type": { - "type": "string", - "enum": [ - "person", - "company" - ] - }, - "nationality": { - "type": "string", - "nullable": true - }, - "designation": { - "type": "string", - "nullable": true - }, - "roles": { - "type": "array", - "description": "Role names held by this owner, e.g. `[\"ubo\", \"shareholder\"]`.", - "items": { - "type": "string" - } - }, - "ownership_min_shares": { - "type": "string", - "nullable": true, - "description": "Lower bound of the ownership percentage as reported by the registry. Free-form string (e.g. `\"25\"`, `\"25-50%\"`); use `effective_ownership_percent` for a parsed numeric value." - }, - "ownership_max_shares": { - "type": "string", - "nullable": true, - "description": "Upper bound of the ownership percentage as reported by the registry. Free-form string; use `effective_ownership_percent` for a parsed numeric value." - }, - "is_active": { - "type": "boolean" - }, - "aml_status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "nullable": true, - "description": "Status of the AML screening run for this beneficial owner. `null` when no AML screening was run." - }, - "kyc_status": { - "type": "string", - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "nullable": true, - "description": "Raw lifecycle status of the child KYC session spawned for this beneficial owner. This is the full session status enum (not a reduced Approved/Declined/Pending set). `null` when no child KYC session exists." - }, - "kyc_session_url": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Shareable URL of the child KYC session. `null` when no child session exists." - }, - "kyc_session_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Session id of the child KYC session. Pass it to this same decision endpoint to fetch the person's full KYC report. `null` when no child session exists." - }, - "verification_workflow_type": { - "type": "string", - "nullable": true, - "description": "Base workflow type of the child session's workflow (e.g. `kyc`). `null` for graph-based workflows or when no child session exists." - }, - "verification_workflow_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Workflow id used by the child session. `null` when no child session exists." - }, - "verification_workflow_label": { - "type": "string", - "nullable": true, - "description": "Human-readable label of the child session's workflow. `null` when unset." - }, - "verification_workflow_features": { - "type": "array", - "nullable": true, - "description": "Per-feature progress of the child verification session, e.g. `[{\"feature\": \"ID_VERIFICATION\", \"status\": \"Approved\"}]`. `null` when the child session has no resolvable feature list.", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "description": "Feature display name (e.g. `ID_VERIFICATION`, `LIVENESS`, `AML`)." - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "description": "Feature-level status inside the child session." - } - } - } - }, - "didit_internal_id": { - "type": "string", - "nullable": true, - "description": "Stable internal identifier of the verified individual behind the child session — useful for cross-session linking. `null` when no child session exists or the user has not been resolved yet." - }, - "kyc_document_type": { - "type": "string", - "nullable": true, - "description": "Document type presented in the child KYC session (e.g. `P`, `ID`, `DL`). `null` until the child session has an ID verification result." - }, - "kyc_nationality": { - "type": "string", - "nullable": true, - "description": "Nationality extracted from the document in the child KYC session. `null` until available." - }, - "kyc_liveness_passed": { - "type": "boolean", - "nullable": true, - "description": "Whether the child KYC session's liveness check passed. `null` when no liveness check has run." - }, - "kyc_verified_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the child KYC session reached `Approved`. `null` while the child session has any other status." - }, - "kyc_aml_categories": { - "type": "object", - "nullable": true, - "description": "Per-category summary derived from the person's AML hits. `null` when there are no hits.", - "properties": { - "sanctions": { - "type": "string", - "enum": [ - "match", - "clear" - ] - }, - "pep": { - "type": "string", - "enum": [ - "match", - "clear" - ] - }, - "adverse_media": { - "type": "string", - "enum": [ - "match", - "clear" - ] - }, - "warnings": { - "type": "string", - "enum": [ - "match", - "clear" - ] - } - } - }, - "kyc_notification_sent_at": { - "type": "string", - "nullable": true, - "description": "ISO 8601 timestamp of the last verification-invite notification sent for this person's child session. `null` when none was sent." - }, - "child_beneficial_owners": { - "type": "array", - "description": "For corporate UBOs: the owning entity's own beneficial owners, as raw objects extracted from the registry. Empty array when not applicable.", - "items": { - "type": "object" - } - }, - "effective_ownership_percent": { - "type": "number", - "nullable": true, - "description": "Parsed numeric ownership percentage — the max bound when available, otherwise the min bound. `null` when neither bound could be parsed." - }, - "voting_min_shares": { - "type": "string", - "nullable": true - }, - "voting_max_shares": { - "type": "string", - "nullable": true - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Appointment date when reported.", - "format": "date" - } - } - } - } - } - }, - "submitted": { - "type": "object", - "description": "Parties submitted by the business admin during the Key People flow (BusinessParty rows where source=USER).", - "properties": { - "parties": { - "type": "array", - "items": { - "type": "object", - "description": "Party submitted by the business admin via the Key People flow (source=USER).", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "entity_type": { - "type": "string", - "enum": [ - "person", - "company" - ] - }, - "source": { - "type": "string", - "enum": [ - "USER" - ], - "description": "Always `USER` here — this bucket only contains parties entered or confirmed by the end user in the Key People flow (registry-discovered people appear under `registry`)." - }, - "name": { - "type": "string" - }, - "first_name": { - "type": "string", - "nullable": true - }, - "last_name": { - "type": "string", - "nullable": true - }, - "company_name": { - "type": "string", - "nullable": true - }, - "registration_number": { - "type": "string", - "nullable": true - }, - "email": { - "type": "string", - "format": "email", - "nullable": true - }, - "nationality": { - "type": "string", - "nullable": true, - "description": "Nationality for persons; country of incorporation for company parties." - }, - "phone_number": { - "type": "string", - "nullable": true - }, - "date_of_birth": { - "type": "string", - "format": "date", - "nullable": true - }, - "position": { - "type": "string", - "nullable": true, - "description": "Free-text job title / position (persons only)." - }, - "custom_fields": { - "type": "object", - "description": "Workflow-defined custom fields collected for this party; keys are defined by the workflow's key-people field descriptors." - }, - "is_active": { - "type": "boolean" - }, - "is_skipped": { - "type": "boolean", - "description": "True when the end user skipped verification for this party (only possible when the role allows skipping)." - }, - "requires_verification": { - "type": "boolean", - "description": "True when a child KYC / KYB sub-session is required for this party based on role settings and ownership thresholds." - }, - "roles": { - "type": "array", - "description": "All roles held by this party, each with its own ownership and voting percentages. There is no scalar `role`/`ownership_percent` on the party itself.", - "items": { - "type": "object", - "properties": { - "role": { - "type": "string", - "enum": [ - "director", - "non_executive_director", - "secretary", - "chairman", - "ubo", - "shareholder", - "representative", - "founder", - "legal_advisor", - "authorized_signatory", - "trustee", - "beneficiary", - "settlor", - "protector", - "company_officer", - "investor", - "other" - ] - }, - "ownership_percent": { - "type": "number", - "nullable": true - }, - "voting_percent": { - "type": "number", - "nullable": true - } - } - } - }, - "kyc_session_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Child KYC session id for person parties requiring verification. `null` otherwise." - }, - "kyc_session_status": { - "type": "string", - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "nullable": true, - "description": "Lifecycle status of the child KYC session. `null` when no child KYC session exists." - }, - "kyc_session_url": { - "type": "string", - "format": "uri", - "nullable": true - }, - "kyb_sub_session_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Child KYB sub-session id for corporate parties requiring verification. `null` otherwise." - }, - "kyb_sub_session_status": { - "type": "string", - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "nullable": true, - "description": "Lifecycle status of the child KYB sub-session. `null` when no sub-session exists." - }, - "kyb_sub_session_url": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Shareable URL of the child KYB sub-session. `null` when no sub-session exists." - }, - "verification_workflow_type": { - "type": "string", - "nullable": true, - "description": "Base workflow type of the child session's workflow (KYC session preferred, falling back to the KYB sub-session). `null` for graph-based workflows or when no child session exists." - }, - "verification_workflow_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Workflow id used by the child session. `null` when no child session exists." - }, - "verification_workflow_label": { - "type": "string", - "nullable": true, - "description": "Human-readable label of the child session's workflow. `null` when unset." - }, - "verification_workflow_features": { - "type": "array", - "nullable": true, - "description": "Per-feature progress of the child verification session, e.g. `[{\"feature\": \"ID_VERIFICATION\", \"status\": \"Approved\"}]`. `null` when the child session has no resolvable feature list.", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "description": "Feature display name (e.g. `ID_VERIFICATION`, `LIVENESS`, `AML`)." - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "description": "Feature-level status inside the child session." - } - } - } - }, - "didit_internal_id": { - "type": "string", - "nullable": true, - "description": "Stable internal identifier of the verified individual behind the child session. `null` when not resolved." - } - } - } - } - } - }, - "ubo_kyc_summary": { - "type": "object", - "nullable": true, - "description": "Aggregate UBO KYC progress, or null when no UBOs have linked KYC sessions.", - "properties": { - "total": { - "type": "integer" - }, - "approved": { - "type": "integer" - }, - "flagged": { - "type": "integer", - "description": "UBOs whose KYC is Declined or In Review." - }, - "pending": { - "type": "integer" - } - } - }, - "warnings": { - "type": "array", - "items": { - "type": "object" - } - } - } - } + "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it in the background of the same request and may overwrite `parsed_address` with the geocoder result." }, - "document_verifications": { - "type": "array", + "place_of_birth": { + "type": "string", + "maxLength": 255, "nullable": true, - "description": "Document verification checks grouped by workflow node. Present only when `session_kind = business`.", - "items": { - "type": "object", - "description": "Documents grouped by workflow node. Present only when session_kind = business.", - "properties": { - "status": { - "type": "string" - }, - "node_id": { - "type": "string", - "nullable": true - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "document_type": { - "type": "string", - "enum": [ - "LEGAL_PRESENCE", - "COMPANY_DETAILS", - "OWNERSHIP_STRUCTURE", - "REPRESENTATIVES_AUTHORIZATION", - "OTHER" - ], - "nullable": true, - "description": "High-level KYB document group the file was classified into. `null` while unclassified." - }, - "document_subtype": { - "type": "string", - "nullable": true, - "description": "Specific OCR-detected subtype (e.g. `CERTIFICATE_OF_INCORPORATION`, `SHAREHOLDER_REGISTRY`); `UNKNOWN` when the OCR could not confidently classify the document." - }, - "document_group": { - "type": "string", - "enum": [ - "LEGAL_PRESENCE", - "COMPANY_DETAILS", - "OWNERSHIP_STRUCTURE", - "REPRESENTATIVES_AUTHORIZATION", - "OTHER" - ], - "nullable": true, - "description": "Alias of `document_type` — the group name used by `groups` and `required_groups`." - }, - "file_url": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Short-lived presigned link to the uploaded file. `null` for analyst-created document request placeholders (`is_requested = true`) that have not been fulfilled yet." - }, - "original_filename": { - "type": "string", - "nullable": true, - "description": "Filename as uploaded by the user or analyst." - }, - "file_size": { - "type": "integer", - "nullable": true, - "description": "File size in bytes." - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "description": "Per-document verification status." - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "cross_check_result": { - "type": "object", - "nullable": true, - "description": "Cross-reference of the OCR-extracted document fields against the registry company profile. An object with `critical`, `non_critical` and `people` sections, each mapping a field name (e.g. `company_name`, `registration_number`, `incorporation_date`, `registered_address`) to a comparison record like `{\"document\": \"...\", \"registry\": \"...\", \"match\": true, \"score\": 0.97}`. `null` when the document had no OCR data or there was no registry company to compare against." - }, - "document_metadata": { - "type": "object", - "nullable": true, - "description": "Metadata extracted from the uploaded corporate document. When metadata extraction succeeded, all 16 keys are always present (individual values are `null` when not applicable to the file type); the whole object is `null` when nothing could be extracted. For PDFs, `overlay_manipulation` carries forensic overlay-text analysis.", - "properties": { - "file_size": { - "type": "integer", - "nullable": true, - "description": "Size of the document." - }, - "content_type": { - "type": "string", - "nullable": true, - "description": "Content type of the document." - }, - "creation_date": { - "type": "string", - "nullable": true, - "description": "Date when the document was created." - }, - "modified_date": { - "type": "string", - "nullable": true, - "description": "Date when the document was modified." - }, - "creator": { - "type": "string", - "nullable": true, - "description": "Authoring tool/user recorded in the file metadata (PDF `Creator`)." - }, - "producer": { - "type": "string", - "nullable": true, - "description": "Software that produced the file (PDF `Producer`)." - }, - "software": { - "type": "string", - "nullable": true, - "description": "Editing software recorded in the file metadata." - }, - "encryption": { - "type": "string", - "nullable": true, - "description": "Encryption scheme of the file, when encrypted." - }, - "is_signed": { - "type": "boolean", - "nullable": true, - "description": "Whether the document carries a digital signature." - }, - "is_tampered": { - "type": "boolean", - "nullable": true, - "description": "Whether the digital signature failed validation (post-signing modification)." - }, - "signature_info": { - "type": "object", - "nullable": true, - "description": "Details of the digital signature(s), when present." - }, - "exif_original_date": { - "type": "string", - "nullable": true, - "description": "EXIF original capture date, for image uploads." - }, - "exif_digitized_date": { - "type": "string", - "nullable": true, - "description": "EXIF digitization date, for image uploads." - }, - "processed_by_known_editor": { - "type": "boolean", - "nullable": true, - "description": "Whether metadata indicates the file passed through a known image/PDF editor." - }, - "has_different_creation_mod_date": { - "type": "boolean", - "nullable": true, - "description": "Whether creation and modification dates differ — a possible (but not conclusive) editing signal." - }, - "overlay_manipulation": { - "type": "object", - "nullable": true, - "description": "PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF.", - "properties": { - "detected": { - "type": "boolean", - "description": "Whether overlay-text manipulation evidence was detected." - }, - "analyzed": { - "type": "boolean", - "description": "Whether the PDF could be analyzed." - }, - "signals": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation." - }, - "manipulated_regions": { - "type": "array", - "description": "Suspected manipulated areas in PDF page coordinates.", - "items": { - "type": "object", - "properties": { - "page": { - "type": "integer" - }, - "x": { - "type": "number" - }, - "y": { - "type": "number" - }, - "width": { - "type": "number" - }, - "height": { - "type": "number" - }, - "page_width": { - "type": "number" - }, - "page_height": { - "type": "number" - } - } - } - } - } - } - } - }, - "description": { - "type": "string", - "nullable": true, - "description": "Analyst-provided label or reason for a document request. `null` for regular uploads." - }, - "is_requested": { - "type": "boolean", - "description": "True for analyst-created document request placeholders awaiting an upload." - }, - "ocr_data": { - "type": "object", - "nullable": true - }, - "is_console_upload": { - "type": "boolean", - "description": "True for documents uploaded from the console rather than the end-user flow. Console uploads never change the feature-level aggregate status." - }, - "is_confirmed": { - "type": "boolean", - "description": "Console uploads start `false` and flip `true` once the admin confirms them; end-user uploads are always `true`." - } - } - } - }, - "groups": { - "type": "object", - "description": "Per-group progress counters keyed by group name (e.g. legal_presence, ownership_structure).", - "additionalProperties": { - "type": "object", - "properties": { - "total": { - "type": "integer" - }, - "approved": { - "type": "integer" - }, - "pending": { - "type": "integer" - }, - "declined": { - "type": "integer" - }, - "in_review": { - "type": "integer" - }, - "other": { - "type": "integer" - }, - "missing": { - "type": "integer", - "description": "Requested placeholders with no file uploaded yet." - } - } - } - }, - "required_groups": { - "type": "array", - "items": { - "type": "string" - } - }, - "warnings": { - "type": "array", - "items": { - "type": "object" - } - } - } - } + "description": "Place of birth as printed on the document." }, - "environment": { + "nationality": { + "type": "string", + "maxLength": 32, + "nullable": true, + "description": "Nationality as printed on the document. Free text up to 32 characters — typically an ISO 3166-1 alpha-3 code (e.g. `ESP`), but not validated as one." + }, + "marital_status": { "type": "string", "enum": [ - "live", - "sandbox" + "SINGLE", + "MARRIED", + "DIVORCED", + "WIDOWED", + "UNKNOWN" ], - "description": "Whether the session was created by a live application (`live`) or a sandbox application (`sandbox`). Sandbox sessions are free, fully simulated, and never bill credits." + "description": "Marital status where the document carries it. Any other value returns `400`." }, - "resubmissions": { - "type": "array", - "description": "Returned only with `include=resubmissions`, for user and business sessions. One entry per resubmission request, oldest first. Each entry carries the results the resubmitted steps returned before the request, so earlier submissions stay readable. Deleting the session deletes every submission.", - "items": { - "type": "object", - "properties": { - "number": { - "type": "integer", - "description": "1 for the first resubmission of the session, 2 for the second, and so on." - }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "When the resubmission was requested." - }, - "previous_status": { - "type": "string", - "description": "The session status just before the request." - }, - "nodes": { - "type": "array", - "description": "The workflow steps sent back to the user.", - "items": { - "type": "object", - "properties": { - "node_id": { - "type": "string" - }, - "feature": { - "type": "string" - } - } - } - }, - "reasons": { - "type": "object", - "nullable": true, - "additionalProperties": { - "type": "string" - }, - "description": "The reason given for each step, keyed by `node_id`." - }, - "requested_by": { - "type": "object", - "nullable": true, - "description": "Who requested the resubmission.", - "properties": { - "name": { - "type": "string" - }, - "email": { - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "results": { - "type": "object", - "additionalProperties": true, - "description": "What the resubmitted steps returned before the request, under the same keys and in the same shape as the current results of this response (`id_verifications`, `nfc_verifications`, `liveness_checks`, `face_matches`, `poa_verifications`, `phone_verifications`, `email_verifications`, `aml_screenings`, `ip_analyses`, `database_validations`, `questionnaire_responses`, `document_ai_documents`, `geolocation_checks`; for business sessions also `document_verifications`). Only the keys of the steps that were sent back appear." - } + "extra_fields": { + "type": "object", + "additionalProperties": true, + "nullable": true, + "description": "Merged view of the document's extra fields: the OCR-extracted values with all operator overrides applied. Keys removed via a `null` override are absent from this map." + }, + "parsed_address": { + "type": "object", + "nullable": true, + "description": "Current structured address (with internal bookkeeping keys stripped), or `null` when none exists. Includes the recomputed `formatted_address` alongside the seven editable keys.", + "properties": { + "address_type": { + "type": "string", + "nullable": true, + "description": "Free-form address classification (e.g. `home`)." + }, + "city": { + "type": "string", + "nullable": true, + "example": "Madrid" + }, + "region": { + "type": "string", + "nullable": true, + "description": "State / province / autonomous community." + }, + "street_1": { + "type": "string", + "nullable": true, + "example": "Avda de Madrid 34" + }, + "street_2": { + "type": "string", + "nullable": true + }, + "postal_code": { + "type": "string", + "nullable": true, + "example": "28013" + }, + "country": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.", + "example": "ES" + }, + "formatted_address": { + "type": "string", + "nullable": true, + "description": "Joined, human-readable form of the structured address. Recomputed server-side after every `parsed_address` edit.", + "example": "Avda de Madrid 34, Madrid, 28013, ES" } } } } + }, + "examples": { + "Updated KYC": { + "summary": "Full KYC record after the correction", + "value": { + "document_type": "P", + "document_subtype": null, + "document_number": "PAB123456", + "personal_number": "99999999R", + "date_of_birth": "1980-01-01", + "date_of_issue": "2021-02-09", + "expiration_date": "2031-02-08", + "issuing_state": "ESP", + "first_name": "Carmen", + "last_name": "Espanola Espanola", + "gender": "F", + "address": "AVDA DE MADRID 34, MADRID, MADRID 28013", + "place_of_birth": "MADRID", + "nationality": "ESP", + "marital_status": "MARRIED", + "extra_fields": { + "mrz_string": "P\": [\"\"]}`; errors on nested `parsed_address` / `extra_fields` keys use an object value: `{\"parsed_address\": {\"\": \"\"}}`.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "Authentication credentials were not provided or are invalid." + "examples": { + "Invalid gender": { + "summary": "gender must be M, F, or U", + "value": { + "gender": [ + "\"X\" is not a valid choice." + ] + } + }, + "Invalid document type": { + "summary": "Not a short code or long-form alias", + "value": { + "document_type": [ + "\"PASSPORT\" is not a valid choice." + ] + } + }, + "Invalid issuing state": { + "summary": "issuing_state is not ISO 3166-1 alpha-3", + "value": { + "issuing_state": [ + "Invalid ISO 3166-1 alpha-3 country code for issuing_state." + ] + } + }, + "Invalid marital status": { + "summary": "marital_status not in the enum", + "value": { + "marital_status": [ + "\"ENGAGED\" is not a valid choice." + ] + } + }, + "Bad date format": { + "summary": "Dates must be YYYY-MM-DD", + "value": { + "date_of_birth": [ + "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." + ] + } + }, + "Unsupported parsed_address key": { + "summary": "Only the seven documented keys are allowed", + "value": { + "parsed_address": { + "zipcode": "Unsupported key. Allowed keys: address_type, city, region, street_1, street_2, postal_code, country" + } + } + }, + "Invalid parsed_address country": { + "summary": "Nested country must be alpha-2, not alpha-3", + "value": { + "parsed_address": { + "country": "Invalid ISO 3166-1 alpha-2 country code." + } + } + }, + "extra_fields not an object": { + "summary": "extra_fields must be a JSON object", + "value": { + "extra_fields": [ + "This field must be a JSON object." + ] } } - }, + } + } + } + }, + "403": { + "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", + "content": { + "application/json": { "examples": { - "Missing or invalid credentials": { - "summary": "No x-api-key header, or the key failed introspection", + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", "value": { "detail": "Authentication credentials were not provided or are invalid." } }, - "Insufficient permissions": { - "summary": "Valid credential lacking read:sessions or access to the owning application", + "Missing Privilege": { + "summary": "Credential lacks write:sessions", "value": { "detail": "You do not have permission to perform this action." } @@ -10211,29 +28547,32 @@ } }, "404": { - "description": "Session not found. Returned when no live session matches the supplied `sessionId` as either a User Verification (KYC) or a Business Verification (KYB) session — a session that has been deleted also lands here. Body is the standard `{\"detail\": ...}` envelope.\n\n**Two distinct `detail` strings.** If the `sessionId` is unknown as both a KYC and a KYB session, the pre-auth owner lookup fails and you receive `{\"detail\": \"Not found.\"}` — this happens before credential validation, so it is returned even for unauthenticated calls. If the session exists but cannot be retrieved under the authenticated application (for example a stale or just-deleted record), the view returns `{\"detail\": \"Session not found.\"}`. Both indicate the same outcome — the session is not accessible — so match on the `404` status code rather than the string.", + "description": "Unknown session, session in a non-editable state, or no KYC record to patch. Each case has a distinct `detail` message (see examples). The unknown-session lookup runs before credential checks.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "Not found." - } - } - }, "examples": { - "Unknown session id": { - "summary": "sessionId is unknown to this application", + "Unknown session": { + "summary": "No session with this ID in your application (or it was deleted)", "value": { "detail": "Not found." } }, - "Session not retrievable": { - "summary": "Session exists but is not retrievable for this application", + "Wrong session state": { + "summary": "Session exists but is not Approved / Declined / In Review / Kyc Expired", "value": { - "detail": "Session not found." + "detail": "No Session matches the given query." + } + }, + "No KYC record": { + "summary": "The workflow never ran an ID-verification step", + "value": { + "detail": "The session does not have a KYC." + } + }, + "No KYC for node": { + "summary": "node_id given but no KYC exists for it", + "value": { + "detail": "The session does not have a KYC for node 'feature_missing'." } } } @@ -10241,55 +28580,22 @@ } }, "429": { - "description": "Rate limit exceeded. GET requests are limited to **600 per minute per credential** (API key, Bearer token, or source IP when unauthenticated). Inspect `Retry-After` / `X-RateLimit-Reset` and back off before retrying. If you hit this limit while polling for decisions, switch to the `status.updated` webhook and fetch the decision once per status change instead.", - "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying the request.", - "schema": { - "type": "integer", - "minimum": 1, - "example": 30 - } - }, - "X-RateLimit-Limit": { - "description": "Maximum number of requests allowed in the current window (600 for GET requests).", - "schema": { - "type": "integer", - "example": 600 - } - }, - "X-RateLimit-Remaining": { - "description": "Requests remaining in the current window.", - "schema": { - "type": "integer", - "example": 0 - } - }, - "X-RateLimit-Reset": { - "description": "Unix timestamp (seconds) at which the current window resets.", - "schema": { - "type": "integer", - "example": 1750000000 - } - } - }, + "description": "Dedicated rate limit exceeded — 10 KYC data updates per minute per API key (rejected requests count toward the budget). The shared 300/min write budget can also trigger with its own message. Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string", - "description": "Human-readable explanation of the rate-limit breach.", - "example": "GET request rate limit exceeded. You can make up to 600 requests per minute." + "type": "string" } } }, "examples": { - "Throttled": { - "summary": "More than 600 GET requests in one minute from the same credential", + "Rate limited": { + "summary": "KYC update budget exhausted", "value": { - "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + "detail": "Session KYC data update rate limit exceeded. You can make up to 10 requests per minute." } } } @@ -10297,82 +28603,164 @@ } } }, - "parameters": [ - { - "name": "sessionId", - "in": "path", - "required": true, - "description": "UUID of the verification session whose decision you want to retrieve. This is the `session_id` returned by `POST /v3/session/`. The same path works for both User Verification (KYC) and Business Verification (KYB) sessions — the server resolves the id against both session types.\n\n**Must be a valid UUID.** The route only matches well-formed UUIDs, so a malformed value is rejected by the URL router before any application code runs and the response is a `404` **HTML** page rather than the JSON `{\"detail\": ...}` envelope shown below. Validate the UUID format client-side so you can distinguish an input error from a genuine missing-session `404`.", - "schema": { - "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" - } - }, - { - "name": "include", - "in": "query", - "required": false, - "description": "Comma-separated list of extra payload sections. Supported values: `events` and `resubmissions`, alone or together (`events,resubmissions`). When `include=events` is passed, the response additionally contains the session event timeline (`events`, including the `WORKFLOW_BRANCH_EVALUATED` / `WORKFLOW_STATUS_NODE_REACHED` routing events described on the Retrieve Session page), the full activity log (`activity_reviews`), blocklist flags (`blocklisted`), a per-session `cost_breakdown`, workflow metadata (`workflow_type`, `workflow_version`, `workflow_version_id`, and `workflow_terminal_node_id` - the last recorded workflow status node that set a session status, or `null` if none has been recorded; historical provenance that is not cleared by manual approvals, declines or resubmissions, and is replaced by a later workflow status node), and session metadata (`api_service` — string or `null`, set only for standalone-API sessions; `session_type` — `API`, `HOSTED`, or `MIGRATED`; `has_device_nfc_support` — boolean). Two fields change relative to the plain shape: the top-level `session_kind` discriminator is **omitted** (the events shape only exists for user sessions), and the `features` array switches from plain strings to `{feature, node_id}` objects — where the email feature is reported as `EMAIL` instead of `EMAIL_VERIFICATION`. This expanded shape is primarily used by the Didit Console; most API integrations should omit the parameter. **KYB limitation:** `include=events` only applies to user (KYC) sessions. Business sessions (`session_kind = \"business\"`) always return the standard business decision shape — `events`, `activity_reviews`, `blocklisted`, `cost_breakdown` and the extra `workflow_*`/session metadata are never returned for business sessions, and their `features` array stays a plain string array. With `include=resubmissions` (user and business sessions alike) the response also contains `resubmissions`: one entry per resubmission request, oldest first, each with `number`, `created_at`, `previous_status`, `nodes`, `reasons`, `requested_by` and `results` (what the resubmitted steps returned before the request, under the same keys and in the same shape as the current results). Each `cost_breakdown` item carries `usage_type`, `price`, `details` and `created_at`: a resubmitted step that runs again is billed as a new item.", - "schema": { - "type": "string", - "enum": [ - "events", - "resubmissions", - "events,resubmissions" - ], - "example": "events" - } - } - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", - "label": "cURL", - "source": "curl -X GET 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/decision/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Accept: application/json'" + "label": "curl", + "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-data/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"first_name\": \"Carmen\",\n \"last_name\": \"Espanola Espanola\",\n \"date_of_birth\": \"1980-01-01\",\n \"issuing_state\": \"ESP\",\n \"parsed_address\": {\n \"street_1\": \"Avda de Madrid 34\",\n \"city\": \"Madrid\",\n \"postal_code\": \"28013\",\n \"country\": \"ES\"\n }\n }'" }, { "lang": "python", "label": "Python", - "source": "import requests\n\nsession_id = \"11111111-2222-3333-4444-555555555555\"\nurl = f\"https://verification.didit.me/v3/session/{session_id}/decision/\"\nheaders = {\n 'x-api-key': 'YOUR_API_KEY',\n \"Accept\": \"application/json\",\n}\n\nresponse = requests.get(url, headers=headers, timeout=15)\nresponse.raise_for_status()\ndecision = response.json()\n\nprint(\"session_kind:\", decision[\"session_kind\"])\nprint(\"top-level status:\", decision[\"status\"])\n\n# nfc_verifications is ALWAYS an array — never a singular `nfc` field.\nfor nfc in decision.get(\"nfc_verifications\") or []:\n print(\"nfc node_id:\", nfc[\"node_id\"], \"status:\", nfc[\"status\"])\n\nfor id_check in decision.get(\"id_verifications\") or []:\n print(\"id node_id:\", id_check[\"node_id\"], \"status:\", id_check[\"status\"])" + "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-data/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\n \"first_name\": \"Carmen\",\n \"last_name\": \"Espanola Espanola\",\n \"date_of_birth\": \"1980-01-01\",\n \"issuing_state\": \"ESP\",\n \"extra_fields\": {\"blood_group\": \"O+\"},\n \"parsed_address\": {\"city\": \"Madrid\", \"postal_code\": \"28013\", \"country\": \"ES\"},\n },\n)\nresponse.raise_for_status()\nkyc = response.json() # full current KYC record\nprint(kyc[\"first_name\"], kyc[\"parsed_address\"][\"formatted_address\"])" }, { "lang": "javascript", "label": "JavaScript", - "source": "const sessionId = '11111111-2222-3333-4444-555555555555';\nconst response = await fetch(\n `https://verification.didit.me/v3/session/${sessionId}/decision/`,\n {\n method: 'GET',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n Accept: 'application/json',\n },\n }\n);\n\nif (!response.ok) {\n throw new Error(`Decision fetch failed: ${response.status}`);\n}\nconst decision = await response.json();\n\nconsole.log('session_kind:', decision.session_kind);\nconsole.log('top-level status:', decision.status);\n\n// nfc_verifications is ALWAYS an array — never a singular `nfc` field.\nfor (const nfc of decision.nfc_verifications ?? []) {\n console.log('nfc node_id:', nfc.node_id, 'status:', nfc.status);\n}\n\nfor (const idCheck of decision.id_verifications ?? []) {\n console.log('id node_id:', idCheck.node_id, 'status:', idCheck.status);\n}" + "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-data/',\n {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n first_name: 'Carmen',\n last_name: 'Espanola Espanola',\n date_of_birth: '1980-01-01',\n issuing_state: 'ESP',\n parsed_address: { city: 'Madrid', postal_code: '28013', country: 'ES' },\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst kyc = await response.json(); // full current KYC record" } ] } }, - "/v3/session/{sessionId}/delete/": { - "delete": { - "summary": "Delete a verification session (KYC or KYB)", - "description": "Delete a single verification session, User Verification (KYC) or Business Verification (KYB), by its `session_id`. The id is resolved against KYC sessions first, then KYB sessions, so both kinds are deleted through this one URL.\n\n**What is deleted:**\n- The session and every verification attempt it holds, including results that a resubmission replaced: the decision, extracted data, all related feature records (ID verification, face, liveness, face match, AML, registry checks, IP analysis, and so on), its reviews and its activity log.\n- All stored files owned by the session: document front/back photos (full, cropped, and privacy-blurred variants), document-capture videos, portrait crops, NFC chip portrait and signature images, face reference images, liveness videos, face-match source/target images, Proof of Address documents, and any extra uploaded files (KYC); uploaded company documents and extra files (KYB). Previously issued media URLs (`https:///...`) stop resolving.\n- The session's face data. Its face embedding is deleted too, unless you choose to keep an image-free template (see **Face biometric data**).\n- Webhook deliveries still queued for the session, and their retries. Webhooks already delivered are not affected.\n- For a Business Verification (KYB) session: the verifications it created for its key people (KYC) and its corporate owners (KYB), recursively, reported as `deleted_child_verifications`. A verification it only links to, such as an existing approved KYC it reused, is not deleted.\n\nThe session disappears from every read endpoint immediately: `GET /v3/session/{sessionId}/decision/` returns `404` and `GET /v3/sessions/` stops listing it. Ongoing AML monitoring and KYB registry monitoring started by the deleted verifications stop.\n\n**What is not affected:** blocklist entries created from this session (face or document) are **not** removed; manage those with the blocklist endpoints. Share tokens issued for the session are not revoked, but there is nothing left for them to import: an import with one returns `400` with `{\"share_token\": [\"Original session does not exist.\"]}`. Credits already consumed are **not** refunded. The parent User or Business entity is **not** deleted; use `POST /v3/users/delete/` or `POST /v3/businesses/delete/` for those. If the User's profile portrait came from this session, the portrait is removed with it.\n\n**Face biometric data:** by default the session's face embedding is deleted with the session (`face_retention_outcome: deleted`). An application that opted in to biometric-template retention (`face_retention_policy: retain_until_user_deleted` on `PATCH /v3/webhook/`), or a request with `retain_face_embeddings: true`, keeps one separately managed, image-free face biometric template anchored to the session's User (`retained_with_user`) so that duplicate detection, Face Search, and biometric authentication keep working. The template never contains the face image, media, identity fields, the decision, or the deleted session's id; it is biometric data deleted at `biometric_template_expires_at`, or earlier on User deletion, privacy erasure, or an explicit purge through `/v3/biometric-templates/`. A session without a linked User cannot retain a template (`ineligible_no_vendor_user`). Send `deletion_instruction: privacy_erasure` for data-subject requests: it purges every retained template for that User and never retains a new one.\n\n**Irreversible:** deletion is permanent and there is no restore endpoint. Export anything you need (for example a decision PDF) before you call it.\n\n**Side effects:** no webhook is emitted for deletions.\n\n**Response:** `200 OK` once the deletion has completed, with a JSON body reporting the `face_retention_outcome`, the retained template's expiry and, for a KYB session, `deleted_child_verifications`. Earlier versions of this endpoint returned `204 No Content`; update clients that assert on `204`. Keep your `instruction_id` in your own erasure log.\n\n**Idempotency:** not idempotent at the HTTP level: the first call returns `200`; repeating it returns `404` because the session no longer resolves. After a `503` the session is still in place; repeat the same request to finish deleting it.\n\n**Authentication:** send your application's API key in the `x-api-key` header; the session must belong to that application. Console user access tokens (`Authorization: Bearer ...`) may also call this endpoint when they carry the `delete:sessions` permission. Authentication and permission failures both return `403`; this API never returns `401`.\n\n**Rate limit:** shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\nTo bulk-delete KYC sessions by their numeric `session_number`, use `POST /v3/sessions/delete/` instead.", - "operationId": "delete_v3_session_by_id", + "/v3/session/{sessionId}/features/{nodeId}/update-status/": { + "patch": { + "summary": "Update an individual feature (step) status", + "description": "Set the status of a single verification feature (step) within a session. Target the feature by its workflow graph `nodeId`. On non-graph workflows, whose steps have no node, use `default` and also pass `feature_type` to say which step you mean.\n\nWorks for both User Verification (KYC) and Business Verification (KYB) sessions and every step type: ID/OCR, Liveness, Face Match, NFC, AML, Proof of Address, Phone, Email, Database Validation, Questionnaire, and the KYB registry / documents / key-people checks. This endpoint is **not** available for standalone API sessions (`session_type = API`).\n\n`new_status` must be one of `Approved`, `Declined`, or `In Review`, and must differ from the feature's current status.\n\n**When the override applies.** Without `defer_until_flow_finishes` (or with `false`) the override applies at once, as it always did; when the person is still in the flow it is recorded as `forced`. With `defer_until_flow_finishes: true` while the person is still in the flow (the session is `Not Started`, `In Progress` or `Resubmitted` and has not finished), the override is stored as `pending_until_flow_finishes` and applies when the flow finishes, after the flow reaches its own decision. On a session whose flow already ended it applies at once either way. A waiting override is cancelled instead when the person redoes its step before finishing, when a newer override on the same check replaces it, when the session leaves the flow unfinished (abandoned, expired, or decided mid-flow), or when you cancel it with `POST /v3/session/{sessionId}/features/overrides/{overrideId}/cancel/`. List a session's overrides with `GET /v3/session/{sessionId}/features/overrides/`.\n\n**Effect on the session.** A session that already has a decision keeps it: only the check changes. When the session has no decision yet and its workflow is waiting on exactly this step, applying the override completes the step: the workflow moves on, later steps run, and a status node can set the session decision and fire `status.updated`.\n\n**Webhooks and activity.** A `data.updated` webhook fires when the override applies (at once, or when the flow finishes), never while it waits or when it is cancelled. Every override is recorded in the session's activity as `FEATURE_OVERRIDE_PENDING`, `FEATURE_OVERRIDE_APPLIED` or `FEATURE_OVERRIDE_CANCELLED`, with who made it; the request's entry carries your `comment`.\n\nAuthenticate with your API key (`x-api-key`) or a user authorization header carrying the `write:sessions` privilege; a scoped API key needs write access to decisions.", + "operationId": "patch_v3_session_update_feature_status", + "x-didit-sdk": { + "resource": "sessions", + "method": "update_feature_status", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], + "security": [ + { + "ApiKeyAuth": [] + } + ], "parameters": [ { "in": "path", "name": "sessionId", "required": true, - "description": "UUID (`session_id`) of the User Verification (KYC) or Business Verification (KYB) session to delete, as returned when the session was created. Must be a canonical hyphenated UUID — a non-UUID value does not match the route and returns `404`.", + "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", "schema": { "type": "string", "format": "uuid", "example": "11111111-2222-3333-4444-555555555555" } + }, + { + "in": "path", + "name": "nodeId", + "required": true, + "description": "Workflow graph node ID of the feature to update (for example `feature_ocr_1`). Use the literal value `default` for non-graph (single-step) workflows where the feature has no node ID.", + "schema": { + "type": "string", + "example": "feature_ocr_1" + } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "new_status" + ], + "properties": { + "new_status": { + "type": "string", + "description": "Target status for this feature. Must differ from the current value.", + "enum": [ + "Approved", + "Declined", + "In Review" + ], + "example": "Approved" + }, + "comment": { + "type": "string", + "description": "Optional free-text note recorded on the session's review trail for this feature change.", + "example": "Document manually verified by compliance." + }, + "feature_type": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "OCR", + "OCR_BACK", + "NFC", + "LIVENESS", + "FACE", + "AGE_ESTIMATION", + "AGE_VERIFICATION", + "FACE_SEARCH", + "FACE_MATCH", + "IP_ANALYSIS", + "AML", + "PHONE", + "PHONE_VERIFICATION", + "EMAIL", + "EMAIL_VERIFICATION", + "POA", + "PROOF_OF_ADDRESS", + "DATABASE_VALIDATION", + "DATA_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE", + "BANK_VERIFICATION", + "GEOLOCATION" + ], + "description": "Which kind of step to target. Needed on non-graph workflows (`nodeId` = `default`), where every step shares no node; optional otherwise. An unknown value returns `400`.", + "example": "LIVENESS" + }, + "defer_until_flow_finishes": { + "type": "boolean", + "default": false, + "description": "While the person is still in the flow, store the override as `pending_until_flow_finishes` and apply it when the flow finishes, instead of at once. Omitted or `false`, the override applies at once. On a session whose flow already ended it applies at once either way.", + "example": false + } + } + }, + "examples": { + "Apply Now": { + "summary": "Approve the ID step at once", + "value": { + "new_status": "Approved", + "comment": "Document manually verified by compliance." + } + }, + "Wait For The Flow": { + "summary": "Approve the liveness step once the person finishes the flow", + "value": { + "new_status": "Approved", + "comment": "Liveness retried with a reviewer present.", + "defer_until_flow_finishes": true + } + }, + "Non-graph Workflow": { + "summary": "Target the liveness step of a workflow without nodes (nodeId = default)", + "value": { + "new_status": "In Review", + "feature_type": "LIVENESS" + } + } + } + } + } + }, "responses": { "200": { - "description": "Session deleted. The body reports what happened to the session's face biometric data and, for a KYB session, how many verifications it created were deleted with it.", + "description": "The override was recorded. `new_status` is the check's status as stored: the requested one once the override applied, the unchanged one while it waits for the flow to finish. `override` says which happened.", "content": { "application/json": { "schema": { @@ -10380,209 +28768,187 @@ "properties": { "session_id": { "type": "string", - "format": "uuid", - "description": "The deleted session." - }, - "session_number": { - "type": "integer", - "nullable": true, - "description": "The deleted session's number." + "format": "uuid" }, - "face_retention_outcome": { + "node_id": { "type": "string", - "enum": [ - "retained_with_user", - "deleted", - "none", - "ineligible_no_vendor_user" - ], - "description": "What happened to the session's face biometric data. `deleted`: the face embedding was deleted with the session (default). `retained_with_user`: the session is deleted and one image-free biometric template stays anchored to the User; see `biometric_template_uuid`. `none`: the session had no face embedding (for example a KYB session, or a KYC session without a liveness selfie). `ineligible_no_vendor_user`: retention was requested but the session has no linked User, so the embedding was deleted with the session. A biometric-store failure is never reported through this field on this endpoint: it returns `503` and the session is not deleted." + "nullable": true, + "description": "The updated step's workflow node, or `null` for a step without a node (the `default` path in a non-graph workflow).", + "example": "feature_ocr_1" }, - "biometric_template_uuid": { + "feature_type": { "type": "string", - "format": "uuid", - "nullable": true, - "description": "Id of the retained biometric template when `face_retention_outcome` is `retained_with_user`; `null` otherwise. Manage it through `/v3/biometric-templates/{template_uuid}/`." + "description": "Model name of the updated feature (e.g. `KYC`, `Face`, `FaceMatch`, `AML`, `POA`).", + "example": "KYC" }, - "biometric_template_expires_at": { + "new_status": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the retained biometric template is deleted, when `face_retention_outcome` is `retained_with_user`; `null` when no template is retained." + "description": "The check's stored status: the requested one once the override applied, the unchanged one while the override waits for the flow to finish.", + "example": "Approved" }, - "deleted_child_verifications": { - "type": "integer", - "description": "For a Business Verification (KYB) session: how many verifications it created (its key people's KYC sessions and its corporate owners' KYB sessions, recursively) were deleted with it. Always `0` for a KYC session." + "override": { + "$ref": "#/components/schemas/FeatureStatusOverride" } - } + }, + "required": [ + "session_id", + "node_id", + "feature_type", + "new_status", + "override" + ] }, "examples": { - "Deleted (default)": { - "value": { - "session_id": "4c5c7f3a-3b1d-4b7e-9c39-2b4f7e6a1d02", - "session_number": 1024, - "face_retention_outcome": "deleted", - "biometric_template_uuid": null, - "biometric_template_expires_at": null, - "deleted_child_verifications": 0 - } - }, - "Retained with User": { - "value": { - "session_id": "4c5c7f3a-3b1d-4b7e-9c39-2b4f7e6a1d02", - "session_number": 1024, - "face_retention_outcome": "retained_with_user", - "biometric_template_uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", - "biometric_template_expires_at": "2027-10-09T10:15:00Z", - "deleted_child_verifications": 0 + "Applied": { + "summary": "The override applied at once", + "value": { + "session_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "node_id": "feature_ocr_1", + "feature_type": "KYC", + "new_status": "Approved", + "override": { + "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", + "node_id": "feature_ocr_1", + "feature_type": "", + "feature_model": "KYC", + "previous_status": "In Review", + "new_status": "Approved", + "state": "applied", + "forced": false, + "reason": "Document manually verified by compliance.", + "requested_by": { + "email": "reviewer@example.com", + "name": "Alex Reviewer", + "type": "CONSOLE_USER" + }, + "requested_at": "2026-10-13T09:15:00Z", + "applied_at": "2026-10-13T09:15:00Z", + "cancelled_at": null, + "cancel_reason": null + } } }, - "Ineligible: no User": { - "summary": "Retention requested on a session created without vendor_data", - "value": { - "session_id": "7a1e9c2d-5f4b-4c3a-8e2d-1b0a9f8e7d6c", - "session_number": 1031, - "face_retention_outcome": "ineligible_no_vendor_user", - "biometric_template_uuid": null, - "biometric_template_expires_at": null, - "deleted_child_verifications": 0 + "Waiting For The Flow": { + "summary": "defer_until_flow_finishes while the person is still in the flow", + "value": { + "session_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "node_id": "feature_liveness_1", + "feature_type": "Face", + "new_status": "Declined", + "override": { + "id": "9b2f4c1e-8d3a-4e5f-a6b7-c8d9e0f1a2b3", + "node_id": "feature_liveness_1", + "feature_type": "", + "feature_model": "Face", + "previous_status": "Declined", + "new_status": "Approved", + "state": "pending_until_flow_finishes", + "forced": false, + "reason": "Liveness retried with a reviewer present.", + "requested_by": { + "email": "reviewer@example.com", + "name": "Alex Reviewer", + "type": "CONSOLE_USER" + }, + "requested_at": "2026-10-13T09:15:00Z", + "applied_at": null, + "cancelled_at": null, + "cancel_reason": null + } } - }, - "KYB session": { - "value": { - "session_id": "0f3c9b7e-2d1a-4e5f-8c6b-7a9d8e1f2c3b", - "session_number": 88, - "face_retention_outcome": "none", - "biometric_template_uuid": null, - "biometric_template_expires_at": null, - "deleted_child_verifications": 3 - }, - "summary": "A business verification deleted with the three verifications it created for its key people" } } } } }, "400": { - "description": "Validation error — nothing is deleted.", + "description": "Validation error. Returned when `new_status` is invalid or unchanged, when `feature_type` is unknown, or when the session is a standalone API session.", "content": { "application/json": { "examples": { - "Privacy erasure cannot retain": { + "Same Status": { + "summary": "new_status equals the current status", "value": { - "retain_face_embeddings": [ - "Privacy erasure cannot retain biometric templates." + "new_status": [ + "The new status is the same as the current status." ] } }, - "No finite retention duration": { - "summary": "Retention requested without face_retention_days on the request or the application", + "API Session": { + "summary": "Feature status cannot be changed on standalone API sessions", + "value": [ + "Cannot change feature status for API sessions." + ] + }, + "Unknown feature_type": { + "summary": "feature_type is not a known step type", "value": { - "detail": "A finite biometric-template retention duration is required." + "feature_type": [ + "Unknown feature_type 'SELFIE'." + ] } } } } } }, - "403": { - "description": "Authentication or permission failure. Missing/invalid credentials also return `403` — this API never returns `401`. Exception: when the `sessionId` does not resolve to a live session, the pre-auth owner lookup returns `404` first, even with missing or invalid credentials.", + "401": { + "description": "The scoped API key has expired.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, "examples": { - "Missing or invalid API key": { - "summary": "No x-api-key header, or the key is invalid", - "value": { - "detail": "Authentication credentials were not provided or are invalid." - } - }, - "Insufficient permission": { - "summary": "User token without delete:sessions, or credentials for an application that does not own the session", + "Expired API Key": { + "summary": "Scoped API key past its expiry date", "value": { - "detail": "You do not have permission to perform this action." + "detail": "This API key has expired." } } } } } }, - "404": { - "description": "No live KYC or KYB session with this `session_id` exists — the id is unknown or the session was already deleted (repeating a successful delete lands here).", + "403": { + "description": "Missing/invalid credentials or insufficient permissions.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, "examples": { - "Not Found": { - "summary": "Unknown or already-deleted session", + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", "value": { - "detail": "Not found." - } - } - } - } - } - }, - "429": { - "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "detail": "Authentication credentials were not provided or are invalid." } - } - }, - "examples": { - "Rate limited": { - "summary": "Write budget exhausted", + }, + "Missing Privilege": { + "summary": "API key lacks write:sessions", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped Key Without Access": { + "summary": "Scoped API key without write access to decisions", "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + "detail": "This API key has no write access to decisions." } } } } } }, - "503": { - "description": "The deletion could not finish because a storage service, a biometric store, or a monitoring provider was unavailable. The session is still in place; repeat the same request until it returns `200`. Some of its stored files or its face embedding may already be deleted, so do not rely on them after a `503`. Retries converge on a single template.", + "404": { + "description": "The session was not found, or it has no feature with the given `nodeId`.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, "examples": { - "Session deletion unavailable": { - "summary": "A storage service or a monitoring provider was unavailable", + "Session Not Found": { + "summary": "Unknown session_id", "value": { - "detail": "The session could not be deleted because a storage service or a monitoring provider is unavailable. The session is still in place; repeat the request to finish deleting it." + "detail": "Session not found." } }, - "Biometric store unavailable": { - "summary": "A biometric store was unavailable while retaining or purging a template", + "Feature Not Found": { + "summary": "No feature for this node_id", "value": { - "detail": "The biometric-template operation could not finish because an external store is unavailable. The operation is retryable." + "detail": "No feature found with node_id 'feature_ocr_1' in this session." } } } @@ -10590,336 +28956,430 @@ } } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", - "label": "curl (default policy)", - "source": "curl -X DELETE \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/ \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "curl", - "label": "curl (retain template)", - "source": "curl -X DELETE \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"retain_face_embeddings\": true, \"face_retention_days\": 365}'" - }, - { - "lang": "curl", - "label": "curl (privacy erasure)", - "source": "curl -X DELETE \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"deletion_instruction\": \"privacy_erasure\", \"instruction_id\": \"dsar-2026-0142\"}'" + "label": "curl", + "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/features/feature_ocr_1/update-status/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"new_status\": \"Approved\",\n \"comment\": \"Document manually verified by compliance.\"\n }'" }, { "lang": "python", "label": "Python", - "source": "import requests\n\nresponse = requests.delete(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json={\"retain_face_embeddings\": True, \"face_retention_days\": 365},\n)\nresponse.raise_for_status()\nprint(response.json()[\"face_retention_outcome\"])" + "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/features/feature_ocr_1/update-status/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\", \"Content-Type\": \"application/json\"},\n json={\"new_status\": \"Approved\", \"comment\": \"Document manually verified by compliance.\"},\n)\nprint(response.json())" + } + ] + } + }, + "/v3/session/{sessionId}/update-aml-hit-status/": { + "patch": { + "summary": "Update an AML hit review status", + "description": "Set the review status of a **single AML hit** (a potential sanction / PEP / watchlist / adverse-media match) returned by AML screening, without changing the overall session decision. Identify the hit by its `hit_id` (the `id` of an entry in the AML check's `hits` array — see the session decision).\n\n`review_status` is one of `Unreviewed`, `Confirmed Match`, `False Positive`, or `Inconclusive`. Reviewer decisions are preserved across ongoing-monitoring updates, so a hit you mark `False Positive` stays that way when the provider re-screens.\n\nA successful call fires a `data.updated` webhook and appends an activity entry (with actor attribution) to the session's review trail. For graph workflows with multiple AML checks, pass `node_id` to target a specific check.\n\nAuthenticate with your API key (`x-api-key`) or a user authorization header — either must carry the `write:sessions` privilege.", + "operationId": "patch_v3_session_update_aml_hit_status", + "x-didit-sdk": { + "resource": "sessions", + "method": "update_aml_hit_status", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "sessionId", + "required": true, + "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/',\n {\n method: 'DELETE',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ deletion_instruction: 'privacy_erasure', instruction_id: 'dsar-2026-0142' }),\n },\n);\nconst body = await resp.json();\nconsole.log(body.face_retention_outcome);" + "in": "query", + "name": "node_id", + "required": false, + "description": "Workflow graph node ID. Required only when the session has multiple AML checks (one per node); omit it for single-AML sessions.", + "schema": { + "type": "string", + "example": "feature_aml_1" + } } ], "requestBody": { - "required": false, - "description": "Optional. Omit the body entirely to follow the application's retention policy with an operational deletion.", + "required": true, "content": { "application/json": { "schema": { "type": "object", + "required": [ + "hit_id", + "review_status" + ], "properties": { - "retain_face_embeddings": { - "type": "boolean", - "nullable": true, - "description": "Override the application's `face_retention_policy` for this deletion. `true` keeps one image-free face biometric template anchored to the session's User after the session is deleted; `false` deletes the face embedding with the session; omit or `null` to follow the application policy. Ignored for KYB sessions and for sessions without a face embedding.", - "example": true - }, - "face_retention_days": { - "type": "integer", - "minimum": 1, - "maximum": 3650, - "description": "Finite retention duration, in days, for a retained template. Required when a template is retained unless the application already sets `face_retention_days`. The template's `expires_at` is the earliest of this duration, the application's general data-retention window, and `face_retention_deadline`.", - "example": 365 - }, - "face_retention_deadline": { + "hit_id": { "type": "string", - "format": "date-time", - "description": "Optional hard expiry for a retained template. Must be in the future.", - "example": "2027-08-28T00:00:00Z" + "description": "The `id` of the hit in the AML check's `hits` array.", + "example": "abc123" }, - "deletion_instruction": { + "review_status": { "type": "string", + "description": "The new review status for the hit.", "enum": [ - "operational_session_delete", - "privacy_erasure" + "Unreviewed", + "Confirmed Match", + "False Positive", + "Inconclusive" ], - "default": "operational_session_delete", - "description": "The class of instruction you are giving. `operational_session_delete` deletes the session and retains a template only when the policy or `retain_face_embeddings` says so. `privacy_erasure` purges every retained biometric template anchored to the session's User before deleting the session, cannot be overridden by the application policy, and cannot be combined with `retain_face_embeddings: true` (`400`).", - "example": "operational_session_delete" + "example": "False Positive" }, - "instruction_id": { + "node_id": { "type": "string", - "maxLength": 255, - "description": "Your durable reference for this deletion instruction (for example an erasure-ticket id). Recorded on the audit trail and on any retained template. Generated by Didit when omitted.", - "example": "dsar-2026-0142" - } - } - }, - "examples": { - "Retain a biometric template": { - "summary": "Delete the session, keep an image-free template for 365 days", - "value": { - "retain_face_embeddings": true, - "face_retention_days": 365, - "instruction_id": "post-approval-cleanup-2026-08" - } - }, - "Force deletion on an opted-in application": { - "summary": "Delete the face embedding regardless of the application policy", - "value": { - "retain_face_embeddings": false - } - }, - "Privacy erasure": { - "summary": "Data-subject request: delete the session and purge every retained template for that person", - "value": { - "deletion_instruction": "privacy_erasure", - "instruction_id": "dsar-2026-0142" + "description": "Optional graph node ID. Use it (here or as a query parameter) when the session has multiple AML checks.", + "example": "feature_aml_1" } } } } } - } - } - }, - "/v3/session/{sessionId}/generate-pdf/": { - "get": { - "summary": "Download a PDF report for a verification session", - "description": "Render and download a compliance-ready PDF report for a finished verification session. The same path serves both User Verification (KYC) and Business Verification (KYB) sessions — the server resolves the `sessionId` against both session types and picks the matching report template.\n\n**Eligible statuses.** The session must already carry a final or reviewable status:\n\n- KYC sessions: `Approved`, `Declined`, `In Review`, or `Kyc Expired`.\n- KYB sessions: `Approved`, `Declined`, or `In Review`.\n\nAny other status (`Not Started`, `In Progress`, `Expired`, `Abandoned`, …) returns `403` with an explanatory `detail` string — see the `403` response below.\n\n**What the report contains.** The PDF mirrors the console session view at the moment of the request:\n\n- **KYC** — rolled-up session status, the warnings list, and one section per executed feature: ID Verification (document images plus extracted fields), NFC, Liveness, Face Match, Email Verification, Phone Verification, Proof of Address, Questionnaire answers, Database Validation, AML Screening, and IP Analysis — followed by session tags, console review activity (comments and status changes), and the session event timeline.\n- **KYB** — registry checks (company data), AML screenings, business document verifications, key-people checks, questionnaire responses, phone/email verifications, IP analyses, and the session event timeline, followed by tags and review activity.\n\n**Branding.** If your application has white-label customization, the report is rendered with your logo and links to your privacy-policy URL; otherwise it carries Didit branding. Reports are rendered in English — there is no language parameter.\n\n**No caching.** Every call re-renders the PDF from the current session data, so a report generated after a manual review reflects the reviewer’s decision. Two calls for the same session can produce byte-different files — archive the downloaded file if you need an immutable copy.\n\n**Latency.** Rendering downloads every stored image (document sides, selfies, extra files) before composing the PDF, so media-heavy sessions can take several seconds. Use a generous client read timeout (60 s recommended) and stream the body to disk.\n\n**Trailing slash.** The canonical route ends with a trailing slash (`…/generate-pdf/`). A request without it is served directly with the same response — there is no `301` redirect, so no client has to follow redirects and `curl` does not need `-L`. Use the slashed URL as in the samples.\n\nIf you need the underlying data as JSON instead of a rendered report, use `GET /v3/session/{sessionId}/decision/`. For every reportable session of one user in a single document, use `GET /v3/users/{vendor_data}/generate-pdf/`.", - "operationId": "get_v3_session_generate_pdf", - "tags": [ - "Sessions" - ], - "parameters": [ - { - "name": "sessionId", - "in": "path", - "required": true, - "description": "UUID of the User Verification (KYC) or Business Verification (KYB) session to render — the `session_id` returned by `POST /v3/session/`. The same path works for both session kinds; use the `Content-Disposition` filename on the response to tell which report type you received.\n\n**Must be a well-formed, lowercase UUID.** The route only matches valid UUIDs, so a malformed value is rejected by the URL router before any application code runs and the response is a `404` **HTML** page rather than the JSON `{\"detail\": ...}` envelope shown below. The route's UUID converter only matches the canonical lowercase form — uppercase-hex UUIDs are rejected with the HTML 404.", - "schema": { - "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" - } - } - ], + }, "responses": { "200": { - "description": "The rendered PDF document, returned directly as binary `application/pdf` — there is no JSON wrapper and no download-URL indirection. The body starts with the `%PDF` magic bytes (PDF 1.7). Save it to a `.pdf` file or stream it through to your caller.", - "headers": { - "Content-Disposition": { - "description": "`attachment; filename=session_{sessionId}.pdf` for KYC sessions, `attachment; filename=business_{sessionId}.pdf` for KYB sessions — the prefix tells you which report template was rendered.", - "schema": { - "type": "string", - "example": "attachment; filename=session_11111111-2222-3333-4444-555555555555.pdf" - } - }, - "Content-Length": { - "description": "Total size of the PDF in bytes; the body is not chunked.", + "description": "Hit review status updated.", + "content": { + "application/json": { "schema": { - "type": "integer", - "example": 25191 + "type": "object", + "required": [ + "message", + "hit_id", + "review_status" + ], + "properties": { + "message": { + "type": "string", + "description": "Always `Hit status updated successfully`." + }, + "hit_id": { + "type": "string", + "description": "The updated hit, with surrounding whitespace removed." + }, + "review_status": { + "type": "string", + "enum": [ + "Unreviewed", + "Confirmed Match", + "False Positive", + "Inconclusive" + ], + "description": "The review status now stored on the hit." + } + } + }, + "example": { + "message": "Hit status updated successfully", + "hit_id": "abc123", + "review_status": "False Positive" } } - }, + } + }, + "400": { + "description": "Validation error — `hit_id` empty or `review_status` not one of the allowed values.", "content": { - "application/pdf": { - "schema": { - "type": "string", - "format": "binary" + "application/json": { + "example": { + "review_status": [ + "\"Maybe\" is not a valid choice." + ] } } } }, "403": { - "description": "Authentication, authorization, or session-status failure. **This endpoint never returns `401`** — Didit's API-key authentication does not emit a `WWW-Authenticate` challenge, so missing or invalid credentials also surface as `403`. Distinguish the failure modes by the `detail` string:\n\n- `\"Authentication credentials were not provided or are invalid.\"` — no `x-api-key` header (and no `Authorization: Bearer` token), or the supplied credential failed introspection (revoked, expired, or malformed).\n- `\"You do not have permission to perform this action.\"` — the credential is valid but cannot read this session: a console-user Bearer token lacks the `read:sessions` permission or cannot access the owning application, or an API key belongs to a different organization than the session.\n- `\"You can only generate a PDF for sessions in review, declined, approved or kyc expired\"` — the KYC session exists but is not in an eligible status yet (for example `Not Started` or `In Progress`). Wait for the `status.updated` webhook before requesting the report.\n- `\"You can only generate a PDF for sessions in review, declined or approved\"` — same condition for KYB sessions.\n\nNote that for a completely unknown `sessionId` the endpoint resolves the owning application before validating credentials, so a missing session returns `404` even when the credential is missing or invalid.", + "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "You can only generate a PDF for sessions in review, declined, approved or kyc expired" - } - } - }, "examples": { - "Missing or invalid credentials": { - "summary": "No x-api-key header, or the key failed introspection", + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", "value": { "detail": "Authentication credentials were not provided or are invalid." } }, - "Insufficient permissions": { - "summary": "Valid credential lacking read:sessions or access to the owning application", + "Missing Privilege": { + "summary": "API key lacks write:sessions", "value": { "detail": "You do not have permission to perform this action." } - }, - "KYC session not finished": { - "summary": "KYC session still Not Started / In Progress", - "value": { - "detail": "You can only generate a PDF for sessions in review, declined, approved or kyc expired" - } - }, - "KYB session not finished": { - "summary": "KYB session still Not Started / In Progress", - "value": { - "detail": "You can only generate a PDF for sessions in review, declined or approved" - } } } } } }, "404": { - "description": "No active session with this `sessionId` exists — it was never created, or it has been deleted. Owner resolution happens before credential validation, so this `404` is returned even for unauthenticated requests. Depending on which lookup misses, the `detail` string is `\"Not found.\"` (the common case) or `\"Session not found.\"`.", + "description": "The session was not found, the session has no AML check (optionally for the given `node_id`), or no hit matches `hit_id`.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "Not found." - } - } - }, "examples": { - "Unknown session": { - "summary": "sessionId does not match any KYC or KYB session", + "No AML Check": { + "summary": "Session has no AML check", "value": { - "detail": "Not found." + "detail": "The session does not have an AML check." } }, - "Recently deleted session": { - "summary": "Session was deleted between lookups", + "Hit Not Found": { + "summary": "Unknown hit_id", "value": { - "detail": "Session not found." + "detail": "Hit with ID 'abc123' not found in the AML check." } } } } } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-aml-hit-status/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"hit_id\": \"abc123\",\n \"review_status\": \"False Positive\"\n }'" }, - "429": { - "description": "Rate limit exceeded. PDF generation has a dedicated limit of **50 requests per minute per credential** (API key, Bearer token, or source IP when unauthenticated), in addition to the global 600/min GET limit. Wait `Retry-After` seconds (also reflected in the `X-RateLimit-*` headers), then retry. Reports are not cached server-side, so download each report once and store the file instead of re-fetching it.", - "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying the request.", - "schema": { - "type": "integer", - "minimum": 1, - "example": 30 - } - }, - "X-RateLimit-Limit": { - "description": "Maximum number of PDF generations allowed in the current window (50).", - "schema": { - "type": "integer", - "example": 50 - } - }, - "X-RateLimit-Remaining": { - "description": "Requests remaining in the current window.", - "schema": { - "type": "integer", - "example": 0 - } - }, - "X-RateLimit-Reset": { - "description": "Unix timestamp (seconds) at which the current window resets.", - "schema": { - "type": "integer", - "example": 1750000000 + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-aml-hit-status/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\", \"Content-Type\": \"application/json\"},\n json={\"hit_id\": \"abc123\", \"review_status\": \"False Positive\"},\n)\nprint(response.json())" + } + ] + } + }, + "/v3/session/{sessionId}/bulk-update-aml-hit-status/": { + "patch": { + "summary": "Bulk update AML hit review statuses", + "description": "Update the review status of **multiple AML hits** in one call. Provide `hit_updates`, a list of `{ hit_id, review_status }` pairs. `review_status` is one of `Unreviewed`, `Confirmed Match`, `False Positive`, or `Inconclusive`.\n\nThe update is all-or-nothing: if any `hit_id` is not found in the AML check, the request returns `404` and no hit is changed. A successful call fires a single `data.updated` webhook and records one activity entry covering all changes. For graph workflows with multiple AML checks, pass `node_id` to target a specific check.\n\nAuthenticate with your API key (`x-api-key`) or a user authorization header — either must carry the `write:sessions` privilege.", + "operationId": "patch_v3_session_bulk_update_aml_hit_status", + "x-didit-sdk": { + "resource": "sessions", + "method": "bulk_update_aml_hit_status", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "sessionId", + "required": true, + "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + }, + { + "in": "query", + "name": "node_id", + "required": false, + "description": "Workflow graph node ID. Required only when the session has multiple AML checks (one per node); omit it for single-AML sessions.", + "schema": { + "type": "string", + "example": "feature_aml_1" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "hit_updates" + ], + "properties": { + "hit_updates": { + "type": "array", + "description": "Non-empty list of hit updates.", + "items": { + "type": "object", + "required": [ + "hit_id", + "review_status" + ], + "properties": { + "hit_id": { + "type": "string", + "example": "abc123" + }, + "review_status": { + "type": "string", + "enum": [ + "Unreviewed", + "Confirmed Match", + "False Positive", + "Inconclusive" + ], + "example": "Confirmed Match" + } + } + } + }, + "node_id": { + "type": "string", + "description": "Optional graph node ID. Use it (here or as a query parameter) when the session has multiple AML checks.", + "example": "feature_aml_1" + } } } - }, + } + } + }, + "responses": { + "200": { + "description": "All hits updated.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "message", + "updated_hits" + ], "properties": { - "detail": { + "message": { "type": "string", - "description": "Human-readable explanation of the rate-limit breach.", - "example": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute." + "description": "`Updated {n} hit(s) successfully`, where `n` is the number of entries in `hit_updates`." + }, + "updated_hits": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The `hit_id` of every entry in `hit_updates`, in request order, including hits whose status did not change." } } }, + "example": { + "message": "Updated 2 hit(s) successfully", + "updated_hits": [ + "abc123", + "def456" + ] + } + } + } + }, + "400": { + "description": "Validation error — `hit_updates` empty, or an entry is missing `hit_id` / `review_status` or carries an invalid value.", + "content": { + "application/json": { + "example": { + "hit_updates": [ + "hit_updates cannot be empty" + ] + } + } + } + }, + "403": { + "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", + "content": { + "application/json": { "examples": { - "Throttled": { - "summary": "More than 50 PDF generations in one minute from the same credential", + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", "value": { - "detail": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute." + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Missing Privilege": { + "summary": "API key lacks write:sessions", + "value": { + "detail": "You do not have permission to perform this action." } } } } } + }, + "404": { + "description": "The session was not found, the session has no AML check, or one or more `hit_id`s do not exist (no hit is changed in that case).", + "content": { + "application/json": { + "example": { + "detail": "Hits not found: zzz999" + } + } + } } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", - "label": "cURL", - "source": "curl --fail \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/generate-pdf/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n --output report.pdf" + "label": "curl", + "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/bulk-update-aml-hit-status/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"hit_updates\": [\n { \"hit_id\": \"abc123\", \"review_status\": \"Confirmed Match\" },\n { \"hit_id\": \"def456\", \"review_status\": \"False Positive\" }\n ]\n }'" }, { "lang": "python", "label": "Python", - "source": "import requests\n\nresponse = requests.get(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/generate-pdf/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n stream=True,\n timeout=60,\n)\nresponse.raise_for_status()\nwith open(\"report.pdf\", \"wb\") as fh:\n for chunk in response.iter_content(chunk_size=8192):\n fh.write(chunk)" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "import { writeFile } from 'node:fs/promises';\n\nconst response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/generate-pdf/',\n { headers: { 'x-api-key': 'YOUR_API_KEY' } },\n);\nif (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);\nawait writeFile('report.pdf', Buffer.from(await response.arrayBuffer()));" + "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/bulk-update-aml-hit-status/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\", \"Content-Type\": \"application/json\"},\n json={\n \"hit_updates\": [\n {\"hit_id\": \"abc123\", \"review_status\": \"Confirmed Match\"},\n {\"hit_id\": \"def456\", \"review_status\": \"False Positive\"},\n ]\n },\n)\nprint(response.json())" } ] } }, - "/v3/session/{sessionId}/sandbox/arm/": { - "post": { - "summary": "Arm a sandbox scenario on a session", - "description": "Persist a sandbox scenario on a `Not Started` session and expand its bundled magic values (email, phone, or `expected_details` fields) onto the session, so the mocked providers reproduce the scenario's outcome once the flow runs. This is the endpoint the pre-flow scenario picker in Didit's hosted verification UI calls when a tester chooses an outcome before starting capture. Build your own picker against it, or call it directly from a sandbox test harness.\n\nAuthenticate with the session's own `Session-Token` (returned in `session_token` by [Create Session](/sessions-api/create-session)), not your application `x-api-key`: this endpoint is meant to be called from the client, not your backend. A scenario is only accepted while `status` is `Not Started`. Calling it again while still `Not Started` re-arms the session: it swaps in the new scenario's magic values and clears only the values the *previous* scenario wrote, so any value the user already typed by hand (email, first name, etc.) survives the re-arm. Arming a session that has moved past `Not Started`, or that belongs to a live (non-sandbox) application, is rejected.\n\nSee [Sandbox & Test Data](/integration/sandbox-testing) for the full scenario catalog and the other two ways to drive an outcome: `sandbox_scenario` at session create, and typing magic values directly.", - "operationId": "post_v3_session_sandbox_arm", + "/v3/session/{sessionId}/update-poa-data/": { + "patch": { + "summary": "Update Proof of Address data extracted from the POA document on a verification session", + "description": "Correct the fields extracted from the user's Proof of Address document — utility bill, bank statement, or other address evidence — after the verification has finished: issuer, dates, the printed name, the address itself, and banking details. Every field in the body is optional: only the keys you send are written, everything else is left untouched (`PATCH` semantics).\n\nThe session must belong to your application and be in `Approved`, `Declined`, `In Review`, or `Kyc Expired` status. Sessions in any other state (e.g. still `In Progress`) return `404` with `\"No Session matches the given query.\"` — distinct from the `\"Not found.\"` returned for an unknown or deleted `sessionId`. The session must also carry a POA record: sessions whose workflow never ran a Proof of Address step return `404` (`\"The session does not have a POA.\"`). For graph workflows with multiple POA steps, target a specific step with `node_id` (query parameter or body field); when omitted, the most recent POA record is patched.\n\n`extra_fields` is merged key-by-key: nine known keys (`bank_iban`, `additional_names`, …) write through to dedicated POA columns, any other key is kept as a custom extra, and `null` / `\"\"` deletes a key. `poa_parsed_address` accepts only the seven documented keys and recomputes `formatted_address` / `poa_formatted_address` after every edit. If the free-text `poa_address` changes, the server may also re-geocode it and refresh the structured address (skipped for sandbox sessions).\n\nUnlike the KYC variant of this endpoint, the `data.updated` [webhook](/integration/webhooks) (trigger `manual_review`) fires only when at least one stored value **actually changed** — re-sending identical values is a silent no-op. When values change, a `POA_DATA_UPDATED` activity entry recording the changed fields with previous and new values is appended to the session's `reviews`, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and the console activity timeline. The session's status and decision are **not** re-evaluated — use [Update Status](#patch-/v3/session/-sessionId-/update-status/) to change the outcome.\n\nAccepts an API key or a console user token; either way the credential needs the `write:sessions` permission. Authentication and permission failures both return `403` — this API never responds `401`. Note that the session lookup runs before credential checks, so an unknown `sessionId` returns `404` even with missing credentials. This endpoint has a dedicated rate limit of 10 requests per minute per API key (invalid requests count too); the shared write budget of 300 requests per minute also applies.\n\nAn empty JSON body `{}` is a no-op: it returns `200` echoing the current POA record without firing the webhook (`has_changes` stays false).", + "operationId": "patch_v3_session_update_poa_data", + "x-didit-sdk": { + "resource": "sessions", + "method": "update_poa_data", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], "security": [ { - "SessionTokenAuth": [] + "ApiKeyAuth": [] } ], "parameters": [ { - "in": "path", "name": "sessionId", + "in": "path", "required": true, - "description": "UUID of the sandbox verification session to arm. Must match the session encoded in the Session-Token used to authenticate.", + "description": "UUID of the User Verification (KYC) session whose attached POA record you are correcting.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + }, + { + "name": "node_id", + "in": "query", + "required": false, + "description": "Workflow graph node whose POA record to patch, for graph workflows with multiple Proof of Address steps (also accepted as a `node_id` body field). When omitted, the most recent POA record on the session is patched. If no POA exists for the given node, the call returns `404` (`\"The session does not have a POA for node ''.\"`).", "schema": { "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" + "example": "feature_poa" } } ], @@ -10929,22 +29389,140 @@ "application/json": { "schema": { "type": "object", - "required": [ - "scenario" - ], + "description": "All fields optional — send only what you want to change.", "properties": { - "scenario": { + "issuing_state": { "type": "string", - "description": "Slug of the scenario to arm. See the catalog at GET /v1/sandbox/scenarios/ or the scenario table in [Sandbox & Test Data](/integration/sandbox-testing).", - "example": "decline_document_expired" + "nullable": true, + "description": "Country that issued the POA document, ISO 3166-1 alpha-3. Case-insensitive (normalized to uppercase); invalid codes return `400`.", + "example": "ESP" + }, + "document_type": { + "type": "string", + "enum": [ + "UTILITY_BILL", + "BANK_STATEMENT", + "GOVERNMENT_ISSUED_DOCUMENT", + "OTHER_POA_DOCUMENT", + "UNKNOWN" + ], + "description": "POA document category. Values are uppercase — lowercase variants like `utility_bill` return `400`.", + "example": "UTILITY_BILL" + }, + "document_language": { + "type": "string", + "maxLength": 50, + "nullable": true, + "description": "Language of the document, typically an ISO 639-1 code (e.g. `es`). Free text — not validated as a language code.", + "example": "es" + }, + "issuer": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Organization that issued the document (utility company, bank, government agency).", + "example": "Energy Provider S.A." + }, + "issue_date": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Issue date of the POA document in `YYYY-MM-DD`. Any other format returns `400`.", + "example": "2026-04-12" + }, + "poa_address": { + "type": "string", + "nullable": true, + "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it in the background of the same request and may overwrite `poa_parsed_address` / `poa_formatted_address` with the geocoder result. The re-geocode also requires `issuing_state` to be present on the POA record.", + "example": "Avda de Madrid 34, 28013 Madrid" + }, + "name_on_document": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Recipient name as printed on the document.", + "example": "Carmen Espanola" + }, + "extra_fields": { + "type": "object", + "additionalProperties": true, + "nullable": true, + "description": "Document-specific extras, merged key-by-key. Nine keys map to dedicated POA columns: `bank_account_number`, `bank_iban`, `bank_sort_code`, `bank_routing_number`, `bank_swift_bic`, `bank_branch_name`, `bank_branch_address`, `document_phone_number`, and `additional_names` (must be a list of strings or `null`, otherwise `400`). Any other key is stored as a custom extra field. Per-key semantics: a value sets the key; `null` or `\"\"` deletes it. Must be a JSON object, otherwise `400`." + }, + "poa_parsed_address": { + "type": "object", + "nullable": true, + "description": "Structured address. Only these nested keys are accepted: `address_type`, `city`, `region`, `street_1`, `street_2`, `postal_code`, `country` — any other key returns `400`. Per-key semantics: a value sets the key, `null` removes it. `formatted_address` (and the top-level `poa_formatted_address`) is recomputed server-side by joining `street_1`, `street_2`, `city`, `region`, `postal_code`, and `country` — you cannot set it directly.", + "properties": { + "address_type": { + "type": "string", + "nullable": true, + "description": "Free-form address classification (e.g. `home`)." + }, + "city": { + "type": "string", + "nullable": true, + "example": "Madrid" + }, + "region": { + "type": "string", + "nullable": true, + "description": "State / province / autonomous community." + }, + "street_1": { + "type": "string", + "nullable": true, + "example": "Avda de Madrid 34" + }, + "street_2": { + "type": "string", + "nullable": true + }, + "postal_code": { + "type": "string", + "nullable": true, + "example": "28013" + }, + "country": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.", + "example": "ES" + } + } } } }, "examples": { - "Arm": { - "summary": "Arm the AML PEP-hit scenario", + "Correct issuer and address": { + "summary": "Fix document classification, issuer, and the structured address", "value": { - "scenario": "decline_aml_hit" + "issuing_state": "ESP", + "document_type": "BANK_STATEMENT", + "issuer": "Banco Example S.A.", + "issue_date": "2026-05-02", + "name_on_document": "Carmen Espanola", + "poa_address": "Avda de Madrid 34, 28013 Madrid", + "extra_fields": { + "bank_iban": "ES9121000418450200051332", + "additional_names": [ + "Carmen E. Espanola" + ] + }, + "poa_parsed_address": { + "street_1": "Avda de Madrid 34", + "city": "Madrid", + "postal_code": "28013", + "country": "ES" + } + } + }, + "Remove a banking detail": { + "summary": "Delete one extra-field key, keep the rest", + "value": { + "extra_fields": { + "bank_iban": null + } } } } @@ -10953,33 +29531,158 @@ }, "responses": { "200": { - "description": "Scenario armed. Its magic values are now on the session. `status` reflects the session's current status, which this call never changes: it is always `Not Started` on success.", + "description": "POA data updated. Returns the **full current POA record** (every updatable field with its stored value, plus the complete `extra_fields` map, `poa_parsed_address`, and `poa_formatted_address`) — not just the fields you submitted. The `data.updated` webhook fires only when at least one value actually changed.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "session_id": { + "issuing_state": { "type": "string", - "format": "uuid" + "nullable": true, + "description": "Country that issued the POA document, ISO 3166-1 alpha-3. Case-insensitive (normalized to uppercase); invalid codes return `400`.", + "example": "ESP" }, - "scenario": { + "document_type": { "type": "string", - "description": "The armed scenario slug (echoes the request)." + "enum": [ + "UTILITY_BILL", + "BANK_STATEMENT", + "GOVERNMENT_ISSUED_DOCUMENT", + "OTHER_POA_DOCUMENT", + "UNKNOWN" + ], + "description": "POA document category. Values are uppercase — lowercase variants like `utility_bill` return `400`.", + "example": "UTILITY_BILL" }, - "status": { + "document_language": { "type": "string", - "example": "Not Started" + "maxLength": 50, + "nullable": true, + "description": "Language of the document, typically an ISO 639-1 code (e.g. `es`). Free text — not validated as a language code.", + "example": "es" + }, + "issuer": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Organization that issued the document (utility company, bank, government agency).", + "example": "Energy Provider S.A." + }, + "issue_date": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Issue date of the POA document in `YYYY-MM-DD`. Any other format returns `400`.", + "example": "2026-04-12" + }, + "poa_address": { + "type": "string", + "nullable": true, + "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it in the background of the same request and may overwrite `poa_parsed_address` / `poa_formatted_address` with the geocoder result.", + "example": "Avda de Madrid 34, 28013 Madrid" + }, + "name_on_document": { + "type": "string", + "maxLength": 255, + "nullable": true, + "description": "Recipient name as printed on the document.", + "example": "Carmen Espanola" + }, + "extra_fields": { + "type": "object", + "additionalProperties": true, + "nullable": true, + "description": "Always present in the response: the nine known keys (with `null` for unset ones, `additional_names` defaulting to `[]`) plus any custom keys previously stored." + }, + "poa_parsed_address": { + "type": "object", + "nullable": true, + "description": "Current structured address, or `null` when none exists. Includes the recomputed `formatted_address` alongside the seven editable keys. Returned as stored — after a server-side geocode it can contain bookkeeping keys (e.g. `raw_results`) alongside the editable keys.", + "properties": { + "address_type": { + "type": "string", + "nullable": true, + "description": "Free-form address classification (e.g. `home`)." + }, + "city": { + "type": "string", + "nullable": true, + "example": "Madrid" + }, + "region": { + "type": "string", + "nullable": true, + "description": "State / province / autonomous community." + }, + "street_1": { + "type": "string", + "nullable": true, + "example": "Avda de Madrid 34" + }, + "street_2": { + "type": "string", + "nullable": true + }, + "postal_code": { + "type": "string", + "nullable": true, + "example": "28013" + }, + "country": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.", + "example": "ES" + }, + "formatted_address": { + "type": "string", + "nullable": true, + "description": "Joined, human-readable form of the structured address. Recomputed server-side after every `poa_parsed_address` edit.", + "example": "Avda de Madrid 34, Madrid, 28013, ES" + } + } + }, + "poa_formatted_address": { + "type": "string", + "nullable": true, + "description": "Top-level copy of the recomputed `formatted_address`, kept in sync when the structured address changes.", + "example": "Avda de Madrid 34, Madrid, 28013, ES" } } }, "examples": { - "Armed": { - "summary": "Scenario armed on a Not Started session", + "Updated POA": { + "summary": "Full POA record after the correction", "value": { - "session_id": "11111111-2222-3333-4444-555555555555", - "scenario": "decline_aml_hit", - "status": "Not Started" + "issuing_state": "ESP", + "document_type": "BANK_STATEMENT", + "document_language": "es", + "issuer": "Banco Example S.A.", + "issue_date": "2026-05-02", + "poa_address": "Avda de Madrid 34, 28013 Madrid", + "name_on_document": "Carmen Espanola", + "extra_fields": { + "bank_account_number": null, + "bank_iban": "ES9121000418450200051332", + "bank_sort_code": null, + "bank_routing_number": null, + "bank_swift_bic": null, + "bank_branch_name": null, + "bank_branch_address": null, + "document_phone_number": null, + "additional_names": [ + "Carmen E. Espanola" + ] + }, + "poa_parsed_address": { + "city": "Madrid", + "street_1": "Avda de Madrid 34", + "postal_code": "28013", + "country": "ES", + "formatted_address": "Avda de Madrid 34, Madrid, 28013, ES" + }, + "poa_formatted_address": "Avda de Madrid 34, Madrid, 28013, ES" } } } @@ -10987,38 +29690,126 @@ } }, "400": { - "description": "Validation error: an unknown scenario slug, or the session has already progressed past `Not Started`.", + "description": "Validation error. Field errors arrive as `{\"\": [\"\"]}`; errors on nested `poa_parsed_address` / `extra_fields` keys use an object value: `{\"extra_fields\": {\"\": \"\"}}`.", "content": { "application/json": { "examples": { - "Unknown Scenario": { - "summary": "scenario is not in the catalog", + "Invalid document type": { + "summary": "Enum is uppercase — lowercase variants are rejected", "value": { - "scenario": [ - "Unknown sandbox scenario: 'not-a-real-scenario'." + "document_type": [ + "\"utility_bill\" is not a valid choice." ] } }, - "Already Started": { - "summary": "Session moved past Not Started", - "value": [ - "Scenario can only be armed before the verification starts." - ] + "Invalid issuing state": { + "summary": "issuing_state is not ISO 3166-1 alpha-3", + "value": { + "issuing_state": [ + "Invalid ISO 3166-1 alpha-3 country code for issuing_state." + ] + } + }, + "additional_names not a list": { + "summary": "additional_names accepts a list of strings or null", + "value": { + "extra_fields": { + "additional_names": "Must be a list or null." + } + } + }, + "Unsupported poa_parsed_address key": { + "summary": "Only the seven documented keys are allowed", + "value": { + "poa_parsed_address": { + "zipcode": "Unsupported key. Allowed keys: address_type, city, region, street_1, street_2, postal_code, country" + } + } + }, + "Bad date format": { + "summary": "Dates must be YYYY-MM-DD", + "value": { + "issue_date": [ + "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." + ] + } + } + } + } + } + }, + "403": { + "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", + "content": { + "application/json": { + "examples": { + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Missing Privilege": { + "summary": "Credential lacks write:sessions", + "value": { + "detail": "You do not have permission to perform this action." + } } } } } }, "404": { - "description": "The Session-Token does not resolve to this `session_id`, or the session belongs to a live (non-sandbox) application.", + "description": "Unknown session, session in a non-editable state, or no POA record to patch. Each case has a distinct `detail` message (see examples). The unknown-session lookup runs before credential checks.", "content": { "application/json": { "examples": { - "Not Found": { - "summary": "Live session, or session_id / token mismatch", + "Unknown session": { + "summary": "No session with this ID in your application (or it was deleted)", "value": { "detail": "Not found." } + }, + "Wrong session state": { + "summary": "Session exists but is not Approved / Declined / In Review / Kyc Expired", + "value": { + "detail": "No Session matches the given query." + } + }, + "No POA record": { + "summary": "The workflow never ran a Proof of Address step", + "value": { + "detail": "The session does not have a POA." + } + }, + "No POA for node": { + "summary": "node_id given but no POA exists for it", + "value": { + "detail": "The session does not have a POA for node 'feature_missing'." + } + } + } + } + } + }, + "429": { + "description": "Dedicated rate limit exceeded — 10 POA data updates per minute per API key (rejected requests count toward the budget). The shared 300/min write budget can also trigger with its own message. Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Rate limited": { + "summary": "POA update budget exhausted", + "value": { + "detail": "Session POA data update rate limit exceeded. You can make up to 10 requests per minute." + } } } } @@ -11029,26 +29820,32 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/sandbox/arm/ \\\n -H 'Session-Token: YOUR_SESSION_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"scenario\": \"decline_aml_hit\"\n }'" + "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-poa-data/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"issuing_state\": \"ESP\",\n \"document_type\": \"BANK_STATEMENT\",\n \"issuer\": \"Banco Example S.A.\",\n \"issue_date\": \"2026-05-02\",\n \"name_on_document\": \"Carmen Espanola\",\n \"poa_address\": \"Avda de Madrid 34, 28013 Madrid\",\n \"extra_fields\": {\n \"bank_iban\": \"ES9121000418450200051332\"\n },\n \"poa_parsed_address\": {\n \"city\": \"Madrid\",\n \"postal_code\": \"28013\",\n \"country\": \"ES\"\n }\n }'" }, { "lang": "python", "label": "Python", - "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/sandbox/arm/\",\n headers={\n \"Session-Token\": \"YOUR_SESSION_TOKEN\",\n \"Content-Type\": \"application/json\",\n },\n json={\"scenario\": \"decline_aml_hit\"},\n)\nresponse.raise_for_status()\nprint(response.json()) # {'session_id': '...', 'scenario': 'decline_aml_hit', 'status': 'Not Started'}" + "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-poa-data/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\n \"issuing_state\": \"ESP\",\n \"document_type\": \"BANK_STATEMENT\",\n \"issuer\": \"Banco Example S.A.\",\n \"issue_date\": \"2026-05-02\",\n \"name_on_document\": \"Carmen Espanola\",\n \"poa_address\": \"Avda de Madrid 34, 28013 Madrid\",\n \"extra_fields\": {\"bank_iban\": \"ES9121000418450200051332\"},\n \"poa_parsed_address\": {\"city\": \"Madrid\", \"postal_code\": \"28013\", \"country\": \"ES\"},\n },\n)\nresponse.raise_for_status()\npoa = response.json() # full current POA record\nprint(poa[\"issuer\"], poa[\"poa_formatted_address\"])" }, { "lang": "javascript", "label": "JavaScript", - "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/sandbox/arm/',\n {\n method: 'POST',\n headers: {\n 'Session-Token': sessionToken,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ scenario: 'decline_aml_hit' }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst { session_id, scenario, status } = await response.json();" + "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-poa-data/',\n {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n issuing_state: 'ESP',\n document_type: 'BANK_STATEMENT',\n issuer: 'Banco Example S.A.',\n issue_date: '2026-05-02',\n name_on_document: 'Carmen Espanola',\n poa_address: 'Avda de Madrid 34, 28013 Madrid',\n poa_parsed_address: { city: 'Madrid', postal_code: '28013', country: 'ES' },\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst poa = await response.json(); // full current POA record" } ] } }, - "/v3/session/{sessionId}/update-status/": { + "/v3/session/{sessionId}/kyb/{companyUuid}/update-data/": { "patch": { - "summary": "Approve, decline, or request resubmission for a verification session", - "description": "Manually override the final decision of a KYC or KYB verification session. Set `new_status` to `Approved` or `Declined` to record a manual decision, or to `Resubmitted` to clear specific verification steps and let the user redo them through the same verification URL. The endpoint accepts both user (KYC) and business (KYB) session IDs.\n\nCall it once a session has reached a reviewable state: the current status must be `Approved`, `Declined`, `In Review`, `Kyc Expired`, `Abandoned`, or `Resubmitted`. Sessions that are still `Not Started`, `In Progress`, or `Awaiting User`, or that ended as `Expired`, return `400`. The new status must differ from the current one, and your API key's organization needs the `write:sessions` permission. The eligible-state check runs before body validation, so an ineligible session returns the `Wrong Current Status` error even when the payload is also invalid.\n\nFor `Resubmitted`, pass `nodes_to_resubmit` to choose which workflow steps the user must redo, or omit it to auto-select existing feature attempts in a resubmittable state (`Declined`, `In Review`, `Not Finished`, `Expired`). Never-attempted features are not auto-selected — list them explicitly in `nodes_to_resubmit`, or the request returns `400` when no recorded attempt needs resubmission. Captured data for the selected steps is deleted, the steps are reordered to match the workflow graph, the session's expiration window restarts, and the original verification URL becomes usable again. Backend-only steps (`AML`, `DATABASE_VALIDATION`, `IP_ANALYSIS`) at the head of the list run immediately without user interaction; if every resubmitted step is backend-only, the session re-finalizes within the same request. `KYB_REGISTRY` and `KYB_KEY_PEOPLE` cannot be resubmitted directly — resubmit the relevant child KYC sessions instead and the parent business session recomputes automatically.\n\nEvery successful call fires the `status.updated` [webhook](/integration/webhooks) once the change commits and appends an activity entry — with actor attribution and your `comment` — to the session's `reviews`, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and in the console activity timeline. The user-profile aggregates linked to the session's `vendor_data` update as well. Declining disables ongoing AML monitoring for the session; re-approving re-enables it (from `Resubmitted`, `Declined`, or `Kyc Expired`) when the workflow has monitoring turned on. Approving or declining a child KYC that belongs to a KYB key-people check recomputes the parent business session's status. Set `send_email: true` (with `email_address`) to notify the user: a status notice for `Approved`/`Declined`, or a resubmission email containing the verification link and per-step reasons for `Resubmitted`.\n\nThe operation is intentionally not repeatable with the same value: re-sending the current status returns `400` (`The new status is the same as the current status.`), so accidental double-submissions are harmless. Authentication and permission failures both return `403` — this API never responds `401`.", - "operationId": "patch_v3_session_update_status", + "summary": "Update registry-extracted company data on a Business Verification (KYB) session", + "description": "Correct the registry-extracted company data on a Business Verification (KYB) session — company name, registration number, addresses, status, contact details — identified by `companyUuid`. Typical callers are a compliance analyst fixing a registry misread, or your back office syncing corrected company data. Every field in the body is optional (`PATCH` semantics); an empty JSON body (`{}`) is a no-op that simply echoes the current company record.\n\nEdits are written in two places at once: fields with a canonical company column (name, registration number, country, incorporation date, tax number, type, statuses, capital fields (`registered_capital`, `registered_capital_amount`, `registered_capital_currency`), alternative names, nature of business, website, email, phone, LEI, location, and `legal_address` → `registered_address`) update that column, and **every** submitted key is also merged into the `registry_data` snapshot (dates as ISO strings) so the \"extracted from registry\" view reflects the correction. Fields without a column — `region`, `registration_date`, `dissolution_date`, `control_scheme` — land in `registry_data` only. `user_provided_data` (what the end user confirmed in the hosted flow) is never touched, and `last_console_edit_at` is stamped on every non-empty PATCH.\n\nUnlike the KYC/POA update endpoints, there is **no session-status gate**: the company can be edited at any point in the business session's lifecycle. The company must belong to the session in the path (`404` with `\"KYB company does not belong to the given session.\"` otherwise) and to your application. Every non-empty PATCH fires the `data.updated` [webhook](/integration/webhooks) on the business session (trigger `console_registry_edit`) — even when the submitted values equal the stored ones. The session's status and decision are **not** re-evaluated, and no review/audit entry is recorded.\n\nAccepts an API key or a console user token; either way the credential needs the `write:businesses` permission. Authentication and permission failures both return `403` — this API never responds `401`. Note that the session lookup runs before credential checks, so an unknown `sessionId` returns `404` even with missing credentials. The shared write budget of 300 requests per minute per API key applies.\n\nFor User Verification sessions, the KYC counterpart is `PATCH /v3/session/{sessionId}/update-data/`; to change the session decision itself use `PATCH /v3/session/{sessionId}/update-status/`.", + "operationId": "patch_v3_session_kyb_company_update_data", + "x-didit-sdk": { + "resource": "sessions", + "method": "update_kyb_company_data", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], @@ -11059,14 +29856,25 @@ ], "parameters": [ { - "in": "path", "name": "sessionId", + "in": "path", "required": true, - "description": "UUID of the verification session to update. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", + "description": "UUID of the parent Business Verification (KYB) session.", "schema": { "type": "string", "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" + "example": "33333333-4444-5555-6666-777777777777" + } + }, + { + "name": "companyUuid", + "in": "path", + "required": true, + "description": "UUID of the company record to update (`registry_checks[].company.uuid` on the business session decision). Must belong to the session referenced by `sessionId`.", + "schema": { + "type": "string", + "format": "uuid", + "example": "88888888-9999-aaaa-bbbb-cccccccccccc" } } ], @@ -11076,140 +29884,191 @@ "application/json": { "schema": { "type": "object", - "required": [ - "new_status" - ], + "description": "All fields optional — send only what you want to change. Provided fields update the canonical company columns (where one exists) and are merged into the `registry_data` snapshot.", "properties": { - "new_status": { + "company_name": { + "type": "string", + "maxLength": 255, + "description": "Legal name of the company. Written to the canonical column and the registry snapshot.", + "example": "Docs Probe Corp" + }, + "registration_number": { + "type": "string", + "maxLength": 100, + "description": "Official registration number with the company registry.", + "example": "DPC-001" + }, + "country_code": { + "type": "string", + "maxLength": 2, + "description": "Country of incorporation, ISO 3166-1 alpha-2. Case-insensitive (`us` is stored as `US`). Alpha-3 codes fail the 2-character limit with `400`.", + "example": "US" + }, + "region": { + "type": "string", + "maxLength": 10, + "nullable": true, + "description": "Subdivision / region tag where the registry has one (e.g. US state). Stored in the registry snapshot only — it has no canonical column." + }, + "incorporation_date": { + "type": "string", + "format": "date", + "description": "Incorporation date in `YYYY-MM-DD`. Optional like every other field — the `PATCH` is partial. Any other date format returns `400`.", + "example": "2015-04-12" + }, + "registration_date": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Registration date where the registry distinguishes it from incorporation. Stored in the registry snapshot only." + }, + "dissolution_date": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Dissolution date for companies that are no longer active. Stored in the registry snapshot only." + }, + "tax_number": { + "type": "string", + "maxLength": 100, + "description": "Tax identification number." + }, + "legal_address": { + "type": "string", + "description": "Legal / registered address. Aliased server-side to the canonical `registered_address` column — the response returns it under `registered_address`, while the registry snapshot keeps the `legal_address` key you sent.", + "example": "1 Example Plaza, Wilmington, DE 19801" + }, + "company_type": { + "type": "string", + "maxLength": 100, + "description": "Legal entity type (e.g. `Limited Liability Company`, `PLC`, `GmbH`).", + "example": "Limited Liability Company" + }, + "registry_status": { + "type": "string", + "enum": [ + "active", + "dissolved", + "deregistered", + "see full details", + "authorised", + "appointed representative", + "unauthorised", + "inactive", + "no longer authorised", + "closed", + "struck off" + ], + "nullable": true, + "description": "Registry-reported company status. Lowercase, some values contain spaces (e.g. `struck off`, `no longer authorised`). Any other value returns `400`.", + "example": "active" + }, + "verification_status": { "type": "string", - "description": "Target status. `Approved` and `Declined` record a final manual decision (each can also overturn the other). `Resubmitted` clears the selected steps and sends the session back to the user. Any other value returns `400`.", "enum": [ - "Approved", - "Declined", - "Resubmitted" + "verified", + "failed", + "unknown" ], - "example": "Approved" + "nullable": true, + "description": "Outcome of the registry verification. Any other value returns `400`.", + "example": "verified" }, - "comment": { + "alternative_names": { "type": "string", - "description": "Free-text reason for the change, stored on the session's review trail and returned in the `reviews` array of [Get Decision](#get-/v3/session/-sessionId-/decision/). For example `Duplicated user`.", - "example": "All checks passed manual review" + "description": "Trade names, former legal names, DBA names (free text)." }, - "nodes_to_resubmit": { - "type": "array", - "description": "Workflow steps the user must redo. Only acted on when `new_status` is `Resubmitted`. For `Approved`/`Declined` the entries are still schema-validated (an invalid `feature` value returns `400`) but schema-valid entries are semantically ignored. If omitted, the server auto-selects existing OCR, Liveness, Face Match, POA, Phone, Email, AML, Database Validation, and Questionnaire attempts whose status is `Declined`, `In Review`, `Not Finished`, or `Expired` (NFC, IP analysis, age estimation, face search, and KYB document attempts are never auto-selected; non-face-match face attempts are selected as `LIVENESS`) — features the user never attempted (no recorded attempt) are NOT selected, so a session with zero attempts returns `400` (\"No features found that need resubmission\") even though nothing was approved; pass `nodes_to_resubmit` explicitly in that case. Steps are executed in workflow-graph order regardless of the order you send them. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and the `KYB` alias are rejected with `400` — those parent checks recompute from their child KYC sessions.", - "items": { - "type": "object", - "required": [ - "node_id", - "feature" - ], - "properties": { - "node_id": { - "type": "string", - "description": "Node identifier as it appears in the session's workflow definition (for example `feature_ocr`, `feature_liveness`).", - "example": "feature_ocr" - }, - "feature": { - "type": "string", - "description": "Feature type of the node. The aliases `ID_VERIFICATION`, `POA`, `PHONE`, and `EMAIL` are normalized server-side to `OCR`, `PROOF_OF_ADDRESS`, `PHONE_VERIFICATION`, and `EMAIL_VERIFICATION`. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and `KYB` pass schema validation but are always rejected with the business-rule `400` shown in the examples.", - "enum": [ - "OCR", - "OCR_BACK", - "NFC", - "AML", - "FACE", - "LIVENESS", - "FACE_MATCH", - "IP_ANALYSIS", - "AGE_ESTIMATION", - "PROOF_OF_ADDRESS", - "PHONE_VERIFICATION", - "EMAIL_VERIFICATION", - "FACE_SEARCH", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_DOCUMENTS", - "ID_VERIFICATION", - "POA", - "PHONE", - "EMAIL", - "KYB_REGISTRY", - "KYB_KEY_PEOPLE", - "KYB" - ], - "example": "OCR" - } - } - } + "nature_of_business": { + "type": "string", + "description": "Short description of the company's activity.", + "example": "Software development" }, - "send_email": { - "type": "boolean", - "description": "Whether to email the user about the change. Requires `email_address`. For `Approved`/`Declined` the user receives a status notice; for `Resubmitted` the email includes the verification link and the per-step resubmission reasons.", - "default": false, - "example": false + "registered_capital": { + "type": "string", + "maxLength": 100, + "description": "Human-readable registered capital (e.g. `USD 50000`). Use `registered_capital_amount` + `registered_capital_currency` for structured values, or this field for free-form text.", + "example": "USD 50000" }, - "email_address": { + "registered_capital_amount": { + "type": "number", + "format": "double", + "nullable": true, + "description": "Numeric registered-capital amount (up to 20 digits, 2 decimal places). Sets the `registered_capital_amount` column and is mirrored into `registry_data` as a decimal string; the response echoes it as a decimal string (e.g. `\"1500000.50\"`)." + }, + "registered_capital_currency": { + "type": "string", + "maxLength": 3, + "nullable": true, + "description": "ISO 4217 currency code for `registered_capital_amount`. Case-insensitive (`usd` is stored as `USD`); values that are not exactly 3 letters return `400` with the ISO 4217 message. Sets the canonical `registered_capital_currency` column and is mirrored into `registry_data`.", + "example": "USD" + }, + "website": { + "type": "string", + "maxLength": 500, + "description": "Company website. Bare domains are accepted and normalized with an `https://` prefix (`docsprobe.example.com` is stored as `https://docsprobe.example.com`); values that still fail URL validation return `400`.", + "example": "https://docsprobe.example.com" + }, + "email": { "type": "string", "format": "email", - "description": "Recipient for the notification email. **Required when `send_email` is `true`** — omitting it returns `400`.", - "example": "user@example.com" + "description": "Contact email on file with the registry.", + "example": "legal@docsprobe.example.com" }, - "email_language": { + "phone": { "type": "string", - "description": "Language for the notification email. Accepts any string at schema level; unsupported codes silently fall back to English (`en`).", - "default": "en", - "example": "en" + "maxLength": 50, + "description": "Contact phone. Must match `+`, digits, spaces, dashes, dots, or parentheses, with 7-15 digits total — otherwise `400`.", + "example": "+1 302 555 0143" }, - "email_preview_token": { + "legal_entity_identifier": { "type": "string", - "writeOnly": true, - "minLength": 1, - "maxLength": 512, - "description": "Optional signed receipt from Preview Resubmission Email. When supplied with send_email=true and new_status=Resubmitted, the recipient, language, checks, current session state, sender and message must still match the preview. A changed or expired receipt returns 409 before changing the session or sending email. Existing integrations may omit this field." + "maxLength": 50, + "description": "LEI, where the company has one." + }, + "location_of_registration": { + "type": "string", + "maxLength": 255, + "description": "City / subdivision of the registry of record." + }, + "vat_number": { + "type": "string", + "maxLength": 64, + "description": "EU VAT number. Merged into the `registry_data` snapshot only — editing it here does not re-run VAT and tax ID validation, so `vat_validation_status` and the other `vat_*` result fields are unchanged." + }, + "control_scheme": { + "type": "string", + "description": "Control / governance scheme when the registry exposes it. Stored in the registry snapshot only." } } }, "examples": { - "Approve Session": { - "summary": "Approve a session after manual review", - "value": { - "new_status": "Approved", - "comment": "All checks passed manual review" - } - }, - "Decline Session": { - "summary": "Decline a session", + "Correct registry data": { + "summary": "Fix the company's registry-extracted fields", "value": { - "new_status": "Declined", - "comment": "Suspected fraud" + "company_name": "Docs Probe Corp", + "registration_number": "DPC-001", + "country_code": "US", + "incorporation_date": "2015-04-12", + "company_type": "Limited Liability Company", + "legal_address": "1 Example Plaza, Wilmington, DE 19801", + "registry_status": "active", + "verification_status": "verified", + "website": "docsprobe.example.com", + "email": "legal@docsprobe.example.com", + "phone": "+1 302 555 0143", + "registered_capital": "USD 50000", + "nature_of_business": "Software development" } }, - "Resubmit Specific Steps": { - "summary": "Request resubmission of chosen steps, with email", + "Single field": { + "summary": "Only the keys you send are written", "value": { - "new_status": "Resubmitted", - "nodes_to_resubmit": [ - { - "node_id": "feature_ocr", - "feature": "OCR" - }, - { - "node_id": "feature_liveness", - "feature": "LIVENESS" - } - ], - "send_email": true, - "email_address": "user@example.com", - "email_language": "en" + "registry_status": "dissolved" } }, - "Resubmit Auto-select": { - "summary": "Request resubmission (auto-select non-approved steps)", - "value": { - "new_status": "Resubmitted" - } + "No-op": { + "summary": "Empty body echoes the current record without firing the webhook", + "value": {} } } } @@ -11217,24 +30076,320 @@ }, "responses": { "200": { - "description": "Status updated. The response contains only the `session_id` — fetch the updated session via [Get Decision](#get-/v3/session/-sessionId-/decision/). The `status.updated` webhook fires once the change commits.", + "description": "Company data updated (or echoed unchanged for an empty body). Returns the full KYB company record — the same shape as `registry_checks[].company` on the business session decision. Note how `legal_address` comes back as `registered_address`. `registered_capital_amount`/`registered_capital_currency` populate their canonical columns and are mirrored into `registry_data`.", "content": { "application/json": { "schema": { "type": "object", + "description": "Full KYB company record — the same shape as the `company` object inside `registry_checks[]` on the business session decision.", "properties": { - "session_id": { + "uuid": { "type": "string", "format": "uuid", - "description": "UUID of the updated session." + "description": "Company record identifier (the `companyUuid` path parameter)." + }, + "node_id": { + "type": "string", + "nullable": true, + "description": "Workflow graph node this company record belongs to, when the workflow is graph-based." + }, + "status": { + "type": "string", + "description": "Feature lifecycle status of the registry check (e.g. `Not Finished`, `Approved`, `Declined`, `In Review`)." + }, + "registry_status": { + "type": "string", + "nullable": true, + "description": "Registry-reported company status (e.g. `active`, `dissolved`, `struck off`)." + }, + "data_resolved": { + "type": "boolean", + "description": "`true` when the company data is complete — always `true` for manual-entry companies; for registry-sourced companies, `true` once the registry fetch resolved." + }, + "company_name": { + "type": "string", + "nullable": true + }, + "registration_number": { + "type": "string", + "nullable": true + }, + "country_code": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country of incorporation." + }, + "region": { + "type": "string", + "nullable": true + }, + "company_type": { + "type": "string", + "nullable": true + }, + "incorporation_date": { + "type": "string", + "format": "date", + "nullable": true + }, + "registered_address": { + "type": "string", + "nullable": true, + "description": "Canonical registered address — updated when you send `legal_address`." + }, + "tax_number": { + "type": "string", + "nullable": true + }, + "risk_level": { + "type": "string", + "nullable": true + }, + "verification_status": { + "type": "string", + "nullable": true, + "description": "`verified`, `failed`, or `unknown`." + }, + "is_from_registry": { + "type": "boolean", + "description": "`true` when the company was pre-filled from a registry search rather than entered manually." + }, + "fetch_status": { + "type": "string", + "nullable": true, + "description": "Registry fetch lifecycle for registry-sourced companies (`pending` / `resolved`); `null` for manual entries." + }, + "alternative_names": { + "type": "string", + "nullable": true, + "description": "Alternative, trading, former or original-language company names as text, when reported." + }, + "nature_of_business": { + "type": "string", + "nullable": true + }, + "registered_capital": { + "type": "string", + "nullable": true + }, + "registered_capital_amount": { + "type": "string", + "nullable": true, + "description": "Canonical column. Returned as a decimal string (e.g. `\"2750000.00\"`)." + }, + "registered_capital_currency": { + "type": "string", + "nullable": true, + "description": "Canonical column. Set by the registry flow or by console/API edits via this endpoint." + }, + "website": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + }, + "phone": { + "type": "string", + "nullable": true + }, + "legal_entity_identifier": { + "type": "string", + "nullable": true + }, + "location_of_registration": { + "type": "string", + "nullable": true + }, + "vat_number": { + "type": "string", + "nullable": true, + "description": "EU VAT number, when collected during the hosted registry step or edited afterwards." + }, + "vat_validation_status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "format_valid", + "invalid_format", + "could_not_validate", + "not_applicable" + ], + "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." + }, + "vat_validated_number": { + "type": "string", + "nullable": true, + "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." + }, + "vat_validation_source": { + "type": "string", + "nullable": true, + "enum": [ + "eu_vies", + "pl_vat_white_list", + "no_brreg", + "ch_uid_register", + "br_receita_federal", + "co_rues", + "cr_hacienda", + "ec_sri", + "py_dnit", + "format_rules", + null + ], + "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." + }, + "vat_validated_name": { + "type": "string", + "nullable": true, + "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_validated_address": { + "type": "string", + "nullable": true, + "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_checked_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the check. `null` when no check ran." + }, + "financial_summary": { + "type": "object", + "nullable": true, + "additionalProperties": true + }, + "officers": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + }, + "description": "Directors / officers attached to the company, including per-person KYC progress." + }, + "beneficial_owners": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + }, + "description": "Beneficial owners attached to the company, including per-person KYC progress." + }, + "addresses": { + "type": "array", + "nullable": true, + "items": { + "type": "object", + "additionalProperties": true + }, + "description": "Registry address detail, when the registry returned any." + }, + "industries": { + "type": "array", + "nullable": true, + "items": { + "type": "object", + "additionalProperties": true + }, + "description": "Registry industry classification, when available." + }, + "accounts": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." + }, + "registry_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Raw registry payload snapshot. Every key you PATCH is merged into this map (dates serialized as ISO strings), so the \"extracted from registry\" view reflects operator corrections." + }, + "user_provided_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "What the end user typed during the hosted registry-confirmation step. Never modified by this endpoint." + }, + "confirmed_by_user_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the end user confirmed the company data in the hosted flow." + }, + "last_console_edit_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Stamped on every successful non-empty PATCH, so UIs can surface \"edited by operator\" context." + }, + "is_editable": { + "type": "boolean", + "description": "`true` while the end user can still edit the registry-sourced data in the hosted flow (registry-sourced and not yet confirmed)." } } }, "examples": { - "Updated": { - "summary": "Returned for Approved, Declined, and Resubmitted alike", + "Updated company": { + "summary": "Full company record after the correction", "value": { - "session_id": "3472fb7c-8f7c-4d1a-9cf4-cf3d74ce1a60" + "uuid": "6b9479f5-6769-46c8-baf1-df63bbbc5f8c", + "node_id": null, + "status": "Not Finished", + "registry_status": "active", + "data_resolved": true, + "company_name": "Docs Probe Corp", + "registration_number": "DPC-001", + "country_code": "US", + "region": null, + "company_type": "Limited Liability Company", + "incorporation_date": "2015-04-12", + "registered_address": "1 Example Plaza, Wilmington, DE 19801", + "tax_number": null, + "risk_level": null, + "verification_status": "verified", + "is_from_registry": false, + "fetch_status": null, + "alternative_names": null, + "nature_of_business": "Software development", + "registered_capital": "USD 50000", + "registered_capital_amount": null, + "registered_capital_currency": "USD", + "website": "https://docsprobe.example.com", + "email": "legal@docsprobe.example.com", + "phone": "+1 302 555 0143", + "legal_entity_identifier": null, + "location_of_registration": null, + "financial_summary": null, + "officers": [], + "beneficial_owners": [], + "addresses": null, + "industries": null, + "accounts": null, + "registry_data": { + "company_name": "Docs Probe Corp", + "registration_number": "DPC-001", + "country_code": "US", + "incorporation_date": "2015-04-12", + "legal_address": "1 Example Plaza, Wilmington, DE 19801", + "company_type": "Limited Liability Company", + "registry_status": "active", + "verification_status": "verified", + "nature_of_business": "Software development", + "registered_capital": "USD 50000", + "registered_capital_currency": "USD", + "website": "https://docsprobe.example.com", + "email": "legal@docsprobe.example.com", + "phone": "+1 302 555 0143" + }, + "user_provided_data": null, + "confirmed_by_user_at": null, + "last_console_edit_at": "2026-06-12T00:02:34.276061Z", + "is_editable": false } } } @@ -11242,75 +30397,71 @@ } }, "400": { - "description": "Validation error. Field errors arrive as `{\"\": [\"\"]}` (some business-rule rejections use a string value instead: `{\"nodes_to_resubmit\": \"\"}`), cross-state errors as `{\"detail\": \"\"}`, and the same-status error as a bare JSON array.", + "description": "Validation error. Field errors arrive as `{\"\": [\"\"]}`. Max-length checks run before format checks, so `country_code: \"USA\"` reports the 2-character limit rather than the ISO message.", "content": { "application/json": { "examples": { - "Wrong Current Status": { - "summary": "Session is not in an updatable state", + "Alpha-3 country code": { + "summary": "country_code must be alpha-2 — length check fires first", "value": { - "detail": "Only sessions with status Approved, Declined, In Review, Kyc Expired, Abandoned, Resubmitted can be updated. Current status: In Progress" + "country_code": [ + "Ensure this field has no more than 2 characters." + ] } }, - "Invalid Status Value": { - "summary": "new_status is a real session status but not one of Approved/Declined/Resubmitted", + "Non-alphabetic country code": { + "summary": "2 characters but not letters", "value": { - "new_status": [ - "You can only update the status to 'Approved', 'Declined', or 'Resubmitted'." + "country_code": [ + "country_code must be a 2-letter ISO 3166-1 alpha-2 code." ] } }, - "Missing new_status": { - "summary": "Required field omitted", + "Invalid currency": { + "summary": "3 characters but not an ISO 4217 code", "value": { - "new_status": [ - "This field is required." + "registered_capital_currency": [ + "Currency must be a 3-letter ISO 4217 code (e.g. USD, EUR, GBP)." ] } }, - "Same Status": { - "summary": "New status equals the current status", - "value": [ - "The new status is the same as the current status." - ] - }, - "Email Address Required": { - "summary": "send_email true without email_address", + "Invalid phone": { + "summary": "Phone fails the format / digit-count check", "value": { - "email_address": [ - "Email address is required when send_email is true." + "phone": [ + "Invalid phone number format." ] } }, - "Non-resubmittable Feature": { - "summary": "Resubmit targets a KYB parent check", + "Invalid registry status": { + "summary": "registry_status not in the enum", "value": { - "nodes_to_resubmit": "The following features cannot be resubmitted directly: KYB_REGISTRY. Resubmit the relevant child KYC sessions instead; the parent will recompute automatically." + "registry_status": [ + "\"liquidated\" is not a valid choice." + ] } }, - "Nothing To Resubmit": { - "summary": "Auto-select found no non-approved features", + "Invalid email": { + "summary": "Malformed email address", "value": { - "nodes_to_resubmit": "No features found that need resubmission. All features are already approved." + "email": [ + "Enter a valid email address." + ] } }, - "Invalid Feature Choice": { - "summary": "Unknown feature in nodes_to_resubmit", + "Invalid website": { + "summary": "Value fails URL validation even after https:// normalization", "value": { - "nodes_to_resubmit": [ - { - "feature": [ - "\"BOGUS\" is not a valid choice. Valid choices are: OCR, OCR_BACK, NFC, AML, FACE, LIVENESS, FACE_MATCH, IP_ANALYSIS, AGE_ESTIMATION, PROOF_OF_ADDRESS, PHONE_VERIFICATION, EMAIL_VERIFICATION, FACE_SEARCH, DATABASE_VALIDATION, QUESTIONNAIRE, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE, ID_VERIFICATION, POA, PHONE, EMAIL, KYB" - ] - } + "website": [ + "Enter a valid URL." ] } }, - "Invalid Status Value (not a status at all)": { - "summary": "new_status is not a valid StatusChoices value", + "Bad date format": { + "summary": "Dates must be YYYY-MM-DD", "value": { - "new_status": [ - "\"Bogus\" is not a valid choice." + "incorporation_date": [ + "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." ] } } @@ -11330,7 +30481,7 @@ } }, "Missing Privilege": { - "summary": "API key lacks write:sessions", + "summary": "Credential lacks write:businesses", "value": { "detail": "You do not have permission to perform this action." } @@ -11340,1542 +30491,3040 @@ } }, "404": { - "description": "No KYC or KYB session with this `session_id` exists in your application (deleted sessions also return `404`).", + "description": "Unknown session, unknown company, or a company that belongs to a different session. Each case has a distinct `detail` message (see examples). The unknown-session lookup runs before credential checks.", "content": { "application/json": { "examples": { - "Not Found": { - "summary": "Unknown session_id", + "Unknown session": { + "summary": "No session with this ID in your application (or it was deleted)", "value": { "detail": "Not found." } + }, + "Unknown company": { + "summary": "No company with this UUID in your application", + "value": { + "detail": "No KYBCompany matches the given query." + } + }, + "Company / session mismatch": { + "summary": "companyUuid exists but belongs to another session", + "value": { + "detail": "KYB company does not belong to the given session." + } } } } } }, "429": { - "description": "A resubmit targeting only backend-only features (AML, DATABASE_VALIDATION, IP_ANALYSIS) was repeated within the 30-second cooldown window; an immediate repeat cannot change the outcome. Back off and retry after the interval indicated in `Retry-After`.", + "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, "examples": { - "Backend-only resubmit throttled": { + "Rate limited": { + "summary": "Write budget exhausted", "value": { - "detail": "This session was already resubmitted for backend-only features within the last 30 seconds; repeating it cannot change the outcome. Wait before resubmitting again." + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." } } } } } - }, - "409": { - "description": "The email preview changed or expired. Generate a new preview before confirming; no session changes or email delivery occur." } }, "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-status/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"new_status\": \"Approved\",\n \"comment\": \"All checks passed manual review\"\n }'" + "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/88888888-9999-aaaa-bbbb-cccccccccccc/update-data/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"company_name\": \"Docs Probe Corp\",\n \"registration_number\": \"DPC-001\",\n \"country_code\": \"US\",\n \"incorporation_date\": \"2015-04-12\",\n \"company_type\": \"Limited Liability Company\",\n \"legal_address\": \"1 Example Plaza, Wilmington, DE 19801\",\n \"registry_status\": \"active\"\n }'" }, { "lang": "python", "label": "Python", - "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-status/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\n \"new_status\": \"Resubmitted\",\n \"nodes_to_resubmit\": [\n {\"node_id\": \"feature_ocr\", \"feature\": \"OCR\"},\n {\"node_id\": \"feature_liveness\", \"feature\": \"LIVENESS\"},\n ],\n \"send_email\": True,\n \"email_address\": \"user@example.com\",\n \"email_language\": \"en\",\n },\n)\nresponse.raise_for_status()\nprint(response.json()) # {'session_id': '11111111-2222-3333-4444-555555555555'}" + "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/\"\n \"88888888-9999-aaaa-bbbb-cccccccccccc/update-data/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\n \"company_name\": \"Docs Probe Corp\",\n \"registration_number\": \"DPC-001\",\n \"country_code\": \"US\",\n \"incorporation_date\": \"2015-04-12\",\n \"legal_address\": \"1 Example Plaza, Wilmington, DE 19801\",\n \"registry_status\": \"active\",\n },\n)\nresponse.raise_for_status()\ncompany = response.json() # full company record\nprint(company[\"company_name\"], company[\"registered_address\"])" }, { "lang": "javascript", "label": "JavaScript", - "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-status/',\n {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n new_status: 'Declined',\n comment: 'Suspected fraud',\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst { session_id } = await response.json();" + "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/' +\n '88888888-9999-aaaa-bbbb-cccccccccccc/update-data/',\n {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n company_name: 'Docs Probe Corp',\n registration_number: 'DPC-001',\n country_code: 'US',\n incorporation_date: '2015-04-12',\n legal_address: '1 Example Plaza, Wilmington, DE 19801',\n registry_status: 'active',\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst company = await response.json(); // full company record" } ] } }, - "/v3/id-verification/": { - "post": { - "summary": "ID Verification (document OCR + fraud checks)", - "description": "OCR an identity document and run document fraud checks in one call — returns the extracted holder fields, the parsed MRZ, an `Approved`/`Declined` `status`, and a `warnings` list explaining every issue found. Supports 14,000+ document types from 220+ countries and territories.\n\n**How it works.** Send the document's `front_image` (required) and, for two-sided documents, the `back_image`. Upload uncropped photos with all four corners of the document visible — the service detects, aligns, and crops the document itself, so do not pre-crop. The document is classified automatically (you never declare the country or document type), the visual zone is read with OCR, and the MRZ and any barcodes are decoded. Cross-checks then validate dates, number formats, and MRZ check digits, and compare the visual zone against the MRZ. With `perform_document_liveness=true`, the images are additionally screened for screen replays, printed copies, and portrait manipulation.\n\n**Decision logic.** `status` is `Approved` unless at least one warning resolves to a decline. Fraud and hard-failure risks always decline: `DOCUMENT_EXPIRED`, `SCREEN_CAPTURE_DETECTED`, `PRINTED_COPY_DETECTED`, `PORTRAIT_MANIPULATION_DETECTED`, `PUBLIC_DOCUMENT_IMAGE_DETECTED`, plus extraction failures (`NAME_NOT_DETECTED`, `DATE_OF_BIRTH_NOT_DETECTED`, `DOCUMENT_NUMBER_NOT_DETECTED`). (`PORTRAIT_IMAGE_NOT_DETECTED`, `COULD_NOT_DETECT_DOCUMENT_TYPE`, `INVALID_DATE`, and `MRZ_NOT_DETECTED` exist in workflow sessions but are never produced by this standalone endpoint.) Three risk groups are configurable per request via `invalid_mrz_action`, `inconsistent_data_action`, and `expiration_date_not_detected_action` (`DECLINE` or `NO_ACTION`). All other warnings (e.g. `POSSIBLE_DUPLICATED_USER`) are informational and never decline on their own. A document that cannot be processed at all returns `400` with `{\"error\": \"COULD_NOT_RECOGNIZE_DOCUMENT\"}`; a readable but problematic document returns `200` with `status: \"Declined\"` — always inspect `id_verification.status` and `id_verification.warnings`, not just the HTTP code.\n\n**Billing.** Each `200` response consumes one ID Verification API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, the cropped document/portrait images are stored and returned as short-lived media URLs (`https:///ocr/...`), and a `status.updated` webhook is emitted to your configured webhook endpoints. When `false`, nothing is stored, `request_id` is a one-off correlation UUID, and `portrait_image`/`front_image`/`back_image` are returned inline as base64-encoded JPEG strings.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload (a fictional \"Sandbox User\" USA identity card), no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", - "operationId": "post_v3id-verification", + "/v3/session/{sessionId}/kyb/parties/{partyUuid}/": { + "patch": { + "summary": "Skip (or re-require) a Key People party on a Business Verification (KYB) session", + "description": "Exempt a Key People party from verification, or put it back in scope. This is the operator escape hatch for a party that can never finish its own check - most often because the individual KYC session handed to a UBO or director was deleted, which freezes that child's status and leaves the KYB parked in `Awaiting User` with nothing left that could move it.\n\nSetting `is_skipped: true` drops the party out of the Key People aggregation and immediately recomputes the `KYBKeyPeopleCheck` and the parent session status, so the business session can settle and then be approved or declined. Sending `is_skipped: false` puts the party back in scope and the check goes back to waiting on it.\n\nUnlike the applicant-facing skip in the hosted Key People screen, this endpoint is **not** gated by the role's `allow_skip` setting - that flag limits what the applicant may decide, not what your analyst may override. `requires_verification` is deliberately left untouched, so an overridden party reads as `requires_verification: true` **and** `is_skipped: true` and the record still shows what the rules asked for.\n\nThe party must belong to the session in the path (`404` with `\"Party does not belong to the given session.\"` otherwise) and to your application. A PATCH that actually changes the flag fires the `data.updated` [webhook](/integration/webhooks) on the business session with trigger `console_key_people_skip`; a PATCH that sets the flag to its current value is a no-op that echoes the party back.\n\nAccepts an API key or a console user token; either way the credential needs the `write:businesses` permission.", + "operationId": "patch_v3_session_kyb_party_update", + "x-didit-sdk": { + "resource": "sessions", + "method": "update_kyb_party", + "auth": "api_key", + "server": true + }, "tags": [ - "Standalone APIs" + "Sessions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "description": "UUID of the parent Business Verification (KYB) session.", + "schema": { + "type": "string", + "format": "uuid", + "example": "33333333-4444-5555-6666-777777777777" + } + }, + { + "name": "partyUuid", + "in": "path", + "required": true, + "description": "UUID of the Key People party to update (`key_people_checks[].submitted.parties[].uuid` on the business session decision). Must belong to the session referenced by `sessionId`.", + "schema": { + "type": "string", + "format": "uuid", + "example": "0a3f1e2c-7c2f-4a1b-9d55-2f8d2a5d6b41" + } + } ], - "parameters": [], "requestBody": { "required": true, "content": { - "multipart/form-data": { + "application/json": { "schema": { "type": "object", "required": [ - "front_image" + "is_skipped" ], "properties": { - "front_image": { - "type": "string", - "format": "binary", - "description": "Front side of the identity document. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`, `pdf`. Maximum upload size: **10 MB** (larger files are rejected with `400`). PDFs are rendered to an image before OCR; encrypted PDFs require `front_image_password`. Images are automatically compressed to ~0.5 MB and EXIF orientation is applied. Upload the full, uncropped capture with all four corners of the document visible." - }, - "front_image_password": { - "type": "string", - "description": "Password to decrypt `front_image` when it is an encrypted PDF. Sending an encrypted PDF without (or with a wrong) password returns `400`.", - "writeOnly": true - }, - "back_image": { - "type": "string", - "format": "binary", - "description": "Back side of the document — send it whenever the document has one (many ID cards carry the MRZ or a barcode on the back; omitting it simply leaves the MRZ and back-side fields empty in the response — this endpoint does not raise a missing-MRZ warning). Same format and size limits as `front_image`. When omitted, `back_image` and back-side fields are `null` in the response." - }, - "back_image_password": { - "type": "string", - "description": "Password to decrypt `back_image` when it is an encrypted PDF.", - "writeOnly": true - }, - "perform_document_liveness": { - "type": "boolean", - "default": true, - "description": "Screens both images for presentation fraud: screen replays (`SCREEN_CAPTURE_DETECTED`), printed copies (`PRINTED_COPY_DETECTED`), and portrait manipulation (`PORTRAIT_MANIPULATION_DETECTED`). Any of these auto-declines. Enabled by default; set to `false` to skip fraud screening and reduce latency.", - "example": true - }, - "minimum_age": { - "type": "integer", - "nullable": true, - "default": null, - "minimum": 1, - "maximum": 120, - "description": "Accepted and validated (1–120) but **currently not applied** by this endpoint — it never produces a `MINIMUM_AGE_NOT_MET` warning from this field. Enforce age rules from the returned `date_of_birth`/`age`, or use a verification-session workflow with age restrictions." - }, - "expiration_date_not_detected_action": { - "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "NO_ACTION", - "description": "What to do when no expiration date can be read (`EXPIRATION_DATE_NOT_DETECTED`). Default `NO_ACTION` because many identity documents print no expiry date. Note: a *detected and past* expiry date always declines (`DOCUMENT_EXPIRED`), regardless of this option." - }, - "invalid_mrz_action": { - "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "DECLINE", - "description": "What to do when the extracted MRZ fails check-digit validation (`MRZ_VALIDATION_FAILED`). A missing MRZ raises no warning on this endpoint. Only relevant for documents that carry an MRZ. `REVIEW` is not supported on this endpoint and returns `400`." - }, - "inconsistent_data_action": { - "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "DECLINE", - "description": "What to do when extracted data is internally inconsistent: `DATA_INCONSISTENT`, `MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAME` (visual zone disagrees with the MRZ), or `DOCUMENT_NAME_DIFFERENT_FROM_OTHER_APPROVED_DOCUMENTS`." - }, - "critical_field_occlusion_action": { - "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "DECLINE", - "description": "What to do when a critical printed field was physically covered on the capture (`CRITICAL_FIELD_OCCLUSION_DETECTED`) - an identity number, a name, the date of birth, or the expiry date. Declines by default, like this endpoint's other document-integrity risks: covering the numbers a report is reconciled against is what a redacted document looks like. The value read for a covered field is dropped from the extraction either way, so the matching `*_NOT_DETECTED` warning usually accompanies it. `REVIEW` is not supported on this endpoint and returns `400`." - }, - "document_or_personal_number_format_mismatch_action": { - "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "NO_ACTION", - "description": "What to do when a number does not match the format its issuing authority uses for that document type (`DOCUMENT_NUMBER_FORMAT_MISMATCH`, `PERSONAL_NUMBER_FORMAT_MISMATCH`). Default `NO_ACTION`, which reports the warning without changing the status. `REVIEW` is not supported on this endpoint and returns `400`." - }, - "preferred_characters": { - "type": "string", - "enum": [ - "latin", - "non_latin" - ], - "default": "latin", - "description": "Preferred script for name/address fields on documents that print both Latin and non-Latin text (Arabic, Cyrillic, CJK, …). `latin` returns transliterated/Latin values; `non_latin` prefers the native script." - }, - "save_api_request": { + "is_skipped": { "type": "boolean", - "default": true, - "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, announced through a `status.updated` webhook, and with document images returned as media URLs. When `false`, nothing is stored, `request_id` is a transient UUID, and images are returned inline as base64 JPEG strings.", + "description": "`true` exempts the party from verification so the Key People check can settle without it; `false` puts it back in scope.", "example": true - }, - "vendor_data": { - "type": "string", - "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", - "example": "user-123" - }, - "metadata": { - "type": "object", - "additionalProperties": true, - "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"onboarding\"}`) — it is parsed into an object.", - "example": { - "flow": "onboarding" - } } } - }, - "example": { - "front_image": "(binary JPEG/PNG/PDF of the document front)", - "back_image": "(binary JPEG/PNG/PDF of the document back)", - "perform_document_liveness": true, - "save_api_request": true, - "vendor_data": "user-123", - "metadata": { - "flow": "onboarding" - } } } } }, "responses": { "200": { - "description": "Document processed. `id_verification.status` is `Approved` or `Declined`; every detected issue is itemized in `id_verification.warnings`. A problematic document still returns `200` with `status: \"Declined\"` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id and image fields are short-lived media URLs; with `save_api_request=false` they are inline base64 JPEG strings.", + "description": "Party updated (or echoed unchanged when the flag already had this value). Returns the party - the same shape as an entry of `key_people_checks[].submitted.parties[]` on the business session decision.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Key People party record - the same shape as an entry of `key_people_checks[].submitted.parties[]` on the business session decision.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Party identifier." + }, + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ], + "description": "Whether the party is an individual or a company." + }, + "name": { + "type": "string", + "description": "Display name of the party." + }, + "is_skipped": { + "type": "boolean", + "description": "Whether the party is exempt from verification." + }, + "requires_verification": { + "type": "boolean", + "description": "What the role and threshold rules asked for. Left untouched by this endpoint." + }, + "kyc_session_status": { + "type": "string", + "nullable": true, + "description": "Status of the linked individual KYC session, frozen at deletion time when the session was deleted." + }, + "kyc_session_url": { + "type": "string", + "nullable": true, + "description": "Link to the linked individual KYC session. `null` when there is none or when it was deleted." + }, + "kyc_session_deleted": { + "type": "boolean", + "description": "The linked individual KYC session was deleted, so it can never settle." + }, + "kyb_sub_session_deleted": { + "type": "boolean", + "description": "Corporate-party counterpart of `kyc_session_deleted`." + } + } + } + } + } + }, + "400": { + "description": "Validation error on the request body. `is_skipped` is the only field this endpoint reads, and it is required. Field errors arrive as `{\"\": [\"\"]}`. Body validation runs *after* the party lookup, so a malformed `is_skipped` sent to a party that does not exist reports `404`, not `400`.", "content": { "application/json": { "examples": { - "Approved": { - "summary": "Two-sided ID card read cleanly (saved — images as media URLs)", + "Missing is_skipped": { + "summary": "Empty body, or a body that omits `is_skipped`", "value": { - "request_id": "11d219ed-d59c-4b1d-8d65-9b933f12d5b8", - "id_verification": { - "status": "Approved", - "document_type": "Identity Card", - "document_subtype": "ID_CARD_GENERIC", - "document_number": "CBX164224", - "personal_number": "20446581H", - "portrait_image": "https:///ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-portrait_image-8c2f.jpg?signature=...", - "front_image": "https:///ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-front_image-4a1e.jpg?signature=...", - "back_image": "https:///ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-back_image-77b0.jpg?signature=...", - "front_image_camera_front": null, - "back_image_camera_front": null, - "front_image_camera_front_face_match_score": null, - "back_image_camera_front_face_match_score": null, - "front_image_quality_score": { - "focus_score": 78.0, - "brightness_score": 83.2, - "brightness_issue": "ok", - "is_document_fully_visible": true, - "resolution_score": 40.7, - "overall_score": 70.2 - }, - "back_image_quality_score": { - "focus_score": 100.0, - "brightness_score": 95.6, - "brightness_issue": "ok", - "is_document_fully_visible": true, - "resolution_score": 43.0, - "overall_score": 84.4 - }, - "date_of_birth": "1980-01-13", - "age": 46, - "expiration_date": "2032-03-31", - "date_of_issue": "2022-03-31", - "issuing_state": "ESP", - "issuing_state_name": "Spain", - "first_name": "Julio Francisco", - "last_name": "Fores Sena", - "full_name": "Julio Francisco Fores Sena", - "gender": "M", - "address": "Brda. Urb. El Cardonal 0052 52 Po3 B,Taco,San Cristobal De La Laguna,Santa Cruz De Tenerife", - "formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain", - "place_of_birth": "Valencia, Valencia", - "marital_status": "UNKNOWN", - "nationality": "ESP", - "extra_fields": { - "first_surname": "Fores", - "second_surname": "Sena" - }, - "mrz": { - "surname": "FORES SENA", - "name": "JULIO FRANCISCO", - "country": "ESP", - "nationality": "ESP", - "birth_date": "800113", - "expiry_date": "320331", - "sex": "M", - "document_type": "ID", - "document_number": "CBX164224", - "optional_data": "20446581H", - "optional_data_2": "", - "birth_date_hash": "9", - "expiry_date_hash": "8", - "document_number_hash": "3", - "final_hash": "3", - "personal_number": "20446581H", - "warnings": [], - "errors": [], - "mrz_type": "TD1", - "mrz_string": "IDESPCBX164224320446581H<<<<<<\n8001139M3203318ESP<<<<<<<<<<<3\nFORES/ocr/f61681b8-9422-4378-ac3e-04c6a247fb45-portrait_image-1d9a.jpg?signature=...", - "front_image": "https:///ocr/f61681b8-9422-4378-ac3e-04c6a247fb45-front_image-5c2b.jpg?signature=...", - "back_image": null, - "front_image_camera_front": null, - "back_image_camera_front": null, - "front_image_camera_front_face_match_score": null, - "back_image_camera_front_face_match_score": null, - "front_image_quality_score": { - "focus_score": 100.0, - "brightness_score": 95.7, - "brightness_issue": "ok", - "is_document_fully_visible": true, - "resolution_score": 22.2, - "overall_score": 79.3 - }, - "back_image_quality_score": null, - "date_of_birth": "1997-01-30", - "age": 29, - "expiration_date": "2025-11-26", - "date_of_issue": null, - "issuing_state": "ESP", - "issuing_state_name": "Spain", - "first_name": "Lucia", - "last_name": "Marin Castro", - "full_name": "Lucia Marin Castro", - "gender": "F", - "address": null, - "formatted_address": null, - "place_of_birth": null, - "marital_status": "UNKNOWN", - "nationality": "ESP", - "extra_fields": { - "first_surname": "Marin", - "second_surname": "Castro" - }, - "mrz": {}, - "parsed_address": null, - "warnings": [ - { - "risk": "DOCUMENT_EXPIRED", - "feature": "ID_VERIFICATION", - "additional_data": null, - "log_type": "error", - "short_description": "Document expired", - "long_description": "The document's expiration date has passed, rendering it no longer valid for use." - } - ], - "barcodes": [] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T02:25:48.117204+00:00" + "detail": "You do not have permission to perform this action." } } - }, + } + } + } + }, + "404": { + "description": "Unknown party, or a party that belongs to a different session. Each case has a distinct `detail` message (see examples). Credential and permission checks run before the party lookup, so an unauthorised call reports `403` rather than `404`. There is no separate session lookup: an unknown `sessionId` paired with a party of yours reports the mismatch message.", + "content": { + "application/json": { + "examples": { + "Unknown party": { + "summary": "No party with this UUID in your application", + "value": { + "detail": "Not found." + } + }, + "Party / session mismatch": { + "summary": "partyUuid exists but hangs off another session", + "value": { + "detail": "Party does not belong to the given session." + } + } + } + } + } + }, + "429": { + "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", + "content": { + "application/json": { "schema": { "type": "object", "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." - }, - "id_verification": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined" - ], - "description": "`Approved` when no warning resolves to a decline; `Declined` otherwise — the `warnings` list explains why." - }, - "document_type": { - "type": "string", - "nullable": true, - "description": "Detected document type display name: `Identity Card`, `Passport`, `Driver's License`, `Residence Permit`, `Health Insurance Card`, or `Tax Card`. `null` when the document could not be classified.", - "example": "Identity Card" - }, - "document_subtype": { - "type": "string", - "nullable": true, - "description": "Finer-grained subtype when available (e.g. `ID_CARD_GENERIC`, `EPASSPORT`).", - "example": "ID_CARD_GENERIC" - }, - "document_number": { - "type": "string", - "nullable": true, - "example": "CBX164224" - }, - "personal_number": { - "type": "string", - "nullable": true, - "description": "Secondary personal/national number printed on some documents.", - "example": "20446581H" - }, - "portrait_image": { - "type": "string", - "nullable": true, - "description": "Cropped portrait photo from the document. A short-lived media URL (`https:///ocr/...`) when `save_api_request=true` (default); an inline base64-encoded JPEG string when `save_api_request=false`. `null` when no portrait was detected." - }, - "front_image": { - "type": "string", - "nullable": true, - "description": "Aligned crop of the document front. Media URL when saved, inline base64 JPEG otherwise." - }, - "back_image": { - "type": "string", - "nullable": true, - "description": "Aligned crop of the document back. `null` when no `back_image` was uploaded." - }, - "front_image_camera_front": { - "type": "string", - "nullable": true, - "description": "Always `null` on this endpoint (populated only in camera-capture verification sessions)." - }, - "back_image_camera_front": { - "type": "string", - "nullable": true, - "description": "Always `null` on this endpoint." - }, - "front_image_camera_front_face_match_score": { - "type": "number", - "nullable": true, - "description": "Always `null` on this endpoint." - }, - "back_image_camera_front_face_match_score": { - "type": "number", - "nullable": true, - "description": "Always `null` on this endpoint." - }, - "front_image_quality_score": { - "type": "object", - "nullable": true, - "description": "Capture-quality diagnostics for the uploaded image (`null` when the side was not provided or scoring failed). All scores are 0–100.", - "properties": { - "focus_score": { - "type": "number", - "format": "float", - "description": "Sharpness of the document crop (higher is sharper)." - }, - "brightness_score": { - "type": "number", - "format": "float", - "description": "Exposure quality (higher is better)." - }, - "brightness_issue": { - "type": "string", - "nullable": true, - "description": "`ok` when exposure is fine, otherwise the detected issue (e.g. too dark / too bright)." - }, - "is_document_fully_visible": { - "type": "boolean", - "nullable": true, - "description": "Whether all four corners of the document are inside the frame." - }, - "resolution_score": { - "type": "number", - "format": "float", - "description": "Effective resolution of the document area." - }, - "overall_score": { - "type": "number", - "format": "float", - "description": "Aggregate quality score." - } - } - }, - "back_image_quality_score": { - "type": "object", - "nullable": true, - "description": "Capture-quality diagnostics for the uploaded image (`null` when the side was not provided or scoring failed). All scores are 0–100.", - "properties": { - "focus_score": { - "type": "number", - "format": "float", - "description": "Sharpness of the document crop (higher is sharper)." - }, - "brightness_score": { - "type": "number", - "format": "float", - "description": "Exposure quality (higher is better)." - }, - "brightness_issue": { - "type": "string", - "nullable": true, - "description": "`ok` when exposure is fine, otherwise the detected issue (e.g. too dark / too bright)." - }, - "is_document_fully_visible": { - "type": "boolean", - "nullable": true, - "description": "Whether all four corners of the document are inside the frame." - }, - "resolution_score": { - "type": "number", - "format": "float", - "description": "Effective resolution of the document area." - }, - "overall_score": { - "type": "number", - "format": "float", - "description": "Aggregate quality score." - } - } - }, - "date_of_birth": { - "type": "string", - "format": "date", - "nullable": true, - "example": "1980-01-13" - }, - "age": { - "type": "integer", - "nullable": true, - "description": "Holder age in years, computed from `date_of_birth`.", - "example": 46 - }, - "expiration_date": { - "type": "string", - "format": "date", - "nullable": true, - "example": "2032-03-31" - }, - "date_of_issue": { - "type": "string", - "format": "date", - "nullable": true, - "example": "2022-03-31" - }, - "issuing_state": { - "type": "string", - "nullable": true, - "description": "Issuing country (ISO 3166-1 alpha-3).", - "example": "ESP" - }, - "issuing_state_name": { - "type": "string", - "nullable": true, - "example": "Spain" - }, - "first_name": { - "type": "string", - "nullable": true, - "example": "Julio Francisco" - }, - "last_name": { - "type": "string", - "nullable": true, - "example": "Fores Sena" - }, - "full_name": { - "type": "string", - "nullable": true, - "example": "Julio Francisco Fores Sena" - }, - "gender": { - "type": "string", - "nullable": true, - "description": "`M`, `F`, or `null` when not printed/detected.", - "example": "M" - }, - "address": { - "type": "string", - "nullable": true, - "description": "Address exactly as printed on the document." - }, - "formatted_address": { - "type": "string", - "nullable": true, - "description": "Geocoded, normalized version of `address`." - }, - "place_of_birth": { - "type": "string", - "nullable": true, - "example": "Valencia, Valencia" - }, - "marital_status": { - "type": "string", - "nullable": true, - "description": "`UNKNOWN` unless the document prints marital status.", - "example": "UNKNOWN" - }, - "nationality": { - "type": "string", - "nullable": true, - "description": "Holder nationality (ISO 3166-1 alpha-3).", - "example": "ESP" - }, - "extra_fields": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Document-specific extras that have no dedicated column (e.g. `first_surname`/`second_surname` on Spanish IDs, license classes on driving licenses)." - }, - "mrz": { - "type": "object", - "description": "Parsed machine-readable zone. An **empty object** (`{}`) when the document has no MRZ or it could not be read — also check the `warnings` list in that case. Dates inside the MRZ use the raw `YYMMDD` format.", - "properties": { - "surname": { - "type": "string", - "example": "FORES SENA" - }, - "name": { - "type": "string", - "example": "JULIO FRANCISCO" - }, - "country": { - "type": "string", - "description": "Issuing country (ISO 3166-1 alpha-3).", - "example": "ESP" - }, - "nationality": { - "type": "string", - "description": "Holder nationality (ISO 3166-1 alpha-3).", - "example": "ESP" - }, - "birth_date": { - "type": "string", - "description": "Raw MRZ date of birth (`YYMMDD`).", - "example": "800113" - }, - "expiry_date": { - "type": "string", - "description": "Raw MRZ expiry date (`YYMMDD`).", - "example": "320331" - }, - "sex": { - "type": "string", - "example": "M" - }, - "document_type": { - "type": "string", - "description": "MRZ document-type code (e.g. `ID`, `P`).", - "example": "ID" - }, - "document_number": { - "type": "string", - "example": "CBX164224" - }, - "optional_data": { - "type": "string", - "nullable": true, - "example": "20446581H" - }, - "optional_data_2": { - "type": "string", - "nullable": true, - "example": "" - }, - "birth_date_hash": { - "type": "string", - "description": "MRZ check digit for the birth date.", - "example": "9" - }, - "expiry_date_hash": { - "type": "string", - "description": "MRZ check digit for the expiry date.", - "example": "8" - }, - "document_number_hash": { - "type": "string", - "description": "MRZ check digit for the document number.", - "example": "3" - }, - "final_hash": { - "type": "string", - "description": "Composite MRZ check digit.", - "example": "3" - }, - "personal_number": { - "type": "string", - "nullable": true, - "example": "20446581H" - }, - "warnings": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Non-fatal MRZ parsing warnings." - }, - "errors": { - "type": "array", - "items": { - "type": "string" - }, - "description": "MRZ check-digit/parsing errors. Non-empty errors surface as an `MRZ_VALIDATION_FAILED` warning." - }, - "mrz_type": { - "type": "string", - "description": "MRZ layout: `TD1` (ID cards, 3 lines), `TD2`, or `TD3` (passports, 2 lines).", - "example": "TD1" - }, - "mrz_string": { - "type": "string", - "description": "Raw MRZ text as read, lines separated by `\\n`." - }, - "mrz_key": { - "type": "string", - "description": "Composite key (document number + check digits) used for cross-checks." - } - } + "detail": { + "type": "string" + } + } + }, + "examples": { + "Rate limited": { + "summary": "Write budget exhausted", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + } + } + } + } + } + } + }, + "/v3/travel-rule/settings/": { + "get": { + "summary": "Get Travel Rule settings", + "description": "Read the application's Travel Rule settings and VASP profile. Auto-creates a default (disabled) settings row and an empty profile on first read.", + "operationId": "getTravelRuleSettings", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "retrieve_settings", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET https://verification.didit.me/v3/travel-rule/settings/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Current Travel Rule settings and VASP profile.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleSettingsDetail" + }, + "examples": { + "Disabled (default)": { + "value": { + "is_enabled": false, + "jurisdiction": "EU", + "name_matching_strictness": "DEFAULT", + "confirmation_timeout_hours": 48, + "timeout_outcome": "HOLD", + "threshold_amount": "0.00", + "inbound_auto_accept": true, + "allow_self_declaration": true, + "allow_screenshot_proof": true, + "auto_wallet_verification": true, + "vasp_attribution_enabled": true, + "legal_name": "", + "lei": "", + "compliance_email": "", + "is_discoverable": true, + "counterparty_screening_enabled": false, + "sunrise_policy": "", + "sunrise_jurisdictions": [ + "CO", + "KH", + "VN" + ], + "non_responsive_policy": "", + "travel_address": "", + "disabled_networks": [], + "networks": { + "gtr": { + "rail": "GTR", + "mode": "unavailable", + "platform_available": false, + "connected": false }, - "parsed_address": { - "type": "object", - "nullable": true, - "description": "Structured, geocoded breakdown of `address`. `null` when the document carries no address or it could not be parsed.", - "properties": { - "street_1": { - "type": "string", - "nullable": true, - "example": "Avenida el Cardonal 52" - }, - "street_2": { - "type": "string", - "nullable": true, - "example": "b" - }, - "city": { - "type": "string", - "nullable": true, - "example": "La Laguna" - }, - "region": { - "type": "string", - "nullable": true, - "example": "Canarias" - }, - "country": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-2 country code.", - "example": "ES" - }, - "postal_code": { - "type": "string", - "nullable": true, - "example": "38108" - }, - "address_type": { - "type": "string", - "nullable": true, - "example": "Avenida" - }, - "formatted_address": { - "type": "string", - "nullable": true, - "description": "Canonical geocoded address string.", - "example": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain" - }, - "raw_results": { - "type": "object", - "additionalProperties": true, - "description": "Raw geocoding payload (`address_components`, `geometry.location`, `place_id`, …) for advanced consumers." - }, - "document_location": { - "type": "object", - "nullable": true, - "properties": { - "latitude": { - "type": "number" - }, - "longitude": { - "type": "number" - } - }, - "description": "Geocoded coordinates of the document address." - }, - "label": { - "type": "string", - "nullable": true, - "description": "Which document field the address was read from.", - "example": "Spain Identity Card Address" - }, - "is_verified": { - "type": "boolean", - "description": "Whether geocoding confirmed the address exists." - }, - "category": { - "type": "string", - "nullable": true, - "description": "Address category (e.g. `Residential`, `Commercial`).", - "example": "Residential" - } - } + "trust": { + "rail": "TRUST", + "mode": "unavailable", + "platform_available": false, + "connected": false }, - "warnings": { - "type": "array", - "description": "Empty on a clean approval. Every entry explains one detected risk; entries with `log_type: \"error\"` set `status` to `Declined`.", - "items": { - "type": "object", - "properties": { - "risk": { - "type": "string", - "description": "Machine-readable risk code. Always-decline risks: `DOCUMENT_EXPIRED`, `MINIMUM_AGE_NOT_MET`, `PORTRAIT_IMAGE_NOT_DETECTED`, `SCREEN_CAPTURE_DETECTED`, `PRINTED_COPY_DETECTED`, `PORTRAIT_MANIPULATION_DETECTED`, `PUBLIC_DOCUMENT_IMAGE_DETECTED`, `INVALID_DATE`, `COULD_NOT_RECOGNIZE_DOCUMENT` (400-only, never a warning), `DOCUMENT_NUMBER_NOT_DETECTED`, `COULD_NOT_DETECT_DOCUMENT_TYPE`, `NAME_NOT_DETECTED`, `DATE_OF_BIRTH_NOT_DETECTED`. Governed by request options: `MRZ_NOT_DETECTED` / `MRZ_VALIDATION_FAILED` (`invalid_mrz_action`), `DATA_INCONSISTENT` / `MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAME` / `DOCUMENT_NAME_DIFFERENT_FROM_OTHER_APPROVED_DOCUMENTS` (`inconsistent_data_action`), `EXPIRATION_DATE_NOT_DETECTED` (`expiration_date_not_detected_action`). Other risks (e.g. `POSSIBLE_DUPLICATED_USER`) are informational.", - "example": "DOCUMENT_EXPIRED" - }, - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION" - ], - "description": "Feature that raised the warning. Always `ID_VERIFICATION` on this endpoint." - }, - "additional_data": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Extra context for some risks (e.g. `POSSIBLE_DUPLICATED_USER` includes `duplicated_session_id`); `null` for most." - }, - "log_type": { - "type": "string", - "enum": [ - "error", - "warning", - "information" - ], - "description": "Severity. `error` warnings drive `status` to `Declined`; `information` entries are advisory and never decline on their own. (`warning` is not produced by this endpoint — its options only accept `DECLINE` or `NO_ACTION`.)" - }, - "short_description": { - "type": "string", - "description": "Human-readable one-line summary of the risk." - }, - "long_description": { - "type": "string", - "description": "Human-readable explanation of the risk." - } - } - } + "verifyvasp": { + "rail": "VERIFYVASP", + "mode": "unavailable", + "platform_available": false, + "connected": false }, - "barcodes": { - "type": "array", - "description": "Barcodes decoded from the document (PDF417, QR, …). Empty when the document has none or none could be read.", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "Barcode type (e.g. `PDF417`, `QR_CODE`, `UNKNOWN`)." - }, - "position": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Location of the barcode in the image, when available." - }, - "data": { - "type": "string", - "description": "Decoded payload." - }, - "data_raw": { - "type": "string", - "description": "Raw (undecoded) payload, when different." - }, - "side": { - "type": "string", - "nullable": true, - "description": "`front` or `back`." - } - } - } + "sygna": { + "rail": "SYGNA", + "mode": "unavailable", + "platform_available": false, + "connected": false } + }, + "didit_network": { + "listed": false, + "missing": [ + "travel_rule_off", + "legal_name_missing", + "compliance_email_missing" + ], + "compliance_email_confirmed": false + } + } + } + } + } + } + } + } + }, + "put": { + "summary": "Update Travel Rule settings", + "description": "Upsert the application's Travel Rule settings and VASP profile. Partial update — only send the fields you want to change. Setting `is_enabled: true` requires a non-blank `legal_name` (already stored or included in the same request), otherwise returns 400.", + "operationId": "updateTravelRuleSettings", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "update_settings", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleSettingsUpdate" + }, + "example": { + "is_enabled": true, + "legal_name": "Origin CASP SL", + "jurisdiction": "EU", + "name_matching_strictness": "DEFAULT", + "confirmation_timeout_hours": 48, + "timeout_outcome": "HOLD", + "is_discoverable": true + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PUT https://verification.didit.me/v3/travel-rule/settings/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"is_enabled\": true, \"legal_name\": \"Origin CASP SL\", \"jurisdiction\": \"EU\"}'" + } + ], + "responses": { + "200": { + "description": "Updated settings and profile, in the same shape as GET.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleSettingsDetail" + } + } + } + }, + "400": { + "description": "Validation error, e.g. enabling without a legal_name, a sunrise_policy or non_responsive_policy other than HOLD, REJECT, PROCEED or empty, an unknown field in `matching_rules`, or a `data_sharing_fields` choice that cannot meet the originator obligations of your jurisdiction's regime (the errors are keyed by entity type under `data_sharing_fields`).", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "example": { + "legal_name": [ + "legal_name is required to enable travel rule." + ] + } + } + } + } + } + } + }, + "/v3/travel-rule/settings/compliance-email/confirmation/": { + "post": { + "summary": "Send the compliance email confirmation", + "description": "Emails a single-use confirmation link to your VASP profile's compliance_email. Opening the link and pressing its confirm button confirms the address, which the Didit network needs before it lists your profile. A new request replaces the previous link; the link stops working when the compliance email changes. One email per minute per address.", + "operationId": "sendTravelRuleComplianceEmailConfirmation", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "send_compliance_email_confirmation", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/settings/compliance-email/confirmation/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "202": { + "description": "The confirmation email was sent.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sent_to": { + "type": "string", + "format": "email" + } + } + }, + "example": { + "sent_to": "compliance@your-vasp.example" + } + } + } + }, + "400": { + "description": "No compliance email is set, or it is already confirmed.", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "examples": { + "No compliance email": { + "value": { + "compliance_email": [ + "Set a compliance email before confirming it." + ] + } + }, + "Already confirmed": { + "value": { + "compliance_email": [ + "This compliance email is already confirmed." + ] + } + } + } + } + } + }, + "429": { + "description": "A confirmation went to this address less than a minute ago." + }, + "503": { + "description": "The email could not be sent. Nothing changed; try again in a moment." + } + } + } + }, + "/v3/travel-rule/wallet-addresses/": { + "get": { + "summary": "List wallet address book entries", + "description": "Paginated list of your application's Travel Rule wallet address book, newest first.", + "operationId": "listTravelRuleWalletAddresses", + "x-didit-sdk": { + "resource": "travel_rule.wallet_addresses", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET https://verification.didit.me/v3/travel-rule/wallet-addresses/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Paginated wallet address book entries.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WalletAddressEntry" } + } + } + } + } + } + } + } + }, + "post": { + "summary": "Register a wallet address", + "description": "Add an entry to your Travel Rule wallet address book so inbound INTERNAL-rail transfers can resolve against it.", + "operationId": "createTravelRuleWalletAddress", + "x-didit-sdk": { + "resource": "travel_rule.wallet_addresses", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WalletAddressEntryCreate" + }, + "example": { + "address": "0xBeneficiaryWallet01", + "chain": "ETH", + "holder_name": "Ana Diaz", + "holder_vendor_data": "user-042", + "self_declared": true + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/wallet-addresses/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"address\": \"0xBeneficiaryWallet01\", \"chain\": \"ETH\", \"holder_name\": \"Ana Diaz\", \"self_declared\": true}'" + } + ], + "responses": { + "201": { + "description": "The created entry.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WalletAddressEntry" + } + } + } + }, + "400": { + "description": "Duplicate wallet (address, chain and memo) for this application, a memo or X-address the chain cannot carry, a missing `address`, an invalid `travel_address`, `self_declared: true` while `allow_self_declaration` is off, or `self_declared: true` while `ownership_validation_mode` is `FULL`.", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "examples": { + "Duplicate entry": { + "value": { + "address": [ + "A wallet address entry with this address and chain already exists." + ] + } + }, + "Invalid travel address": { + "value": { + "travel_address": [ + "Invalid travel address." + ] + } + }, + "Self-declaration disabled": { + "value": { + "self_declared": [ + "Self-declared ownership is disabled for this application." + ] + } + } + } + } + } + } + } + } + }, + "/v3/travel-rule/wallet-addresses/{entry_uuid}/": { + "patch": { + "summary": "Update wallet address holder metadata", + "description": "Partial update of holder_name, holder_vendor_data, entity_type, and travel_address. Setting travel_address attaches a TRP routing address to the entry; invalid values are rejected with 400. The response echoes only these four fields.", + "operationId": "updateTravelRuleWalletAddress", + "x-didit-sdk": { + "resource": "travel_rule.wallet_addresses", + "method": "update", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "entry_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WalletAddressEntryUpdate" + }, + "example": { + "holder_name": "Ana Updated", + "travel_address": "ta2ih1MicegQ3JqiKz9xT89KsSuwaDJYw8DgzmeSEp1h9tduJcNDZ5SgKLEHeyGMcS1s2HgbM6undFrMQknhM5aTXw3G9KBzi16hx" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH https://verification.didit.me/v3/travel-rule/wallet-addresses/{entry_uuid}/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"holder_name\": \"Ana Updated\", \"travel_address\": \"ta2ih1MicegQ3JqiKz9xT89KsSuwaDJYw8DgzmeSEp1h9tduJcNDZ5SgKLEHeyGMcS1s2HgbM6undFrMQknhM5aTXw3G9KBzi16hx\"}'" + } + ], + "responses": { + "200": { + "description": "The updated fields.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WalletAddressEntryUpdate" + } + } + } + }, + "400": { + "description": "`travel_address` is not a valid travel address.", + "content": { + "application/json": { + "examples": { + "Invalid travel address": { + "value": { + "travel_address": [ + "Invalid travel address." + ] + } + } + } + } + } + }, + "404": { + "description": "Unknown or deleted entry." + } + } + }, + "delete": { + "summary": "Delete a wallet address", + "description": "Deletes the entry together with its ownership proofs and the screenshots they stored. It stops matching new transfers immediately and the address is freed up for re-registration. Transfers that already used the wallet keep their own record of it and read the same. Deletion is irreversible.", + "operationId": "deleteTravelRuleWalletAddress", + "x-didit-sdk": { + "resource": "travel_rule.wallet_addresses", + "method": "delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "entry_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE https://verification.didit.me/v3/travel-rule/wallet-addresses/{entry_uuid}/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "204": { + "description": "Deleted." + }, + "404": { + "description": "Unknown or already deleted entry." + }, + "503": { + "description": "`wallet_address_deletion_incomplete`: a stored file could not be deleted, so the deletion stopped before it finished. The address-book entry is still there; retry the same request to finish it.", + "content": { + "application/json": { + "example": { + "detail": "A stored file could not be deleted, so the deletion stopped before it finished. Try again.", + "code": "wallet_address_deletion_incomplete" + } + } + } + } + } + } + }, + "/v3/travel-rule/vasps/": { + "get": { + "summary": "Search the VASP directory", + "description": "Paginated, name-sorted search of the VASP directory: the shared directory entries, which Didit builds from the ESMA interim MiCA register, the public TRISA directory, the global LEI register and its own curation, plus the members of the Didit network. Each row carries the routes your application has to it today. Your own application's profile is never listed. Rows also carry the shared due-diligence assessment and your application's own decision, entity screening and questionnaires.", + "operationId": "listTravelRuleVasps", + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "search", + "in": "query", + "schema": { + "type": "string", + "maxLength": 255 + }, + "description": "A name or alias fragment (case-insensitive), an exact LEI, or a domain (example.com or a URL on it). Didit network members match on their legal name, LEI or the domain of their compliance email." + }, + { + "name": "network", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA", + "TRISA" + ] + }, + "description": "Only directory entries a network's member directory currently lists." + }, + { + "name": "jurisdiction", + "in": "query", + "schema": { + "type": "string" + }, + "description": "ISO 3166 alpha-2 or alpha-3 country code. 400 for anything else." + }, + { + "name": "reachable", + "in": "query", + "schema": { + "type": "boolean" + }, + "description": "true: only VASPs your application has an available route to (Didit network members included). false: only directory entries it cannot reach today." + }, + { + "name": "kind", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "directory", + "didit" + ] + }, + "description": "Only directory entries, or only Didit network members." + }, + { + "name": "risk", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, + "description": "Only directory entries whose risk_level is this band. Didit network members, which are not scored, never match." + }, + { + "name": "responsiveness", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "RESPONSIVE", + "UNRESPONSIVE", + "UNKNOWN" + ] + }, + "description": "Only directory entries whose responsiveness.status is this value. Didit network members never match." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/travel-rule/vasps/?search=counterparty&reachable=true' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Paginated, name-sorted VASP directory results.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" }, - "vendor_data": { + "next": { "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent, or `null`." + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VaspDirectoryEntry" + } + } + } + } + } + } + }, + "400": { + "description": "A filter is not valid: the body names each field, for example an unknown network, country code, risk band or responsiveness status.", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "example": { + "jurisdiction": [ + "Use an ISO 3166 alpha-2 or alpha-3 country code." + ], + "network": [ + "\"SWIFT\" is not a valid choice." + ] + } + } + } + } + }, + "x-didit-sdk": { + "resource": "travel_rule", + "method": "list_vasps", + "auth": "api_key", + "server": true + } + } + }, + "/v3/travel-rule/vasps/{vasp_id}/": { + "get": { + "summary": "Get a VASP directory entry", + "description": "One directory entry or Didit network member by the id the search returned, with the same fields and the routes your application has to it.", + "operationId": "getTravelRuleVasp", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "retrieve_vasp", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "vasp_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The id of a row of GET /v3/travel-rule/vasps/." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET https://verification.didit.me/v3/travel-rule/vasps/{vasp_id}/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "The entry.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VaspDirectoryEntry" + } + } + } + }, + "404": { + "description": "No entry or listed Didit network member has this id.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "No directory entry has this id." + } + } + } + } + } + } + }, + "/v3/travel-rule/vasps/{vasp_id}/decision/": { + "get": { + "summary": "Get your decision on a VASP", + "description": "Your application's decision on a counterparty (a directory entry or a Didit network member): approved, restricted or blocked. An entity of a brand with no decision of its own follows the brand's decision, named in inherited_from. Private to your application.", + "operationId": "getTravelRuleVaspDecision", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "retrieve_vasp_decision", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "vasp_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET https://verification.didit.me/v3/travel-rule/vasps/{vasp_id}/decision/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "The decision that applies, or null.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyDecisionResponse" + }, + "examples": { + "Own decision": { + "value": { + "decision": { + "decision": "APPROVED", + "reason": "Licensed in DE, questionnaire reviewed", + "decided_by": "", + "updated_at": "2026-10-14T10:05:00Z", + "inherited_from": null + } + } + }, + "Inherited from the brand": { + "value": { + "decision": { + "decision": "BLOCKED", + "reason": "Group refused after review", + "decided_by": "", + "updated_at": "2026-10-14T10:05:00Z", + "inherited_from": { + "id": "5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b", + "name": "Example Group" + } + } + } + }, + "No decision": { + "value": { + "decision": null + } + } + } + } + } + }, + "404": { + "description": "No directory entry or listed Didit network member has this id. Your own VASP profile is never a counterparty of yours.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "No directory entry or Didit network member has this id." + } + } + } + } + } + }, + "put": { + "summary": "Set your decision on a VASP", + "description": "Make or change your application's decision on a counterparty: APPROVED, RESTRICTED or BLOCKED, with an optional reason. It never changes the shared directory entry and acts only through your transaction rules (counterparty_dd_status). A decision on a brand also covers each of its entities that has no decision of its own.", + "operationId": "updateTravelRuleVaspDecision", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "update_vasp_decision", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "vasp_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyDecisionRequest" + }, + "example": { + "decision": "BLOCKED", + "reason": "Unlicensed in its home jurisdiction" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PUT https://verification.didit.me/v3/travel-rule/vasps/{vasp_id}/decision/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"decision\": \"BLOCKED\", \"reason\": \"Unlicensed in its home jurisdiction\"}'" + } + ], + "responses": { + "200": { + "description": "The decision as it now applies.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyDecisionResponse" + }, + "example": { + "decision": { + "decision": "BLOCKED", + "reason": "Unlicensed in its home jurisdiction", + "decided_by": "", + "updated_at": "2026-10-14T10:05:00Z", + "inherited_from": null + } + } + } + } + }, + "400": { + "description": "The decision is missing or not one of APPROVED, RESTRICTED or BLOCKED, or the reason is longer than 255 characters.", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "example": { + "decision": [ + "\"MAYBE\" is not a valid choice." + ] + } + } + } + }, + "404": { + "description": "No directory entry or listed Didit network member has this id. Your own VASP profile is never a counterparty of yours.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "No directory entry or Didit network member has this id." + } + } + } + } + } + }, + "delete": { + "summary": "Clear your decision on a VASP", + "description": "Clear your application's own decision on a counterparty. The response shows the decision that applies afterwards: null, or for an entity of a brand the brand's decision, which the entity follows again.", + "operationId": "clearTravelRuleVaspDecision", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "clear_vasp_decision", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "vasp_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE https://verification.didit.me/v3/travel-rule/vasps/{vasp_id}/decision/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "The decision cleared; the body shows the decision that applies now.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyDecisionResponse" + }, + "example": { + "decision": null + } + } + } + }, + "404": { + "description": "No counterparty has this id, or your application has no decision of its own on it (a decision it only inherits from its brand cannot be cleared here: clear the brand's).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Unknown id": { + "value": { + "detail": "No directory entry or Didit network member has this id." + } + }, + "No decision of its own": { + "value": { + "detail": "This application has no decision of its own on this counterparty." + } + } + } + } + } + } + } + } + }, + "/v3/travel-rule/vasps/{vasp_id}/screening/": { + "get": { + "summary": "Get a VASP's entity screening", + "description": "Whether counterparty screening is on for your application, what one screening costs your organization, how long a result stays fresh, and your latest sanctions and adverse-media screening of the counterparty. Reading it is free.", + "operationId": "getTravelRuleVaspScreening", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "retrieve_vasp_screening", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "vasp_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET https://verification.didit.me/v3/travel-rule/vasps/{vasp_id}/screening/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Your screening setting, its price and your latest screening.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyScreeningStatus" + }, + "example": { + "enabled": true, + "price": "0.20", + "fresh_days": 30, + "screening": { + "id": "7a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d", + "status": "Approved", + "total_hits": 0, + "score": null, + "screened_at": "2026-10-14T09:58:00Z", + "fresh": true, + "flagged": false + } + } + } + } + }, + "403": { + "description": "Your organization's contract has no rate configured for counterparty screening.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "No contract rate is configured for this usage type." + } + } + } + }, + "404": { + "description": "No directory entry or listed Didit network member has this id. Your own VASP profile is never a counterparty of yours.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "No directory entry or Didit network member has this id." + } + } + } + } + } + }, + "post": { + "summary": "Screen a VASP", + "description": "Screen the counterparty entity against sanctions and adverse media for your application. Opt-in: counterparty_screening_enabled must be on in your Travel Rule settings. Each new screening is billed at your organization's Company AML Screening price; a screening younger than 30 days is returned instead (200, not billed) unless you send refresh: true. One screening of a counterparty runs at a time per application. The result is private to your application. Sandbox applications are never screened.", + "operationId": "screenTravelRuleVasp", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "screen_vasp", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "vasp_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyScreeningRequest" + }, + "example": { + "refresh": false + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/vasps/{vasp_id}/screening/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"refresh\": false}'" + } + ], + "responses": { + "200": { + "description": "A fresh screening already existed and was returned. Nothing was billed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyScreeningResult" + }, + "example": { + "outcome": "reused", + "screening": { + "id": "7a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d", + "status": "Approved", + "total_hits": 0, + "score": null, + "screened_at": "2026-10-14T09:58:00Z", + "fresh": true, + "flagged": false + } + } + } + } + }, + "201": { + "description": "A new screening ran and was billed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyScreeningResult" + }, + "example": { + "outcome": "screened", + "screening": { + "id": "7a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d", + "status": "In Review", + "total_hits": 2, + "score": 85.0, + "screened_at": "2026-10-14T09:58:00Z", + "fresh": true, + "flagged": true + } + } + } + } + }, + "400": { + "description": "The body is not valid, or the counterparty has no name to screen.", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "example": { + "detail": [ + "This counterparty has no name to screen." + ] + } + } + } + }, + "403": { + "description": "Your organization's balance cannot cover the screening (code insufficient_credits; nothing ran and nothing was billed), or your contract has no rate configured for it.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" }, - "metadata": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Echo of the `metadata` object you sent, or `null`." + "code": { + "type": "string" + } + } + }, + "example": { + "detail": "You don't have enough credits to perform this request. Please top up at https://business.didit.me", + "code": "insufficient_credits" + } + } + } + }, + "404": { + "description": "No directory entry or listed Didit network member has this id. Your own VASP profile is never a counterparty of yours.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "No directory entry or Didit network member has this id." + } + } + } + }, + "409": { + "description": "Nothing ran and nothing was billed: counterparty screening is off for your application, the application is a sandbox one, or a screening of this counterparty is already running.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Screening off": { + "value": { + "detail": "Counterparty screening is turned off. Enable it in the Travel Rule settings first." + } + }, + "Sandbox application": { + "value": { + "detail": "Counterparty screening does not run for sandbox applications." + } + }, + "Already running": { + "value": { + "detail": "A screening of this counterparty is already running. Try again in a moment." + } + } + } + } + } + }, + "502": { + "description": "The screening could not be completed. Nothing was billed; try again in a moment.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "The screening provider did not answer. Try again in a moment." + } + } + } + } + } + } + }, + "/v3/travel-rule/vasps/{vasp_id}/questionnaires/": { + "get": { + "summary": "List due-diligence questionnaires with a VASP", + "description": "Every due-diligence questionnaire your application sent the counterparty, newest first, with its status and, once answered, the answers. Private to your application.", + "operationId": "listTravelRuleVaspQuestionnaires", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "list_vasp_questionnaires", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "vasp_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET https://verification.didit.me/v3/travel-rule/vasps/{vasp_id}/questionnaires/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Your questionnaires with this counterparty.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyQuestionnaireList" + }, + "example": { + "questionnaires": [ + { + "id": "3f9e8d7c-6b5a-4c3d-9e2f-1a0b9c8d7e6f", + "status": "SENT", + "form_version": "1", + "sent_to": "travelrule@counterparty.example", + "sent_at": "2026-10-14T10:00:00Z", + "expires_at": "2026-11-13T10:00:00Z", + "answered_at": null, + "valid_until": null, + "current": false, + "answers": {} }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp (UTC) of when the response was generated." + { + "id": "8b7a6c5d-4e3f-4a2b-9c1d-0e9f8a7b6c5d", + "status": "ANSWERED", + "form_version": "1", + "sent_to": "travelrule@counterparty.example", + "sent_at": "2026-09-02T08:00:00Z", + "expires_at": "2026-10-02T08:00:00Z", + "answered_at": "2026-09-04T15:20:00Z", + "valid_until": "2027-09-04T15:20:00Z", + "current": true, + "answers": { + "legal_name": "Example Counterparty VASP, incorporated in the example jurisdiction.", + "licensing": "Crypto-asset service provider authorised under MiCA by its national regulator, licence active.", + "jurisdictions": "EU and EEA. We do not serve the United States.", + "aml_program": "Written AML/CFT policy, annual independent review, last completed in March 2026.", + "compliance_officer": "Head of Compliance, reachable at compliance@counterparty.example.", + "sanctions_screening": "Customers at onboarding and daily; counterparties on every transfer.", + "transaction_monitoring": "Rule-based monitoring with analyst review; suspicious activity reports filed with the FIU.", + "travel_rule": "TRP and email; we answer requests within one business day.", + "custody": "Segregated customer wallets held with a qualified custodian, insured.", + "ownership": "No shareholder holds 25 percent or more; parent company Example Holding Group.", + "record_keeping": "Five-year record-keeping for Travel Rule and transaction data, as our regulator requires.", + "data_protection": "Encrypted at rest and in transit, stored in the EU." + } + } + ] + } + } + } + }, + "404": { + "description": "No directory entry or listed Didit network member has this id. Your own VASP profile is never a counterparty of yours.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "No directory entry or Didit network member has this id." + } + } + } + } + } + }, + "post": { + "summary": "Send a due-diligence questionnaire to a VASP", + "description": "Email Didit's standard due-diligence questionnaire to a compliance address on record for the counterparty, with a single-use answer link. The counterparty has 30 days to answer every question; the answers stay current for 365 days. A new send replaces your earlier unanswered questionnaire to the same counterparty, whose link stops working. One email per minute per address.", + "operationId": "sendTravelRuleVaspQuestionnaire", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "send_vasp_questionnaire", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "vasp_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyQuestionnaireRequest" + }, + "example": { + "sent_to": "travelrule@counterparty.example" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/vasps/{vasp_id}/questionnaires/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"sent_to\": \"travelrule@counterparty.example\"}'" + } + ], + "responses": { + "202": { + "description": "The questionnaire was emailed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyQuestionnaire" + }, + "example": { + "id": "3f9e8d7c-6b5a-4c3d-9e2f-1a0b9c8d7e6f", + "status": "SENT", + "form_version": "1", + "sent_to": "travelrule@counterparty.example", + "sent_at": "2026-10-14T10:00:00Z", + "expires_at": "2026-11-13T10:00:00Z", + "answered_at": null, + "valid_until": null, + "current": false, + "answers": {} + } + } + } + }, + "400": { + "description": "sent_to is not a valid email address or not a compliance address on record for the counterparty, or the counterparty has no compliance address on record.", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "examples": { + "Not on record": { + "value": { + "sent_to": [ + "Send the questionnaire to a compliance address on record for this counterparty." + ] + } + }, + "No address": { + "value": { + "sent_to": [ + "No compliance address is on record for this counterparty. Add a contact for it first." + ] + } + } + } + } + } + }, + "404": { + "description": "No directory entry or listed Didit network member has this id. Your own VASP profile is never a counterparty of yours.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "No directory entry or Didit network member has this id." + } + } + } + }, + "429": { + "description": "A questionnaire went to this address less than a minute ago." + }, + "503": { + "description": "The email could not be sent and no questionnaire was created. Try again in a moment." + } + } + } + }, + "/v3/transactions/{transaction_id}/travel-rule/": { + "patch": { + "summary": "Finish, cancel, resend, or update the originator wallet of a Travel Rule transfer", + "description": "Provide `payment_txn_id` to report the on-chain hash and move an outbound COMPLETED transfer to FINISHED (its INTERNAL-rail counterpart also moves to FINISHED), `{\"action\": \"cancel\"}` to cancel a non-terminal transfer, or `{\"action\": \"resend\"}` to route an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA again (improve the counterparty data first, e.g. add a travel_address to the destination wallet entry). resend applies to outbound transfers only. `{\"action\": \"update_compliance_email\", \"compliance_email\": \"desk@example.com\"}` replaces the compliance email of an outbound transfer in one of those three statuses, the fix for `reason: email_undeliverable`: the status does not change, and the next `resend` sends the request to that address. A resend is queued, not run inside the request: the response shows the transfer back in AWAITING_COUNTERPARTY with a `resend queued` timeline entry and a queued `delivery`, and where it went arrives as a `travel_rule.status.updated` webhook. When the transfer went over GTR, VerifyVASP or Sygna Bridge, the network learns the on-chain hash or the cancellation after the response, with retries; `delivery` shows how that message stands. `{\"action\": \"update_originator_wallet\", \"wallet_address\": \"...\"}` records the wallet the funds actually left from on an outbound transfer, for example the address a UTXO withdrawal was broadcast from rather than the main wallet you declared. It changes the record only: nothing is sent to the counterparty again and the status stays. The originator becomes `chain_details.originator` with `source: updated`, the change is added to the `timeline` with the previous and the new wallet, and the INTERNAL-rail counterpart of the transfer is updated the same way.", + "operationId": "patchTravelRuleTransfer", + "x-didit-sdk": { + "resource": "transactions.travel_rule", + "method": "update_transfer", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "transaction_id", + "in": "path", + "required": true, + "description": "Didit-stable transaction UUID that owns the transfer.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleTransferPatchRequest" + }, + "examples": { + "Finish": { + "value": { + "payment_txn_id": "0xchainhash" + } + }, + "Cancel": { + "value": { + "action": "cancel" + } + }, + "Resend": { + "value": { + "action": "resend" + } + }, + "Update compliance email": { + "value": { + "action": "update_compliance_email", + "compliance_email": "desk@example.com" + } + }, + "Update originator wallet": { + "value": { + "action": "update_originator_wallet", + "wallet_address": "bc1qOriginatorInputAddress01" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH https://verification.didit.me/v3/transactions/{transaction_id}/travel-rule/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"payment_txn_id\": \"0xchainhash\"}'" + } + ], + "responses": { + "200": { + "description": "The updated transfer. After `update_compliance_email` its status is unchanged and it keeps `reason: email_undeliverable` until the next `resend`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleTransferDetail" + } + } + } + }, + "400": { + "description": "Neither payment_txn_id nor a supported action was provided, `compliance_email` is missing or not an email address on `update_compliance_email`, or, for `update_originator_wallet`, no wallet_address, a wallet or memo the transfer's chain cannot carry, or a wallet that is not one of the transfer's UTXO inputs.", + "content": { + "application/json": { + "examples": { + "No action": { + "value": { + "detail": [ + "Provide payment_txn_id, action=cancel, action=resend, action=beneficiary_vasp, action=update_originator_wallet or action=update_compliance_email." + ] + } + }, + "Invalid compliance email": { + "value": { + "compliance_email": [ + "Enter a valid email address." + ] + } + } + } + } + } + }, + "404": { + "description": "Unknown transaction, or the transaction has no Travel Rule transfer." + }, + "409": { + "description": "The transfer is not in a state that allows this operation (e.g. not COMPLETED/outbound for finish, already terminal for cancel, or not an outbound transfer in a resendable status for resend). `update_compliance_email` answers `409` for an inbound transfer, and for an outbound one outside COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE and NOT_ENOUGH_COUNTERPARTY_DATA. `update_originator_wallet` answers `409` for an inbound, cancelled or expired transfer.", + "content": { + "application/json": { + "examples": { + "Cancel from a terminal status": { + "value": { + "detail": "Transfer is already in a terminal state." + } + }, + "Resend not allowed": { + "value": { + "detail": "Transfer cannot be resent from its current state." + } + }, + "Finish not allowed": { + "value": { + "detail": "Transfer must be an outbound transfer in COMPLETED status to finish." + } + }, + "Compliance email cannot change": { + "value": { + "detail": "The compliance email of this transfer cannot change from its current state." } } } } } + } + } + } + }, + "/v3/transactions/{transaction_id}/travel-rule/ownership/": { + "post": { + "summary": "Confirm or deny wallet ownership", + "description": "Resolve a transfer that waits on wallet ownership: call it on your own side of the transfer when it is UNCONFIRMED_OWNERSHIP, or when it is an inbound transfer in ON_HOLD. Confirming marks the matching wallet address book entry as ownership-verified (for an inbound ON_HOLD transfer with no entry, Didit creates one from beneficiary_data.wallet_address) and runs the beneficiary name match on both sides of the transfer; denying declines both sides. An inbound transfer whose deposit already settled moves on to FINISHED when the match succeeds. When the counterparty wallet was declared self-hosted (counterparty.payment_method.method_type: unhosted_wallet), confirming records that the customer verified who controls that wallet and completes the transfer (COMPLETED) in both directions, or FINISHED for an inbound deposit whose on-chain hash is already known. The wallet verified is the counterparty's: the destination of an outbound withdrawal, or the source (originator's wallet) of an inbound deposit. If it is not in your wallet address book yet, Didit adds it as ownership-verified under the counterparty's name; if the book already holds it under a different holder name, the transfer moves to COUNTERPARTY_MISMATCHED_DATA. For an inbound TRP transfer in UNCONFIRMED_OWNERSHIP, ON_HOLD or COUNTERPARTY_MISMATCHED_DATA, Didit sends your decision to the originating VASP instead: approving returns the payment address (wallet_address, or the matched entry's address) and moves the transfer to COMPLETED, and denying declines it. The same call also decides an inbound transfer that a GTR or Sygna Bridge counterparty sent and that waits for your review (`UNCONFIRMED_OWNERSHIP`, `ON_HOLD` or `COUNTERPARTY_MISMATCHED_DATA`): `confirmed: true` approves it (pass `wallet_address` when the transfer names none) and `confirmed: false` declines it, and the network is answered after the response, with retries. VerifyVASP takes its answer when it asks, so a VerifyVASP transfer that would need your review is declined at once and cannot be decided later.", + "operationId": "confirmTravelRuleOwnership", + "x-didit-sdk": { + "resource": "transactions.travel_rule", + "method": "confirm_ownership", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "transaction_id", + "in": "path", + "required": true, + "description": "Didit-stable UUID of the transaction your application holds for this transfer: the beneficiary side's transaction on an INTERNAL-rail transfer, or the transaction you created (outbound or inbound) when the counterparty wallet was declared self-hosted.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleOwnershipConfirmRequest" + }, + "example": { + "confirmed": true + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/transactions/{transaction_id}/travel-rule/ownership/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"confirmed\": true}'" + } + ], + "responses": { + "200": { + "description": "The updated transfer. For an inbound transfer the counterparty's originator_data/beneficiary_data stay masked (`data_masked: true`) until ownership is confirmed, then `data_unmasked` records when, why and by whom.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleTransferDetail" + } + } + } }, - "400": { - "description": "Validation or processing error. Field-level problems return DRF's standard envelope (one array of messages per offending field). Encrypted-PDF problems return `{\"detail\": ...}`. A document image in which no document can be recognized at all returns `{\"error\": \"COULD_NOT_RECOGNIZE_DOCUMENT\"}`. PDF uploads can additionally fail with \"The submitted PDF file is corrupted or invalid.\" or a no-pages error.", + "409": { + "description": "The transfer is not waiting on ownership confirmation, no wallet address book entry matches its counterparty wallet, an INTERNAL transfer from another Didit customer needs a control proof (a signed message or a test deposit) before it can be confirmed, or a network transfer was already answered over its network (a VerifyVASP transfer, or a GTR or Sygna Bridge transfer you or Didit already decided).", "content": { "application/json": { "examples": { - "Missing front image": { - "summary": "`front_image` not included in the form data", + "Not awaiting confirmation": { "value": { - "front_image": [ - "No file was submitted." - ] + "detail": "Transfer is not awaiting ownership confirmation." } }, - "Unsupported file extension": { - "summary": "File extension outside tiff/jpg/jpeg/png/webp/pdf", + "No matching address book entry": { "value": { - "front_image": [ - "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp, pdf." - ] + "detail": "wallet address entry no longer available" } - }, - "File too large": { - "summary": "Upload exceeds the 10 MB limit", + } + } + } + } + }, + "400": { + "description": "Validation error: confirmed is missing, an inbound TRP approval has no wallet_address and no address to fall back on, or `confirmed: true` on a declared self-hosted wallet transfer that names no counterparty wallet and no `wallet_address` was sent.", + "content": { + "application/json": { + "examples": { + "No payment address": { "value": { - "front_image": [ - "File size should not exceed 10 MB" + "wallet_address": [ + "wallet_address is required to approve." ] } - }, - "Invalid option value": { - "summary": "Options only accept DECLINE or NO_ACTION", + } + } + } + } + }, + "404": { + "description": "Unknown transaction, or the transaction has no Travel Rule transfer." + }, + "502": { + "description": "Inbound TRP transfer only: Didit could not deliver your decision to the originating VASP, so the transfer status does not change.", + "content": { + "application/json": { + "examples": { + "Callback not delivered": { "value": { - "invalid_mrz_action": [ - "\"REVIEW\" is not a valid choice." - ] + "detail": "Counterparty inquiry-resolution callback could not be delivered." } - }, - "Encrypted PDF": { - "summary": "PDF uploaded without the required password", - "value": { - "detail": "The PDF is encrypted. Please upload a decrypted PDF or a photo instead." + } + } + } + } + } + } + } + }, + "/v3/transactions/{transaction_id}/travel-rule/messages/": { + "get": { + "summary": "List Travel Rule messages", + "description": "Messages and document requests exchanged with the counterparty VASP about this transfer.", + "operationId": "listTravelRuleMessages", + "x-didit-sdk": { + "resource": "transactions.travel_rule", + "method": "list_messages", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "transaction_id", + "in": "path", + "required": true, + "description": "Didit-stable transaction UUID that owns the transfer.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET https://verification.didit.me/v3/transactions/{transaction_id}/travel-rule/messages/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "The messages, oldest first.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TravelRuleMessage" + } + } + } + } + }, + "404": { + "description": "Unknown transaction or token, or no Travel Rule transfer on the transaction." + } + } + }, + "post": { + "summary": "Send a Travel Rule message or document request", + "description": "Sends a message or a document request to the counterparty VASP. Works on INTERNAL transfers (the other Didit customer sees it on its side of the pair) and EMAIL transfers (the counterparty is emailed and answers in the pickup portal). A transfer over TRP, GTR, TRUST, VerifyVASP or Sygna carries no messages, because those protocols have none. Limits: 100 messages per transfer, 5000 characters per body, 10 requested documents, 5 files of 10 MB each.", + "operationId": "createTravelRuleMessage", + "x-didit-sdk": { + "resource": "transactions.travel_rule", + "method": "create_message", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "transaction_id", + "in": "path", + "required": true, + "description": "Didit-stable transaction UUID that owns the transfer.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleMessageCreateRequest" + }, + "example": { + "kind": "DOCUMENT_REQUEST", + "body": "Please confirm the date of birth of the beneficiary.", + "requested_documents": [ + "Proof of address" + ] + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/TravelRuleMessageCreateRequest" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/transactions/{transaction_id}/travel-rule/messages/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"kind\": \"DOCUMENT_REQUEST\", \"body\": \"Please confirm the date of birth of the beneficiary.\", \"requested_documents\": [\"Proof of address\"]}'" + } + ], + "responses": { + "201": { + "description": "The stored message.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleMessage" + } + } + } + }, + "400": { + "description": "Invalid body, a file type or size that is not accepted, more than 100 messages on the transfer, or a `reply_to` that is not a message of this transfer.", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "example": { + "requested_documents": [ + "Name at least one document the counterparty should send." + ] + } + } + } + }, + "404": { + "description": "Unknown transaction or token, or no Travel Rule transfer on the transaction." + }, + "409": { + "description": "No message can reach the counterparty. `detail` gives the reason: the transfer runs over a network rail (TRP, GTR, TRUST, VerifyVASP or Sygna), it has no counterparty yet, an INTERNAL transfer's data did not match the wallet holder so nothing reached the recipient, or an EMAIL transfer's pickup link has expired or the transfer is closed.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" } + } + } + } + } + } + } + } + }, + "/v3/travel-rule/pickup/{token}/": { + "get": { + "summary": "Get email-rail pickup info", + "description": "Public, unauthenticated. Lets a non-Didit counterparty VASP preview the pending exchange from the emailed pickup link. The token itself is the authentication.", + "operationId": "getTravelRulePickup", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "retrieve_pickup", + "auth": "public", + "server": false + }, + "tags": [ + "Travel Rule" + ], + "security": [ + {} + ], + "parameters": [ + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "contact", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "The link's `?contact=` value, the contact the link was sent to. Echoed back as `contact` unchanged; it is not validated." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl https://verification.didit.me/v3/travel-rule/pickup/{token}/" + } + ], + "responses": { + "200": { + "description": "Pickup preview.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRulePickupInfo" + }, + "example": { + "reference": "33333333-4444-5555-6666-777777777777", + "requested_at": "2026-07-05T10:00:00Z", + "originator_vasp_name": "Origin CASP SL", + "originator_vasp_lei": "529900EXAMPLEORIG001", + "recipient_vasp_name": "Example Counterparty VASP", + "language": "es", + "amount": "0.250000000000000000", + "currency": "ETH", + "fiat_amount": "", + "fiat_currency": "", + "chain": "ETH", + "beneficiary_wallet": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb", + "status": "AWAITING_COUNTERPARTY", + "ivms_version": "ivms101.2023", + "deadline_at": "2026-07-07T10:00:00Z", + "reminders_sent": 0, + "contact": "", + "decline_reasons": [ + "data_mismatch", + "not_our_customer", + "wallet_not_hosted", + "sanctions", + "risk_appetite", + "jurisdiction", + "insufficient_data", + "other" + ], + "join_invitation": { + "eligible": false, + "url": "" }, - "Wrong PDF password": { - "summary": "`front_image_password` / `back_image_password` does not decrypt the PDF", - "value": { - "detail": "The PDF password is incorrect. Please provide the correct password." + "originator_data": { + "name": "Jane Doe", + "wallet_address": "0x77ccff0cc624a53cd8b8d8703ced826710211ec7" + }, + "beneficiary_data": { + "name": "Ana Diaz", + "wallet_address": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb" + }, + "ivms_request": { + "originator": { + "originatorPersons": [ + { + "naturalPerson": { + "name": { + "nameIdentifier": [ + { + "primaryIdentifier": "Doe", + "secondaryIdentifier": "Jane", + "nameIdentifierType": "LEGL" + } + ] + } + } + } + ], + "accountNumber": [ + "0x77ccff0cc624a53cd8b8d8703ced826710211ec7" + ] + }, + "beneficiary": { + "beneficiaryPersons": [ + { + "naturalPerson": { + "name": { + "nameIdentifier": [ + { + "primaryIdentifier": "Diaz", + "secondaryIdentifier": "Ana", + "nameIdentifierType": "LEGL" + } + ] + } + } + } + ], + "accountNumber": [ + "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb" + ] } }, - "Document not recognized": { - "summary": "No identity document could be detected in the image", + "data_masked": false + } + } + } + }, + "404": { + "description": "Unknown token." + }, + "410": { + "description": "The window has closed: the counterparty already answered, the transfer left AWAITING_COUNTERPARTY, or the deadline passed." + } + } + } + }, + "/v3/travel-rule/pickup/{token}/respond/": { + "post": { + "summary": "Respond to an email-rail pickup", + "description": "Public, unauthenticated. Accepts or declines the exchange from the emailed pickup link. On accept, beneficiary_name is checked against the originator's name_matching_strictness policy.", + "operationId": "respondTravelRulePickup", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "respond_pickup", + "auth": "public", + "server": false + }, + "tags": [ + "Travel Rule" + ], + "security": [ + {} + ], + "parameters": [ + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "contact", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "The link's `?contact=` value. Used when the body sends no `contact`." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRulePickupRespondRequest" + }, + "example": { + "decision": "accept", + "beneficiary_name": "Carlos Ruiz" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/pickup/{token}/respond/ \\\n -H 'Content-Type: application/json' \\\n -d '{\"decision\": \"accept\", \"beneficiary_name\": \"Carlos Ruiz\"}'" + } + ], + "responses": { + "200": { + "description": "The resulting exchange status: COMPLETED or COUNTERPARTY_MISMATCHED_DATA after an accept, COUNTERPARTY_VASP_GENERAL_DECLINE after a decline, which also echoes `decline_reason`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "decline_reason": { + "type": "string", + "description": "Present after a decline." + } + } + }, + "examples": { + "accepted": { "value": { - "error": "COULD_NOT_RECOGNIZE_DOCUMENT" + "status": "COMPLETED" } }, - "PDF conversion failure": { - "summary": "Uploaded PDF could not be converted to an image", + "declined": { "value": { - "error": "Failed to convert front image PDF." + "status": "COUNTERPARTY_VASP_GENERAL_DECLINE", + "decline_reason": "not_our_customer" } } } } } }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", + "400": { + "description": "A field is invalid: for example `decision` missing or not `accept` or `decline`, `decline_detail` missing on a decline with `decline_reason: other`, an `ivms_response` that is not an IVMS 101 object or is longer than 16384 characters, or a `contact` in the body that is not a UUID (`{\"contact\": [\"Must be a valid UUID.\"]}`).", + "content": { + "application/json": { + "example": { + "decline_detail": [ + "Say what the reason is when you choose another reason." + ] + } + } + } + }, + "404": { + "description": "Unknown token." + }, + "410": { + "description": "The window has closed: the counterparty already answered, the transfer left AWAITING_COUNTERPARTY, or the deadline passed." + } + } + } + }, + "/v3/travel-rule/pickup/{token}/messages/": { + "get": { + "summary": "List pickup messages", + "description": "Public, unauthenticated. The messages of an EMAIL-rail transfer as the counterparty VASP reads them: what the customer sent is `INBOUND` and is signed with the customer's VASP name. The token is the authentication.", + "operationId": "listTravelRulePickupMessages", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "list_pickup_messages", + "auth": "public", + "server": false + }, + "tags": [ + "Travel Rule" + ], + "security": [ + {} + ], + "parameters": [ + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl https://verification.didit.me/v3/travel-rule/pickup/{token}/messages/" + } + ], + "responses": { + "200": { + "description": "The messages, oldest first, from the counterparty's side.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TravelRuleMessage" + } + } + } + } + }, + "404": { + "description": "Unknown token." + }, + "410": { + "description": "The pickup link has expired or the transfer is closed." + } + } + }, + "post": { + "summary": "Reply in the pickup portal", + "description": "Public, unauthenticated. The counterparty VASP writes back on an EMAIL-rail transfer. Same body as the transaction's messages; attachments must be PDF, PNG, JPEG or WebP. The token is the authentication.", + "operationId": "createTravelRulePickupMessage", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "create_pickup_message", + "auth": "public", + "server": false + }, + "tags": [ + "Travel Rule" + ], + "security": [ + {} + ], + "parameters": [ + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleMessageCreateRequest" + }, + "example": { + "kind": "MESSAGE", + "body": "Attached is the proof of address of the wallet holder." + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/TravelRuleMessageCreateRequest" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/pickup/{token}/messages/ \\\n -H 'Content-Type: application/json' \\\n -d '{\"kind\": \"MESSAGE\", \"body\": \"Attached is the proof of address of the wallet holder.\"}'" + } + ], + "responses": { + "201": { + "description": "The stored message.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleMessage" + } + } + } + }, + "400": { + "description": "Invalid body, a file type or size that is not accepted, more than 100 messages on the transfer, or a `reply_to` that is not a message of this transfer.", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "example": { + "requested_documents": [ + "Name at least one document the counterparty should send." + ] + } + } + } + }, + "404": { + "description": "Unknown token." + }, + "410": { + "description": "The pickup link has expired or the transfer is closed." + } + } + } + }, + "/v3/travel-rule/inbound/": { + "post": { + "summary": "Register an after-deposit (sunrise) inbound transfer", + "description": "Register Travel Rule data for a crypto deposit that already settled on-chain before any exchange took place. Creates an inbound travelRule transaction (or reuses an unclaimed deposit) and resolves the transfer against your wallet address book. For a newly created transaction, Didit sends transaction.created first and the travel_rule.status.updated webhooks after it.", + "operationId": "registerTravelRuleInbound", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "register_inbound", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleInboundRequest" + }, + "example": { + "chain": "ETH", + "txid": "0xdeposit-hash", + "wallet_address": "0xYourDepositWallet", + "amount": "500.00", + "currency": "USDC", + "originator_data": { + "name": "Origin CASP SL" + }, + "beneficiary_data": { + "name": "Ana Diaz" + }, + "originating_vasp": { + "name": "Origin CASP SL", + "lei": "", + "travel_address": "ta9JbXbsBSuS5ozArJRFVgvBHMVnDGUnrPPChSZYQsuZTn3kPrJXUQ7RSLUEJ5KjyPvr8C9sAxmPtZfGsQVKCi4n8ShxP9BkbDdbtjtA9aumNybAZ1BxC" + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/inbound/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"chain\":\"ETH\",\"txid\":\"0xdeposit-hash\",\"wallet_address\":\"0xYourDepositWallet\",\"amount\":\"500.00\",\"currency\":\"USDC\"}'" + } + ], + "responses": { + "200": { + "description": "A transfer for this txid and wallet_address was already registered; the existing one is returned unchanged (created is false).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleInboundResponse" + } + } + } + }, + "201": { + "description": "A new Travel Rule transfer was registered, on a new inbound transaction or on an existing unclaimed deposit (created is true).", "content": { "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" - } - } + "schema": { + "$ref": "#/components/schemas/TravelRuleInboundResponse" } } } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "400": { + "description": "A required field (`chain`, `txid`, `wallet_address`, `amount`, `currency`) is missing, `originating_vasp.lei` is not a valid LEI, `originating_vasp.travel_address` is not a valid travel address, a memo, X-address or `utxo` the chain cannot carry, or `originator_data` / `beneficiary_data` that is not an object.", "content": { "application/json": { "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", + "Missing field": { "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + "txid": [ + "This field is required." + ] } - } - }, - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + }, + "Invalid LEI": { + "value": { + "originating_vasp": { + "lei": [ + "Invalid LEI: expected 20 alphanumeric characters (ISO 17442)." + ] + } } } } } } } + } + } + }, + "/v3/travel-rule/test-vasp/inbound/": { + "post": { + "summary": "Trigger an inbound transfer from the Didit Test VASP (sandbox)", + "description": "Sandbox applications only. The Didit Test VASP sends your application an inbound Travel Rule transfer, so you can exercise the receiving side of an exchange end to end before going live. The transfer is booked and decided by the same logic a real inbound transfer from a network runs against your wallet address book, so the inbox, the decision and the wallet ownership flow behave as they do live. Nothing leaves Didit. A live application receives 400. Send the same `reference` again to get the transfer the first call booked instead of a second one.", + "operationId": "triggerTravelRuleTestVaspInbound", + "x-didit-sdk": { + "resource": "transactions.travel_rule", + "method": "trigger_test_vasp_inbound", + "auth": "api_key", + "server": true }, + "tags": [ + "Travel Rule" + ], "security": [ { "ApiKeyAuth": [] } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl -X POST 'https://verification.didit.me/v3/id-verification/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'front_image=@./id_front.jpg' \\\n -F 'back_image=@./id_back.jpg' \\\n -F 'perform_document_liveness=true' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nurl = 'https://verification.didit.me/v3/id-verification/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('id_front.jpg', 'rb') as front_f, open('id_back.jpg', 'rb') as back_f:\n files = {\n 'front_image': ('id_front.jpg', front_f, 'image/jpeg'),\n 'back_image': ('id_back.jpg', back_f, 'image/jpeg'),\n }\n data = {\n 'perform_document_liveness': 'true',\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=120)\n\nresp.raise_for_status()\nid_v = resp.json()['id_verification']\nprint('status:', id_v['status'])\nprint('name:', id_v.get('full_name'))\nprint('document:', id_v.get('document_type'), id_v.get('document_number'))\nfor w in id_v.get('warnings', []):\n print('warning:', w['risk'], '-', w['log_type'])" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('front_image', new Blob([fs.readFileSync('./id_front.jpg')]), 'id_front.jpg');\nform.append('back_image', new Blob([fs.readFileSync('./id_back.jpg')]), 'id_back.jpg');\nform.append('perform_document_liveness', 'true');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/id-verification/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`ID verification failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.id_verification.status);\nconsole.log('warnings:', body.id_verification.warnings.map((w) => w.risk));" - } - ] - } - }, - "/v3/face-match/": { - "post": { - "summary": "Face Match", - "description": "Compare two face images (1:1) and get back a similarity `score` (0–100) plus an `Approved`/`Declined` `status`.\n\n**How it works.** Both images are decoded and analyzed by a biometric model (set `rotate_image=true` if captures may be sideways or upside down), then a similarity score between the two faces is computed. A score **strictly above** `face_match_score_decline_threshold` (default `30`) returns `status: \"Approved\"`; a score at or below it returns `Declined` with a `LOW_FACE_MATCH_SIMILARITY` warning. If no face pair can be scored at all, the call still returns `200` with `status: \"Declined\"` and a `NO_REFERENCE_IMAGE` warning — `score` is `null` on the default save path, or `0` with `save_api_request=false`. Submit one clear, front-facing face per image for best results.\n\n**Billing.** Each `200` response consumes one Face Match API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, both images are stored as the session's face-match source/target images, and a `status.updated` webhook is emitted to your configured webhook endpoints. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.\n\n**Face search index.** This endpoint never enrolls faces into your face search index and never runs blocklist or duplicate screening — use `POST /v3/passive-liveness/` or `POST /v3/face-search/` for those.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", - "operationId": "post_v3face-match", - "tags": [ - "Standalone APIs" - ], - "parameters": [], "requestBody": { "required": true, "content": { - "multipart/form-data": { + "application/json": { "schema": { - "type": "object", - "required": [ - "user_image", - "ref_image" - ], - "properties": { - "user_image": { - "type": "string", - "format": "binary", - "description": "Live or candidate face image to verify (e.g. a selfie). Submit a single front-facing photo with the subject's face clearly visible and well lit. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.5 MB before processing, so very high resolutions do not improve accuracy." - }, - "ref_image": { - "type": "string", - "format": "binary", - "description": "Reference face image to compare against (e.g. an ID document portrait or a previously enrolled photo). Same format and size limits as `user_image`." - }, - "face_match_score_decline_threshold": { - "type": "number", - "format": "float", - "default": 30, - "minimum": 0, - "maximum": 100, - "description": "Similarity threshold (0–100) that drives the response `status`. A computed score **at or below** this value sets `status` to `Declined` and adds a `LOW_FACE_MATCH_SIMILARITY` warning; a score strictly above it returns `Approved`. Default `30` is permissive — raise it (e.g. `50`–`60`) for stricter verification. Values outside 0–100 return `400`.", - "example": 50 - }, - "rotate_image": { - "type": "boolean", - "default": false, - "description": "When `true`, the service tries the input images in 90-degree increments (0, 90, 180, 270) and uses the orientation that yields the best face detection — useful for mobile captures with missing EXIF orientation. Adds latency, so only enable it if you cannot guarantee upright images.", - "example": false - }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, and announced through a `status.updated` webhook. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only.", - "example": true - }, - "vendor_data": { - "type": "string", - "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", - "example": "user-123" - }, - "metadata": { - "type": "object", - "additionalProperties": true, - "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"login\"}`) — it is parsed into an object.", - "example": { - "flow": "login", - "device_id": "abc123" - } - } - } + "$ref": "#/components/schemas/TravelRuleTestVaspInboundRequest" }, "example": { - "user_image": "(binary JPEG/PNG of the live selfie)", - "ref_image": "(binary JPEG/PNG of the reference photo)", - "face_match_score_decline_threshold": 50, - "rotate_image": false, - "save_api_request": true, - "vendor_data": "user-123", - "metadata": { - "flow": "login" - } + "beneficiary_wallet_address": "0xYourSandboxWallet", + "scenario": "accept", + "amount": "1", + "reference": "demo-001" } } } }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/test-vasp/inbound/ \\\n -H 'x-api-key: YOUR_SANDBOX_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"beneficiary_wallet_address\":\"0xYourSandboxWallet\",\"scenario\":\"accept\",\"reference\":\"demo-001\"}'" + } + ], "responses": { - "200": { - "description": "Face match computed. `face_match.score` is a 0–100 float (two decimals); `status` is `Approved` when the score is strictly above `face_match_score_decline_threshold`, otherwise `Declined` with an explanatory warning. A low similarity or unscorable pair still returns `200` — inspect `face_match.status` and `face_match.warnings`, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", + "201": { + "description": "The inbound transfer was booked and decided. A repeat with the same `reference` returns the same body.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleTestVaspInboundResponse" + } + } + } + }, + "400": { + "description": "Invalid body, an unknown or unsupported `scenario`, or the application is a live application.", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "409": { + "description": "The transfer could not be booked, for example because Travel Rule is not enabled for the application.", "content": { "application/json": { - "examples": { - "Approved": { - "summary": "Same person — score above the threshold", - "value": { - "request_id": "f9f5a777-8002-44c6-baaf-128bcfd6e226", - "face_match": { - "status": "Approved", - "score": 99.41, - "user_image": { - "entities": [ - { - "bbox": [ - 661, - 728, - 1688, - 2188 - ], - "confidence": 0.732973, - "age": 26.91, - "gender": "male", - "race": null - } - ], - "best_angle": 0 - }, - "ref_image": { - "entities": [ - { - "bbox": [ - 653, - 745, - 1691, - 2185 - ], - "confidence": 0.722082, - "age": 27.0, - "gender": "male", - "race": null - } - ], - "best_angle": 0 - }, - "warnings": [] - }, - "vendor_data": "user-123", - "metadata": { - "flow": "login" - }, - "created_at": "2026-06-12T01:04:42.763237+00:00" - } - }, - "Declined - low similarity": { - "summary": "Score at or below the decline threshold", - "value": { - "request_id": "7cdda24e-311a-46d3-a3bb-e5d9b564ce73", - "face_match": { - "status": "Declined", - "score": 24.87, - "user_image": { - "entities": [ - { - "bbox": [ - 661, - 728, - 1688, - 2188 - ], - "confidence": 0.732973, - "age": 26.91, - "gender": "male", - "race": null - } - ], - "best_angle": 0 - }, - "ref_image": { - "entities": [ - { - "bbox": [ - 156, - 234, - 679, - 898 - ], - "confidence": 0.717775, - "age": 41.2, - "gender": "male", - "race": null - } - ], - "best_angle": 0 - }, - "warnings": [ - { - "risk": "LOW_FACE_MATCH_SIMILARITY", - "feature": "FACEMATCH", - "additional_data": null, - "log_type": "error", - "short_description": "Low face match similarity", - "long_description": "The facial features of the provided image don't closely match the reference image, suggesting a potential identity mismatch." - } - ] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T01:08:19.859116+00:00" - } - } - }, "schema": { "type": "object", "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." - }, - "face_match": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined" - ], - "description": "`Approved` when the similarity score is strictly above `face_match_score_decline_threshold` and a score could be computed; `Declined` otherwise (a warning explains why)." - }, - "score": { - "type": "number", - "format": "float", - "nullable": true, - "minimum": 0, - "maximum": 100, - "description": "Similarity score from 0 to 100 — the model's confidence that both images depict the same person. Rounded to two decimals only when `save_api_request` is true; with `save_api_request=false` the raw float is returned. `null` when no face pair could be scored (with the default `save_api_request=true`); with `save_api_request=false` the same condition can surface as `score: 0`. Either way a `NO_REFERENCE_IMAGE` warning is present and `status` is `Declined`.", - "example": 99.41 - }, - "user_image": { - "type": "object", - "description": "Face-detection results for the analyzed images.", - "properties": { - "entities": { - "type": "array", - "items": { - "type": "object", - "properties": { - "bbox": { - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 4, - "maxItems": 4, - "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.", - "example": [ - 661, - 728, - 1688, - 2188 - ] - }, - "confidence": { - "type": "number", - "format": "float", - "minimum": 0, - "maximum": 1, - "description": "Face-detection confidence (0–1).", - "example": 0.732973 - }, - "age": { - "type": "number", - "format": "float", - "description": "Model-estimated age of the detected face, in years. Informational only — it does not affect the face-match decision.", - "example": 26.91 - }, - "gender": { - "type": "string", - "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.", - "example": "male" - }, - "race": { - "type": "string", - "nullable": true, - "description": "Reserved field — always `null` in responses from this endpoint.", - "example": null - } - } - }, - "description": "One entry per detected face." - }, - "best_angle": { - "type": "integer", - "description": "Rotation (degrees: 0, 90, 180, or 270) that produced the best face detection. Only non-zero when `rotate_image=true` corrected the orientation.", - "example": 0 - } - } - }, - "ref_image": { - "type": "object", - "description": "Face-detection results for the reference image, same shape as `user_image`.", - "properties": { - "entities": { - "type": "array", - "items": { - "type": "object", - "properties": { - "bbox": { - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 4, - "maxItems": 4, - "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.", - "example": [ - 661, - 728, - 1688, - 2188 - ] - }, - "confidence": { - "type": "number", - "format": "float", - "minimum": 0, - "maximum": 1, - "description": "Face-detection confidence (0–1).", - "example": 0.732973 - }, - "age": { - "type": "number", - "format": "float", - "description": "Model-estimated age of the detected face, in years. Informational only — it does not affect the face-match decision.", - "example": 26.91 - }, - "gender": { - "type": "string", - "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.", - "example": "male" - }, - "race": { - "type": "string", - "nullable": true, - "description": "Reserved field — always `null` in responses from this endpoint.", - "example": null - } - } - }, - "description": "One entry per detected face." - }, - "best_angle": { - "type": "integer", - "example": 0 - } - } - }, - "warnings": { - "type": "array", - "description": "Empty on a clean approval. `NO_REFERENCE_IMAGE` (`error`) — no face pair could be scored; `LOW_FACE_MATCH_SIMILARITY` (`error`) — score at or below the decline threshold. Any of these sets `status` to `Declined`.", - "items": { - "type": "object", - "properties": { - "risk": { - "type": "string", - "enum": [ - "NO_REFERENCE_IMAGE", - "LOW_FACE_MATCH_SIMILARITY" - ], - "description": "Machine-readable risk code." - }, - "feature": { - "type": "string", - "enum": [ - "FACEMATCH" - ], - "description": "Feature that raised the warning. Always `FACEMATCH` on this endpoint." - }, - "additional_data": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Always `null` for face-match warnings." - }, - "log_type": { - "type": "string", - "enum": [ - "error", - "warning", - "information" - ], - "description": "Severity. `error` warnings drive `status` to `Declined`; `warning` and `information` entries are advisory and never decline on their own." - }, - "short_description": { - "type": "string", - "description": "Human-readable one-line summary of the risk." - }, - "long_description": { - "type": "string", - "description": "Human-readable explanation of the risk." - } - } - } - } - } - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent, or `null`." - }, - "metadata": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Echo of the `metadata` object you sent, or `null`." - }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp (UTC) of when the response was generated, e.g. `2026-06-12T01:04:42.763237+00:00`." + "detail": { + "type": "string" } } } } } + } + } + } + }, + "/v3/travel-rule/widget-session/": { + "post": { + "summary": "Mint a wallet-ownership widget session", + "description": "Mint a hosted wallet-ownership widget URL/token. Send the customer to the returned url to prove control of a wallet (message signing, Satoshi test, screenshot, or self-declaration). The widget is web-only in this release - open the url in the system browser or a custom tab, never inside a mobile SDK webview.", + "operationId": "createTravelRuleWidgetSession", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "create_widget_session", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleWidgetSessionCreate" + }, + "example": { + "wallet_address": "0xd3b62acb76fb27a7f8961ec40fa11746b113aca9", + "chain": "ETH", + "holder_name": "Jane Doe", + "vendor_data": "user-042", + "transaction_id": "99999999-aaaa-bbbb-cccc-dddddddddddd", + "expires_in_minutes": 60 + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/widget-session/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"wallet_address\":\"0xd3b62acb76fb27a7f8961ec40fa11746b113aca9\",\"chain\":\"ETH\",\"holder_name\":\"Jane Doe\",\"transaction_id\":\"99999999-aaaa-bbbb-cccc-dddddddddddd\"}'" + } + ], + "responses": { + "201": { + "description": "The minted widget session.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleWidgetSessionResponse" + } + } + } }, "400": { - "description": "Validation error. Returned when a required image is missing, exceeds 5 MB, has an unsupported extension, or when an option is out of range. The body is DRF's standard field-error envelope: one array of messages per offending field.", + "description": "Invalid body: `callback_url` is not an absolute http(s) URL, `expires_in_minutes` is outside 5 to 10080, `transaction_id` is not a UUID, or a field is longer than its limit.", "content": { "application/json": { "examples": { - "Missing required image": { - "summary": "`ref_image` (or `user_image`) not included in the form data", - "value": { - "ref_image": [ - "No file was submitted." - ] - } - }, - "Unsupported file extension": { - "summary": "File extension outside tiff/jpg/jpeg/png/webp", - "value": { - "user_image": [ - "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp." - ] - } - }, - "File too large": { - "summary": "Upload exceeds the 5 MB limit", - "value": { - "user_image": [ - "File size should not exceed 5 MB" - ] - } - }, - "Threshold out of range": { - "summary": "`face_match_score_decline_threshold` outside 0–100", + "Invalid callback_url": { "value": { - "face_match_score_decline_threshold": [ - "Ensure this value is less than or equal to 100." + "callback_url": [ + "callback_url must be an absolute http(s) URL." ] } } @@ -12883,1281 +33532,1370 @@ } } }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", + "404": { + "description": "`transaction_id` does not name a Travel Rule transfer in this application." + } + } + } + }, + "/v3/travel-rule/widget/{token}/": { + "get": { + "summary": "Get wallet-ownership widget context", + "description": "Public read endpoint keyed by the widget session token - no API key required. Returns the widget context plus the status of every proof, so you can poll it to detect completion; completed_at becomes non-null once a proof is verified. A SATOSHI_TEST proof can sit at status PENDING while its deposit is seen on-chain but unconfirmed - a background job automatically rechecks it and completes it once the deposit confirms, so no client action is needed while it waits. Each proof carries the fields its method needs: deposit_address, expected_amount, and its own expires_at for SATOSHI_TEST; challenge for MESSAGE_SIGNING. Prefer the travel_rule.status.updated webhook over polling when the session is linked to a transfer.", + "operationId": "getTravelRuleWidgetContext", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "retrieve_widget_context", + "auth": "public", + "server": false + }, + "tags": [ + "Travel Rule" + ], + "security": [ + {} + ], + "parameters": [ + { + "name": "token", + "in": "path", + "required": true, + "description": "Opaque session token returned when the widget session was minted.", + "schema": { + "type": "string" + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl https://verification.didit.me/v3/travel-rule/widget/{token}/" + } + ], + "responses": { + "200": { + "description": "The widget context and proof statuses.", "content": { "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." + "schema": { + "$ref": "#/components/schemas/TravelRuleWidgetContext" + } + } + } + }, + "404": { + "description": "Unknown token." + }, + "410": { + "description": "The session expired without completing." + } + } + } + }, + "/v3/travel-rule/widget/{token}/vasps/": { + "get": { + "summary": "Search VASPs from the wallet widget", + "description": "Public read endpoint keyed by the widget session token, no API key required: the VASP picker of the end user's widget. Same search and filters as GET /v3/travel-rule/vasps/ for the widget's application, answering only who each VASP is and whether it can be reached.", + "operationId": "listTravelRuleWidgetVasps", + "x-didit-sdk": { + "resource": "travel_rule", + "method": "list_widget_vasps", + "auth": "public", + "server": false + }, + "tags": [ + "Travel Rule" + ], + "security": [ + {} + ], + "parameters": [ + { + "name": "token", + "in": "path", + "required": true, + "description": "Opaque session token returned when the widget session was minted.", + "schema": { + "type": "string" + } + }, + { + "name": "search", + "in": "query", + "schema": { + "type": "string", + "maxLength": 255 + }, + "description": "A name or alias fragment (case-insensitive), an exact LEI, or a domain (example.com or a URL on it). Didit network members match on their legal name, LEI or the domain of their compliance email." + }, + { + "name": "network", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA", + "TRISA" + ] + }, + "description": "Only directory entries a network's member directory currently lists." + }, + { + "name": "jurisdiction", + "in": "query", + "schema": { + "type": "string" + }, + "description": "ISO 3166 alpha-2 or alpha-3 country code. 400 for anything else." + }, + { + "name": "reachable", + "in": "query", + "schema": { + "type": "boolean" + }, + "description": "true: only VASPs your application has an available route to (Didit network members included). false: only directory entries it cannot reach today." + }, + { + "name": "kind", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "directory", + "didit" + ] + }, + "description": "Only directory entries, or only Didit network members." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl 'https://verification.didit.me/v3/travel-rule/widget/{token}/vasps/?search=counterparty'" + } + ], + "responses": { + "200": { + "description": "Paginated, name-sorted results.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VaspDirectoryWidgetEntry" + } } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } + } + } + } + }, + "400": { + "description": "A filter is not valid: the body names each field, for example an unknown network or country code.", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" } } + }, + "example": { + "jurisdiction": [ + "Use an ISO 3166 alpha-2 or alpha-3 country code." + ], + "network": [ + "\"SWIFT\" is not a valid choice." + ] } } } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "404": { + "description": "Unknown token." + }, + "410": { + "description": "The session expired." + } + } + } + }, + "/v3/organization/{organization_id}/application/{application_id}/vendor-users/toggle-ongoing-monitoring/": { + "patch": { + "summary": "Toggle AML ongoing monitoring for users", + "description": "Enables or disables AML ongoing monitoring at the user level. Exactly one monitored AML screening is kept per user; users without any AML screening (for example imported users) get one created and screened first, billed as a regular AML screening. Set estimate_only to preview counts and costs without writing. Batches of 20 or fewer resolve inline (200, COMPLETED); larger batches return 202 and run asynchronously.", + "operationId": "toggle_vendor_users_ongoing_monitoring", + "x-didit-sdk": { + "resource": "users", + "method": "set_ongoing_monitoring", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OngoingMonitoringToggleRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Estimate (when estimate_only) or the completed job for small batches processed inline.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OngoingMonitoringToggleResult" + } + } + } + }, + "202": { + "description": "Larger batches are processed asynchronously; poll the job endpoint with the returned job_id.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OngoingMonitoringJob" + } + } + } + }, + "403": { + "description": "Missing write:sessions privilege, or insufficient balance to enable (the estimate is included in the error body)." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/vendor-users/toggle-ongoing-monitoring/{job_id}/": { + "get": { + "summary": "Get AML ongoing monitoring job", + "description": "Progress and per-outcome counters of a monitoring toggle job. Poll every couple of seconds while status is PENDING or PROCESSING.", + "operationId": "get_vendor_users_ongoing_monitoring_job", + "x-didit-sdk": { + "resource": "users", + "method": "retrieve_ongoing_monitoring_job", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Monitoring job UUID returned by the toggle endpoint." + } + ], + "responses": { + "200": { + "description": "The job.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OngoingMonitoringJob" + } + } + } + }, + "404": { + "description": "Unknown job for this application." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/vendor-businesses/toggle-ongoing-monitoring/": { + "patch": { + "summary": "Toggle AML ongoing monitoring for businesses", + "description": "Business-level equivalent of the user toggle: one monitored AML screening per business, with businesses lacking any AML screening screened first (company entity). Same estimate_only, inline fast-path and async job contract.", + "operationId": "toggle_vendor_businesses_ongoing_monitoring", + "x-didit-sdk": { + "resource": "businesses", + "method": "set_ongoing_monitoring", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Businesses" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OngoingMonitoringToggleRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Estimate (when estimate_only) or the completed job for small batches processed inline.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OngoingMonitoringToggleResult" + } + } + } + }, + "202": { + "description": "Larger batches are processed asynchronously; poll the job endpoint with the returned job_id.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OngoingMonitoringJob" + } + } + } + }, + "403": { + "description": "Missing write:sessions privilege, or insufficient balance to enable (the estimate is included in the error body)." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/vendor-businesses/toggle-ongoing-monitoring/{job_id}/": { + "get": { + "summary": "Get AML ongoing monitoring job", + "description": "Progress and per-outcome counters of a monitoring toggle job. Poll every couple of seconds while status is PENDING or PROCESSING.", + "operationId": "get_vendor_businesses_ongoing_monitoring_job", + "x-didit-sdk": { + "resource": "businesses", + "method": "retrieve_ongoing_monitoring_job", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Businesses" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Monitoring job UUID returned by the toggle endpoint." + } + ], + "responses": { + "200": { + "description": "The job.", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } + "$ref": "#/components/schemas/OngoingMonitoringJob" } } } + }, + "404": { + "description": "Unknown job for this application." } }, "security": [ { "ApiKeyAuth": [] } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/businesses/toggle-ongoing-monitoring/": { + "patch": { + "summary": "Toggle AML ongoing monitoring for business sessions", + "description": "Session-level toggle for KYB verifications, addressed by session number. Sessions without an AML screening get one created and attached to the session itself. Same estimate_only, inline fast-path and async job contract as the user-level endpoints. Replaces the legacy synchronous behavior that skipped sessions without an AML screening.", + "operationId": "toggle_business_sessions_ongoing_monitoring", + "x-didit-sdk": { + "resource": "businesses", + "method": "set_session_ongoing_monitoring", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Businesses" ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl -X POST 'https://verification.didit.me/v3/face-match/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'user_image=@./selfie.jpg' \\\n -F 'ref_image=@./id_portrait.jpg' \\\n -F 'face_match_score_decline_threshold=50' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" - }, + "parameters": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\nurl = 'https://verification.didit.me/v3/face-match/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as user_f, open('id_portrait.jpg', 'rb') as ref_f:\n files = {\n 'user_image': ('selfie.jpg', user_f, 'image/jpeg'),\n 'ref_image': ('id_portrait.jpg', ref_f, 'image/jpeg'),\n }\n data = {\n 'face_match_score_decline_threshold': 50,\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nbody = resp.json()\nprint('status:', body['face_match']['status'])\nprint('score:', body['face_match']['score'])" + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, { - "lang": "javascript", - "label": "JavaScript", - "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('ref_image', new Blob([fs.readFileSync('./id_portrait.jpg')]), 'id_portrait.jpg');\nform.append('face_match_score_decline_threshold', '50');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/face-match/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`Face match failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.face_match.status, 'score:', body.face_match.score);" + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." } - ] - } - }, - "/v3/age-estimation/": { - "post": { - "summary": "Age Estimation (face age + passive liveness)", - "description": "Estimate a person's age from a single face photo and run a passive liveness check in the same call — returns the model-predicted `age_estimation` (in years), a liveness `score` (0–100), and an `Approved`/`Declined` `status`. The age is **model-predicted from the face, not verified against a document** — use it for age gates and low-friction checks, not as legal proof of age.\n\n**How it works.** The image is analyzed for faces (set `rotate_image=true` if captures may be sideways or upside down); each detected face gets an estimated age, and the largest face in the frame drives `age_estimation`. In parallel, a passive liveness model scores whether the photo shows a live person (no user challenge required) and detects presentation attacks (photos of photos, screens, masks). Submit one clear, front-facing, well-lit face per image for best results.\n\n**Decision logic.** Any warning declines: `status` is `Approved` only when the `warnings` array is empty. A liveness `score` at or below `face_liveness_score_decline_threshold` (default `30`) adds `LOW_LIVENESS_SCORE`; a detected attack adds `LIVENESS_FACE_ATTACK`; no face found by the liveness model adds `NO_FACE_DETECTED`; no estimable face age adds `AGE_NOT_DETECTED` (`age_estimation` is `null`); an estimated age below `age_estimation_decline_threshold` (default `18`) adds `AGE_BELOW_MINIMUM`. Borderline ages near your threshold deserve a fallback to document-based verification — the model's error grows for faces close to the limit.\n\n**Billing.** Each `200` response consumes one Age Estimation API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/` (the result is exposed there in `liveness_checks[]`, with `features: [\"AGE_ESTIMATION\"]`), the face crop and reference image are stored, the face embedding is added to your application's face index (used by face search and duplicate detection), and a `status.updated` webhook is emitted. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload (`age_estimation: 30`, no warnings), no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", - "operationId": "post_v3age-estimation", - "tags": [ - "Standalone APIs" ], - "parameters": [], "requestBody": { "required": true, "content": { - "multipart/form-data": { + "application/json": { "schema": { "type": "object", "required": [ - "user_image" + "is_enabled" ], "properties": { - "user_image": { - "type": "string", - "format": "binary", - "description": "Face photo to analyze (e.g. a selfie). Submit a single front-facing photo with the subject's face clearly visible and well lit. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp` (no PDF). Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.3 MB before processing, so very high resolutions do not improve accuracy." - }, - "face_liveness_score_decline_threshold": { - "type": "number", - "format": "float", - "default": 30, - "minimum": 0, - "maximum": 100, - "description": "Liveness threshold (0–100). A liveness `score` **at or below** this value adds a `LOW_LIVENESS_SCORE` warning and declines. Raise it for stricter anti-spoofing; values outside 0–100 return `400`.", - "example": 30 - }, - "age_estimation_decline_threshold": { - "type": "integer", - "default": 18, - "minimum": 0, - "maximum": 100, - "description": "Minimum acceptable estimated age in years. An estimated age **below** this value adds an `AGE_BELOW_MINIMUM` warning and declines. Set `0` to disable the age check (the call then only runs liveness + age estimation without an age-based decision).", - "example": 18 + "is_enabled": { + "type": "boolean", + "description": "true to enable monitoring, false to disable it." }, - "rotate_image": { + "estimate_only": { "type": "boolean", "default": false, - "description": "When `true`, the service tries the input image in 90-degree increments (0, 90, 180, 270) and uses the orientation that yields the best face detection — useful for mobile captures with missing EXIF orientation. Adds latency, so only enable it if you cannot guarantee upright images.", - "example": false + "description": "When true, returns the cost estimate and performs no writes." }, - "save_api_request": { + "update_all": { "type": "boolean", - "default": true, - "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, announced through a `status.updated` webhook, and with the detected face added to your application's face index. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only.", - "example": true + "default": false, + "description": "Apply to every entity of the application (scoped by the filter_* params when present)." }, - "vendor_data": { - "type": "string", - "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", - "example": "user-123" + "filter_search": { + "type": "string" }, - "metadata": { - "type": "object", - "additionalProperties": true, - "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"age-gate\"}`) — it is parsed into an object.", - "example": { - "flow": "age-gate" - } - } - } - }, - "example": { - "user_image": "(binary JPEG/PNG of the selfie)", - "face_liveness_score_decline_threshold": 30, - "age_estimation_decline_threshold": 18, - "rotate_image": false, - "save_api_request": true, - "vendor_data": "user-123", - "metadata": { - "flow": "age-gate" - } - } - } - } - }, - "responses": { - "200": { - "description": "Age estimation and passive liveness completed. `age_estimation.age_estimation` is the model-predicted age in years of the largest detected face (`null` if none); `age_estimation.score` is the passive-liveness score (0–100); `status` is `Approved` only when `warnings` is empty. An underage or spoofed capture still returns `200` with `status: \"Declined\"` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", - "content": { - "application/json": { - "examples": { - "Approved": { - "summary": "Live adult face — both checks passed", - "value": { - "request_id": "0c40ba43-64ab-4e2e-b4b8-7d1f12f81bc1", - "age_estimation": { - "status": "Approved", - "method": "PASSIVE", - "score": 97.5, - "user_image": { - "entities": [ - { - "age": 27.33, - "bbox": [ - 40, - 40, - 100, - 100 - ], - "confidence": 0.7177750468254089, - "gender": "male" - } - ], - "best_angle": 0 - }, - "age_estimation": 27.33, - "warnings": [] - }, - "vendor_data": "user-123", - "metadata": { - "flow": "age-gate" - }, - "created_at": "2026-06-12T02:24:11.512941+00:00" + "filter_status": { + "type": "array", + "items": { + "type": "string" } }, - "Declined - age below threshold": { - "summary": "Estimated age below `age_estimation_decline_threshold`", - "value": { - "request_id": "9be41a6f-2f4e-4f57-92f4-b3a4f6f0a1c2", - "age_estimation": { - "status": "Declined", - "method": "PASSIVE", - "score": 95.0, - "user_image": { - "entities": [ - { - "age": 16.5, - "bbox": [ - 40, - 40, - 100, - 100 - ], - "confidence": 0.7177750468254089, - "gender": "female" - } - ], - "best_angle": 0 - }, - "age_estimation": 16.5, - "warnings": [ - { - "risk": "AGE_BELOW_MINIMUM", - "feature": "LIVENESS", - "additional_data": null, - "log_type": "error", - "short_description": "Age below minimum", - "long_description": "The age of the face is below the minimum age threshold for the application." - } - ] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T02:26:40.118332+00:00" - } + "filter_country": { + "type": "string" }, - "Declined - liveness too low": { - "summary": "Liveness score at or below the decline threshold (possible spoof)", - "value": { - "request_id": "5f6f2f1f-7c4f-43b9-8d62-0a8f4c2d9e77", - "age_estimation": { - "status": "Declined", - "method": "PASSIVE", - "score": 5.0, - "user_image": { - "entities": [ - { - "age": 25.0, - "bbox": [ - 40, - 40, - 100, - 100 - ], - "confidence": 0.7177750468254089, - "gender": "male" - } - ], - "best_angle": 0 - }, - "age_estimation": 25.0, - "warnings": [ - { - "risk": "LOW_LIVENESS_SCORE", - "feature": "LIVENESS", - "additional_data": null, - "log_type": "error", - "short_description": "Low liveness score", - "long_description": "The liveness check resulted in a low score, indicating potential use of non-live facial representations or poor-quality biometric data." - } - ] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T02:27:02.901547+00:00" - } - } - }, - "schema": { - "type": "object", - "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." - }, - "age_estimation": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined" - ], - "description": "`Approved` when no warnings were raised; `Declined` otherwise (every warning this endpoint produces declines)." - }, - "method": { - "type": "string", - "enum": [ - "PASSIVE" - ], - "description": "Liveness method. Always `PASSIVE` on this endpoint (single-image, no user challenge)." - }, - "score": { - "type": "number", - "format": "float", - "nullable": true, - "minimum": 0, - "maximum": 100, - "description": "Passive-liveness score from 0 to 100 — the model's confidence that the photo shows a live person. Compared against `face_liveness_score_decline_threshold`; at or below it the call declines with `LOW_LIVENESS_SCORE`. `null` when the liveness model returns no confidence — the call still declines with `LOW_LIVENESS_SCORE` because null is treated as 0 for the threshold comparison.", - "example": 97.5 - }, - "user_image": { - "type": "object", - "description": "Face-detection results for the analyzed image.", - "properties": { - "entities": { - "type": "array", - "description": "One entry per detected face. Empty when no face was found (then `age_estimation` is `null` and an `AGE_NOT_DETECTED` warning is added).", - "items": { - "type": "object", - "properties": { - "age": { - "type": "number", - "format": "float", - "description": "Model-estimated age of this face, in years.", - "example": 27.33 - }, - "bbox": { - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 4, - "maxItems": 4, - "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.", - "example": [ - 40, - 40, - 100, - 100 - ] - }, - "confidence": { - "type": "number", - "format": "float", - "minimum": 0, - "maximum": 1, - "description": "Face-detection confidence (0–1).", - "example": 0.7177750468254089 - }, - "gender": { - "type": "string", - "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.", - "example": "male" - } - } - } - }, - "best_angle": { - "type": "integer", - "nullable": true, - "description": "Rotation (degrees: 0, 90, 180, or 270) that produced the best face detection. Only non-zero when `rotate_image=true` corrected the orientation.", - "example": 0 - } - } - }, - "age_estimation": { - "type": "number", - "format": "float", - "nullable": true, - "description": "Model-predicted age in years of the **largest** detected face. `null` when no face age could be estimated (an `AGE_NOT_DETECTED` warning is added and the call declines).", - "example": 27.33 - }, - "warnings": { - "type": "array", - "description": "Empty on a clean approval. Any entry sets `status` to `Declined`.", - "items": { - "type": "object", - "properties": { - "risk": { - "type": "string", - "enum": [ - "NO_FACE_DETECTED", - "LOW_LIVENESS_SCORE", - "LIVENESS_FACE_ATTACK", - "AGE_NOT_DETECTED", - "AGE_BELOW_MINIMUM" - ], - "description": "Machine-readable risk code. `NO_FACE_DETECTED` — the liveness model found no face; `LOW_LIVENESS_SCORE` — liveness `score` at or below `face_liveness_score_decline_threshold`; `LIVENESS_FACE_ATTACK` — a presentation attack (photo/screen/mask) was detected; `AGE_NOT_DETECTED` — no face age could be estimated; `AGE_BELOW_MINIMUM` — estimated age below `age_estimation_decline_threshold`. `NO_FACE_DETECTED` and `LOW_LIVENESS_SCORE` are mutually exclusive — when no face is found only `NO_FACE_DETECTED` is emitted." - }, - "feature": { - "type": "string", - "enum": [ - "LIVENESS" - ], - "description": "Feature that raised the warning. Always `LIVENESS` on this endpoint." - }, - "additional_data": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Always `null` for age-estimation warnings." - }, - "log_type": { - "type": "string", - "enum": [ - "error" - ], - "description": "Severity. Every warning this endpoint produces is an `error` and sets `status` to `Declined`." - }, - "short_description": { - "type": "string", - "description": "Human-readable one-line summary of the risk." - }, - "long_description": { - "type": "string", - "description": "Human-readable explanation of the risk." - } - } - } - } - } - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent, or `null`." - }, - "metadata": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Echo of the `metadata` object you sent, or `null`." - }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp (UTC) of when the response was generated." - } - } - } - } - } - }, - "400": { - "description": "Validation error. Returned when `user_image` is missing, exceeds 5 MB, has an unsupported extension, or when a threshold is out of range. The body is DRF's standard field-error envelope: one array of messages per offending field.", - "content": { - "application/json": { - "examples": { - "Missing user image": { - "summary": "`user_image` not included in the form data", - "value": { - "user_image": [ - "No file was submitted." - ] - } + "filter_date_from": { + "type": "string", + "format": "date" }, - "Unsupported file extension": { - "summary": "File extension outside tiff/jpg/jpeg/png/webp", - "value": { - "user_image": [ - "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp." - ] - } + "filter_date_to": { + "type": "string", + "format": "date" }, - "File too large": { - "summary": "Upload exceeds the 5 MB limit", - "value": { - "user_image": [ - "File size should not exceed 5 MB" - ] - } + "session_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Business session numbers to toggle. Required unless update_all (or the legacy toggle_all alias) is true." }, - "Threshold out of range": { - "summary": "`face_liveness_score_decline_threshold` outside 0–100", - "value": { - "face_liveness_score_decline_threshold": [ - "Ensure this value is less than or equal to 100." - ] - } + "toggle_all": { + "type": "boolean", + "default": false, + "description": "Legacy alias of update_all." + }, + "filter_workflow_id": { + "type": "string" } } } } - }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", - "content": { - "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" - } - } + } + }, + "responses": { + "200": { + "description": "Estimate (when estimate_only) or the completed job for small batches processed inline.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OngoingMonitoringToggleResult" } } } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "202": { + "description": "Larger batches are processed asynchronously; poll the job endpoint with the returned job_id.", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } + "$ref": "#/components/schemas/OngoingMonitoringJob" } } } + }, + "403": { + "description": "Missing write:sessions privilege, or insufficient balance to enable (the estimate is included in the error body)." } }, "security": [ { "ApiKeyAuth": [] } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/businesses/toggle-ongoing-monitoring/{job_id}/": { + "get": { + "summary": "Get AML ongoing monitoring job", + "description": "Progress and per-outcome counters of a monitoring toggle job. Poll every couple of seconds while status is PENDING or PROCESSING.", + "operationId": "get_business_sessions_ongoing_monitoring_job", + "x-didit-sdk": { + "resource": "businesses", + "method": "retrieve_session_ongoing_monitoring_job", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Businesses" ], - "x-codeSamples": [ + "parameters": [ { - "lang": "curl", - "label": "cURL", - "source": "curl -X POST 'https://verification.didit.me/v3/age-estimation/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'user_image=@./selfie.jpg' \\\n -F 'age_estimation_decline_threshold=18' \\\n -F 'face_liveness_score_decline_threshold=30' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, { - "lang": "python", - "label": "Python", - "source": "import requests\n\nurl = 'https://verification.didit.me/v3/age-estimation/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as f:\n files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}\n data = {\n 'age_estimation_decline_threshold': 18,\n 'face_liveness_score_decline_threshold': 30,\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nresult = resp.json()['age_estimation']\nprint('status:', result['status'])\nprint('estimated age:', result['age_estimation'])\nprint('liveness score:', result['score'])\nfor w in result['warnings']:\n print('warning:', w['risk'])" + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." }, { - "lang": "javascript", - "label": "JavaScript", - "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('age_estimation_decline_threshold', '18');\nform.append('face_liveness_score_decline_threshold', '30');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/age-estimation/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`Age estimation failed: ${response.status}`);\nconst { age_estimation } = await response.json();\nconsole.log('status:', age_estimation.status, 'age:', age_estimation.age_estimation, 'liveness:', age_estimation.score);" + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Monitoring job UUID returned by the toggle endpoint." } - ] - } - }, - "/v3/poa/": { - "post": { - "summary": "Proof of Address (document OCR + address checks)", - "description": "Extract and validate a Proof of Address document — utility bills, bank statements, and government-issued documents — in one call. Returns the issuer, issue/expiry dates, the holder name, the raw and parsed/geocoded address, an `Approved`/`Declined` `status`, and a `warnings` list explaining every issue found.\n\n**Latency.** Extraction is LLM-based: typical calls take **5–15 seconds**, multi-page PDFs up to **~30 seconds**. Configure a client timeout of **at least 45 seconds** and do not retry before the call completes.\n\n**How it works.** The document (image or PDF, up to 15 MB; encrypted PDFs require `document_password`) is classified into a document type and subtype (e.g. `UTILITY_BILL` / `WATER_BILL`), and the issuer, dates, holder name, and address are extracted with LLM-based extraction. The address is parsed and geocoded into `poa_parsed_address`. PDF/EXIF forensics screen for tampering — modification after digital signing, known PDF editors, overlay-text manipulation, suspicious re-exports — surfacing `SUSPECTED_DOCUMENT_MANIPULATION` or `DOCUMENT_METADATA_MISMATCH`. Document age is validated against `poa_document_age_months`, and the language against `poa_languages_allowed`. If you pass `expected_address`, `expected_country`, or `expected_first_name`/`expected_last_name`, the extracted values are cross-checked and mismatches are flagged.\n\n**Decision logic.** `status` is `Approved` unless at least one warning resolves to a decline. Hard failures always decline: `MISSING_ADDRESS_INFORMATION`, `POA_DOCUMENT_EXPIRED`, `INVALID_DOCUMENT_TYPE`, `UNABLE_TO_VALIDATE_DOCUMENT_AGE`, plus `UNABLE_TO_EXTRACT_ISSUE_DATE` and `POA_NAME_NOT_DETECTED` (not configurable on this endpoint). Five risk groups are configurable per request via the `poa_*_action` options (`DECLINE` or `NO_ACTION`, all defaulting to `DECLINE`). `POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION` and `UNPARSABLE_OR_INVALID_ADDRESS` are informational on this endpoint and never decline. A document that cannot be processed at all returns `400`; a readable but problematic document returns `200` with `status: \"Declined\"` — always inspect `poa.status` and `poa.warnings`, not just the HTTP code.\n\n**Billing.** Each `200` response consumes one Proof of Address API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any document processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the uploaded document is stored, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/` (the decision additionally exposes `document_file` and the `document_metadata` forensics, which this response omits), and a `status.updated` webhook is emitted. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload (a fictional \"Sandbox Power Co.\" electricity bill), no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\nNote: `FUTURE_ISSUE_DATE` and `POOR_DOCUMENT_QUALITY` exist in workflow sessions but are never produced by this standalone endpoint — a future-dated document is not auto-declined here.", - "operationId": "post_v3poa", - "tags": [ - "Standalone APIs" ], - "parameters": [], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "required": [ - "document" - ], - "properties": { - "document": { - "type": "string", - "format": "binary", - "description": "The Proof of Address document. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `pdf`, `webp`. Maximum upload size: **15 MB** (larger files are rejected with `400`). Multi-page PDFs are supported (expect higher latency); encrypted PDFs require `document_password`. Images are automatically compressed before processing." - }, - "document_password": { - "type": "string", - "description": "Password to decrypt `document` when it is an encrypted PDF. Sending an encrypted PDF without (or with a wrong) password returns `400`.", - "writeOnly": true - }, - "expected_address": { - "type": "string", - "description": "Address to verify against the document. It is geocoded and compared with the extracted address; a mismatch (or an expected address that cannot be geocoded/verified) adds `ADDRESS_MISMATCH_WITH_PROVIDED`, governed by `poa_address_mismatch_action`. The geocoded form is echoed back in `expected_details_formatted_address` / `expected_details_parsed_address`.", - "example": "Av. Monseñor Tavella 1291, Salta, Argentina" - }, - "expected_country": { - "type": "string", - "description": "Country to verify against the document (ISO 3166-1 code, e.g. `ARG` or `AR`). A mismatch with the document's country adds `POA_COUNTRY_MISMATCH_WITH_PROVIDED`, governed by `poa_address_mismatch_action`.", - "example": "ARG" - }, - "expected_first_name": { - "type": "string", - "description": "First name to verify against `name_on_document` (fuzzy match, transliteration-aware). A score below the match threshold adds `NAME_MISMATCH_WITH_PROVIDED`, governed by `poa_address_mismatch_action`.", - "example": "Sophia" - }, - "expected_last_name": { - "type": "string", - "description": "Last name to verify against `name_on_document`, combined with `expected_first_name` for the match score.", - "example": "Martinez" - }, - "poa_languages_allowed": { - "type": "string", - "description": "Comma-separated list of accepted document languages as ISO 639-1 codes (e.g. `en,es,fr`). When omitted or blank, **all 51 supported languages** are accepted. A document in a language outside the list adds `UNSUPPORTED_DOCUMENT_LANGUAGE` (governed by `poa_unsupported_language_action`); an unknown code in the list returns `400` with the full set of supported codes.", - "example": "en,es" - }, - "poa_document_age_months": { - "type": "string", - "description": "Comma-separated `key:value` pairs setting the maximum document age in months per document type — keys: `utility_bill`, `bank_statement`, `government_issued_document`, `other_poa_document`; values: `1`–`120`, or `-1` for unlimited (no age check). Defaults when omitted: `utility_bill:3,bank_statement:3,government_issued_document:12,other_poa_document:12`. A document older than its limit declines with `POA_DOCUMENT_EXPIRED`. **Caution:** when you provide this field, list *every* type you accept — omitted types are treated as not accepted (informational `POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION`) and their age check is skipped.", - "example": "utility_bill:3,bank_statement:6" - }, - "poa_name_mismatch_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "DECLINE", - "description": "Accepted but **currently not applied** — name-mismatch warnings (`NAME_MISMATCH_WITH_PROVIDED`) follow `poa_address_mismatch_action` instead. Set `poa_address_mismatch_action` to control both name and address mismatch behavior." - }, - "poa_document_issues_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "DECLINE", - "description": "What to do on file-integrity problems — in practice only `DOCUMENT_METADATA_MISMATCH` is produced by this endpoint." - }, - "poa_document_authenticity_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "DECLINE", - "description": "What to do when document manipulation is suspected (`SUSPECTED_DOCUMENT_MANIPULATION`) — e.g. the PDF was modified after digital signing, processed by a known PDF editor, shows overlay-text manipulation, or has inconsistent EXIF dates." - }, - "poa_unsupported_language_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "DECLINE", - "description": "What to do when the document language is outside `poa_languages_allowed` (`UNSUPPORTED_DOCUMENT_LANGUAGE`)." - }, - "poa_address_mismatch_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "DECLINE", - "description": "What to do when the extracted details disagree with the `expected_*` fields: `ADDRESS_MISMATCH_WITH_PROVIDED`, `NAME_MISMATCH_WITH_PROVIDED`, and `POA_COUNTRY_MISMATCH_WITH_PROVIDED` all follow this action." - }, - "poa_issuer_not_identified_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "DECLINE", - "description": "What to do when no issuing company/organization can be identified on the document (`ISSUER_NOT_IDENTIFIED`)." - }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, announced through a `status.updated` webhook, and with the uploaded document stored. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only.", - "example": true - }, - "vendor_data": { - "type": "string", - "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", - "example": "user-123" - }, - "metadata": { - "type": "object", - "additionalProperties": true, - "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"onboarding\"}`) — it is parsed into an object.", - "example": { - "flow": "onboarding" - } - } + "responses": { + "200": { + "description": "The job.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OngoingMonitoringJob" } - }, - "example": { - "document": "(binary JPEG/PNG/PDF of the utility bill or bank statement)", - "expected_address": "Av. Monseñor Tavella 1291, Salta, Argentina", - "expected_country": "ARG", - "expected_first_name": "Sophia", - "expected_last_name": "Martinez", - "poa_languages_allowed": "en,es", - "save_api_request": true, - "vendor_data": "user-123" } } + }, + "404": { + "description": "Unknown job for this application." } }, - "responses": { - "200": { - "description": "Document processed. `poa.status` is `Approved` or `Declined`; every detected issue is itemized in `poa.warnings`. An expired, mismatched, or suspicious document still returns `200` with `status: \"Declined\"` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", - "content": { - "application/json": { - "examples": { - "Approved": { - "summary": "Utility bill read cleanly, no expected-details checks", - "value": { - "request_id": "4b523a8d-ed32-45e4-8aa8-a89eda459d88", - "poa": { - "issuing_state": "ARG", - "document_type": "UTILITY_BILL", - "document_subtype": "WATER_BILL", - "document_language": "es", - "issuer": "Aguas del Norte", - "issue_date": "2026-04-10", - "expiration_date": null, - "poa_address": "AVDA. MONSEÑOR TAVELLA N° 1291, B° VILLA LAVALLE CP: 4400, CAPITAL - SALTA", - "poa_formatted_address": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina", - "poa_parsed_address": { - "street_1": "Avenida Monseñor Tavella 1291", - "street_2": "b", - "city": "Salta", - "region": "Salta", - "country": "AR", - "postal_code": "A4400", - "document_location": { - "latitude": -24.8208682, - "longitude": -65.4130954 - } - }, - "expected_details_address": null, - "expected_details_formatted_address": null, - "expected_details_parsed_address": null, - "name_on_document": "OESTE EMBOTELLADORA S.A.", - "extra_fields": { - "bank_account_number": null, - "bank_iban": null, - "bank_sort_code": null, - "bank_routing_number": null, - "bank_swift_bic": null, - "bank_branch_name": null, - "bank_branch_address": null, - "document_phone_number": null, - "additional_names": [ - "LA EMBOTELLADORA DEL NORTE SA" - ] - }, - "detected_codes": [ - { - "type": "QR_CODE", - "page_number": 1, - "position": [ - [ - 820, - 1120 - ], - [ - 1002, - 1120 - ], - [ - 1002, - 1302 - ], - [ - 820, - 1302 - ] - ], - "x": 820, - "y": 1120, - "width": 182, - "height": 182, - "page_width": 1190, - "page_height": 1684, - "raw_payload": "aHR0cHM6Ly9hZ3Vhc2RlbG5vcnRlLmNvbS9mYWN0dXJhLzk5ODgxMQ==", - "parsed_payload": "https://aguasdelnorte.com/factura/998811" - }, - { - "type": "CODE_128", - "page_number": 1, - "position": [ - [ - 112, - 1480 - ], - [ - 560, - 1480 - ], - [ - 560, - 1540 - ], - [ - 112, - 1540 - ] - ], - "x": 112, - "y": 1480, - "width": 448, - "height": 60, - "page_width": 1190, - "page_height": 1684, - "raw_payload": "MDA5OTg4MTEwMDA0Mg==", - "parsed_payload": "0099881100042" - } - ], - "status": "Approved", - "warnings": [] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T02:29:02.439655+00:00" - } - }, - "Declined - document too old": { - "summary": "Issue date older than the allowed `poa_document_age_months`", - "value": { - "request_id": "f61681b8-9422-4378-ac3e-04c6a247fb45", - "poa": { - "issuing_state": "ARG", - "document_type": "UTILITY_BILL", - "document_subtype": "WATER_BILL", - "document_language": "es", - "issuer": "Aguas del Norte", - "issue_date": "2021-02-10", - "expiration_date": null, - "poa_address": "AVDA. MONSEÑOR TAVELLA N° 1291, B° VILLA LAVALLE CP: 4400, CAPITAL - SALTA", - "poa_formatted_address": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina", - "poa_parsed_address": { - "street_1": "Avenida Monseñor Tavella 1291", - "street_2": "b", - "city": "Salta", - "region": "Salta", - "country": "AR", - "postal_code": "A4400", - "document_location": { - "latitude": -24.8208682, - "longitude": -65.4130954 - } - }, - "expected_details_address": null, - "expected_details_formatted_address": null, - "expected_details_parsed_address": null, - "name_on_document": "OESTE EMBOTELLADORA S.A.", - "extra_fields": { - "bank_account_number": null, - "bank_iban": null, - "bank_sort_code": null, - "bank_routing_number": null, - "bank_swift_bic": null, - "bank_branch_name": null, - "bank_branch_address": null, - "document_phone_number": null, - "additional_names": [ - "LA EMBOTELLADORA DEL NORTE SA" - ] - }, - "status": "Declined", - "warnings": [ - { - "risk": "POA_DOCUMENT_EXPIRED", - "feature": "PROOF_OF_ADDRESS", - "additional_data": { - "expiration_reason": "age_window", - "max_age_months": 3, - "document_subtype": "WATER_BILL", - "document_type": "UTILITY_BILL", - "issue_date": "2021-02-10" - }, - "log_type": "error", - "short_description": "Document expired", - "long_description": "The submitted document is older than the maximum accepted age of 3 months from its issue date, which exceeds the acceptable time period for validity." - } - ] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T02:23:02.165493+00:00" - } - }, - "Declined - name mismatch": { - "summary": "`expected_first_name`/`expected_last_name` do not match `name_on_document`", - "value": { - "request_id": "0a7f2f9c-58f1-4b8e-b1e3-2f6f9d4f7a21", - "poa": { - "issuing_state": "ARG", - "document_type": "UTILITY_BILL", - "document_subtype": "WATER_BILL", - "document_language": "es", - "issuer": "Aguas del Norte", - "issue_date": "2021-02-10", - "expiration_date": null, - "poa_address": "AVDA. MONSEÑOR TAVELLA N° 1291, B° VILLA LAVALLE CP: 4400, CAPITAL - SALTA", - "poa_formatted_address": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina", - "poa_parsed_address": { - "street_1": "Avenida Monseñor Tavella 1291", - "street_2": "b", - "city": "Salta", - "region": "Salta", - "country": "AR", - "postal_code": "A4400", - "document_location": { - "latitude": -24.8208682, - "longitude": -65.4130954 - } - }, - "expected_details_address": null, - "expected_details_formatted_address": null, - "expected_details_parsed_address": null, - "name_on_document": "OESTE EMBOTELLADORA S.A.", - "extra_fields": { - "bank_account_number": null, - "bank_iban": null, - "bank_sort_code": null, - "bank_routing_number": null, - "bank_swift_bic": null, - "bank_branch_name": null, - "bank_branch_address": null, - "document_phone_number": null, - "additional_names": [ - "LA EMBOTELLADORA DEL NORTE SA" - ] - }, - "status": "Declined", - "warnings": [ - { - "risk": "NAME_MISMATCH_WITH_PROVIDED", - "feature": "PROOF_OF_ADDRESS", - "additional_data": null, - "log_type": "error", - "short_description": "Name mismatch with provided information", - "long_description": "The full name on the document does not match the name from the user's verified identity documents, or the full name sent by API." - } - ] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T02:27:21.330172+00:00" - } - } - }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/": { + "get": { + "summary": "List cases", + "description": "List cases for the application, newest first. Supports filtering, search, and sorting; see query parameters. Paginated with `limit`/`offset`.", + "operationId": "list_cases", + "x-didit-sdk": { + "resource": "cases", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "description": "Page size. Defaults to 50." + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset of the first record." + }, + { + "name": "status__in", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Comma-separated statuses to include, e.g. OPEN,AWAITING_USER." + }, + { + "name": "priority", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Comma-separated priorities to include." + }, + { + "name": "source__in", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Comma-separated sources to include." + }, + { + "name": "blueprint", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Filter to cases on this blueprint." + }, + { + "name": "assigned_to", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Filter to cases assigned to this user." + }, + { + "name": "unassigned", + "in": "query", + "schema": { + "type": "boolean" + }, + "description": "true for unassigned cases, false for assigned cases." + }, + { + "name": "tag", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Filter to cases carrying this exact tag." + }, + { + "name": "search", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Matches title, case_number, tag, subject name/UUID/external ID, linked session UUID, or linked transaction UUID." + }, + { + "name": "date_from", + "in": "query", + "schema": { + "type": "string", + "format": "date" + }, + "description": "Created-at lower bound (inclusive)." + }, + { + "name": "date_to", + "in": "query", + "schema": { + "type": "string", + "format": "date" + }, + "description": "Created-at upper bound (inclusive)." + }, + { + "name": "due_before", + "in": "query", + "schema": { + "type": "string", + "format": "date-time" + }, + "description": "due_at upper bound." + }, + { + "name": "overdue", + "in": "query", + "schema": { + "type": "boolean" + }, + "description": "true to return only overdue open/awaiting-user cases." + }, + { + "name": "ordering", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "created_at", + "-created_at", + "due_at", + "-due_at" + ], + "default": "-created_at" + }, + "description": "Sort order. due_at sorts nulls last." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/?status__in=OPEN,AWAITING_USER&ordering=-due_at' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'status__in': 'OPEN,AWAITING_USER', 'ordering': '-due_at'},\n timeout=10,\n)\nresp.raise_for_status()\nfor case in resp.json()['results']:\n print(case['case_number'], case['title'], case['priority'], case['due_at'])" + } + ], + "responses": { + "200": { + "description": "Paginated case list.", + "content": { + "application/json": { "schema": { "type": "object", "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." - }, - "poa": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined" - ], - "description": "`Approved` when no warning resolves to a decline; `Declined` otherwise — the `warnings` list explains why." - }, - "issuing_state": { - "type": "string", - "nullable": true, - "description": "Country the document was issued in, as extracted from the document — usually ISO 3166-1 alpha-3 (e.g. `ARG`).", - "example": "ARG" - }, - "document_type": { - "type": "string", - "enum": [ - "UTILITY_BILL", - "BANK_STATEMENT", - "GOVERNMENT_ISSUED_DOCUMENT", - "OTHER_POA_DOCUMENT", - "UNKNOWN" - ], - "description": "Detected document category. `UNKNOWN` means the document could not be classified as an acceptable Proof of Address and auto-declines with `INVALID_DOCUMENT_TYPE`. Never null — classification failures fall back to `UNKNOWN`." - }, - "document_subtype": { - "type": "string", - "description": "Finer-grained classification within `document_type` — e.g. `ELECTRICITY_BILL`, `WATER_BILL`, `GAS_BILL`, `INTERNET_BILL`, `PHONE_BILL` (utility bills); `ACCOUNT_STATEMENT`, `CREDIT_CARD_STATEMENT` (bank statements); `TAX_ASSESSMENT`, `RESIDENCY_CERTIFICATE` (government documents). `UNKNOWN` when no subtype could be determined. Never null — classification failures fall back to `UNKNOWN`.", - "example": "WATER_BILL" - }, - "document_language": { - "type": "string", - "nullable": true, - "description": "Detected document language (ISO 639-1).", - "example": "es" - }, - "issuer": { - "type": "string", - "nullable": true, - "description": "Company or organization that issued the document. `null` adds `ISSUER_NOT_IDENTIFIED`.", - "example": "Aguas del Norte" - }, - "issue_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Document issue date (`YYYY-MM-DD`). `null` when it could not be extracted — that declines with `UNABLE_TO_EXTRACT_ISSUE_DATE` on this endpoint.", - "example": "2021-02-10" - }, - "expiration_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Explicit expiry/validity date printed on the document, when present. Independent of the `poa_document_age_months` age check." - }, - "poa_address": { - "type": "string", - "nullable": true, - "description": "Address exactly as printed on the document. `null` auto-declines with `MISSING_ADDRESS_INFORMATION`." - }, - "poa_formatted_address": { - "type": "string", - "nullable": true, - "description": "Geocoded, normalized version of `poa_address`.", - "example": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina" - }, - "poa_parsed_address": { - "type": "object", - "nullable": true, - "description": "Structured, geocoded breakdown of the document address. `null` when the address could not be parsed.", - "properties": { - "street_1": { - "type": "string", - "nullable": true, - "example": "Avenida Monseñor Tavella 1291" - }, - "street_2": { - "type": "string", - "nullable": true - }, - "city": { - "type": "string", - "nullable": true, - "example": "Salta" - }, - "region": { - "type": "string", - "nullable": true, - "example": "Salta" - }, - "country": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-2 country code.", - "example": "AR" - }, - "postal_code": { - "type": "string", - "nullable": true, - "example": "A4400" - }, - "document_location": { - "type": "object", - "nullable": true, - "properties": { - "latitude": { - "type": "number" - }, - "longitude": { - "type": "number" - } - }, - "description": "Geocoded coordinates of the document address." - } - } - }, - "expected_details_address": { - "type": "string", - "nullable": true, - "description": "Echo of the `expected_address` you sent, or `null`." - }, - "expected_details_formatted_address": { - "type": "string", - "nullable": true, - "description": "Geocoded, normalized version of `expected_address`; `null` when not provided or not geocodable." - }, - "expected_details_parsed_address": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Full geocoding result for `expected_address` (street/city/region/country/postal code plus raw geocoder output and an `is_verified` flag). `null` when `expected_address` was not provided." - }, - "name_on_document": { - "type": "string", - "nullable": true, - "description": "Primary holder name extracted from the document; compared against `expected_first_name`/`expected_last_name` when provided. `null` declines with `POA_NAME_NOT_DETECTED` on this endpoint.", - "example": "OESTE EMBOTELLADORA S.A." - }, - "extra_fields": { - "type": "object", - "description": "Document-specific extras. Bank fields are populated for bank statements when printed on the document; `null` otherwise.", - "properties": { - "bank_account_number": { - "type": "string", - "nullable": true - }, - "bank_iban": { - "type": "string", - "nullable": true - }, - "bank_sort_code": { - "type": "string", - "nullable": true - }, - "bank_routing_number": { - "type": "string", - "nullable": true - }, - "bank_swift_bic": { - "type": "string", - "nullable": true - }, - "bank_branch_name": { - "type": "string", - "nullable": true - }, - "bank_branch_address": { - "type": "string", - "nullable": true - }, - "document_phone_number": { - "type": "string", - "nullable": true, - "description": "Contact phone number printed on the document." - }, - "additional_names": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Other holder names found on the document (e.g. co-account holders, previous account names)." - } - } - }, - "warnings": { - "type": "array", - "description": "Empty on a clean approval. Every entry explains one detected risk; entries with `log_type: \"error\"` set `status` to `Declined`.", - "items": { - "type": "object", - "properties": { - "risk": { - "type": "string", - "description": "Machine-readable risk code. Always-decline: `MISSING_ADDRESS_INFORMATION`, `POA_DOCUMENT_EXPIRED`, `INVALID_DOCUMENT_TYPE`, `UNABLE_TO_VALIDATE_DOCUMENT_AGE`, plus `UNABLE_TO_EXTRACT_ISSUE_DATE` and `POA_NAME_NOT_DETECTED` (fixed to decline on this endpoint). Governed by request options: `NAME_MISMATCH_WITH_PROVIDED` / `ADDRESS_MISMATCH_WITH_PROVIDED` / `POA_COUNTRY_MISMATCH_WITH_PROVIDED` (`poa_address_mismatch_action`), `POOR_DOCUMENT_QUALITY` (never produced by this endpoint) / `DOCUMENT_METADATA_MISMATCH` (`poa_document_issues_action`), `SUSPECTED_DOCUMENT_MANIPULATION` (`poa_document_authenticity_action`), `UNSUPPORTED_DOCUMENT_LANGUAGE` (`poa_unsupported_language_action`), `ISSUER_NOT_IDENTIFIED` (`poa_issuer_not_identified_action`). Informational on this endpoint: `POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION`, `UNPARSABLE_OR_INVALID_ADDRESS`.", - "example": "POA_DOCUMENT_EXPIRED" - }, - "feature": { - "type": "string", - "enum": [ - "PROOF_OF_ADDRESS" - ], - "description": "Feature that raised the warning. Always `PROOF_OF_ADDRESS` on this endpoint." - }, - "additional_data": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Extra context for some risks — e.g. `POA_DOCUMENT_EXPIRED` includes `expiration_reason` (`printed_expiration` or `age_window`), `document_type`, `document_subtype`, and `issue_date`, plus `expiration_date` on the `printed_expiration` path or `max_age_months` on the `age_window` path; `SUSPECTED_DOCUMENT_MANIPULATION` includes `reason` and `detection_method`. `null` for most risks." - }, - "log_type": { - "type": "string", - "enum": [ - "error", - "information" - ], - "description": "Severity. `error` warnings drive `status` to `Declined`; `information` entries are advisory and never decline on their own. (`warning` is not produced by this endpoint — its options only accept `DECLINE` or `NO_ACTION`.)" - }, - "short_description": { - "type": "string", - "description": "Human-readable one-line summary of the risk." - }, - "long_description": { - "type": "string", - "description": "Human-readable explanation of the risk." - } - } - } - }, - "detected_codes": { - "type": "array", - "description": "QR codes and barcodes detected across every processed page of the document. Empty when none are found. A located but undecodable QR code has `parsed_payload: null`.", - "items": { - "$ref": "#/components/schemas/DocumentAIDetectedCode" - } - } - } + "count": { + "type": "integer", + "description": "Number of matching rows. Exact up to 100, capped at 100 beyond that for performance. Rely on `next` being null to detect the last page." }, - "vendor_data": { + "next": { "type": "string", "nullable": true, - "description": "Echo of the `vendor_data` you sent, or `null`." + "description": "Absolute URL of the next page, or null." }, - "metadata": { - "type": "object", + "previous": { + "type": "string", "nullable": true, - "additionalProperties": true, - "description": "Echo of the `metadata` object you sent, or `null`." + "description": "Absolute URL of the previous page, or null." }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp (UTC) of when the response was generated." + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseListItem" + } + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "post": { + "summary": "Create case", + "description": "Create a case anchored to exactly one subject (a user or a business), directly or by deriving the subject from an attached session/transaction link. Source is always `MANUAL` for API-created cases.", + "operationId": "create_case", + "x-didit-sdk": { + "resource": "cases", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "title" + ], + "properties": { + "title": { + "type": "string", + "maxLength": 255 + }, + "blueprint": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "CaseBlueprint UUID. Omit for no blueprint." + }, + "priority": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "default": "MEDIUM" + }, + "due_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "subject": { + "type": "object", + "description": "Required unless links is provided; the case's exactly-one subject.", + "properties": { + "type": { + "type": "string", + "enum": [ + "user", + "business" + ] + }, + "uuid": { + "type": "string", + "format": "uuid" + } + } + }, + "links": { + "type": "array", + "description": "Sessions/business sessions/transactions to attach on creation, e.g. [{\"entity_type\": \"transaction\", \"entity_uuid\": \"...\"}]. When subject is omitted, the subject is derived from the first link's owner.", + "items": { + "type": "object" } + }, + "assigned_to": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "User UUID to assign immediately." + }, + "review_duplicates": { + "type": "boolean", + "default": false, + "description": "Recheck existing investigations before creating the case. When true, a subject with existing cases requires a nonblank separate_incident_reason. The console always enables this protection. Omitting it preserves existing API behavior." + }, + "separate_incident_reason": { + "type": "string", + "maxLength": 1000 + }, + "source_context": { + "$ref": "#/components/schemas/CaseDuplicateCaseFraudSourceContextRequest" } } } } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"title\": \"Manual - Jane Doe\", \"blueprint\": \"b1de0000-0000-4000-8000-000000000002\", \"priority\": \"HIGH\", \"subject\": {\"type\": \"user\", \"uuid\": \"5b9a0000-0000-4000-8000-00000000f00d\"}, \"tags\": [\"review\"]}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'title': 'Manual - Jane Doe',\n 'blueprint': 'b1de0000-0000-4000-8000-000000000002',\n 'priority': 'HIGH',\n 'subject': {'type': 'user', 'uuid': '5b9a0000-0000-4000-8000-00000000f00d'},\n },\n timeout=10,\n)\nresp.raise_for_status()\ncase = resp.json()\nprint(case['case_number'], case['uuid'])" + } + ], + "responses": { + "201": { + "description": "Case created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseDetail" + } + } + } }, "400": { - "description": "Validation or processing error. Field-level problems return DRF's standard envelope (one array of messages per offending field). Encrypted-PDF problems return `{\"detail\": ...}`. A document the extraction pipeline cannot process returns `{\"error\": [\"Error extracting POA information\"]}`.", + "description": "Neither subject nor links provided, or the subject/blueprint/assignee was not found.", "content": { "application/json": { - "examples": { - "Missing document": { - "summary": "`document` not included in the form data", - "value": { - "document": [ - "No file was submitted." - ] - } - }, - "Unsupported file extension": { - "summary": "File extension outside tiff/jpg/jpeg/png/pdf/webp", - "value": { - "document": [ - "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, pdf, webp." - ] + "schema": { + "type": "object" + } + } + } + }, + "409": { + "description": "Duplicate review is enabled and an existing case was found without a separate-incident reason. No case was created." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/": { + "get": { + "summary": "Get case", + "description": "Retrieve a case with its subject, blueprint, notes, events, links, and checklist.", + "operationId": "get_case", + "x-didit-sdk": { + "resource": "cases", + "method": "retrieve", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Case detail.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseDetail" + } + } + } + }, + "404": { + "description": "Case (or nested resource) not found in this application.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "patch": { + "summary": "Update case", + "description": "Update the case's title, priority, due date, tags, or owning review team. Send only the fields you want to change; fields you omit keep their value. `tags` replaces the whole tag list. `team` takes the uuid of an active review team of this application (list them with `GET .../cases/teams/`), or null to clear the team. Requires a full-access API key, or `write:cases` for a console member: a key with restricted access gets 403. Logs PRIORITY_CHANGED, DUE_DATE_CHANGED, TAG_ADDED, TAG_REMOVED and TEAM_CHANGED events as applicable; a title change is saved without a timeline event. Returns the updated case.", + "operationId": "update_case", + "x-didit-sdk": { + "resource": "cases", + "method": "update", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string", + "maxLength": 255, + "description": "New case title." }, - "File too large": { - "summary": "Upload exceeds the 15 MB limit", - "value": { - "document": [ - "File size should not exceed 15 MB" - ] - } + "priority": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] }, - "Encrypted PDF": { - "summary": "PDF uploaded without the required password", - "value": { - "detail": "The PDF is encrypted. Please upload a decrypted PDF or a photo instead." - } + "due_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "New deadline. Send null to clear it." }, - "Wrong PDF password": { - "summary": "`document_password` does not decrypt the PDF", - "value": { - "detail": "The PDF password is incorrect. Please provide the correct password." - } + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The complete tag list. It replaces the current tags." }, - "Invalid language code": { - "summary": "`poa_languages_allowed` contains an unsupported code", - "value": { - "poa_languages_allowed": [ - "Invalid language code: 'xx'. Must be one of the supported languages: ['ar', 'bn', 'hy', 'bg', 'bs', 'ca', 'cnr', 'sq', 'zh', 'hr', 'cs', 'da', 'nl', 'en', 'et', 'fi', 'fr', 'ka', 'kk', 'de', 'el', 'he', 'hi', 'hu', 'id', 'it', 'ja', 'ko', 'ky', 'lv', 'lt', 'mk', 'mn', 'ms', 'no', 'fa', 'pl', 'pt', 'ro', 'ru', 'sr', 'sk', 'sl', 'so', 'es', 'sv', 'th', 'tr', 'uk', 'uz', 'vi']." - ] + "team": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "UUID of the review team that owns the case. It must be an active team of this application. Send null to clear the team." + } + } + }, + "examples": { + "Change priority and deadline": { + "value": { + "priority": "HIGH", + "due_at": "2026-11-01T00:00:00Z" + } + }, + "Move the case to a team": { + "value": { + "team": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + } + }, + "Clear the team": { + "value": { + "team": null + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"priority\": \"HIGH\", \"due_at\": \"2026-08-01T00:00:00Z\"}'" + }, + { + "lang": "curl", + "label": "curl (change team)", + "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"team\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\"}'" + } + ], + "responses": { + "200": { + "description": "Updated case.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseDetail" + } + } + } + }, + "400": { + "description": "A field failed validation. The body is keyed by field name. `team` is rejected when it is not an active review team of this application.", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" } - }, - "Invalid document age value": { - "summary": "`poa_document_age_months` value outside 1–120 / -1", + } + }, + "examples": { + "Team not active in this application": { "value": { - "poa_document_age_months": [ - "Invalid integer value for 'utility_bill': '999'. Must be -1 (unlimited) or a positive integer between 1 and 120." + "team": [ + "Choose an active team of this application." ] } }, - "Extraction failure": { - "summary": "The document could not be processed by the extraction pipeline", + "Invalid priority": { "value": { - "error": [ - "Error extracting POA information" + "priority": [ + "\"URGENT\" is not a valid choice." ] } } @@ -14166,38 +34904,30 @@ } }, "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any document processing happens.", + "description": "The API key belongs to another application, or it is a key with restricted access: Cases endpoints need a full-access key. A console member token needs `write:cases`.", "content": { "application/json": { "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", + "Restricted API Key": { + "summary": "API key with restricted access", "value": { - "detail": "You do not have permission to perform this action." + "detail": "This API key has no access to this endpoint." } }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", + "Missing Privilege": { + "summary": "Console member without write:cases, or a key of another application", "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + "detail": "You do not have permission to perform this action." } } } } } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "404": { + "description": "Case not found in this application.", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { "type": "object", "properties": { @@ -14205,6 +34935,9 @@ "type": "string" } } + }, + "example": { + "detail": "No Case matches the given query." } } } @@ -14214,831 +34947,653 @@ { "ApiKeyAuth": [] } + ] + }, + "delete": { + "summary": "Delete case", + "description": "Delete a case. There is no undelete endpoint.", + "operationId": "delete_case", + "x-didit-sdk": { + "resource": "cases", + "method": "delete", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" ], - "x-codeSamples": [ + "parameters": [ { - "lang": "curl", - "label": "cURL", - "source": "curl -X POST 'https://verification.didit.me/v3/poa/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'document=@./utility-bill.pdf' \\\n -F 'expected_address=Av. Monseñor Tavella 1291, Salta, Argentina' \\\n -F 'expected_country=ARG' \\\n -F 'expected_first_name=Sophia' \\\n -F 'expected_last_name=Martinez' \\\n -F 'poa_languages_allowed=en,es' \\\n -F 'vendor_data=user-123' \\\n --max-time 45" + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, { - "lang": "python", - "label": "Python", - "source": "import requests\n\nurl = 'https://verification.didit.me/v3/poa/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('utility-bill.pdf', 'rb') as f:\n files = {'document': ('utility-bill.pdf', f, 'application/pdf')}\n data = {\n 'expected_address': 'Av. Monseñor Tavella 1291, Salta, Argentina',\n 'expected_country': 'ARG',\n 'expected_first_name': 'Sophia',\n 'expected_last_name': 'Martinez',\n 'poa_languages_allowed': 'en,es',\n 'vendor_data': 'user-123',\n }\n # LLM-based extraction takes 5-15s (multi-page PDFs up to ~30s) - allow headroom\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=45)\n\nresp.raise_for_status()\npoa = resp.json()['poa']\nprint('status:', poa['status'])\nprint('issuer:', poa['issuer'], '| issued:', poa['issue_date'])\nprint('address:', poa['poa_formatted_address'])\nfor w in poa['warnings']:\n print('warning:', w['risk'])" + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." }, { - "lang": "javascript", - "label": "JavaScript", - "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('document', new Blob([fs.readFileSync('./utility-bill.pdf')]), 'utility-bill.pdf');\nform.append('expected_address', 'Av. Monseñor Tavella 1291, Salta, Argentina');\nform.append('expected_country', 'ARG');\nform.append('expected_first_name', 'Sophia');\nform.append('expected_last_name', 'Martinez');\nform.append('poa_languages_allowed', 'en,es');\nform.append('vendor_data', 'user-123');\n\n// LLM-based extraction takes 5-15s (multi-page PDFs up to ~30s) - allow >=45s\nconst response = await fetch('https://verification.didit.me/v3/poa/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n signal: AbortSignal.timeout(45_000),\n});\n\nif (!response.ok) throw new Error(`POA verification failed: ${response.status}`);\nconst { poa } = await response.json();\nconsole.log('status:', poa.status);\nconsole.log('warnings:', poa.warnings.map((w) => w.risk));" + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "204": { + "description": "Case deleted." + } + }, + "security": [ + { + "ApiKeyAuth": [] } ] } }, - "/v3/document-ai/": { + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/assign/": { "post": { - "summary": "Document AI (custom field extraction + tamper checks)", - "description": "Extract the fields **you** define from any supporting document — payslips, proof of funds, source-of-wealth letters, tax statements, contracts, invoices — in one call. Send the file plus a `fields` definition (key, name, extraction instruction, type) and get back typed values (`text`, `number`, `date` as `YYYY-MM-DD`), an optional name-match score against the expected person or company, PDF/EXIF tamper signals, an `Approved`/`Declined`/`In Review` `status`, and a `warnings` list explaining every issue found. This is the server-to-server counterpart of the hosted Document AI workflow step (see `/core-technology/document-ai/overview`); it uses Didit's default extraction model and does not expose provider selection.\n\n**Latency.** Extraction is LLM-based: typical calls take **5–15 seconds**, multi-page PDFs up to **~30 seconds**. Configure a client timeout of **at least 45 seconds** and do not retry before the call completes.\n\n**Request format.** `multipart/form-data` only. `fields` is **one JSON-encoded array string** (1–30 items, at most 16 KB); bracket-style form arrays are rejected with `400`. Each item is `{key, name, instruction, type, required, is_full_name}` — `key` is the JSON key you get back (`^[a-z0-9_]{1,64}$`, unique), `instruction` tells the model what to read, `type` is `text` (default) | `number` | `date`, `required` raises `DOCUMENT_AI_MISSING_REQUIRED_FIELDS` when the value cannot be read, and at most one field may set `is_full_name: true` to mark the value compared against the expected name.\n\n**Name matching.** Pass `expected_first_name` + `expected_last_name` (person) **or** `expected_company_name` (+ optional `expected_company_country`) — not both. The value of the `is_full_name` field (or, when none is marked, the first text field whose key looks like a name) is compared fuzzily; `name_match_score` (0–100) and `name_on_document` are always returned when both sides exist, and a score below `document_ai_name_match_score_threshold` (default 80) adds `DOCUMENT_AI_NAME_MISMATCH`, governed by `document_ai_name_mismatch_action`.\n\n**Decision logic.** Each risk maps to a configurable action: `DECLINE` → `status: \"Declined\"`, `REVIEW` → `status: \"In Review\"`, `NO_ACTION` → informational only. Defaults mirror the workflow step: unreadable document `REVIEW`, missing required fields `REVIEW`, suspected manipulation `DECLINE`, name mismatch `NO_ACTION`, unsupported file `REVIEW`. A document that cannot be processed at all returns `400`; a readable but problematic document returns `200` with `Declined`/`In Review` — always inspect `document_ai.status` and `document_ai.warnings`, not just the HTTP code.\n\n**Billing.** Each `200` response consumes one Document AI API credit — **$0.20 per document, no free tier** (standalone APIs never have one). When the organization's balance cannot cover the call, the endpoint returns `403` before any processing. Sandbox API keys return a deterministic mock response and are not charged.\n\n**Persistence.** With `save_api_request=true` (default) the call is stored as an API-type session: the document, the extracted values, the field definitions and the name-match snapshot are kept, the session shows up in the Business Console, and `request_id` works with `GET /v3/session/{sessionId}/decision/`.", - "operationId": "post_v3document-ai", + "summary": "Assign or unassign case", + "description": "Assign the case to a user, or unassign it by sending assignee_user_id: null. assignee_email/assignee_name auto-provision a User row when the assignee does not exist yet.", + "operationId": "assign_case", + "x-didit-sdk": { + "resource": "cases", + "method": "assign", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Standalone APIs" + "Cases" ], - "security": [ + "parameters": [ { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "required": [ - "document", - "fields" - ], - "properties": { - "document": { - "type": "string", - "format": "binary", - "description": "The document to read. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `pdf`, `webp`, `zip`. Maximum upload size: **30 MB** (larger files are rejected with `400`). Multi-page PDFs are supported (expect higher latency); encrypted PDFs are rejected with `400`. Images are automatically compressed before processing." - }, - "fields": { - "type": "string", - "description": "One JSON-encoded array string with **1–30** field definitions (maximum 16 KB). Each item: `key` (required, `^[a-z0-9_]{1,64}$`, unique — the key returned in `extracted_data`), `name` (≤100 chars, shown in messages), `instruction` (≤500 chars, what the model should read), `type` (`text` default | `number` | `date`), `required` (boolean, default `false`), `is_full_name` (boolean, default `false`, at most one per request). Bracket-style form arrays, invalid JSON, duplicate keys, or more than one `is_full_name` return `400` before any processing or billing.", - "example": "[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]" - }, - "expected_first_name": { - "type": "string", - "description": "First name to verify against the document's name field (fuzzy match, transliteration-aware). Use together with `expected_last_name`; cannot be combined with `expected_company_name`.", - "example": "Sophia" - }, - "expected_last_name": { - "type": "string", - "description": "Last name to verify against the document's name field, combined with `expected_first_name` for `name_match_score`.", - "example": "Martinez" - }, - "expected_company_name": { - "type": "string", - "description": "Company name to verify against the document's name field (for business documents). Cannot be combined with `expected_first_name` / `expected_last_name` (no example value on purpose — the generated request sample uses the person fields; swap them for this one when verifying a business document)." - }, - "expected_company_country": { - "type": "string", - "description": "Optional ISO 3166-1 alpha-2 country code (two ASCII letters, e.g. `FR`) that tunes company-name normalization for `expected_company_name`. Only used with `expected_company_name`." - }, - "document_ai_name_match_score_threshold": { - "type": "integer", - "minimum": 0, - "maximum": 100, - "default": 80, - "description": "Minimum `name_match_score` (0–100) to consider the name matched. A lower score adds `DOCUMENT_AI_NAME_MISMATCH`, governed by `document_ai_name_mismatch_action`.", - "example": 80 - }, - "document_ai_unreadable_document_action": { - "type": "string", - "enum": [ - "DECLINE", - "REVIEW", - "NO_ACTION" - ], - "default": "REVIEW", - "description": "What to do when no field could be read from the document (`DOCUMENT_AI_UNREADABLE_DOCUMENT`)." - }, - "document_ai_missing_required_fields_action": { - "type": "string", - "enum": [ - "DECLINE", - "REVIEW", - "NO_ACTION" - ], - "default": "REVIEW", - "description": "What to do when at least one `required` field could not be read (`DOCUMENT_AI_MISSING_REQUIRED_FIELDS`; `additional_data.missing_fields` lists them)." - }, - "document_ai_document_tampering_action": { - "type": "string", - "enum": [ - "DECLINE", - "REVIEW", - "NO_ACTION" - ], - "default": "DECLINE", - "description": "What to do when PDF/EXIF forensics suspect manipulation — modified after digital signing, known PDF editor, overlay-text manipulation, suspicious re-export, image anomalies (`DOCUMENT_AI_SUSPECTED_MANIPULATION`)." - }, - "document_ai_name_mismatch_action": { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "assignee_user_id" + ], + "properties": { + "assignee_user_id": { "type": "string", - "enum": [ - "DECLINE", - "REVIEW", - "NO_ACTION" - ], - "default": "NO_ACTION", - "description": "What to do when `name_match_score` is below the threshold (`DOCUMENT_AI_NAME_MISMATCH`)." + "format": "uuid", + "nullable": true, + "description": "null to unassign." }, - "document_ai_unsupported_file_action": { + "assignee_email": { "type": "string", - "enum": [ - "DECLINE", - "REVIEW", - "NO_ACTION" - ], - "default": "REVIEW", - "description": "What to do when the upload is empty or not a processable file (`DOCUMENT_AI_UNSUPPORTED_FILE`)." - }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, with the uploaded document, the extracted values, the field definitions and the name-match snapshot stored. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only; the call is still billed.", - "example": true + "format": "email", + "nullable": true }, - "vendor_data": { + "assignee_name": { "type": "string", - "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.", - "example": "user-123" - }, - "metadata": { - "type": "object", - "additionalProperties": true, - "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"onboarding\"}`) — it is parsed into an object.", - "example": { - "flow": "onboarding" - } + "nullable": true } } } } } }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/assign/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"assignee_user_id\": \"9a000000-0000-4000-8000-0000000000aa\"}'" + } + ], "responses": { "200": { - "description": "Document processed. `document_ai.status` is `Approved`, `Declined`, or `In Review`; every detected issue is itemized in `document_ai.warnings`. A problematic document still returns `200` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", + "description": "Case assigned/unassigned.", "content": { "application/json": { - "examples": { - "Approved": { - "summary": "Payslip read cleanly, name matched", - "value": { - "request_id": "9d6a3b1e-2f0c-4a8e-9b7d-1c2e3f4a5b6c", - "document_ai": { - "status": "Approved", - "extracted_data": { - "employer_name": "Acme Payments Ltd", - "employee_name": "Sophia Martinez", - "net_pay": 3120.5, - "pay_date": "2026-07-31" - }, - "detected_codes": [ - { - "type": "CODE_128", - "page_number": 1, - "position": [ - [ - 96, - 456 - ], - [ - 96, - 616 - ], - [ - 1012, - 611 - ], - [ - 1015, - 456 - ] - ], - "x": 96, - "y": 456, - "width": 919, - "height": 160, - "page_width": 1200, - "page_height": 1700, - "raw_payload": "SU5WLTIwMjYtMDA0Mg==", - "parsed_payload": "INV-2026-0042" - } - ], - "fields": [ - { - "key": "employer_name", - "name": "Employer name", - "instruction": "The employer or company issuing the payslip.", - "type": "text", - "required": true, - "is_full_name": false - }, - { - "key": "employee_name", - "name": "Employee name", - "instruction": "The full name of the employee as printed on the payslip.", - "type": "text", - "required": true, - "is_full_name": true - }, - { - "key": "net_pay", - "name": "Net pay", - "instruction": "The net amount paid to the employee for the period.", - "type": "number", - "required": true, - "is_full_name": false - }, - { - "key": "pay_date", - "name": "Pay date", - "instruction": "The payment date shown on the payslip.", - "type": "date", - "required": false, - "is_full_name": false - } - ], - "name_match_score": 100.0, - "name_on_document": "Sophia Martinez", - "warnings": [] - }, - "vendor_data": "user-123", - "metadata": { - "flow": "onboarding" - }, - "created_at": "2026-08-19T16:40:12.318Z" - } - }, - "In Review — missing required field": { - "summary": "`net_pay` could not be read; default action REVIEW", - "value": { - "request_id": "3f2a9c7b-5d4e-4b1a-8c6f-0e9d8c7b6a5f", - "document_ai": { - "status": "In Review", - "extracted_data": { - "employer_name": "Acme Payments Ltd", - "employee_name": "Sophia Martinez", - "net_pay": null, - "pay_date": "2026-07-31" - }, - "detected_codes": [], - "fields": [ - { - "key": "employer_name", - "name": "Employer name", - "instruction": "The employer or company issuing the payslip.", - "type": "text", - "required": true, - "is_full_name": false - }, - { - "key": "employee_name", - "name": "Employee name", - "instruction": "The full name of the employee as printed on the payslip.", - "type": "text", - "required": true, - "is_full_name": true - }, - { - "key": "net_pay", - "name": "Net pay", - "instruction": "The net amount paid to the employee for the period.", - "type": "number", - "required": true, - "is_full_name": false - }, - { - "key": "pay_date", - "name": "Pay date", - "instruction": "The payment date shown on the payslip.", - "type": "date", - "required": false, - "is_full_name": false - } - ], - "name_match_score": 100.0, - "name_on_document": "Sophia Martinez", - "warnings": [ - { - "risk": "DOCUMENT_AI_MISSING_REQUIRED_FIELDS", - "feature": "DOCUMENT_AI", - "additional_data": { - "missing_fields": [ - "Net pay" - ] - }, - "log_type": "warning", - "short_description": "Required fields could not be extracted", - "long_description": "One or more fields configured as required could not be extracted from the uploaded document." - } - ] - }, - "vendor_data": null, - "metadata": null, - "created_at": "2026-08-19T16:41:05.002Z" - } - }, - "Declined — suspected manipulation": { - "summary": "PDF forensics flagged a known editor; default action DECLINE", - "value": { - "request_id": "7c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f", - "document_ai": { - "status": "Declined", - "extracted_data": { - "employer_name": "Acme Payments Ltd", - "employee_name": "Sophia Martinez", - "net_pay": 3120.5, - "pay_date": "2026-07-31" - }, - "detected_codes": [], - "fields": [ - { - "key": "employer_name", - "name": "Employer name", - "instruction": "The employer or company issuing the payslip.", - "type": "text", - "required": true, - "is_full_name": false - }, - { - "key": "employee_name", - "name": "Employee name", - "instruction": "The full name of the employee as printed on the payslip.", - "type": "text", - "required": true, - "is_full_name": true - }, - { - "key": "net_pay", - "name": "Net pay", - "instruction": "The net amount paid to the employee for the period.", - "type": "number", - "required": true, - "is_full_name": false - }, - { - "key": "pay_date", - "name": "Pay date", - "instruction": "The payment date shown on the payslip.", - "type": "date", - "required": false, - "is_full_name": false - } - ], - "name_match_score": null, - "name_on_document": "Sophia Martinez", - "warnings": [ - { - "risk": "DOCUMENT_AI_SUSPECTED_MANIPULATION", - "feature": "DOCUMENT_AI", - "additional_data": { - "detection_method": "known_editor", - "processed_by_known_editor": "ilovepdf" - }, - "log_type": "error", - "short_description": "Suspected document manipulation", - "long_description": "The system detected signs of potential manipulation or editing in the uploaded document." - } - ] - }, - "vendor_data": null, - "metadata": null, - "created_at": "2026-08-19T16:42:40.771Z" - } - } - }, "schema": { - "type": "object", - "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." - }, - "document_ai": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined", - "In Review" - ], - "description": "`Approved` when no warning resolves to a decline or review; `Declined` when at least one risk maps to `DECLINE`; otherwise `In Review` when at least one maps to `REVIEW`. The `warnings` list explains why." - }, - "extracted_data": { - "type": "object", - "additionalProperties": true, - "description": "One entry per requested field key. Values are typed per the field `type` — strings for `text`, numbers for `number`, `YYYY-MM-DD` strings for `date` — or `null` when the value could not be read.", - "example": { - "employer_name": "Acme Payments Ltd", - "employee_name": "Sophia Martinez", - "net_pay": 3120.5, - "pay_date": "2026-07-31" - } - }, - "detected_codes": { - "type": "array", - "description": "QR codes and barcodes detected across every processed page. Empty when none are found. A located but undecodable QR code has `parsed_payload: null`.", - "items": { - "$ref": "#/components/schemas/DocumentAIDetectedCode" - } - }, - "fields": { - "type": "array", - "description": "The normalized field definitions that were used for this extraction, echoed back (also stored with the session when `save_api_request=true`).", - "items": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "name": { - "type": "string" - }, - "instruction": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "text", - "number", - "date" - ] - }, - "required": { - "type": "boolean" - }, - "is_full_name": { - "type": "boolean" - } - } - } - }, - "name_match_score": { - "type": "number", - "nullable": true, - "minimum": 0, - "maximum": 100, - "description": "Fuzzy similarity (0–100) between `name_on_document` and the expected person/company name. Returned whenever both a name value and an expected name exist — on matches **and** mismatches; `null` otherwise.", - "example": 96.0 - }, - "name_on_document": { - "type": "string", - "nullable": true, - "description": "The name value taken from the document (the `is_full_name` field, or the first name-like text field) that was compared against the expected name. `null` when no name value was available.", - "example": "Sophia Martinez" - }, - "warnings": { - "type": "array", - "description": "Empty on a clean approval. Every entry explains one detected risk; entries with `log_type: \"error\"` set `status` to `Declined`, entries with `log_type: \"warning\"` set it to `In Review`.", - "items": { - "type": "object", - "properties": { - "risk": { - "type": "string", - "enum": [ - "DOCUMENT_AI_UNREADABLE_DOCUMENT", - "DOCUMENT_AI_MISSING_REQUIRED_FIELDS", - "DOCUMENT_AI_SUSPECTED_MANIPULATION", - "DOCUMENT_AI_NAME_MISMATCH", - "DOCUMENT_AI_UNSUPPORTED_FILE" - ], - "description": "Machine-readable risk code. Each one is governed by the matching `document_ai_*_action` request option.", - "example": "DOCUMENT_AI_MISSING_REQUIRED_FIELDS" - }, - "feature": { - "type": "string", - "enum": [ - "DOCUMENT_AI" - ], - "description": "Feature that raised the warning. Always `DOCUMENT_AI` on this endpoint." - }, - "additional_data": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Extra context for the risk: `{\"missing_fields\": [\"Net pay\"]}` for missing required fields, `{\"name_on_document\", \"score\", \"threshold\"}` for a name mismatch, and the forensic finding (`detection_method`, producer/editor details) for suspected manipulation. `null` when there is nothing to add.", - "example": { - "missing_fields": [ - "Net pay" - ] - } - }, - "log_type": { - "type": "string", - "enum": [ - "error", - "warning", - "information" - ], - "description": "Severity derived from the configured action: `error` (DECLINE), `warning` (REVIEW), `information` (NO_ACTION)." - }, - "short_description": { - "type": "string", - "description": "Human-readable one-liner for the risk.", - "example": "Required fields could not be extracted" - }, - "long_description": { - "type": "string", - "description": "Human-readable explanation, interpolated with `additional_data` where relevant." - } - } - } - } - } - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent, or `null`." - }, - "metadata": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Echo of the `metadata` object you sent, or `null`." - }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "When the request was processed (UTC, ISO 8601)." - } + "$ref": "#/components/schemas/CaseDetail" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/resolve/": { + "post": { + "summary": "Resolve case", + "description": "Resolve the case with one of five resolutions: FALSE_POSITIVE (no concern found), VALID_THREAT (concern confirmed), INCONCLUSIVE, REQUIREMENTS_NOT_MET or REQUIREMENTS_SATISFIED. Blocked with 400 if the blueprint's checklist is not optional and items remain incomplete, or if the blueprint requires a resolution note and none is given. When the case's blueprint has a four-eyes checker step (four_eyes_checker_blueprint), the resolution is staged (case.pending_resolution) and the case moves to the checker blueprint instead of resolving immediately; a different officer must call approve or reject-approval. Submit a four-eyes resolution as a console member: a resolution an API key submits has no recorded maker, so approve and reject-approval return 403 for every caller. On a four-eyes blueprint you can name the member who must approve with `checker`: then only that member can approve or reject the resolution, and the response's `checker` names them. Without `checker`, the resolution waits on the checker blueprint's queue. Sending `checker` for a blueprint without a checker step returns 400.", + "operationId": "resolve_case", + "x-didit-sdk": { + "resource": "cases", + "method": "resolve", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "resolution" + ], + "properties": { + "resolution": { + "type": "string", + "enum": [ + "FALSE_POSITIVE", + "VALID_THREAT", + "INCONCLUSIVE", + "REQUIREMENTS_NOT_MET", + "REQUIREMENTS_SATISFIED" + ] + }, + "note": { + "type": "string", + "nullable": true, + "description": "Required when the blueprint sets require_resolution_note." + }, + "checker": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Four-eyes blueprints only. The user uuid of the member who must approve this resolution (the same id as `assigned_to.uuid` on a case, or `members[].user_id` on a review team). The member must have case access to this application and cannot be the officer who submits the resolution. Omit it or send null to leave the resolution to the checker queue." + } + } + }, + "examples": { + "Resolve": { + "value": { + "resolution": "FALSE_POSITIVE", + "note": "Confirmed legitimate activity after reviewing documents." + } + }, + "Four-eyes blueprint with a named checker": { + "value": { + "resolution": "VALID_THREAT", + "note": "Document tampering confirmed.", + "checker": "3fa85f64-5717-4562-b3fc-2c963f66afa6" } } } } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/resolve/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"resolution\": \"FALSE_POSITIVE\", \"note\": \"Confirmed legitimate activity after reviewing documents.\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/resolve/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'resolution': 'FALSE_POSITIVE', 'note': 'Confirmed legitimate activity.'},\n timeout=10,\n)\nresp.raise_for_status()\ncase = resp.json()\nprint(case['status'], case['resolution'], case.get('pending_resolution'))" + } + ], + "responses": { + "200": { + "description": "Resolved, or staged for 4-eyes approval.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseDetail" + } + } + } }, "400": { - "description": "Validation or processing error. Field-level problems return DRF's standard envelope (one array of messages per offending field). Encrypted or unreadable documents and extraction failures return `{\"error\": ...}`.", + "description": "The resolution was refused. Non-field errors come as a list of strings: an incomplete checklist, a missing required note, or a `checker` that could not be checked or is not an active reviewer with case access to this application. `checker` errors come keyed by the field: the named member is the officer submitting the resolution, or the case's blueprint has no checker step.", "content": { "application/json": { - "examples": { - "Missing document": { - "summary": "`document` not included in the form data", - "value": { - "document": [ - "No file was submitted." - ] - } - }, - "Invalid fields payload": { - "summary": "`fields` is not one JSON-encoded array string", - "value": { - "fields": [ - "fields must be a valid JSON array string." - ] + "schema": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } + ] + }, + "examples": { + "Incomplete checklist": { + "value": [ + "Case has incomplete checklist items: Verify source of funds" + ] }, - "Duplicate field keys": { - "summary": "Two items share the same `key`", - "value": { - "fields": [ - "Every fields item must have a unique key." - ] - } + "Resolution note required": { + "value": [ + "A resolution note is required to resolve this case." + ] }, - "Conflicting expectations": { - "summary": "Person and company expectations sent together", - "value": { - "non_field_errors": [ - "Provide either person expected-name fields or expected_company_name, not both." - ] - } + "Reviewer access could not be verified": { + "value": [ + "Reviewer access could not be verified. Try again." + ] }, - "Invalid metadata": { - "summary": "`metadata` is not a JSON object (or contains a NUL character)", - "value": { - "metadata": [ - "Metadata must be a JSON object." - ] - } + "Checker is not an active reviewer": { + "value": [ + "Choose an active reviewer with case access to this application." + ] }, - "Oversized vendor_data": { - "summary": "`vendor_data` over the 2,000-byte cap", + "Checker is the submitting officer": { "value": { - "vendor_data": [ - "vendor_data is too long (maximum 2000 bytes)." + "checker": [ + "The officer who submitted the resolution cannot be its checker." ] } }, - "Encrypted PDF": { - "summary": "Password-protected PDF", - "value": { - "error": "The PDF is encrypted. Please upload a decrypted PDF or a photo instead." - } - }, - "Extraction failed": { - "summary": "The extraction pipeline could not process the document", + "Policy has no checker step": { "value": { - "error": [ - "Error extracting Document AI information" + "checker": [ + "This review policy has no checker step, so the case resolves directly." ] } } - }, - "schema": { - "type": "object", - "additionalProperties": true } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/reopen/": { + "post": { + "summary": "Reopen case", + "description": "Move an awaiting-user or resolved case back to open, clearing resolution, resolution_notes, resolved_at, and resolved_by_email.", + "operationId": "reopen_case", + "x-didit-sdk": { + "resource": "cases", + "method": "reopen", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls — and contract-billed organizations whose rate card has no Document AI API entry — return `403` with `{\"error\": ...}` before any document processing happens.", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/reopen/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Reopen case.", "content": { "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" - } - }, - "Contract rate missing": { - "summary": "Contract-billed organization whose rate card has no Document AI API entry", - "value": { - "error": "Document AI API is not included in your contract rate card. Contact support@didit.me to enable it." - } - } - }, "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - }, - "error": { - "type": "string" - } - } + "$ref": "#/components/schemas/CaseDetail" } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/request-info/": { + "post": { + "summary": "Request info (awaiting user)", + "description": "Move an open case to awaiting_user while more information is collected from the verified user. Fails with 400 if a resolution is currently pending 4-eyes approval.", + "operationId": "request_case_info", + "x-didit-sdk": { + "resource": "cases", + "method": "request_info", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/request-info/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Request info (awaiting user).", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } + "$ref": "#/components/schemas/CaseDetail" } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/aml/": { + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/escalate/": { "post": { - "summary": "AML screening (standalone)", - "operationId": "post_v3aml", + "summary": "Escalate case", + "description": "Move the case to its blueprint's configured escalation_blueprint. Requires the current blueprint to have escalation_enabled and an escalation_blueprint set, otherwise 400.", + "operationId": "escalate_case", + "x-didit-sdk": { + "resource": "cases", + "method": "escalate", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Standalone APIs" + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/escalate/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } ], + "responses": { + "200": { + "description": "Escalate case.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseDetail" + } + } + } + } + }, "security": [ { "ApiKeyAuth": [] } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/transfer/": { + "post": { + "summary": "Transfer case", + "description": "Move the case to a different, active blueprint of the same application. Requires the current blueprint to have transfer_enabled, otherwise 400.", + "operationId": "transfer_case", + "x-didit-sdk": { + "resource": "cases", + "method": "transfer", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" ], "parameters": [ { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Optional retry-safety key, up to 255 characters. Reusing the same key with the same request body returns the original response without running or billing a second screening. Reusing the key with a different body returns `409`.", + "name": "organization_id", + "in": "path", + "required": true, "schema": { "type": "string", - "maxLength": 255 - } - } - ], - "description": "Screen a person or company against PEP, sanctions, warning/watchlist, and adverse-media datasets in one call.\n\n**Two-score model.** Each hit carries a `match_score` (0–100, identity confidence) and a `risk_score` (0–100, entity risk). Hits whose `match_score` is below `aml_match_score_threshold` (default **93**) are tagged `review_status: \"False Positive\"` and ignored for risk purposes; the highest `risk_score` among the remaining hits becomes `aml.score`. The final `aml.status` follows your thresholds: `score` > `aml_score_review_threshold` (default **100**) → `Declined`; `score` > `aml_score_approve_threshold` (default **80**) → `In Review`; otherwise `Approved`. Whenever `score` is non-zero and ≥ the approve threshold a `POSSIBLE_MATCH_FOUND` warning is added (a score of exactly 0 never warns).\n\n**Match-score weights.** `aml_name_weight` + `aml_dob_weight` + `aml_country_weight` must sum to exactly 100 (defaults 60/25/15). Components missing from the request are re-normalized away; a matching `document_number` acts as a golden key that overrides `match_score` to 100.\n\n**Latency.** Plain screenings typically complete in a few seconds. With `include_adverse_media=true` the API waits for adverse-media enrichment (an initial ~5 s pause plus up to ~25 s of polling) — configure a client timeout of **at least 60 seconds**.\n\n**Persistence.** `save_api_request` defaults to **true**: the screening is stored as a session (`request_id` works with `GET /v3/session/{sessionId}/decision/`), it appears in the console, a `status.updated` webhook fires, and hit review statuses can be updated later. With `save_api_request=false` nothing is stored and `request_id` is a transient correlation id. `include_ongoing_monitoring=true` keeps re-screening the profile and requires `save_api_request=true` (enforced with a `400`).\n\n**Idempotency.** Send an `Idempotency-Key` header when retrying after a timeout or transient error. The key is scoped to your application and the exact request body. The first completed request stores its response; later identical retries return that same response and do not run or bill another AML screening. If the original request is still running, retry later with the same key. If the same key is reused with a different body, the API returns `409`.\n\n**Billing.** One AML API credit is charged after the first successful screening. Replayed idempotent responses are not charged again; an insufficient balance returns `403` before any new screening happens.\n\n**Sandbox.** Keys from sandbox applications skip screening and billing entirely and return a mocked result — `Approved` with zero hits unless you pass `sandbox_scenario`. `save_api_request` works the same as on live keys, so a saved sandbox screening is retrievable through `GET /v3/session/{sessionId}/decision/` and `GET /v3/session/{sessionId}/generate-pdf/`.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "full_name" - ], - "properties": { - "full_name": { - "type": "string", - "minLength": 2, - "description": "Full name of the person or company to screen. Minimum **2 characters** - the screening provider's own floor. A shorter or blank name returns `400` on this field before any screening runs, so nothing is billed.", - "example": "Antonio Ejemplo Modelo" - }, - "entity_type": { - "type": "string", - "enum": [ - "person", - "company" - ], - "default": "person", - "description": "Type of entity to screen. Defaults to `person`." - }, - "date_of_birth": { - "type": "string", - "format": "date", - "description": "Date of birth (persons) or incorporation date (companies), `YYYY-MM-DD`. Improves match precision via the DOB weight.", - "example": "1972-02-29" - }, - "nationality": { - "type": "string", - "description": "ISO 3166-1 **alpha-2** country code (e.g. `ES`). Invalid codes return `400`. Improves match precision via the country weight.", - "example": "ES" - }, - "document_number": { - "type": "string", - "description": "Identity document number. Not weighted — acts as a golden key: an exact match overrides the hit's `match_score` to 100, a hard mismatch penalizes it. Sent to the screening provider as a search filter only when it is **2-50 characters**; a value outside that range is accepted and stored, and the screening still runs and is billed, just not narrowed by document number." - }, - "aml_score_approve_threshold": { - "type": "number", - "minimum": 0, - "maximum": 100, - "default": 80, - "description": "Risk-score threshold at or below which the result is `Approved`. Must be ≤ `aml_score_review_threshold` (else `400`)." - }, - "aml_score_review_threshold": { - "type": "number", - "minimum": 0, - "maximum": 100, - "default": 100, - "description": "Risk-score threshold above which the result is `Declined`. Scores between the two thresholds produce `In Review`." - }, - "aml_name_weight": { - "type": "integer", - "minimum": 0, - "maximum": 100, - "default": 60, - "description": "Weight of name similarity in the per-hit match score. The three weights must sum to exactly 100 (else `400`)." - }, - "aml_dob_weight": { - "type": "integer", - "minimum": 0, - "maximum": 100, - "default": 25, - "description": "Weight of date of birth in the per-hit match score." - }, - "aml_country_weight": { - "type": "integer", - "minimum": 0, - "maximum": 100, - "default": 15, - "description": "Weight of country/nationality in the per-hit match score." - }, - "aml_match_score_threshold": { - "type": "integer", - "minimum": 0, - "maximum": 100, - "default": 93, - "description": "Per-hit cutoff: hits with `match_score` below this are `False Positive` (excluded from risk assessment); at or above are `Unreviewed` possible matches." - }, - "include_adverse_media": { - "type": "boolean", - "default": false, - "description": "Also search news media for negative coverage. Adds up to ~30 s of latency while results are gathered — use a ≥60 s client timeout." - }, - "include_ongoing_monitoring": { - "type": "boolean", - "default": false, - "description": "Keep re-screening this profile and push changes via webhook. Requires `save_api_request=true` (else `400`)." - }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "Persist the screening as a session (console visibility, decision endpoint, webhooks, hit review). Set to `false` for a stateless check." - }, - "vendor_data": { - "type": "string", - "description": "Your identifier for the screened user; echoed back and stored with the session." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Free-form JSON stored with the request and echoed back." - }, - "sandbox_scenario": { + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "target_blueprint" + ], + "properties": { + "target_blueprint": { "type": "string", - "description": "Sandbox-only scenario slug. Only accepted when the API key belongs to a sandbox-mode application; rejected on live applications. A standalone screening has no session to arm, so the slug goes on the screening request itself: `decline_aml_hit` returns one production-shaped hit and settles the screening as `Declined`, `review_aml_possible_match` settles it `In Review`, and a scenario that arms no AML outcome leaves the screening clean. See the scenario catalog at GET /v1/sandbox/scenarios/ or [Sandbox & Test Data](/integration/sandbox-testing).", - "example": "decline_aml_hit" + "format": "uuid" } } } @@ -15047,1328 +35602,1203 @@ }, "x-codeSamples": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/aml/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -H 'Idempotency-Key: aml-user-1234-2026-08-20' \\\n -d '{\n \"full_name\": \"Antonio Ejemplo Modelo\",\n \"entity_type\": \"person\",\n \"date_of_birth\": \"1972-02-29\",\n \"nationality\": \"ES\",\n \"include_adverse_media\": false,\n \"vendor_data\": \"user-1234\"\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/aml/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n \"Idempotency-Key\": \"aml-user-1234-2026-08-20\",\n },\n json={\n \"full_name\": \"Antonio Ejemplo Modelo\",\n \"entity_type\": \"person\",\n \"date_of_birth\": \"1972-02-29\",\n \"nationality\": \"ES\",\n \"include_adverse_media\": False,\n \"vendor_data\": \"user-1234\",\n },\n timeout=60, # adverse media enrichment can add ~30s\n)\nresp.raise_for_status()\nprint(resp.json()[\"aml\"][\"status\"]) # Approved / In Review / Declined" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/aml/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n 'Idempotency-Key': 'aml-user-1234-2026-08-20',\n },\n body: JSON.stringify({\n full_name: 'Antonio Ejemplo Modelo',\n entity_type: 'person',\n date_of_birth: '1972-02-29',\n nationality: 'ES',\n include_adverse_media: false,\n vendor_data: 'user-1234',\n }),\n});\nconst data = await res.json();\nconsole.log(data.aml.status);" - } - ], - "responses": { - "200": { - "description": "Screening completed. `aml.status` reflects your thresholds; every hit at or above the internal inclusion floor is listed in `aml.hits` with its identity `match_score`, entity `risk_score`, and a `score_breakdown` explaining the match.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." - }, - "aml": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "In Review", - "Declined" - ], - "description": "`Declined` when `score` > `aml_score_review_threshold`; `In Review` when `score` > `aml_score_approve_threshold`; `Approved` otherwise (including when no score is available)." - }, - "total_hits": { - "type": "integer", - "description": "Number of entries in `hits`." - }, - "entity_type": { - "type": "string", - "enum": [ - "person", - "company" - ], - "description": "Echo of the screened entity type." - }, - "hits": { - "type": "array", - "items": { - "type": "object", - "description": "One screening hit. `match_score` (0–100) measures identity confidence — hits below `aml_match_score_threshold` are tagged `review_status: \"False Positive\"` and excluded from the risk assessment; hits at or above it are `\"Unreviewed\"` possible matches. `risk_score` (0–100) measures how risky the matched entity is and drives the top-level `aml.score`.", - "properties": { - "id": { - "type": "string", - "nullable": true, - "description": "Stable identifier of the matched entity in the screening datasets." - }, - "url": { - "type": "string", - "nullable": true, - "description": "Source URL for the matched entity, when available." - }, - "match": { - "type": "boolean", - "nullable": true, - "description": "Legacy exact-match flag. Prefer `match_score` + `review_status`." - }, - "score": { - "type": "number", - "nullable": true, - "description": "DEPRECATED weighted match score in the 0–1 range. Use `match_score` instead." - }, - "match_score": { - "type": "number", - "nullable": true, - "description": "Identity confidence (0–100). Compared against `aml_match_score_threshold` to split False Positives from Possible Matches." - }, - "risk_score": { - "type": "number", - "nullable": true, - "description": "Entity risk score (0–100). The highest `risk_score` among non-false-positive hits becomes the top-level `aml.score`." - }, - "target": { - "type": "boolean", - "nullable": true - }, - "caption": { - "type": "string", - "nullable": true, - "description": "Display name of the matched entity." - }, - "datasets": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - }, - "description": "Datasets the entity appears in (e.g. `PEP`, `PEP Level 1`, sanctions list names)." - }, - "features": { - "type": "object", - "nullable": true - }, - "properties": { - "type": "object", - "nullable": true, - "description": "Entity profile: `name`, `alias`, `notes`, `gender`, `birthDate`, `birthPlace`, `nationality`, `position`, and other biographical arrays (each nullable)." - }, - "rca_name": { - "type": "string", - "nullable": true, - "description": "Name of the related close associate through which the entity was matched, when the hit is an RCA." - }, - "pep_matches": { - "type": "array", - "description": "PEP list entries backing this hit.", - "items": { - "type": "object", - "properties": { - "list_name": { - "type": "string" - }, - "publisher": { - "type": "string" - }, - "source_url": { - "type": "string" - }, - "description": { - "type": "string" - }, - "matched_name": { - "type": "string" - }, - "pep_position": { - "type": "string" - }, - "date_of_birth": { - "type": "string" - }, - "place_of_birth": { - "type": "string" - }, - "aliases": { - "type": "array", - "items": { - "type": "string" - } - }, - "education": { - "type": "array", - "items": { - "type": "string" - } - }, - "other_sources": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "sanction_matches": { - "type": "array", - "items": { - "type": "object" - }, - "description": "Sanctions list entries backing this hit (same backing-list shape as `pep_matches`, plus sanction-specific fields)." - }, - "warning_matches": { - "type": "array", - "items": { - "type": "object" - }, - "description": "Warning/watchlist entries backing this hit." - }, - "adverse_media_matches": { - "type": "array", - "description": "Adverse-media articles. Populated only when `include_adverse_media=true`.", - "items": { - "type": "object", - "properties": { - "headline": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "sentiment": { - "type": "string" - }, - "sentiment_score": { - "type": "number" - }, - "source_url": { - "type": "string" - }, - "thumbnail": { - "type": "string", - "nullable": true - }, - "country": { - "type": "string", - "nullable": true - }, - "author_name": { - "type": "string", - "nullable": true - }, - "publication_date": { - "type": "string", - "nullable": true - }, - "adverse_keywords": { - "type": "array", - "items": { - "type": "string" - } - }, - "other_sources": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "adverse_media_details": { - "type": "object", - "nullable": true, - "description": "Aggregated adverse-media sentiment for the entity (`sentiment`, `sentiment_score`, `adverse_keywords` frequency map). Populated only when `include_adverse_media=true`." - }, - "first_seen": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the entity first appeared in the datasets." - }, - "last_seen": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Most recent dataset refresh that included the entity." - }, - "linked_entities": { - "type": "array", - "description": "Known relationships of the matched entity (family, associates, companies).", - "items": { - "type": "object", - "properties": { - "name": { - "type": "array", - "items": { - "type": "string" - } - }, - "relation": { - "type": "array", - "items": { - "type": "string" - } - }, - "status": { - "type": "array", - "items": { - "type": "string" - } - }, - "details": { - "type": "array", - "items": { - "type": "string" - } - }, - "active": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "risk_view": { - "type": "object", - "nullable": true, - "description": "Per-dimension risk breakdown (`crimes`, `countries`, `categories`, `custom_list`), each with `score`, `weightage`, `risk_level`, and a `risk_scores` map." - }, - "additional_information": { - "type": "object", - "nullable": true, - "description": "Free-form extra context, e.g. a `flag_summary` array explaining why the entity is flagged." - }, - "review_status": { - "type": "string", - "nullable": true, - "enum": [ - "False Positive", - "Unreviewed", - "Confirmed Match", - "Inconclusive" - ], - "description": "`False Positive` — `match_score` below `aml_match_score_threshold`, excluded from risk assessment. `Unreviewed` — possible match awaiting review. `Confirmed Match` / `Inconclusive` — set manually by a reviewer (via the Business Console) on saved sessions; that review capability is not part of this spec." - }, - "score_breakdown": { - "type": "object", - "nullable": true, - "description": "How `match_score` was computed. Name, date of birth, and country each contribute `_score` × `_weight_normalized` points; weights are re-normalized when a component is missing from the screened data. The document number is not weighted — it acts as a 'golden key': `MATCH` overrides the score to 100, `NEUTRAL` leaves it unchanged, `HARD_MISMATCH` applies a penalty.", - "properties": { - "name_score": { - "type": "integer", - "nullable": true, - "description": "Raw name similarity (0–100)." - }, - "name_weight": { - "type": "integer", - "nullable": true, - "description": "Configured name weight (`aml_name_weight`)." - }, - "name_weight_normalized": { - "type": "number", - "nullable": true, - "description": "Name weight after re-normalization (0–100)." - }, - "name_contribution": { - "type": "number", - "nullable": true, - "description": "Points the name contributed to `total_score`." - }, - "dob_score": { - "type": "integer", - "nullable": true, - "description": "Date-of-birth score as a percentage of its weight (−100 to 100)." - }, - "dob_weight": { - "type": "integer", - "nullable": true, - "description": "Configured DOB weight (`aml_dob_weight`)." - }, - "dob_weight_normalized": { - "type": "number", - "nullable": true, - "description": "DOB weight after re-normalization (0–100)." - }, - "dob_contribution": { - "type": "number", - "nullable": true, - "description": "Points the DOB contributed to `total_score`." - }, - "country_score": { - "type": "integer", - "nullable": true, - "description": "Country score as a percentage of its weight (−100 to 100)." - }, - "country_weight": { - "type": "integer", - "nullable": true, - "description": "Configured country weight (`aml_country_weight`)." - }, - "country_weight_normalized": { - "type": "number", - "nullable": true, - "description": "Country weight after re-normalization (0–100)." - }, - "country_contribution": { - "type": "number", - "nullable": true, - "description": "Points the country contributed to `total_score`." - }, - "document_number_match_type": { - "type": "string", - "nullable": true, - "enum": [ - "MATCH", - "NEUTRAL", - "HARD_MISMATCH" - ], - "description": "Golden-key result for the document number comparison." - }, - "document_number_effect": { - "type": "string", - "nullable": true, - "description": "Human-readable description of the document-number effect." - }, - "total_score": { - "type": "integer", - "nullable": true, - "description": "Final weighted match score (0–100) — mirrors `match_score`." - } - } - } - } - } - }, - "score": { - "type": "number", - "nullable": true, - "description": "Overall AML risk score (0–100): the highest `risk_score` among hits that are not False Positives. Drives `status` via the two thresholds." - }, - "screened_data": { - "type": "object", - "description": "Echo of what was screened: `full_name`, `date_of_birth`, `nationality`, `document_number` (absent fields are `null`).", - "properties": { - "full_name": { - "type": "string" - }, - "date_of_birth": { - "type": "string", - "nullable": true - }, - "nationality": { - "type": "string", - "nullable": true - }, - "document_number": { - "type": "string", - "nullable": true - } - } - }, - "warnings": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "description": "Feature that raised the warning." - }, - "risk": { - "type": "string", - "description": "Machine-readable risk identifier." - }, - "additional_data": { - "type": "object", - "nullable": true, - "description": "Extra context for the warning, when available." - }, - "log_type": { - "type": "string", - "description": "`warning`, `information`, or `error` — how the risk affected the outcome." - }, - "short_description": { - "type": "string", - "description": "One-line human-readable summary." - }, - "long_description": { - "type": "string", - "description": "Full human-readable explanation." - } - } - }, - "description": "Contains a `POSSIBLE_MATCH_FOUND` warning whenever `score` is non-zero and ≥ `aml_score_approve_threshold` (a score of exactly 0 never warns); empty otherwise." - } - } - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Echo of the `metadata` you sent." - }, - "created_at": { - "type": "string", - "format": "date-time" + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/transfer/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"target_blueprint\": \"b1de0000-0000-4000-8000-000000000002\"}'" + } + ], + "responses": { + "200": { + "description": "Case transferred.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseDetail" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/approve/": { + "post": { + "summary": "Approve staged resolution (4-eyes)", + "description": "Apply a resolution staged by resolve on a four-eyes blueprint. The caller must be a different officer than the one who submitted it, otherwise 403. When the resolution names a checker (the case's `checker`), only that member can approve it: any other caller, including an API key, gets 403.", + "operationId": "approve_case_resolution", + "x-didit-sdk": { + "resource": "cases", + "method": "approve_resolution", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/approve/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Resolution applied.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseDetail" + } + } + } + }, + "409": { + "description": "Case has no resolution pending approval.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + }, + "403": { + "description": "The caller cannot approve this resolution: they submitted it, the case has no recorded maker, or the resolution names a different checker. An API key always gets this 403 when a checker is named.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" } } }, "examples": { - "No hits (Approved)": { - "summary": "Clean screening — zero hits", + "Caller submitted the resolution": { "value": { - "request_id": "d27c5509-7a66-45a9-b05e-d69d9f4c336a", - "aml": { - "status": "Approved", - "total_hits": 0, - "entity_type": "person", - "hits": [], - "score": 0, - "screened_data": { - "full_name": "Maximilian Mustermann-Doe", - "date_of_birth": "1992-03-14", - "nationality": "DE", - "document_number": null - }, - "warnings": [] - }, - "vendor_data": "user-1234", - "metadata": { - "internal_ref": "abc-123" - }, - "created_at": "2026-06-11T10:21:07.013938+00:00" + "detail": "The officer who submitted the resolution cannot approve it." } }, - "PEP hit (Approved — risk below approve threshold)": { - "summary": "One PEP possible match; risk score 73 stays under the default approve threshold of 80", + "No recorded maker": { "value": { - "request_id": "0b7e7a3e-65a1-4f3d-9c7a-1de2f9a40111", - "aml": { - "status": "Approved", - "total_hits": 1, - "entity_type": "person", - "hits": [ - { - "id": "kDpfszrbkDdaxpMVZw3NLg", - "url": "https://news.example.org/articles/sample", - "match": false, - "score": 0.93, - "match_score": 93.0, - "risk_score": 73.0, - "target": null, - "caption": "Antonio Ejemplo Modelo", - "datasets": [ - "PEP Level 1", - "PEP" - ], - "features": null, - "rca_name": "", - "first_seen": "2026-01-07T00:00:00", - "last_seen": "2026-01-07T00:00:00", - "review_status": "Unreviewed", - "properties": { - "name": [ - "Antonio Ejemplo Modelo", - "Antonio Ejemplo" - ], - "alias": [ - "Antonio Ejemplo Modelo", - "Ejemplo Modelo, Pedro" - ], - "notes": [ - "Prime Minister of Spain" - ], - "gender": [ - "male" - ], - "birthDate": [ - "1972-02-29" - ], - "birthPlace": [ - "Madrid, Spain" - ], - "nationality": null, - "position": null - }, - "risk_view": { - "crimes": { - "score": 0.0, - "weightage": 20, - "risk_level": "Low", - "risk_scores": {} - }, - "countries": { - "score": 9.64, - "weightage": 30, - "risk_level": "Low", - "risk_scores": { - "Spain": 25.71 - } - }, - "categories": { - "score": 62.5, - "weightage": 50, - "risk_level": "High", - "risk_scores": { - "PEP": 100.0, - "PEP Level 1": 100.0 - } - }, - "custom_list": {} - }, - "score_breakdown": { - "name_score": 89, - "name_weight": 60, - "name_weight_normalized": 60.0, - "name_contribution": 53.4, - "dob_score": 100, - "dob_weight": 25, - "dob_weight_normalized": 25.0, - "dob_contribution": 25.0, - "country_score": 100, - "country_weight": 15, - "country_weight_normalized": 15.0, - "country_contribution": 15.0, - "document_number_match_type": "NEUTRAL", - "document_number_effect": "No document number provided for screening", - "total_score": 93 - }, - "pep_matches": [ - { - "aliases": [], - "education": [], - "list_name": "Presidency of the Government of Spain", - "publisher": "Presidency of the Government of Spain", - "source_url": "https://news.example.org/articles/sample", - "description": "The Presidency of the Government is the central institutional body in Spain that supports the Prime Minister and coordinates the general action of the central government.", - "matched_name": "Antonio Ejemplo Modelo", - "pep_position": "", - "date_of_birth": "1972-02-29", - "other_sources": [], - "place_of_birth": "Madrid, Spain" - } - ], - "sanction_matches": [], - "warning_matches": [], - "adverse_media_matches": [], - "adverse_media_details": null, - "linked_entities": [ - { - "name": [ - "Maria Begona Gomez Fernandez" - ], - "active": [], - "status": [], - "details": [], - "relation": [ - "spouse" - ] - } - ], - "additional_information": { - "flag_summary": [ - "Antonio Ejemplo Modelo has been flagged as a Level 1 Politically Exposed Person (PEP) from Spain, serving as the President of the Government of Spain." - ] - } - } - ], - "score": 73.0, - "screened_data": { - "full_name": "Pedro Sanchez Perez-Castejon", - "date_of_birth": "1972-02-29", - "nationality": "ES", - "document_number": null - }, - "warnings": [] - }, - "vendor_data": null, - "metadata": null, - "created_at": "2026-06-11T10:23:44.812000+00:00" + "detail": "Case has no recorded maker for this pending resolution; approve is not permitted." + } + }, + "Caller is not the named checker": { + "value": { + "detail": "Only the named checker can approve this resolution." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/reject-approval/": { + "post": { + "summary": "Reject staged resolution (4-eyes)", + "description": "Reject a resolution staged by resolve and return the case to its maker blueprint. The caller must be a different officer than the one who submitted it, otherwise 403. When the resolution names a checker (the case's `checker`), only that member can reject it: any other caller, including an API key, gets 403.", + "operationId": "reject_case_resolution", + "x-didit-sdk": { + "resource": "cases", + "method": "reject_resolution", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "notes": { + "type": "string", + "nullable": true + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/reject-approval/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"notes\": \"Needs a clearer rationale before this can be approved.\"}'" + } + ], + "responses": { + "200": { + "description": "Resolution rejected, case returned to the maker blueprint.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseDetail" + } + } + } + }, + "409": { + "description": "Case has no resolution pending approval.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + }, + "403": { + "description": "The caller cannot reject this resolution: they submitted it, the case has no recorded maker, or the resolution names a different checker. An API key always gets this 403 when a checker is named.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Caller submitted the resolution": { + "value": { + "detail": "The officer who submitted the resolution cannot reject it." + } + }, + "No recorded maker": { + "value": { + "detail": "Case has no recorded maker for this pending resolution; reject is not permitted." + } + }, + "Caller is not the named checker": { + "value": { + "detail": "Only the named checker can reject this resolution." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/entity-actions/": { + "post": { + "operationId": "apply_case_entity_action", + "x-didit-sdk": { + "resource": "cases", + "method": "apply_entity_action", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Management" + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CaseActionCaseEntityActionRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseActionCaseEntityActionRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CaseActionCaseEntityActionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseActionCaseEntityActionResult" + } + } + }, + "description": "" + }, + "409": { + "description": "The reference belongs to a different action payload." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Apply one explicit action to the case subject. Supply a unique client_reference to obtain an action receipt and make retries idempotent. Reusing that reference with a different payload returns 409. With a reference, inspect receipt.status even when HTTP status is 200: FAILED means no action was applied. Repeating a failed request does not execute it again unless retry is true. Omitting client_reference preserves the legacy response." + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/print/": { + "get": { + "summary": "Print case (PDF)", + "description": "Render the full case (summary, subject, checklist state, notes, attached sessions/transactions, events, FIU report list) as a PDF and return a presigned download URL.", + "operationId": "print_case", + "x-didit-sdk": { + "resource": "cases", + "method": "print", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/print/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Presigned PDF URL.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri" } } } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/": { + "get": { + "operationId": "list_case_notes", + "x-didit-sdk": { + "resource": "cases.notes", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, - "400": { - "description": "Validation error. Field-level problems return DRF's standard envelope (one array of messages per offending field); cross-field rules (weight sum, threshold ordering, monitoring-without-save) return `{\"error\": [...]}`.", + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "required": false, + "in": "query", + "description": "Number of results to return per page.", + "schema": { + "type": "integer" + } + }, + { + "name": "offset", + "required": false, + "in": "query", + "description": "The initial index from which to return the results.", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { "content": { "application/json": { - "examples": { - "Missing full_name": { - "summary": "`full_name` is the only required field", - "value": { - "full_name": [ - "This field is required." - ] - } - }, - "Short full_name": { - "summary": "`full_name` must be at least 2 characters", - "value": { - "full_name": [ - "Ensure this field has at least 2 characters." - ] - } - }, - "Invalid nationality": { - "summary": "`nationality` must be ISO 3166-1 alpha-2", - "value": { - "nationality": [ - "Invalid ISO 3166-1 alpha-2 country code." - ] - } - }, - "Weights do not sum to 100": { - "summary": "`aml_name_weight` + `aml_dob_weight` + `aml_country_weight` ≠ 100", - "value": { - "error": [ - "AML weights must sum to 100. Current sum: 90 (name: 50, dob: 25, country: 15)" - ] - } - }, - "Thresholds out of order": { - "summary": "Approve threshold above review threshold", - "value": { - "error": [ - "Approve threshold (90.0) cannot be greater than review threshold (80.0)" - ] - } - }, - "Monitoring without saving": { - "summary": "`include_ongoing_monitoring=true` with `save_api_request=false`", - "value": { - "error": [ - "Ongoing monitoring can only be enabled when the API request is saved." - ] - } - } + "schema": { + "$ref": "#/components/schemas/CaseCollaborationPaginatedCaseNoteList" } } - } + }, + "description": "" }, "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", + "description": "The caller lacks the required cases permission." + }, + "404": { + "description": "The case or note is unavailable for this caller." + } + }, + "tags": [ + "Case Management" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "post": { + "operationId": "create_case_note", + "x-didit-sdk": { + "resource": "cases.notes", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CaseCollaborationCaseNoteCreateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseCollaborationCaseNoteCreateRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CaseCollaborationCaseNoteCreateRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { "content": { "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" - } - } + "schema": { + "$ref": "#/components/schemas/CaseCollaborationCaseNote" } } - } + }, + "description": "" }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "200": { "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } + "$ref": "#/components/schemas/CaseCollaborationCaseNote" } } - } + }, + "description": "" + }, + "403": { + "description": "The caller lacks the required cases permission." + }, + "404": { + "description": "The case or note is unavailable for this caller." + }, + "409": { + "description": "This client reference was already used for another note or for a note that has been deleted." } - } + }, + "tags": [ + "Case Management" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Add a case-scoped note. An optional client_reference makes an unchanged retry return the same note without duplicate activity or notifications. reply_to must identify a live note in this case. The response includes a bounded parent excerpt without copying that excerpt into the saved reply." } }, - "/v3/face-search/": { - "post": { - "summary": "Face Search", - "description": "Search a face image (1:N) against your application's face search index — faces enrolled from verification sessions, saved standalone API calls, direct user-profile face uploads, and your block/allow lists — and get back ranked similarity matches plus blocklist and duplicate warnings.\n\n**How it works.** The largest detected face in `user_image` is embedded and searched against the index, scoped to your application. Up to **5 matches** above the similarity floor are returned in `matches`, each with `similarity_percentage`, the originating session (`session_id`, `session_number`, `status`, `vendor_data`), `user_details` extracted during that session's document verification, `is_blocklisted` / `is_allowlisted` flags, and a `source` discriminator (`session`, `imported`, or `list_entry`).\n\n- `search_type=most_similar` (default) ranks every enrolled face by similarity — use it for deduplication and fraud-ring investigation.\n- `search_type=blocklisted_or_approved` restricts candidates to blocklisted faces, allowlisted faces, faces from approved sessions, and imported user-profile faces, ranking blocklisted entries first — use it when the primary goal is blocklist screening.\n\n`status` is `Declined` only when a `FACE_IN_BLOCKLIST` or `POSSIBLE_FACE_IN_BLOCKLIST` warning fires; `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` (`information`) and `MULTIPLE_FACES_DETECTED` (`warning`) never decline. Unlike Passive Liveness, this endpoint does **not** exclude prior sessions with the same `vendor_data` from matching. A passive-liveness analysis also runs internally and is stored with the persisted session, but its result is not part of this response.\n\n**Billing.** Each `200` response consumes one Face Search API credit (standalone APIs have no free tier). Insufficient balance returns `403` before any image processing.\n\n**Session persistence and face enrollment (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session (Business Console, `GET /v3/session/{sessionId}/decision/` via the returned `request_id`, `status.updated` webhook) and the searched face is enrolled into your face search index. Faces enrolled by Face Search calls are excluded from future Face Search results, so repeated searches never match each other. When `false`, the call is one-shot: nothing is stored, the face is not enrolled, and `match_image_url` values are internal storage paths rather than downloadable URLs.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", - "operationId": "post_v3face-search", - "tags": [ - "Standalone APIs" + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/{note_uuid}/": { + "patch": { + "operationId": "root_partial_update", + "x-didit-sdk": { + "resource": "cases.notes", + "method": "update", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "note_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } ], - "parameters": [], "requestBody": { - "required": true, "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CaseCollaborationPatchedCaseNoteUpdateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseCollaborationPatchedCaseNoteUpdateRequest" + } + }, "multipart/form-data": { "schema": { - "type": "object", - "required": [ - "user_image" - ], - "properties": { - "user_image": { - "type": "string", - "format": "binary", - "description": "Front-facing face image to search with. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.5 MB before processing, so very high resolutions do not improve accuracy. The image must contain at least one detectable face — otherwise the endpoint returns `400`. When several faces are present, the largest one is searched and a `MULTIPLE_FACES_DETECTED` warning is added." - }, - "search_type": { - "type": "string", - "default": "most_similar", - "enum": [ - "most_similar", - "blocklisted_or_approved" - ], - "description": "Search policy. `most_similar` (default) ranks every enrolled face in your application by similarity. `blocklisted_or_approved` restricts candidates to blocklisted faces, allowlisted faces, faces from approved sessions, and imported user-profile faces — with blocklisted entries ranked first.", - "example": "most_similar" - }, - "rotate_image": { - "type": "boolean", - "default": false, - "description": "When `true`, the service tries 90-degree rotations of the input and uses the orientation that yields the best face detection. Useful when EXIF orientation is missing. Adds latency.", - "example": false - }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "When `true` (default), persists the call as an API-type session, emits a `status.updated` webhook, presigns `match_image_url` values, and enrolls the searched face into your face search index (Face Search enrollments are excluded from future searches). When `false`, the search is one-shot — no session is stored and the face is not enrolled.", - "example": true - }, - "vendor_data": { + "$ref": "#/components/schemas/CaseCollaborationPatchedCaseNoteUpdateRequest" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseCollaborationCaseNote" + } + } + }, + "description": "" + }, + "403": { + "description": "The caller lacks the required cases permission." + }, + "404": { + "description": "The case or note is unavailable for this caller." + } + }, + "tags": [ + "Case Management" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Pin or unpin an existing note. Pinned notes appear first. Pin changes are recorded in case activity and do not change evidence or source decisions." + }, + "delete": { + "operationId": "delete_case_note", + "x-didit-sdk": { + "resource": "cases.notes", + "method": "delete", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "note_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "responses": { + "204": { + "description": "The note is permanently deleted. Replies remain, without the deleted parent excerpt. Attachments are deleted unless another note still uses them." + }, + "403": { + "description": "The caller lacks the required cases permission." + }, + "404": { + "description": "The case or note is unavailable for this caller." + } + }, + "tags": [ + "Case Management" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/presign/": { + "post": { + "summary": "Upload note attachment", + "description": "Upload a file (JPEG, PNG, WebP, PDF, plain text, or CSV; max 5 MB) to attach to a note. Returns the s3_key to pass in the note's attachments array.", + "operationId": "upload_case_note_attachment", + "x-didit-sdk": { + "resource": "cases.notes", + "method": "create_attachment_upload", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file" + ], + "properties": { + "file": { "type": "string", - "description": "Optional opaque identifier (your user id, email, UUID…) stored on the persisted session and echoed back. It does not exclude same-user faces from the search results.", - "example": "user-123" - }, - "metadata": { - "type": "object", - "additionalProperties": true, - "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back. In multipart requests, send it as a JSON-encoded string field — it is parsed into an object.", - "example": { - "flow": "dedup_check" - } + "format": "binary" } } - }, - "example": { - "user_image": "(binary JPEG/PNG selfie)", - "search_type": "most_similar", - "rotate_image": false, - "save_api_request": true, - "vendor_data": "user-123", - "metadata": { - "flow": "dedup_check" - } } } } }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/notes/presign/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'file=@evidence.pdf'" + } + ], "responses": { - "200": { - "description": "Face search completed. `face_search.matches` holds up to 5 similar faces ordered by similarity; an empty array means no enrolled face exceeded the similarity floor. `status` is `Declined` only on blocklist hits — duplicate matches alone return `Approved`, so inspect `matches` and `warnings`, not just `status`. When `save_api_request=true`, `request_id` is the persisted session id. Each match carries `source` (`session`, `imported`, `list_entry`, or `retained_template`), `vendor_user_id`, and `biometric_template_id`; a `retained_template` match comes from an image-free biometric template kept after the source session was deleted and has `null` session fields, `match_image_url`, and `user_details`.", + "201": { + "description": "File uploaded.", "content": { "application/json": { - "examples": { - "Blocklist hit (Declined)": { - "summary": "The face matches a blocklisted face", - "value": { - "request_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef", - "face_search": { - "status": "Declined", - "total_matches": 1, - "matches": [ - { - "session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86", - "session_number": 323442, - "similarity_percentage": 99.99, - "source": "session", - "vendor_data": "user-1", - "verification_date": "2025-01-01T00:00:00Z", - "user_details": { - "full_name": "Jane Marie Doe", - "document_type": "ID", - "document_number": "X1234567" - }, - "match_image_url": "https:///face/3f6a1c2e/reference.jpg", - "status": "Approved", - "is_blocklisted": true, - "is_allowlisted": false, - "api_service": null, - "vendor_user_id": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f", - "biometric_template_id": null - } - ], - "user_image": { - "entities": [ - { - "bbox": [ - 40, - 40, - 120, - 120 - ], - "confidence": 0.732973 - } - ], - "best_angle": 0 - }, - "warnings": [ - { - "risk": "FACE_IN_BLOCKLIST", - "feature": "LIVENESS", - "additional_data": { - "blocklisted_session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86", - "blocklisted_session_number": 323442, - "api_service": null - }, - "log_type": "error", - "short_description": "Face in blocklist", - "long_description": "The system identified a face in the blocklist, which means the face is not allowed to be verified." - } - ] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T01:04:42.763237+00:00" - } - }, - "Duplicate found (Approved)": { - "summary": "The face already appeared in another approved session — informational, no decline", - "value": { - "request_id": "5e0c3a1f-7b2d-4c8e-9f10-2a3b4c5d6e7f", - "face_search": { - "status": "Approved", - "total_matches": 1, - "matches": [ - { - "session_id": "1f2e3d4c-5b6a-7980-1122-334455667788", - "session_number": 1024, - "similarity_percentage": 87.42, - "source": "session", - "vendor_data": "user-9", - "verification_date": "2025-11-20T09:15:00Z", - "user_details": null, - "match_image_url": "https:///face/9c8b7a6d/reference.jpg", - "status": "Approved", - "is_blocklisted": false, - "is_allowlisted": false, - "api_service": "PASSIVE_LIVENESS", - "vendor_user_id": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f", - "biometric_template_id": null - } - ], - "user_image": { - "entities": [ - { - "bbox": [ - 40, - 40, - 120, - 120 - ], - "confidence": 0.732973 - } - ], - "best_angle": 0 - }, - "warnings": [ - { - "risk": "DUPLICATED_FACE", - "feature": "LIVENESS", - "additional_data": { - "duplicated_session_id": "1f2e3d4c-5b6a-7980-1122-334455667788", - "duplicated_session_number": 1024, - "api_service": "PASSIVE_LIVENESS" - }, - "log_type": "information", - "short_description": "Duplicated face from other approved session", - "long_description": "The system identified a duplicated face from another approved session, requiring further investigation." - } - ] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T01:04:42.763237+00:00" - } - }, - "No matches (Approved)": { - "summary": "No enrolled face exceeded the similarity floor", - "value": { - "request_id": "9d8c7b6a-5f4e-3d2c-1b0a-998877665544", - "face_search": { - "status": "Approved", - "total_matches": 0, - "matches": [], - "user_image": { - "entities": [ - { - "bbox": [ - 40, - 40, - 120, - 120 - ], - "confidence": 0.732973 - } - ], - "best_angle": 0 - }, - "warnings": [] - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T01:04:42.763237+00:00" - } - } - }, "schema": { "type": "object", "properties": { - "request_id": { + "s3_key": { + "type": "string" + }, + "url": { "type": "string", - "format": "uuid", - "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." + "format": "uri" }, - "face_search": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined" - ], - "description": "`Declined` only when a `FACE_IN_BLOCKLIST` or `POSSIBLE_FACE_IN_BLOCKLIST` warning fired. Duplicate matches and multiple-face warnings never decline." - }, - "total_matches": { - "type": "integer", - "description": "Number of entries in `matches` (0–5).", - "example": 1 - }, - "matches": { - "type": "array", - "maxItems": 5, - "description": "Up to 5 faces from your face search index that exceed the similarity floor, ordered by similarity (with blocklisted entries ranked first when `search_type=blocklisted_or_approved`). Empty when no enrolled face is similar enough.", - "items": { - "type": "object", - "properties": { - "session_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Id of the verification session the matched face belongs to. `null` when `source` is `imported`, `list_entry`, or `retained_template`.", - "example": "882c42d5-8a4d-4d20-8080-a22f57822c86" - }, - "session_number": { - "type": "integer", - "nullable": true, - "description": "Human-friendly session number shown in the Business Console. `null` for non-session matches.", - "example": 323442 - }, - "similarity_percentage": { - "type": "number", - "format": "float", - "minimum": 0, - "maximum": 100, - "description": "Similarity between the submitted face and the matched face (0–100).", - "example": 99.99 - }, - "source": { - "type": "string", - "enum": [ - "session", - "imported", - "list_entry", - "retained_template" - ], - "description": "Where the matched face was enrolled from: `session` — a verification session or a saved standalone API call; `imported` — a face uploaded directly to a User profile; `list_entry` — a face attached to one of your lists; `retained_template` — an image-free biometric template stored on a User, listed under `/v3/biometric-templates/`." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "`vendor_data` of the matched session or linked User for imported and retained-template faces.", - "example": "user-1" - }, - "vendor_user_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Linked User id. `null` for list entries and session matches without a User.", - "example": "2f7c1a9e-4b28-4a6d-97f1-83576f80e1b4" - }, - "biometric_template_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Id of the retained biometric template. Set only when `source` is `retained_template`; `null` otherwise.", - "example": null - }, - "verification_date": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the matched face was originally captured, or when a retained template was created (`YYYY-MM-DDThh:mm:ssZ`). `null` for `list_entry` matches.", - "example": "2025-01-01T00:00:00Z" - }, - "user_details": { - "type": "object", - "nullable": true, - "description": "Identity data extracted during the matched session's document verification. `null` when the matched session has no document data (e.g. liveness-only sessions) and always `null` for `retained_template` matches. For `source: imported` matches it is populated from the User profile instead: `full_name` set, `document_type`/`document_number` null.", - "properties": { - "full_name": { - "type": "string", - "nullable": true, - "example": "Jane Marie Doe" - }, - "document_type": { - "type": "string", - "nullable": true, - "example": "ID" - }, - "document_number": { - "type": "string", - "nullable": true, - "example": "X1234567" - } - } - }, - "match_image_url": { - "type": "string", - "nullable": true, - "description": "Time-limited URL (`https:///...`) of the matched face's reference image when `save_api_request=true`. With `save_api_request=false` this is an internal storage path that is not directly downloadable. Always `null` for `retained_template` matches because retained templates contain no image.", - "example": "https:///face/3f6a1c2e/reference.jpg" - }, - "status": { - "type": "string", - "nullable": true, - "description": "Status of the matched session (`Approved`, `Declined`, `In Review`, …). `null` for `imported`/`list_entry` matches.", - "example": "Approved" - }, - "is_blocklisted": { - "type": "boolean", - "description": "`true` when the matched face is on your face blocklist. Any sufficiently similar blocklisted match declines the search." - }, - "is_allowlisted": { - "type": "boolean", - "description": "`true` when the matched face is on one of your allowlists. Allowlisted matches suppress duplicate warnings." - }, - "api_service": { - "type": "string", - "nullable": true, - "description": "Set when the matched face was enrolled by a standalone API call (e.g. `PASSIVE_LIVENESS`); `null` for regular verification sessions and imported faces.", - "example": null - } - } - } - }, - "user_image": { - "type": "object", - "description": "Face-detection results for the submitted image. Entries here carry only `bbox` and `confidence`.", - "properties": { - "entities": { - "type": "array", - "description": "One entry per detected face. The largest face is the one searched.", - "items": { - "type": "object", - "properties": { - "bbox": { - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 4, - "maxItems": 4, - "description": "Bounding box `[x_min, y_min, x_max, y_max]` in pixels.", - "example": [ - 40, - 40, - 120, - 120 - ] - }, - "confidence": { - "type": "number", - "format": "float", - "minimum": 0, - "maximum": 1, - "description": "Face-detection confidence (0–1).", - "example": 0.732973 - } - } - } - }, - "best_angle": { - "type": "integer", - "description": "Rotation (degrees) that produced the best face detection; non-zero only when `rotate_image=true` corrected the orientation. Never null on this endpoint (defaults to 0 when the model omits it).", - "example": 0 - } - } - }, - "warnings": { - "type": "array", - "description": "Risk signals. `FACE_IN_BLOCKLIST` / `POSSIBLE_FACE_IN_BLOCKLIST` (`error`) decline the search; `MULTIPLE_FACES_DETECTED` (`warning`) and `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` (`information`) are advisory.", - "items": { - "type": "object", - "properties": { - "risk": { - "type": "string", - "enum": [ - "MULTIPLE_FACES_DETECTED", - "FACE_IN_BLOCKLIST", - "POSSIBLE_FACE_IN_BLOCKLIST", - "DUPLICATED_FACE", - "POSSIBLE_DUPLICATED_FACE" - ], - "description": "Machine-readable risk code." - }, - "feature": { - "type": "string", - "enum": [ - "LIVENESS" - ], - "description": "Feature that raised the warning. Always `LIVENESS` on this endpoint." - }, - "additional_data": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "`null` for `MULTIPLE_FACES_DETECTED`. Blocklist hits carry `{blocklisted_session_id, blocklisted_session_number, api_service}`; duplicate hits carry `{duplicated_session_id, duplicated_session_number, api_service}` pointing at the first matching session." - }, - "log_type": { - "type": "string", - "enum": [ - "error", - "warning", - "information" - ], - "description": "Severity. `error` warnings drive `status` to `Declined`; `warning` and `information` entries are advisory and never decline on their own." - }, - "short_description": { - "type": "string", - "description": "Human-readable one-line summary of the risk." - }, - "long_description": { - "type": "string", - "description": "Human-readable explanation of the risk." - } - } - } - } - } + "filename": { + "type": "string" + }, + "file_size": { + "type": "integer" + } + } + } + } + } + }, + "400": { + "description": "No file, unsupported content type, or file exceeds 5 MB.", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "get": { + "summary": "Get note attachment URL", + "description": "Mint a fresh presigned download URL for an attachment already stored on one of the case's notes.", + "operationId": "get_case_note_attachment_url", + "x-didit-sdk": { + "resource": "cases.notes", + "method": "retrieve_attachment_url", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + }, + { + "name": "s3_key", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The attachment's s3_key, as returned by upload or listed on the note.", + "required": true + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/notes/presign/?s3_key=case-notes/c1a5e000-0000-4000-8000-000000000001/evidence.pdf' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Presigned URL.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "s3_key": { + "type": "string" }, - "vendor_data": { + "url": { "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent, or `null`." - }, - "metadata": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Echo of the `metadata` object you sent, or `null`." + "format": "uri" }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp (UTC) of when the response was generated, e.g. `2026-06-12T01:04:42.763237+00:00`." + "filename": { + "type": "string" } } } } } }, - "400": { - "description": "Validation error. Returned when `user_image` is missing, exceeds 5 MB, has an unsupported extension, or when **no face is detected** in the submitted image. Field errors use DRF's `{field: [messages]}` envelope; face-detection failures use `{\"error\": ...}`.", + "404": { + "description": "s3_key missing or does not belong to this case.", "content": { "application/json": { - "examples": { - "No face detected": { - "summary": "The image decodes fine but contains no detectable face", - "value": { - "error": "No face detected in the image" - } - }, - "Missing image": { - "summary": "`user_image` not included in the form data", - "value": { - "user_image": [ - "No file was submitted." - ] - } - }, - "Unsupported file extension": { - "summary": "File extension outside tiff/jpg/jpeg/png/webp", - "value": { - "user_image": [ - "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp." - ] - } - }, - "File too large": { - "summary": "Upload exceeds the 5 MB limit", - "value": { - "user_image": [ - "File size should not exceed 5 MB" - ] - } - } + "schema": { + "type": "object" } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/links/": { + "get": { + "summary": "List case links", + "description": "List sessions, business sessions, and transactions attached to the case.", + "operationId": "list_case_links", + "x-didit-sdk": { + "resource": "cases.links", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/links/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Case links.", "content": { "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseLink" + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "post": { + "summary": "Add case link", + "description": "Attach a session, business session, or transaction to the case. Rejected with 400 if the entity belongs to a different subject than the case.", + "operationId": "create_case_link", + "x-didit-sdk": { + "resource": "cases.links", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "entity_type", + "entity_uuid" + ], + "properties": { + "entity_type": { + "type": "string", + "enum": [ + "session", + "business_session", + "transaction" + ] }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" - } + "entity_uuid": { + "type": "string", + "format": "uuid" } } } } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/links/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"entity_type\": \"transaction\", \"entity_uuid\": \"77000000-0000-4000-8000-000000000077\"}'" + } + ], + "responses": { + "201": { + "description": "Link created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseLink" + } + } + } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "400": { + "description": "Entity belongs to a different profile than the case subject.", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { "type": "object", "properties": { @@ -16385,1214 +36815,1039 @@ { "ApiKeyAuth": [] } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl -X POST 'https://verification.didit.me/v3/face-search/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'user_image=@./selfie.jpg' \\\n -F 'search_type=most_similar' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nurl = 'https://verification.didit.me/v3/face-search/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as f:\n files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}\n data = {\n 'search_type': 'most_similar',\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nbody = resp.json()\nprint('status:', body['face_search']['status'])\nprint('matches:', body['face_search']['total_matches'])\nfor m in body['face_search']['matches']:\n print(f\" session={m['session_id']} similarity={m['similarity_percentage']} blocklisted={m['is_blocklisted']}\")" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('search_type', 'most_similar');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/face-search/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`Face search failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.face_search.status, 'matches:', body.face_search.total_matches);\nbody.face_search.matches.forEach(m =>\n console.log(` session=${m.session_id} similarity=${m.similarity_percentage} blocklisted=${m.is_blocklisted}`)\n);" - } ] } }, - "/v3/sessions/": { - "get": { - "summary": "List verification sessions (KYC and/or KYB) with filters and pagination", - "description": "Returns a paginated list of verification sessions for the application identified by your API key, newest first (descending `session_number`).\n\n**Canonical URL:** the route is `/v3/sessions/` (trailing slash). Requests to `/v3/sessions` without the slash receive a `301` redirect to the slashed URL with the query string preserved; most HTTP clients follow it transparently on GET.\n\n**When to use this endpoint vs webhooks.** Use this endpoint for dashboards, back-office search, reconciliation, and batch exports. Do not poll it to track the progress of individual verifications - configure [webhooks](/integration/webhooks) instead, which push `status.updated` events the moment a session changes. For one session's full result, call `GET /v3/session/{sessionId}/decision/`.\n\n**KYC and KYB sessions.** `session_kind` selects which kinds are listed:\n- `user` (default) - User Verification (KYC) sessions only. Backward-compatible behavior.\n- `business` - Business Verification (KYB) sessions only. Rows have a different, smaller shape (see the response schema).\n- `all` - both kinds in one response: the KYC page and the KYB page for the same `limit`/`offset` are concatenated in `results` (KYC rows first), so a page can contain up to `2 x limit` rows. `count` is the sum of both kinds and `next`/`previous` reflect the KYC-side pagination. For strict pagination of mixed data, call `user` and `business` separately.\n\nEvery row carries a `session_kind` discriminator (`\"user\"` or `\"business\"`).\n\n**Pagination.** Standard limit/offset: `limit` (default 50) and `offset` (default 0), with `next`/`previous` ready-made page URLs. For KYC rows `count` is exact. For KYB rows `count` is computed with a bounded scan and is capped at 100 - when there are more than 100 matches it stays at 100, so iterate using `next` (or by checking whether a full page came back) rather than computing total pages from `count`.\n\n**Filter semantics.** All filters combine with AND and apply to KYC rows. When `session_kind` is `business` or `all`, the KYB side honors the shared parameters `vendor_data`, `status`, `workflow_id`, `country` (registry-company country code), `date_from`, `date_to`, `search` (company name, registration number — type registration numbers without separators, e.g. `DPC001` not `DPC-001` — vendor_data, session number, or session UUID), `warning`, and `organization_name`; the remaining KYC-only filters are ignored for KYB rows. Comma-separated parameters (`status`, `country`, `document_type`, `region`, `workflow_id`, `document_name`, `warning`, `organization_name`, `session_type`) match any of the listed values (OR within the parameter). `vendor_data` is an exact match - use `search` for partial matching. Unknown values in list filters simply return no rows (no error); malformed typed values (`date_from`, `date_to`, `didit_internal_id`, `api_service`, `session_kind`) return `400` with a per-field error object.\n\n**Search.** `search` adapts to the input: a full UUID looks up `session_id`; digits (with optional `+ - . space`) match session-number prefix, document/personal numbers, phone-number prefix, or `vendor_data`; values containing `@` match email prefixes; any other text matches names (accent-insensitive) and `vendor_data`.\n\nNo fixed per-endpoint rate limit is enforced, but keep list polling infrequent and rely on webhooks for real-time updates.", - "operationId": "get_v3_sessions", + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/links/{link_uuid}/": { + "delete": { + "summary": "Remove case link", + "description": "Detach a linked session, business session, or transaction from the case.", + "operationId": "delete_case_link", + "x-didit-sdk": { + "resource": "cases.links", + "method": "delete", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + }, + { + "name": "link_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "11000000-0000-4000-8000-000000000004" + }, + "description": "Link UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/links/11000000-0000-4000-8000-000000000004/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "204": { + "description": "Link removed." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/events/": { + "get": { + "summary": "List case events", + "description": "Read-only audit timeline of the case (created, status changes, assignment, resolution, escalation, transfers, tag/priority/due-date changes, entity actions, and more).", + "operationId": "list_case_events", + "x-didit-sdk": { + "resource": "cases", + "method": "list_events", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "description": "Page size." + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/events/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Paginated events, newest first.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Number of matching rows. Exact up to 100, capped at 100 beyond that for performance. Rely on `next` being null to detect the last page." + }, + "next": { + "type": "string", + "nullable": true, + "description": "Absolute URL of the next page, or null." + }, + "previous": { + "type": "string", + "nullable": true, + "description": "Absolute URL of the previous page, or null." + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseEvent" + } + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/checklist/": { + "get": { + "summary": "List case checklist", + "description": "List the case's checklist items, copied from the blueprint's content_config.checklist.items at creation, ordered.", + "operationId": "list_case_checklist", + "x-didit-sdk": { + "resource": "cases.checklist", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/checklist/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Checklist items.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseChecklistItem" + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/checklist/{item_uuid}/": { + "patch": { + "summary": "Toggle checklist item", + "description": "Mark a checklist item complete or incomplete. Resolution is blocked while any item is incomplete, unless the blueprint marks the checklist optional.", + "operationId": "toggle_case_checklist_item", + "x-didit-sdk": { + "resource": "cases.checklist", + "method": "toggle_item", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Sessions" + "Cases" ], "parameters": [ { - "name": "session_kind", - "in": "query", - "required": false, - "description": "Which kind of sessions to list: `user` (KYC only, default), `business` (KYB only), or `all` (both, concatenated - see the description for merged-pagination semantics). Matching is case-insensitive (`BUSINESS` works); any value other than a case variant of `user`/`business`/`all` returns `400`.", + "name": "organization_id", + "in": "path", + "required": true, "schema": { "type": "string", - "enum": [ - "user", - "business", - "all" - ], - "default": "user" - }, - "example": "all" - }, - { - "name": "limit", - "in": "query", - "required": false, - "description": "Maximum number of rows per page (per kind when `session_kind=all`). Defaults to 50.", - "schema": { - "type": "integer", - "default": 50, - "minimum": 1 + "format": "uuid" }, - "example": 20 + "description": "Organization UUID." }, { - "name": "offset", - "in": "query", - "required": false, - "description": "Number of rows to skip before the first returned row. Defaults to 0. Prefer following the `next`/`previous` URLs from the response.", + "name": "application_id", + "in": "path", + "required": true, "schema": { - "type": "integer", - "default": 0, - "minimum": 0 + "type": "string", + "format": "uuid" }, - "example": 20 + "description": "Application UUID." }, { - "name": "status", - "in": "query", - "required": false, - "description": "Comma-separated list of session statuses to include, e.g. `Approved,In Review`. Values are case-sensitive; see [Verification Statuses](/integration/verification-statuses). Unknown values match nothing.", + "name": "case_uuid", + "in": "path", + "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" }, - "example": "Approved,In Review" + "description": "Case UUID." }, { - "name": "vendor_data", - "in": "query", - "required": false, - "description": "Exact-match filter on the `vendor_data` you sent at session creation (your own user/business identifier). For partial matches use `search` instead.", + "name": "item_uuid", + "in": "path", + "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid", + "example": "c4ec0000-0000-4000-8000-000000000005" }, - "example": "your-user-id-123" - }, + "description": "Checklist item UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "is_completed" + ], + "properties": { + "is_completed": { + "type": "boolean" + } + } + } + } + } + }, + "x-codeSamples": [ { - "name": "search", - "in": "query", - "required": false, - "description": "Smart free-text search. A full UUID matches `session_id`; numeric input matches session-number prefix, document/personal numbers, phone prefix, or `vendor_data`; input containing `@` matches email prefixes; other text matches names (accent-insensitive) and `vendor_data`. Free text containing digits additionally matches document/personal-number search fields and session-number prefixes.", - "schema": { - "type": "string" - }, - "example": "jane doe" - }, + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/checklist/c4ec0000-0000-4000-8000-000000000005/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"is_completed\": true}'" + } + ], + "responses": { + "200": { + "description": "Updated checklist item.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseChecklistItem" + } + } + } + } + }, + "security": [ { - "name": "workflow_id", - "in": "query", - "required": false, - "description": "Comma-separated workflow UUIDs (find them on the Workflows page in the Console). Malformed UUIDs in the list are ignored; if none are valid the result is empty.", - "schema": { - "type": "string" - }, - "example": "9f9b1234-aaaa-bbbb-cccc-1234567890ab" - }, + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/aml/": { + "get": { + "summary": "List case AML hits", + "description": "List AML screening hits for the case's subject. Same shape as the hits returned by POST /v3/aml/.", + "operationId": "list_case_aml_hits", + "x-didit-sdk": { + "resource": "cases", + "method": "list_aml_hits", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ { - "name": "date_from", - "in": "query", - "required": false, - "description": "Only sessions created on or after this date (UTC), format `YYYY-MM-DD`. Malformed dates return `400`.", + "name": "organization_id", + "in": "path", + "required": true, "schema": { "type": "string", - "format": "date" + "format": "uuid" }, - "example": "2026-06-01" + "description": "Organization UUID." }, { - "name": "date_to", - "in": "query", - "required": false, - "description": "Only sessions created up to this date (UTC), format `YYYY-MM-DD`. For KYC rows the entire day is included; for KYB rows the comparison is `created_at <= date_to` (midnight), so use the next day's date to include a full day of KYB sessions.", + "name": "application_id", + "in": "path", + "required": true, "schema": { "type": "string", - "format": "date" + "format": "uuid" }, - "example": "2026-06-30" + "description": "Application UUID." }, { - "name": "country", - "in": "query", - "required": false, - "description": "Comma-separated country filter. For KYC rows: ISO 3166-1 alpha-3 codes of the ID document's issuing state (e.g. `ESP,USA`), which may differ from nationality. For KYB rows: the registry company's country code.", + "name": "case_uuid", + "in": "path", + "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" }, - "example": "ESP,USA" - }, + "description": "Case UUID." + } + ], + "x-codeSamples": [ { - "name": "document_type", - "in": "query", - "required": false, - "description": "Comma-separated ID-document type codes verified in the session (KYC rows only): `P` (Passport), `ID` (Identity Card), `DL` (Driver's License), `RP` (Residence Permit), `SSC` (Social Security Card), `HIC` (Health Insurance Card), `WP` (Work Permit), `TC` (Tax Card), `VISA`, `PSC` (Public Service Card), `BC` (Birth Certificate), `OTHER`. Note the response field `document_type` contains the display name, not the code.", - "schema": { - "type": "string" - }, - "example": "P,ID" - }, + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/aml/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "AML hits.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AMLScreening" + } + } + } + } + } + }, + "security": [ { - "name": "document_name", - "in": "query", - "required": false, - "description": "Exact document names; multiple names can be passed comma-separated, but the value is only split on commas when it contains more than one ` - ` separator (a lone name with an embedded comma is matched whole). Names are as recognized by document verification (front or back side), e.g. `Spain - Id Card (2021)`. KYC rows only.", - "schema": { - "type": "string" - }, - "example": "Spain - Id Card (2021)" - }, + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/statistics/": { + "get": { + "summary": "Case analytics", + "description": "Aggregate case analytics for the date range (defaults to the last 30 days; max 731 days). Charts, tables, totals, assignment events, and current queue counts use the same application-scoped filters. Filters use current case values. Dates apply to each metric event, so older cases assigned or resolved during the period remain included. Queue depth is independent of the date range. Invalid filters return 400; an unknown blueprint or assignee returns an empty result.", + "operationId": "get_case_analytics", + "x-didit-sdk": { + "resource": "cases", + "method": "retrieve_statistics", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ { - "name": "region", - "in": "query", - "required": false, - "description": "Comma-separated document region/state values (for documents issued per region, such as US driver's licenses). KYC rows only.", + "name": "organization_id", + "in": "path", + "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, - "example": "CA,TX" + "description": "Organization UUID." }, { - "name": "didit_internal_id", - "in": "query", - "required": false, - "description": "Filter by Didit's internal user identifier (the `didit_internal_id` response field) to list all sessions of one verified user. Must be a valid UUID; malformed values return `400`. KYC rows only.", + "name": "application_id", + "in": "path", + "required": true, "schema": { "type": "string", "format": "uuid" }, - "example": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147" + "description": "Application UUID." }, { - "name": "session_type", + "name": "date_from", "in": "query", - "required": false, - "description": "Comma-separated session creation channels: `API`, `HOSTED`, `MIGRATED`. The special value `ALL` (or omitting the parameter) disables the filter. KYC rows only.", "schema": { - "type": "string" + "type": "string", + "format": "date" }, - "example": "HOSTED" + "description": "Range start (inclusive). Defaults to 30 days ago." }, { - "name": "api_service", + "name": "date_to", "in": "query", - "required": false, - "description": "Filter standalone API sessions by service. Invalid values return `400`. KYC rows only.", "schema": { "type": "string", - "enum": [ - "ID_VERIFICATION", - "FACE_MATCH", - "AGE_ESTIMATION", - "FACE_SEARCH", - "POA", - "AML", - "KYB_REGISTRY", - "PASSIVE_LIVENESS", - "DATABASE_VALIDATION", - "PHONE_VERIFICATION", - "EMAIL_VERIFICATION" - ] + "format": "date" }, - "example": "AML" + "description": "Range end (inclusive). Defaults to today." }, { - "name": "screened_full_name", + "name": "blueprint", "in": "query", "required": false, - "description": "Accent-insensitive name search inside AML and database-validation screened data. When combined with `api_service=AML` or `api_service=DATABASE_VALIDATION`, the search is restricted to that feature's screened data. KYC rows only.", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, - "example": "Jane Doe" + "description": "Filter by the current blueprint UUID." }, { - "name": "warning", + "name": "assigned_to", "in": "query", "required": false, - "description": "Comma-separated warning risk codes; returns sessions that raised any of them (e.g. `POSSIBLE_FRAUD`). Risk codes are listed per feature in the [warnings documentation](/integration/webhooks). Applies to KYC rows and, with business warning codes, to KYB rows.", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, - "example": "POSSIBLE_FRAUD" + "description": "Filter by the current assigned reviewer user UUID." }, { - "name": "status_initialized_as_in_review", + "name": "priority", "in": "query", "required": false, - "description": "When `true`, only sessions whose decision initially landed in `In Review` (useful to audit manual-review volume). KYC rows only.", "schema": { - "type": "boolean" + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] }, - "example": true + "description": "Filter by current case priority." }, { - "name": "is_kyc_to_be_reviewed", + "name": "source", "in": "query", "required": false, - "description": "`true` returns sessions whose document verification is flagged for manual review; `false` returns sessions with document verification explicitly not flagged. Omit to disable. KYC rows only.", "schema": { - "type": "boolean" + "type": "string", + "enum": [ + "MANUAL", + "AML_SCREENING", + "TRANSACTION_RULE", + "WORKFLOW_BUILDER" + ] }, - "example": true + "description": "Filter by case creation source." }, { - "name": "euuid", + "name": "status", "in": "query", "required": false, - "description": "Filter by the end user's euuid (UUID assigned to the verifying user, used by Console deep links). Must be a valid UUID; malformed values return a 400 field error. KYC rows only.", "schema": { "type": "string", - "format": "uuid" + "enum": [ + "OPEN", + "AWAITING_USER", + "RESOLVED" + ] }, - "example": "0f8fad5b-d9cb-469f-a165-70867728950e" - }, + "description": "Filter by current case status." + } + ], + "x-codeSamples": [ { - "name": "organization_name", - "in": "query", - "required": false, - "description": "Comma-separated organization names. Console-oriented; with an API key (already scoped to one application) it only matches your own organization's name.", - "schema": { - "type": "string" - }, - "example": "Acme Inc" + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/statistics/?date_from=2026-06-01&date_to=2026-06-30' \\\n -H 'x-api-key: YOUR_API_KEY'" } ], "responses": { "200": { - "description": "Paginated session list. `results` items are KYC rows, KYB rows, or both depending on `session_kind`; discriminate with each row's `session_kind` field.", + "description": "Analytics payload.", "content": { "application/json": { "schema": { "type": "object", - "required": [ - "count", - "next", - "previous", - "results" - ], "properties": { - "count": { - "type": "integer", - "description": "Number of sessions matching the filters. Exact for KYC rows; for KYB rows it is capped at 100 (use `next` to know whether more pages exist). For `session_kind=all` it is the sum of both kinds.", - "example": 2 - }, - "next": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Absolute URL of the next page, or `null` on the last page.", - "example": "https://verification.didit.me/v3/sessions/?limit=20&offset=20" - }, - "previous": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Absolute URL of the previous page, or `null` on the first page.", - "example": null - }, - "results": { + "series": { "type": "array", - "description": "Session rows, newest first. With `session_kind=all`, KYC rows come first, then KYB rows.", "items": { - "oneOf": [ - { - "title": "KYC session row (session_kind: user)", + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date" + }, + "created": { + "type": "integer" + }, + "reviewed": { + "type": "integer" + } + } + } + }, + "tables": { + "type": "object", + "properties": { + "officer": { + "type": "array", + "items": { "type": "object", - "description": "Summary row for a User Verification (KYC) session. Identity fields (`full_name`, `country`, `document_type`, `portrait_image`, contact fields) are populated progressively as the user completes verification steps, and are `null` until the relevant step finishes. Fields restricted by the workflow's response-attributes configuration are also returned as `null`.", - "required": [ - "session_id", - "session_kind", - "session_number", - "session_url", - "portrait_image", - "document_type", - "full_name", - "country", - "status", - "vendor_data", - "didit_internal_id", - "created_at", - "features", - "phone_number", - "email_address", - "additional_details" - ], "properties": { - "session_id": { + "key": { "type": "string", - "format": "uuid", - "description": "Unique identifier of the verification session. Use it with `GET /v3/session/{sessionId}/decision/` to fetch the full decision payload.", - "example": "5b3720ed-d429-42ef-b67f-37ea805f48ee" + "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions." + }, + "created": { + "type": "integer" + }, + "assigned": { + "type": "integer" + }, + "resolved": { + "type": "integer" + }, + "threat_rate": { + "type": "number", + "format": "float" + }, + "false_positive_rate": { + "type": "number", + "format": "float" + }, + "unresolved": { + "type": "integer" + }, + "reassignment_rate": { + "type": "number", + "format": "float" + }, + "escalation_rate": { + "type": "number", + "format": "float" + }, + "avg_assign_hours": { + "type": "number", + "format": "float" + }, + "avg_resolve_hours": { + "type": "number", + "format": "float" + }, + "avg_handling_hours": { + "type": "number", + "format": "float" + } + } + } + }, + "blueprint": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions." + }, + "created": { + "type": "integer" + }, + "assigned": { + "type": "integer" + }, + "resolved": { + "type": "integer" + }, + "threat_rate": { + "type": "number", + "format": "float" + }, + "false_positive_rate": { + "type": "number", + "format": "float" + }, + "unresolved": { + "type": "integer" }, - "session_kind": { - "type": "string", - "enum": [ - "user" - ], - "description": "Row discriminator. Always `\"user\"` for KYC rows; Business Verification (KYB) rows have `\"business\"` and a different shape.", - "example": "user" + "reassignment_rate": { + "type": "number", + "format": "float" }, - "session_number": { - "type": "integer", - "description": "Human-friendly sequential number of the session within your application. Also matched by the `search` parameter.", - "example": 720 + "escalation_rate": { + "type": "number", + "format": "float" }, - "session_url": { - "type": "string", - "nullable": true, - "description": "Hosted verification URL the end user opens to complete the flow. `null` when the session has no hosted URL.", - "example": "https://verify.didit.me/session/CuXoBYCYVO5Y" + "avg_assign_hours": { + "type": "number", + "format": "float" }, - "portrait_image": { - "type": "string", - "nullable": true, - "description": "Expiring (presigned) URL of the best available portrait: the NFC chip photo when available, otherwise the ID document portrait, otherwise the liveness reference image. `null` until one of those exists. Download promptly; the link expires.", - "example": "https:///portraits/5b3720ed.jpg?X-Amz-Expires=3600&X-Amz-Signature=..." + "avg_resolve_hours": { + "type": "number", + "format": "float" }, - "document_type": { + "avg_handling_hours": { + "type": "number", + "format": "float" + } + } + } + }, + "source": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { "type": "string", - "nullable": true, - "enum": [ - "Passport", - "Identity Card", - "Driver's License", - "Residence Permit", - "Health Insurance Card", - "Tax Card" - ], - "description": "Human-readable type of the verified ID document. `null` before document verification completes, and also `null` after completion when the verified code has no display mapping (`SSC`, `WP`, `VISA`, `PSC`, `BC`, `OTHER`). Note: the `document_type` query parameter filters by short codes (`P`, `ID`, `DL`, `RP`, ...), not by these display values.", - "example": "Passport" + "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions." }, - "full_name": { - "type": "string", - "nullable": true, - "description": "Best available full name for the session, resolved in order: ID document OCR, AML screened data, database-validation screened data, proof-of-address document, then the expected details provided at session creation. `null` when none is available yet.", - "example": "Jane Marie Doe" + "created": { + "type": "integer" }, - "country": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-3 country associated with the session, resolved in order: ID document issuing state, database-validation issuing state, proof-of-address issuing state, expected details, then the verified phone's country. May differ from nationality.", - "example": "ESP" + "assigned": { + "type": "integer" }, - "status": { - "type": "string", - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "description": "Overall session status. See the [Verification Statuses](/integration/verification-statuses) page for the lifecycle.", - "example": "Approved" + "resolved": { + "type": "integer" }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own identifier for the end user, echoed back exactly as sent at session creation.", - "example": "your-user-id-123" + "threat_rate": { + "type": "number", + "format": "float" }, - "didit_internal_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Didit's internal identifier for the verified user profile behind this session. Stable across multiple sessions of the same user; filterable via the `didit_internal_id` query parameter.", - "example": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147" + "false_positive_rate": { + "type": "number", + "format": "float" }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "UTC timestamp when the session was created.", - "example": "2026-06-09T12:26:54.328364Z" + "unresolved": { + "type": "integer" }, - "features": { - "type": "array", - "description": "Per-feature status for every verification step configured in the session's workflow, in workflow order.", - "items": { - "type": "object", - "required": [ - "feature", - "status" - ], - "properties": { - "feature": { - "type": "string", - "enum": [ - "ID_VERIFICATION", - "NFC", - "LIVENESS", - "FACE_MATCH", - "POA", - "QUESTIONNAIRE", - "EMAIL_VERIFICATION", - "PHONE", - "AML", - "IP_ANALYSIS", - "AGE_ESTIMATION", - "DATABASE_VALIDATION" - ], - "description": "Verification feature identifier.", - "example": "ID_VERIFICATION" - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review", - "Resub Requested" - ], - "description": "Status of this feature. `Not Finished` until the step completes.", - "example": "Approved" - } - } - } + "reassignment_rate": { + "type": "number", + "format": "float" }, - "phone_number": { - "type": "object", - "nullable": true, - "description": "Phone associated with the session: the verified phone when phone verification ran, otherwise the contact phone provided at session creation. `null` when neither exists.", - "required": [ - "number", - "is_verified" - ], - "properties": { - "number": { - "type": "string", - "description": "Phone number in E.164 format.", - "example": "+14155552671" - }, - "is_verified": { - "type": "boolean", - "description": "`true` only when the phone-verification feature approved this number.", - "example": true - } - } + "escalation_rate": { + "type": "number", + "format": "float" }, - "email_address": { - "type": "object", - "nullable": true, - "description": "Email associated with the session: the verified email when email verification ran, otherwise the contact email provided at session creation. `null` when neither exists.", - "required": [ - "email", - "is_verified" - ], - "properties": { - "email": { - "type": "string", - "format": "email", - "example": "jane@example.com" - }, - "is_verified": { - "type": "boolean", - "description": "`true` only when the email-verification feature approved this address.", - "example": true - } - } + "avg_assign_hours": { + "type": "number", + "format": "float" }, - "additional_details": { - "type": "object", - "nullable": true, - "description": "Compact per-feature extras. Only features that produced data appear as keys; `null` when no feature produced extras. Fields excluded by the workflow's response-attributes configuration are omitted.", - "properties": { - "aml": { - "type": "object", - "description": "Present when AML screening ran.", - "properties": { - "total_hits": { - "type": "integer", - "description": "Number of AML screening hits.", - "example": 0 - }, - "entity_type": { - "type": "string", - "description": "Screened entity type.", - "example": "person" - }, - "is_adverse_media_fetched": { - "type": "boolean", - "example": false - }, - "screened_data": { - "type": "object", - "description": "Subset of the data that was screened. Only non-empty keys are included.", - "properties": { - "nationality": { - "type": "string", - "example": "ESP" - }, - "document_number": { - "type": "string", - "example": "XX1234567" - }, - "date_of_birth": { - "type": "string", - "example": "1990-05-21" - } - } - } - } - }, - "database_validation": { - "type": "object", - "description": "Present when database validation ran.", - "properties": { - "validation_type": { - "type": "string", - "example": "DOCUMENT_VALIDATION" - }, - "match_type": { - "type": "string", - "example": "FULL_MATCH" - }, - "identification_number": { - "type": "string", - "description": "Tax, personal, or document number that was validated (first available).", - "example": "12345678Z" - } - } - }, - "poa": { - "type": "object", - "description": "Present when proof of address ran.", - "properties": { - "document_type": { - "type": "string", - "example": "UTILITY_BILL" - }, - "issuer": { - "type": "string", - "example": "Energy Co" - }, - "address": { - "type": "string", - "example": "Calle Mayor 1, 28013 Madrid" - }, - "name_on_document": { - "type": "string", - "example": "Jane Marie Doe" - } - } - }, - "liveness": { - "type": "object", - "description": "Present when a liveness check ran.", - "properties": { - "method": { - "type": "string", - "example": "PASSIVE" - }, - "score": { - "type": "number", - "description": "Liveness confidence score, rounded to 2 decimals.", - "example": 0.98 - } - } - }, - "face_match": { - "type": "object", - "description": "Present when face match ran.", - "properties": { - "score": { - "type": "number", - "description": "Face-match similarity score, rounded to 2 decimals.", - "example": 0.95 - }, - "source_image": { - "type": "string", - "description": "Expiring (presigned) URL of the source face image.", - "example": "https:///faces/source.jpg?X-Amz-Expires=3600&..." - }, - "target_image": { - "type": "string", - "description": "Expiring (presigned) URL of the target face image.", - "example": "https:///faces/target.jpg?X-Amz-Expires=3600&..." - } - } - }, - "phone_verification": { - "type": "object", - "description": "Present when phone verification ran.", - "properties": { - "country_code": { - "type": "string", - "description": "ISO 3166-1 alpha-2 country of the phone number.", - "example": "US" - }, - "is_temporary": { - "type": "boolean", - "example": false - }, - "is_ported": { - "type": "boolean", - "example": false - }, - "is_blocked": { - "type": "boolean", - "example": false - } - } - }, - "email_verification": { - "type": "object", - "description": "Present when email verification ran.", - "properties": { - "is_breached": { - "type": "boolean", - "example": false - }, - "is_disposable": { - "type": "boolean", - "example": false - }, - "is_undeliverable": { - "type": "boolean", - "example": false - }, - "is_blocklisted": { - "type": "boolean", - "example": false - } - } - } - } + "avg_resolve_hours": { + "type": "number", + "format": "float" + }, + "avg_handling_hours": { + "type": "number", + "format": "float" } } - }, - { - "title": "KYB session row (session_kind: business)", + } + }, + "status": { + "type": "array", + "items": { "type": "object", - "description": "Summary row for a Business Verification (KYB) session. Returned only when `session_kind` is `business` or `all`.", - "required": [ - "session_id", - "session_kind", - "session_number", - "status", - "vendor_data", - "created_at", - "workflow_id", - "workflow_type", - "workflow_label", - "company_name", - "registration_number", - "country" - ], "properties": { - "session_id": { + "key": { "type": "string", - "format": "uuid", - "description": "Unique identifier of the business verification session.", - "example": "8a1f63c2-1b9e-4f3a-9c41-2f6a1f0b7d52" + "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions." }, - "session_kind": { - "type": "string", - "enum": [ - "business" - ], - "description": "Row discriminator. Always `\"business\"` for KYB rows.", - "example": "business" + "created": { + "type": "integer" }, - "session_number": { - "type": "integer", - "description": "Human-friendly sequential number of the business session within your application. Numbered independently from KYC sessions.", - "example": 42 + "assigned": { + "type": "integer" }, - "status": { - "type": "string", - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "description": "Overall session status. See the [Verification Statuses](/integration/verification-statuses) page.", - "example": "Approved" + "resolved": { + "type": "integer" }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own identifier for the business, echoed back exactly as sent at session creation.", - "example": "your-company-id-77" + "threat_rate": { + "type": "number", + "format": "float" }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "UTC timestamp when the session was created.", - "example": "2026-06-08T09:14:02.118450Z" + "false_positive_rate": { + "type": "number", + "format": "float" }, - "workflow_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Identifier of the workflow this session was created with.", - "example": "9f9b1234-aaaa-bbbb-cccc-1234567890ab" + "unresolved": { + "type": "integer" }, - "workflow_type": { - "type": "string", - "nullable": true, - "description": "Workflow type. `kyb` for business workflows.", - "example": "kyb" + "reassignment_rate": { + "type": "number", + "format": "float" }, - "workflow_label": { - "type": "string", - "nullable": true, - "description": "Display name of the workflow.", - "example": "Standard KYB" + "escalation_rate": { + "type": "number", + "format": "float" }, - "company_name": { - "type": "string", - "nullable": true, - "description": "Name of the company being verified, from the registry company linked to the session. `null` until a company is selected.", - "example": "Acme Holdings S.L." + "avg_assign_hours": { + "type": "number", + "format": "float" }, - "registration_number": { - "type": "string", - "nullable": true, - "description": "Company registration number from the registry company linked to the session.", - "example": "B12345678" + "avg_resolve_hours": { + "type": "number", + "format": "float" }, - "country": { - "type": "string", - "nullable": true, - "description": "Country code of the registry company linked to the session.", - "example": "ES" + "avg_handling_hours": { + "type": "number", + "format": "float" } } } - ] + } } - } - } - }, - "examples": { - "KYC sessions (default)": { - "summary": "session_kind=user - one completed and one not-started session", - "value": { - "count": 2, - "next": null, - "previous": null, - "results": [ - { - "session_id": "5b3720ed-d429-42ef-b67f-37ea805f48ee", - "session_kind": "user", - "session_number": 720, - "session_url": "https://verify.didit.me/session/CuXoBYCYVO5Y", - "portrait_image": "https:///portraits/5b3720ed.jpg?X-Amz-Expires=3600&X-Amz-Signature=...", - "document_type": "Passport", - "full_name": "Jane Marie Doe", - "country": "ESP", - "status": "Approved", - "vendor_data": "your-user-id-123", - "didit_internal_id": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147", - "created_at": "2026-06-09T12:26:54.328364Z", - "features": [ - { - "feature": "ID_VERIFICATION", - "status": "Approved" - }, - { - "feature": "LIVENESS", - "status": "Approved" - }, - { - "feature": "FACE_MATCH", - "status": "Approved" - }, - { - "feature": "AML", - "status": "Approved" - }, - { - "feature": "IP_ANALYSIS", - "status": "Approved" - } - ], - "phone_number": { - "number": "+14155552671", - "is_verified": true - }, - "email_address": { - "email": "jane@example.com", - "is_verified": true - }, - "additional_details": { - "aml": { - "total_hits": 0, - "entity_type": "person", - "is_adverse_media_fetched": false - }, - "liveness": { - "method": "PASSIVE", - "score": 0.98 - }, - "face_match": { - "score": 0.95 - } - } + }, + "totals": { + "type": "object", + "properties": { + "created": { + "type": "integer" }, - { - "session_id": "038ad62f-5ebd-46c2-afe9-4b2956bf687f", - "session_kind": "user", - "session_number": 719, - "session_url": "https://verify.didit.me/session/7RGKiO6ioGH6", - "portrait_image": null, - "document_type": null, - "full_name": null, - "country": null, - "status": "Not Started", - "vendor_data": "your-user-id-124", - "didit_internal_id": "eb4e679d-ca4b-461f-9685-687bac29ebdb", - "created_at": "2026-06-09T08:02:23.343053Z", - "features": [ - { - "feature": "ID_VERIFICATION", - "status": "Not Finished" - }, - { - "feature": "LIVENESS", - "status": "Not Finished" - }, - { - "feature": "FACE_MATCH", - "status": "Not Finished" - }, - { - "feature": "AML", - "status": "Not Finished" - }, - { - "feature": "IP_ANALYSIS", - "status": "Not Finished" - } - ], - "phone_number": null, - "email_address": null, - "additional_details": null + "assigned": { + "type": "integer" + }, + "resolved": { + "type": "integer" + }, + "threat_rate": { + "type": "number", + "format": "float" + }, + "false_positive_rate": { + "type": "number", + "format": "float" + }, + "unresolved": { + "type": "integer" + }, + "reassignment_rate": { + "type": "number", + "format": "float" + }, + "escalation_rate": { + "type": "number", + "format": "float" + }, + "avg_assign_hours": { + "type": "number", + "format": "float" + }, + "avg_resolve_hours": { + "type": "number", + "format": "float" + }, + "avg_handling_hours": { + "type": "number", + "format": "float" } - ] - } - }, - "Mixed KYC + KYB (session_kind=all)": { - "summary": "session_kind=all - KYC row followed by a KYB row", - "value": { - "count": 2, - "next": null, - "previous": null, - "results": [ - { - "session_id": "5b3720ed-d429-42ef-b67f-37ea805f48ee", - "session_kind": "user", - "session_number": 720, - "session_url": "https://verify.didit.me/session/CuXoBYCYVO5Y", - "portrait_image": null, - "document_type": "Identity Card", - "full_name": "Jane Marie Doe", - "country": "ESP", - "status": "In Review", - "vendor_data": "your-user-id-123", - "didit_internal_id": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147", - "created_at": "2026-06-09T12:26:54.328364Z", - "features": [ - { - "feature": "ID_VERIFICATION", - "status": "In Review" - }, - { - "feature": "LIVENESS", - "status": "Approved" - } - ], - "phone_number": null, - "email_address": null, - "additional_details": { - "liveness": { - "method": "PASSIVE", - "score": 0.98 - } - } + } + }, + "queue": { + "type": "object", + "description": "Live snapshot, not scoped to the date range.", + "properties": { + "open": { + "type": "integer" }, - { - "session_id": "8a1f63c2-1b9e-4f3a-9c41-2f6a1f0b7d52", - "session_kind": "business", - "session_number": 42, - "status": "Approved", - "vendor_data": "your-company-id-77", - "created_at": "2026-06-08T09:14:02.118450Z", - "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab", - "workflow_type": "kyb", - "workflow_label": "Standard KYB", - "company_name": "Acme Holdings S.L.", - "registration_number": "B12345678", - "country": "ES" + "overdue": { + "type": "integer" } - ] + } } } } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/bulk/status/": { + "post": { + "summary": "Bulk update case status", + "description": "Apply the same status transition to several cases in one call: OPEN reopens, AWAITING_USER requests info, RESOLVED resolves (resolution required). Each case is evaluated independently; gate failures (incomplete checklist, missing required note) are reported per case without aborting the batch.", + "operationId": "bulk_update_case_status", + "x-didit-sdk": { + "resource": "cases", + "method": "bulk_update_status", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "301": { - "description": "Redirect to the canonical trailing-slash URL. Returned when the request path is `/v3/sessions` (no trailing slash); the `Location` header points to `/v3/sessions/` with the query string preserved. GET clients typically follow it automatically." - }, - "400": { - "description": "Invalid query parameter. The body is an object keyed by the offending parameter.", - "content": { - "application/json": { - "examples": { - "Invalid session_kind": { - "summary": "session_kind not one of user/business/all", - "value": { - "session_kind": "Must be \"user\", \"business\", or \"all\"." - } + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "case_uuids", + "status" + ], + "properties": { + "case_uuids": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "minItems": 1 }, - "Invalid date": { - "summary": "Malformed date_from/date_to", - "value": { - "date_from": [ - "Enter a valid date." - ] - } + "status": { + "type": "string", + "enum": [ + "OPEN", + "AWAITING_USER", + "RESOLVED" + ] }, - "Invalid didit_internal_id": { - "summary": "didit_internal_id is not a UUID", - "value": { - "didit_internal_id": [ - "Enter a valid UUID." - ] - } + "resolution": { + "type": "string", + "enum": [ + "FALSE_POSITIVE", + "VALID_THREAT", + "INCONCLUSIVE", + "REQUIREMENTS_NOT_MET", + "REQUIREMENTS_SATISFIED" + ], + "description": "Required when status is RESOLVED." }, - "Invalid api_service": { - "summary": "api_service not in the allowed choices", - "value": { - "api_service": [ - "Select a valid choice. FOO is not one of the available choices." - ] - } - } - } - } - } - }, - "403": { - "description": "Missing, malformed, or revoked API key. This API never returns `401`; all authentication failures are `403`.", - "content": { - "application/json": { - "examples": { - "Forbidden": { - "summary": "Missing or invalid x-api-key", - "value": { - "detail": "You do not have permission to perform this action." - } + "note": { + "type": "string", + "nullable": true } } } } } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -s 'https://verification.didit.me/v3/sessions/?status=Approved,In%20Review&date_from=2026-06-01&limit=20' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nurl = \"https://verification.didit.me/v3/sessions/\"\nheaders = {\"x-api-key\": \"YOUR_API_KEY\"}\nparams = {\n \"session_kind\": \"user\", # \"user\" (default) | \"business\" | \"all\"\n \"status\": \"Approved,In Review\", # comma-separated, OR within the list\n \"date_from\": \"2026-06-01\",\n \"limit\": 20,\n}\n\nwhile url:\n response = requests.get(url, headers=headers, params=params)\n response.raise_for_status()\n page = response.json()\n for session in page[\"results\"]:\n print(session[\"session_kind\"], session[\"session_id\"], session[\"status\"])\n url, params = page[\"next\"], None # \"next\" already carries the query string" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const params = new URLSearchParams({\n session_kind: 'all',\n status: 'Approved,In Review',\n date_from: '2026-06-01',\n limit: '20',\n});\n\nconst response = await fetch(\n `https://verification.didit.me/v3/sessions/?${params}`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\n\nconst { count, next, results } = await response.json();\nfor (const session of results) {\n // session.session_kind is \"user\" (KYC) or \"business\" (KYB)\n console.log(session.session_kind, session.session_id, session.status);\n}" + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/bulk/status/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"case_uuids\": [\"c1a5e000-0000-4000-8000-000000000001\"], \"status\": \"RESOLVED\", \"resolution\": \"FALSE_POSITIVE\"}'" } - ] - } - }, - "/v3/session/{sessionId}/share/": { - "post": { - "summary": "Mint a share token for a finished verification session", - "description": "Mint a short-lived JWT that lets a specific Didit application import this finished session. Pair with `POST /v3/session/import-shared/`.", - "operationId": "post_v3_session_share", - "tags": [ - "Sessions" ], "responses": { "200": { - "description": "Share token minted.", + "description": "Per-case results.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "share_token": { - "type": "string", - "description": "HS256-signed JWT. Pass this verbatim as `share_token` to [`POST /v3/session/import-shared/`](/sessions-api/import-shared-session) on the target application.", - "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - }, - "for_application_id": { - "type": "string", - "format": "uuid", - "description": "Echoes the target application UUID that the token is bound to.", - "example": "a5f3bca2-46e2-411e-90ef-a580900a57ee" - }, - "session_kind": { - "type": "string", - "enum": [ - "user", - "business" - ], - "description": "Whether the source session is a User Verification (KYC) or Business Verification (KYB) session." - } - } - }, - "examples": { - "User session": { - "summary": "Share a KYC session", - "value": { - "share_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiMjBiYjNjM2ItNjE0Ni00MzlmLTg0YTQtYmQzMGQwMGFjNmEyIiwiZnJvbV9hcHBsaWNhdGlvbl9pZCI6ImRiZDIwZTM0LTQyZTktNGYyYy1iYTkxLWNmMDc2MjAxNmY2NCIsImZvcl9hcHBsaWNhdGlvbl9pZCI6ImE1ZjNiY2EyLTQ2ZTItNDExZS05MGVmLWE1ODA5MDBhNTdlZSIsImlhdCI6MTc1MzYzMDY2NiwiZXhwIjoxNzUzNjM0MjY2fQ.JJ9pNE_hqZsOtbR0XYZIWw4JzidjdEl279iUrsIkhGE", - "for_application_id": "a5f3bca2-46e2-411e-90ef-a580900a57ee", - "session_kind": "user" - } - }, - "Business session": { - "summary": "Share a KYB session", - "value": { - "share_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - "for_application_id": "a5f3bca2-46e2-411e-90ef-a580900a57ee", - "session_kind": "business" - } - } - } - } - } - }, - "401": { - "description": "Missing or invalid credentials. Unlike most v3 endpoints, this endpoint authenticates through the permission decorator and returns `401` when no valid token/key is presented.", - "content": { - "application/json": { - "examples": { - "Unauthorized": { - "summary": "Unauthorized", - "value": { - "detail": "Authentication credentials were not provided or are invalid." - } - } - } - } - } - }, - "404": { - "description": "No session with the given `session_id` exists in your application.", - "content": { - "application/json": { - "examples": { - "Not Found": { - "summary": "Session not found", - "value": { - "detail": "Not found." - } - } - } - } - } - }, - "400": { - "description": "Validation error. Field errors are keyed by field name; the not-finished error arrives under a `detail` key (as an array).", - "content": { - "application/json": { - "examples": { - "Wrong status": { - "summary": "Source session is not finished", - "value": { - "detail": [ - "Only finished sessions (\"Approved\", \"Declined\", \"In Review\") can be shared." - ] - } - }, - "Target missing": { - "summary": "Target application does not exist", - "value": { - "for_application_id": [ - "Target application does not exist." - ] - } - }, - "Self-share": { - "summary": "Target is the calling application", - "value": { - "for_application_id": [ - "Cannot share a session with the same application." - ] - } - }, - "TTL bounds": { - "summary": "ttl_in_seconds outside bounds", - "value": { - "ttl_in_seconds": [ - "Ensure this value is greater than or equal to 60." - ] - } - } - } - } - } - }, - "403": { - "description": "The credentials are valid but lack the `write:sessions` permission for this application.", - "content": { - "application/json": { - "examples": { - "No Permission": { - "value": { - "detail": "You do not have permission to perform this action." - }, - "summary": "No Permission" - } + "$ref": "#/components/schemas/CaseBulkUpdateResult" } } } } }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/bulk/assign/": { + "post": { + "summary": "Bulk assign cases", + "description": "Assign (or unassign, with assignee_user_id: null) several cases at once. All cases must share the same blueprint; mismatches are reported per case without aborting the batch.", + "operationId": "bulk_assign_cases", + "x-didit-sdk": { + "resource": "cases", + "method": "bulk_assign", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Cases" + ], "parameters": [ { + "name": "organization_id", "in": "path", - "name": "sessionId", "required": true, - "description": "UUID of the source verification session to mint a token for.", "schema": { "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" - } - } - ], - "security": [ + "format": "uuid" + }, + "description": "Organization UUID." + }, { - "ApiKeyAuth": [] + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." } ], "requestBody": { @@ -17602,28 +37857,33 @@ "schema": { "type": "object", "required": [ - "for_application_id" + "case_uuids", + "assignee_user_id" ], "properties": { - "for_application_id": { + "case_uuids": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "minItems": 1 + }, + "assignee_user_id": { "type": "string", "format": "uuid", - "description": "UUID of the Didit application that will redeem the token. Must exist, must not be deleted, and must differ from the calling application. Find it in the Business Console under **Settings → Application**.", - "example": "a5f3bca2-46e2-411e-90ef-a580900a57ee" + "nullable": true }, - "ttl_in_seconds": { - "type": "integer", - "description": "Token lifetime, in seconds. Minimum `60`, maximum `86400` (24 h). Defaults to `3600` (1 h).", - "minimum": 60, - "maximum": 86400, - "default": 3600, - "example": 3600 + "assignee_email": { + "type": "string", + "format": "email", + "nullable": true + }, + "assignee_name": { + "type": "string", + "nullable": true } } - }, - "example": { - "for_application_id": "a5f3bca2-46e2-411e-90ef-a580900a57ee", - "ttl_in_seconds": 7200 } } } @@ -17632,186 +37892,120 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/share/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"for_application_id\": \"a5f3bca2-46e2-411e-90ef-a580900a57ee\",\n \"ttl_in_seconds\": 7200\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/share/\",\n headers={\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n json={\n \"for_application_id\": \"a5f3bca2-46e2-411e-90ef-a580900a57ee\",\n \"ttl_in_seconds\": 7200,\n },\n)\nresponse.raise_for_status()\nshare_token = response.json()[\"share_token\"]" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/share/',\n {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n for_application_id: 'a5f3bca2-46e2-411e-90ef-a580900a57ee',\n ttl_in_seconds: 7200,\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst { share_token } = await response.json();" + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/bulk/assign/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"case_uuids\": [\"c1a5e000-0000-4000-8000-000000000001\"], \"assignee_user_id\": \"9a000000-0000-4000-8000-0000000000aa\"}'" } - ] - } - }, - "/v3/session/import-shared/": { - "post": { - "summary": "Redeem a share token to clone a verification session into your application", - "description": "Redeem a share token to clone a KYC or KYB session into the calling application with a fresh `session_id`. Not idempotent — a token redeems once per receiver.", - "operationId": "post_v3_session_import_shared", - "tags": [ - "Sessions" ], "responses": { - "201": { - "description": "Session cloned. The body is the V3 decision payload for both kinds, discriminated by `session_kind`: `\"user\"` (KYC) with list-based feature blocks such as `id_verifications`, `liveness_checks`, `aml_screenings`; `\"business\"` (KYB) with blocks like `registry_checks` and `key_people_checks`. Both include `shared_from_session` — the `session_id` of the original session in the source application. With `trust_review: false` the cloned session's `status` is forced to `In Review`.", + "200": { + "description": "Per-case results.", "content": { "application/json": { - "examples": { - "User session imported": { - "summary": "Cloned KYC session (truncated — full V3 decision payload in reality)", - "value": { - "session_id": "11111111-2222-3333-4444-555555555555", - "session_kind": "user", - "shared_from_session": "99999999-8888-7777-6666-555555555555", - "session_number": 43762, - "session_url": null, - "status": "In Review", - "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab", - "vendor_data": "user-1", - "created_at": "2026-05-17T08:42:11Z", - "expires_at": "2026-05-24T08:42:11Z" - } - }, - "Business session imported": { - "summary": "Cloned KYB session (truncated — full V3 KYB decision payload in reality)", - "value": { - "session_id": "22222222-3333-4444-5555-666666666666", - "session_kind": "business", - "shared_from_session": "88888888-7777-6666-5555-444444444444", - "session_number": 982, - "session_url": null, - "status": "Approved", - "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab", - "vendor_data": "company-1" - } - } + "schema": { + "$ref": "#/components/schemas/CaseBulkUpdateResult" } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/": { + "get": { + "summary": "List blueprints", + "description": "List case blueprints for the application.", + "operationId": "list_case_blueprints", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Blueprints" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "400": { - "description": "Validation error (token invalid, expired, wrong target, or references a session that no longer exists).", - "content": { - "application/json": { - "examples": { - "Invalid Share Token": { - "summary": "Invalid JWT", - "value": { - "share_token": [ - "Invalid share token." - ] - } - }, - "Expired Share Token": { - "summary": "Token past `exp`", - "value": { - "share_token": [ - "Share token has expired." - ] - } - }, - "Wrong audience": { - "summary": "Token not bound to this application", - "value": { - "share_token": [ - "This token is not valid for this application." - ] - } - }, - "Original missing": { - "summary": "Source session was deleted", - "value": { - "share_token": [ - "Original session does not exist." - ] - } - } - } - } - } + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." }, - "403": { - "description": "Missing/invalid API key (this endpoint returns `403`, never `401`, for authentication failures), or this source session has already been imported into the calling application (one redeem per receiver).", - "content": { - "application/json": { - "examples": { - "Forbidden": { - "summary": "Forbidden", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Already imported": { - "summary": "Source already imported once", - "value": { - "detail": "This session has already been shared with your application." - } - } - } - } - } + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "description": "Page size." }, - "404": { - "description": "`workflow_id` does not belong to the calling application.", + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Paginated blueprint list.", "content": { "application/json": { - "examples": { - "Workflow not found": { - "summary": "Workflow unknown", - "value": { - "detail": "Workflow does not exist for this application." - } - } - } - } - } - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "share_token", - "trust_review", - "workflow_id" - ], - "properties": { - "share_token": { - "type": "string", - "description": "JWT share token issued by [`POST /v3/session/{sessionId}/share/`](/sessions-api/share-session).", - "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - }, - "trust_review": { - "type": "boolean", - "description": "If `true`, the cloned session keeps the source's final `status`. If `false`, it is forced into `In Review`.", - "example": false - }, - "workflow_id": { - "type": "string", - "format": "uuid", - "description": "UUID of a workflow in the calling application. Cross-application IDs are rejected with `404`.", - "example": "9f9b1234-aaaa-bbbb-cccc-1234567890ab" - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Optional override for the cloned session's `vendor_data`.", - "example": "user-1" + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Number of matching rows, capped at 100 for performance." + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseBlueprintListItem" + } + } } } - }, - "example": { - "share_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - "trust_review": false, - "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab", - "vendor_data": "user-1" } } } @@ -17820,40 +38014,44 @@ { "ApiKeyAuth": [] } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST \\\n https://verification.didit.me/v3/session/import-shared/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"share_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"trust_review\": false,\n \"workflow_id\": \"9f9b1234-aaaa-bbbb-cccc-1234567890ab\",\n \"vendor_data\": \"user-1\"\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/import-shared/\",\n headers={\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n json={\n \"share_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"trust_review\": False,\n \"workflow_id\": \"9f9b1234-aaaa-bbbb-cccc-1234567890ab\",\n \"vendor_data\": \"user-1\",\n },\n)\nresponse.raise_for_status()\nimported = response.json()" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/import-shared/',\n {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n share_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',\n trust_review: false,\n workflow_id: '9f9b1234-aaaa-bbbb-cccc-1234567890ab',\n vendor_data: 'user-1',\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst imported = await response.json();" - } ] - } - }, - "/v3/kyb/search/": { + }, "post": { - "summary": "KYB registry company search (standalone)", - "operationId": "post_v3_kyb_search", + "summary": "Create blueprint", + "description": "Create a case blueprint: general settings (assignment, escalation, transfer, 4-eyes pairing) plus content_config, the six sections that drive the case page. Set publish=false to create an unpublished draft; Publish creates its first live version. Discarding a never-published draft deletes it.", + "operationId": "create_case_blueprint", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Standalone APIs" + "Case Blueprints" ], - "security": [ + "parameters": [ { - "ApiKeyAuth": [] + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." } ], - "parameters": [], - "description": "Search official company registries for candidate companies by name and/or registration number. This is step 1 of the two-step registry flow: **search** returns lightweight candidates, then `POST /v3/kyb/select/` retrieves the full profile of one candidate.\n\n**Search billing.** Successful searches using a paid company-data source cost USD 0.50 per request, regardless of candidate count. Empty results, failures and results served only by other sources are not charged. The applicable price is reserved before searching; insufficient balance returns 403 with code `insufficient_credits`. Contract rates apply where agreed, and an unconfigured contract search rate returns 403. Sandbox searches are free. Repeating a successful search can incur another charge; callback delivery does not add a charge. Profile retrieval is billed separately. This search fee also applies to v2. Candidates always come back with `fetch_status: \"pending\"`: the full company data is only fetched (and billed) on select.\n\n**`kyb_response_id` is ephemeral.** It is a per-search handle, not a stable company id. Select promptly after searching and re-search to get a fresh handle instead of storing it.\n\n**Two modes.**\n- *Synchronous (default):* the API polls the registry until the search resolves and returns the final candidate list (`search_resolved: true`).\n- *Asynchronous:* pass `webhook_url` and the API returns immediately — usually with `search_status: \"pending\"` and an empty candidate list — then POSTs a `kyb.registry_search.resolved` callback to your URL when the registry finishes. The callback body is `{\"event_id\", \"event_type\": \"kyb.registry_search.resolved\", \"request_id\", \"vendor_data\", \"metadata\", \"search_status\", \"search_resolved\", \"kyb_registry\", \"timestamp\", \"created_at\"}` — note `timestamp` and `created_at` in the callback are epoch integers, unlike this endpoint's ISO `created_at` — and is sent **unsigned** (header `X-Didit-Unsigned-Callback: true`, no `X-Signature`).\n\n**Country codes.** ISO 3166-1 alpha-2 (`GB`, `DE`), plus `XX-YY` subdivision codes where registries are regional — e.g. `US-CA` for California.\n\n**Sandbox.** Keys from sandbox applications return one static candidate without contacting any registry.", "requestBody": { "required": true, "content": { @@ -17861,50 +38059,85 @@ "schema": { "type": "object", "required": [ - "country_code" + "name", + "assignee_pool" ], "properties": { - "country_code": { - "type": "string", - "maxLength": 10, - "description": "ISO 3166-1 alpha-2 code of the country whose company registry to search. For example, `GB`. Where company registries operate at state or province level, append the ISO 3166-2 subdivision code as `XX-YY`: `US-CA` searches the California registry. United States searches always require the subdivision — a bare `US` is rejected. Case-insensitive; normalized to upper case.", - "example": "GB" - }, "name": { "type": "string", - "maxLength": 255, - "description": "Company name to search for. Either `name` or `registration_number` is required.", - "example": "Tesco" + "maxLength": 24, + "description": "Max 24 characters, e.g. \"AML investigation\"." }, - "registration_number": { + "description": { "type": "string", - "maxLength": 100, - "description": "Registry registration number to search for. Either `name` or `registration_number` is required." + "nullable": true }, - "search_type": { + "assignee_pool": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "minItems": 1, + "description": "List of User UUIDs. Empty arrays are rejected with \"Pick at least one assignee\"." + }, + "assignment_method": { "type": "string", "enum": [ - "contains", - "start_with", - "fuzzy" + "MANUAL", + "AUTOMATIC" ], - "default": "contains", - "description": "Name-matching strategy applied by the registry search." + "default": "MANUAL" }, - "vendor_data": { - "type": "string", - "description": "Your identifier for this lookup; echoed back and included in the webhook callback." + "max_active_cases": { + "type": "integer", + "nullable": true, + "minimum": 1, + "description": "Automatic-mode threshold: \"fewer than N cases of this blueprint assigned\"." }, - "metadata": { - "type": "object", + "escalation_enabled": { + "type": "boolean", + "default": false + }, + "escalation_blueprint": { + "type": "string", + "format": "uuid", "nullable": true, - "description": "Free-form JSON; echoed back and included in the webhook callback." + "description": "Target blueprint UUID. Not enforced at save time, but the escalate action fails with 400 until both escalation_enabled and escalation_blueprint are set." }, - "webhook_url": { + "transfer_enabled": { + "type": "boolean", + "default": false + }, + "four_eyes_checker_blueprint": { "type": "string", - "format": "uri", - "maxLength": 500, - "description": "Switches the search to asynchronous mode: the API returns immediately and POSTs an unsigned `kyb.registry_search.resolved` callback to this URL when the candidate list is ready." + "format": "uuid", + "nullable": true, + "description": "Setting this enables 4-eyes review: resolutions on this blueprint stage on the checker blueprint's queue." + }, + "require_resolution_note": { + "type": "boolean", + "default": false + }, + "content_config": { + "$ref": "#/components/schemas/CaseContentConfig" + }, + "is_active": { + "type": "boolean", + "default": true + }, + "default_deadline_hours": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 8760, + "description": "Default deadline for newly opened cases. Existing case deadlines are unchanged." + }, + "publish": { + "type": "boolean", + "writeOnly": true, + "default": true, + "description": "Set false to save a never-published draft. The record remains inactive at version 0 until the draft is explicitly published. Omitting this field preserves immediate creation for existing integrations." } } } @@ -17915,348 +38148,234 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/kyb/search/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"country_code\": \"GB\",\n \"name\": \"Tesco\",\n \"search_type\": \"contains\",\n \"vendor_data\": \"company-1234\"\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/kyb/search/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n timeout=60, # synchronous mode waits for the registry to resolve\n)\nresp.raise_for_status()\ncandidates = resp.json()[\"kyb_registry\"][\"companies\"]\nkyb_response_id = candidates[0][\"kyb_response_id\"] # pass to /v3/kyb/select/" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/kyb/search/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ country_code: 'GB', name: 'Tesco' }),\n});\nconst data = await res.json();\nconst kybResponseId = data.kyb_registry.companies[0].kyb_response_id;" + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"name\": \"AML investigation\", \"assignee_pool\": [\"9a000000-0000-4000-8000-0000000000aa\"], \"assignment_method\": \"AUTOMATIC\", \"max_active_cases\": 15, \"require_resolution_note\": true, \"content_config\": {\"aml_control\": {\"enabled\": true, \"financial\": true, \"identity\": false}}}'" } ], "responses": { - "200": { - "description": "Search accepted. Synchronous mode returns the resolved candidate list; webhook mode returns immediately (often `search_status: \"pending\"`) and delivers the final list via the `kyb.registry_search.resolved` callback.", + "201": { + "description": "Blueprint created.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "With `webhook_url`: the persisted search-request id, echoed as `request_id` in the `kyb.registry_search.resolved` callback. Without `webhook_url`: a transient correlation UUID (the search is not stored)." - }, - "kyb_registry": { - "type": "object", - "properties": { - "companies": { - "type": "array", - "items": { - "type": "object", - "properties": { - "kyb_response_id": { - "type": "string", - "nullable": true, - "description": "Handle for this candidate — pass it to `POST /v3/kyb/select/` to retrieve the full profile. Ephemeral and scoped to this search: do not store it long-term; re-search to get a fresh one.", - "example": "69aeeb95febb0f1704042259" - }, - "name": { - "type": "string", - "nullable": true, - "description": "Registered company name.", - "example": "Tesco PLC" - }, - "registration_number": { - "type": "string", - "nullable": true, - "description": "Registry registration number.", - "example": "00445790" - }, - "status": { - "type": "string", - "nullable": true, - "description": "Normalized registry status of the company (e.g. `active`, `dissolved`, `inactive`, `struck off`).", - "example": "active" - }, - "type": { - "type": "string", - "nullable": true, - "description": "Legal form as reported by the registry.", - "example": "Public Limited Company" - }, - "risk_level": { - "type": "string", - "nullable": true, - "description": "Registry-reported risk level, when available. Usually `null` at search time." - }, - "fetch_status": { - "type": "string", - "nullable": true, - "description": "Always `pending` for search candidates — the full company profile is only fetched (and billed) when you select the candidate.", - "example": "pending" - } - } - }, - "description": "Deduplicated candidate list. Empty while `search_resolved=false` (webhook mode) or when nothing matched." - }, - "pagination": { - "type": "object", - "properties": { - "total": { - "type": "integer", - "description": "Number of candidates returned." - }, - "page": { - "type": "integer" - }, - "per_page": { - "type": "integer", - "example": 25 - } - } - }, - "search_status": { - "type": "string", - "nullable": true, - "enum": [ - "pending", - "resolved" - ], - "description": "Registry search lifecycle status." - }, - "search_resolved": { - "type": "boolean", - "description": "`true` once the candidate list is final." - } - } - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Echo of the `metadata` you sent." - }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - }, - "examples": { - "Resolved search": { - "summary": "Synchronous mode — final candidate list", - "value": { - "request_id": "0c7e9a40-3a13-4f7e-8a44-9d2e2f1c5b6a", - "kyb_registry": { - "companies": [ - { - "kyb_response_id": "69aeeb95febb0f1704042259", - "name": "Tesco PLC", - "registration_number": "00445790", - "status": "active", - "type": "Public Limited Company", - "risk_level": null, - "fetch_status": "pending" - }, - { - "kyb_response_id": "69aeeb95febb0f170404225a", - "name": "E-TESCO LTD", - "registration_number": "15168476", - "status": "active", - "type": "Private Limited Company", - "risk_level": null, - "fetch_status": "pending" - } - ], - "pagination": { - "total": 2, - "page": 1, - "per_page": 25 - }, - "search_status": "resolved", - "search_resolved": true - }, - "vendor_data": "company-1234", - "metadata": null, - "created_at": "2026-06-11T10:40:00.000000+00:00" - } - }, - "Pending search (webhook mode)": { - "summary": "`webhook_url` provided — candidates arrive via the kyb.registry_search.resolved callback", - "value": { - "request_id": "5f2d8c1b-7e4a-4b9c-9f0d-3a6e8c2b1d4f", - "kyb_registry": { - "companies": [], - "pagination": { - "total": 0, - "page": 1, - "per_page": 25 - }, - "search_status": "pending", - "search_resolved": false - }, - "vendor_data": "company-1234", - "metadata": { - "source": "onboarding" - }, - "created_at": "2026-06-11T10:41:00.000000+00:00" - } - } + "$ref": "#/components/schemas/CaseBlueprintDetail" } } } }, "400": { - "description": "Validation error. Field-level problems use DRF's standard envelope; the name/registration-number rule reports under `non_field_errors`.", - "content": { - "application/json": { - "examples": { - "Missing search criteria": { - "summary": "Neither `name` nor `registration_number` provided", - "value": { - "non_field_errors": [ - "Either 'name' or 'registration_number' is required." - ] - } - }, - "Invalid country_code": { - "summary": "Must be `XX` (alpha-2) or `XX-YY` subdivision", - "value": { - "country_code": [ - "country_code must be a 2-letter ISO code." - ] - } - }, - "Invalid search_type": { - "summary": "Only contains / start_with / fuzzy", - "value": { - "search_type": [ - "\"exact\" is not a valid choice." - ] - } - }, - "Invalid webhook_url": { - "summary": "`webhook_url` must be a valid URL", - "value": { - "webhook_url": [ - "Enter a valid URL." - ] - } - } - } - } - } - }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Also returned before searching when the applicable search fee cannot be covered (`code: \"insufficient_credits\"`), or when a contract search rate is missing.", - "content": { - "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } - }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "description": "Name over 24 chars, empty assignee_pool, or a cross-referenced blueprint (escalation/checker) not found.", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } + "type": "object" } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/": { + "get": { + "summary": "Get blueprint", + "description": "Retrieve a blueprint, including its connected creation sources (AML config, transaction rules, workflow nodes) and which blueprints escalate to it.", + "operationId": "get_case_blueprint", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "retrieve", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Blueprints" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "502": { - "description": "The KYB registry provider is temporarily unavailable - a 5xx response, timeout, or connection failure from the upstream registry. The search is not billed. Retry later, or, in Didit's hosted verification flow, the user is automatically offered manual company entry instead of a retry-only error.", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "blueprint_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "b1de0000-0000-4000-8000-000000000002" + }, + "description": "Blueprint UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/b1de0000-0000-4000-8000-000000000002/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Blueprint detail.", "content": { "application/json": { - "examples": { - "Registry provider outage": { - "summary": "The registry provider did not answer the search", - "value": { - "detail": "The KYB registry provider is temporarily unavailable. Please try again shortly.", - "code": "kyb_registry_provider_unavailable" - } - } - }, "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - } - } + "$ref": "#/components/schemas/CaseBlueprintDetail" } } } } - } - } - }, - "/v3/kyb/select/": { - "post": { - "summary": "KYB registry company select — Lite tier (standalone)", - "operationId": "post_v3_kyb_select", + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "patch": { + "summary": "Update blueprint", + "description": "Partially update a blueprint's general settings or content_config.", + "operationId": "update_case_blueprint", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "update", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Standalone APIs" + "Case Blueprints" ], - "security": [ + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, { - "ApiKeyAuth": [] + "name": "blueprint_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "b1de0000-0000-4000-8000-000000000002" + }, + "description": "Blueprint UUID." } ], - "parameters": [], - "description": "Retrieve the **Lite** registry profile of one candidate returned by `POST /v3/kyb/search/` — company details, registered address, identifiers, status, activity and the officers, plus the raw registry payload. Shareholders and ultimate beneficial owners are sold by `POST /v3/kyb/shareholders/` and `POST /v3/kyb/ubo/`.\n\n**Pairing with search.** Pass the candidate's `kyb_response_id` exactly as returned by a search of the same application. The handle is ephemeral and scoped to that search — select promptly, and re-search for a fresh handle rather than storing it.\n\n**Billing.** Each select is billed at the country's Lite price (any search fee is separate); see [KYB registry pricing](/getting-started/kyb-registry-pricing). The balance is checked before any registry call (`403` when short) and the charge is recorded against the created business session.\n\n**Persistence is mandatory.** Every select creates a business session (its id is the `request_id`) so billing and retrieval can be tracked; it appears in the console. `save_api_request` is accepted for symmetry but must be `true` (the default) — sending `false` returns `400`.", "requestBody": { - "required": true, + "required": false, "content": { "application/json": { "schema": { "type": "object", - "required": [ - "kyb_response_id" - ], "properties": { - "kyb_response_id": { + "name": { "type": "string", - "maxLength": 255, - "description": "Candidate handle from `POST /v3/kyb/search/` (`kyb_registry.companies[].kyb_response_id`). Ephemeral — use it shortly after the search that produced it.", - "example": "69aeeb95febb0f1704042259" + "maxLength": 24, + "description": "Max 24 characters, e.g. \"AML investigation\"." }, - "vendor_data": { + "description": { "type": "string", - "description": "Your identifier for this company; echoed back and stored with the business session." + "nullable": true }, - "metadata": { - "type": "object", + "assignee_pool": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "minItems": 1, + "description": "List of User UUIDs. Empty arrays are rejected with \"Pick at least one assignee\"." + }, + "assignment_method": { + "type": "string", + "enum": [ + "MANUAL", + "AUTOMATIC" + ] + }, + "max_active_cases": { + "type": "integer", "nullable": true, - "description": "Free-form JSON stored with the business session and echoed back." + "minimum": 1, + "description": "Automatic-mode threshold: \"fewer than N cases of this blueprint assigned\"." }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "Must be `true` (the default). KYB registry selects are always persisted so billing and retrieval can be tracked; `false` returns `400`." + "escalation_enabled": { + "type": "boolean" + }, + "escalation_blueprint": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Target blueprint UUID. Not enforced at save time, but the escalate action fails with 400 until both escalation_enabled and escalation_blueprint are set." + }, + "transfer_enabled": { + "type": "boolean" + }, + "four_eyes_checker_blueprint": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Setting this enables 4-eyes review: resolutions on this blueprint stage on the checker blueprint's queue." + }, + "require_resolution_note": { + "type": "boolean" + }, + "content_config": { + "$ref": "#/components/schemas/CaseContentConfig" + }, + "is_active": { + "type": "boolean" + }, + "default_deadline_hours": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 8760, + "description": "Default deadline for newly opened cases. Existing case deadlines are unchanged." + }, + "expected_version": { + "type": "integer", + "minimum": 1, + "description": "The version opened by the editor. A stale version returns 409 without changing the review policy." } } } @@ -18267,2461 +38386,4206 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/kyb/select/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"kyb_response_id\": \"69aeeb95febb0f1704042259\",\n \"vendor_data\": \"company-1234\"\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import os, requests\n\nsearch = requests.post(\n \"https://verification.didit.me/v3/kyb/search/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n timeout=60,\n).json()\nkyb_response_id = search[\"kyb_registry\"][\"companies\"][0][\"kyb_response_id\"]\n\nresp = requests.post(\n \"https://verification.didit.me/v3/kyb/select/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"kyb_response_id\": kyb_response_id, \"vendor_data\": \"company-1234\"},\n timeout=90, # waits for the registry to resolve the full profile\n)\nresp.raise_for_status()\ncompany = resp.json()[\"kyb_registry\"]\nprint(company[\"company_name\"], company[\"registry_status\"], company[\"status\"])" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/kyb/select/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n kyb_response_id: '69aeeb95febb0f1704042259',\n vendor_data: 'company-1234',\n }),\n});\nconst data = await res.json();\nconsole.log(data.kyb_registry.company_name, data.kyb_registry.registry_status);" + "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/b1de0000-0000-4000-8000-000000000002/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"transfer_enabled\": true}'" } ], "responses": { "200": { - "description": "Company retrieved and billed. `kyb_registry` carries the full profile; check `data_resolved` — when `false` the registry was still processing and the stored record completes automatically once it resolves.", + "description": "Updated blueprint.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Id of the business session created for this retrieval. The stored record (visible in the console) keeps updating if the registry resolves after the response." - }, - "registry_tier_requested": { - "type": "string", - "enum": [ - "basic", - "shareholders", - "ubo" - ], - "description": "The tier this endpoint sells: `basic` (Lite) for select, `shareholders`, `ubo`." - }, - "registry_tier_delivered": { - "type": "string", - "enum": [ - "basic", - "shareholders", - "ubo" - ], - "description": "The tier actually delivered and billed. Lower than requested when the country's registries hold no ownership data, or the registry has no ownership record for this company." - }, - "kyb_registry": { - "type": "object", - "description": "Full registry company profile.", - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Didit's id for this company record." - }, - "node_id": { - "type": "string", - "example": "feature_kyb_registry" - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review" - ], - "description": "Didit's assessment of the registry check (e.g. `In Review` when the company is not active in the registry or ownership data could not be derived)." - }, - "registry_status": { - "type": "string", - "nullable": true, - "enum": [ - "active", - "dissolved", - "deregistered", - "see full details", - "authorised", - "appointed representative", - "unauthorised", - "inactive", - "no longer authorised", - "closed", - "struck off" - ], - "description": "Normalized company status as reported by the registry." - }, - "data_resolved": { - "type": "boolean", - "description": "`true` once the registry returned the full profile. `false` when the registry was still processing after polling — the stored record completes automatically once the registry resolves." - }, - "company_name": { - "type": "string", - "example": "Tesco PLC" - }, - "registration_number": { - "type": "string", - "nullable": true, - "example": "00445790" - }, - "country_code": { - "type": "string", - "description": "ISO 3166-1 alpha-2.", - "example": "GB" - }, - "region": { - "type": "string", - "nullable": true, - "description": "Subdivision code when the registry is regional (e.g. `CA` for `US-CA`); empty otherwise." - }, - "company_type": { - "type": "string", - "nullable": true, - "example": "Public Limited Company" - }, - "incorporation_date": { - "type": "string", - "format": "date", - "nullable": true, - "example": "1947-11-27" - }, - "registered_address": { - "type": "string", - "nullable": true, - "example": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA" - }, - "tax_number": { - "type": "string", - "nullable": true, - "example": "GB412 8925 93" - }, - "risk_level": { - "type": "string", - "nullable": true, - "description": "Registry-reported risk level, when available." - }, - "verification_status": { - "type": "string", - "nullable": true, - "description": "Registry verification status, when available." - }, - "is_from_registry": { - "type": "boolean", - "description": "Always `true` for this endpoint — the profile was retrieved from an official registry." - }, - "fetch_status": { - "type": "string", - "enum": [ - "pending", - "resolved" - ], - "description": "Registry fetch lifecycle. Mirrored by `data_resolved`." - }, - "alternative_names": { - "type": "string", - "nullable": true, - "description": "Alternative, trading, former or original-language company names as text, when reported." - }, - "nature_of_business": { - "type": "string", - "nullable": true, - "description": "Activity description or classification code, when reported." - }, - "registered_capital": { - "type": "string", - "nullable": true - }, - "registered_capital_amount": { - "type": "string", - "nullable": true, - "description": "Decimal amount as a string, when reported." - }, - "registered_capital_currency": { - "type": "string", - "nullable": true, - "description": "ISO 4217 code, when reported." - }, - "website": { - "type": "string", - "nullable": true, - "example": "https://www.tesco.com/" - }, - "email": { - "type": "string", - "nullable": true, - "example": "customer.service@tesco.co.uk" - }, - "phone": { - "type": "string", - "nullable": true, - "example": "03301231688" - }, - "legal_entity_identifier": { - "type": "string", - "nullable": true, - "description": "LEI, when reported." - }, - "location_of_registration": { - "type": "string", - "nullable": true, - "description": "Registry/jurisdiction the company is registered with, when reported." - }, - "vat_number": { - "type": "string", - "nullable": true, - "description": "EU VAT number, when collected during the hosted registry step or edited afterwards. At select time this is typically not yet set — VAT and tax ID validation runs when the end user submits the registry step in the hosted flow." - }, - "vat_validation_status": { - "type": "string", - "enum": [ - "valid", - "invalid", - "format_valid", - "invalid_format", - "could_not_validate", - "not_applicable" - ], - "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." - }, - "vat_validated_number": { - "type": "string", - "nullable": true, - "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." - }, - "vat_validation_source": { - "type": "string", - "nullable": true, - "enum": [ - "eu_vies", - "pl_vat_white_list", - "no_brreg", - "ch_uid_register", - "br_receita_federal", - "co_rues", - "cr_hacienda", - "ec_sri", - "py_dnit", - "format_rules", - null - ], - "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." - }, - "vat_validated_name": { - "type": "string", - "nullable": true, - "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_validated_address": { - "type": "string", - "nullable": true, - "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_checked_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the check. `null` when no check ran." - }, - "financial_summary": { - "type": "object", - "nullable": true, - "description": "Registry-reported share/capital details (currency, total shares, share classes), when available." - }, - "officers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string", - "example": "MELISSA BETHELL" - }, - "designation": { - "type": "string", - "nullable": true, - "description": "Raw registry designation (e.g. `Director`, `Secretary`)." - }, - "role": { - "type": "string", - "description": "Normalized role derived from the designation. Full 17-value model set; registry parsing on this endpoint typically emits director, non_executive_director, secretary, chairman, or company_officer.", - "enum": [ - "director", - "non_executive_director", - "secretary", - "chairman", - "ubo", - "shareholder", - "representative", - "founder", - "legal_advisor", - "authorized_signatory", - "trustee", - "beneficiary", - "company_officer", - "settlor", - "protector", - "investor", - "other" - ] - }, - "nationality": { - "type": "string", - "nullable": true - }, - "is_active": { - "type": "boolean", - "description": "`false` when the registry marks the appointment as resigned." - }, - "kyc_status": { - "type": "string", - "nullable": true, - "enum": [ - "Approved", - "Declined", - "Pending" - ], - "description": "Status of an associated KYC session, when one was started for this officer. `null` otherwise." - }, - "kyc_session_url": { - "type": "string", - "nullable": true, - "description": "Verification URL of the associated KYC session, when one exists." - }, - "first_name": { - "type": "string", - "nullable": true, - "description": "Given name when explicitly reported by the registry; not inferred from the full name." - }, - "last_name": { - "type": "string", - "nullable": true, - "description": "Family name when explicitly reported by the registry." - }, - "address": { - "type": "string", - "nullable": true, - "description": "Officer address reported by the registry." - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Date the officer was appointed.", - "format": "date" - }, - "termination_date": { - "type": "string", - "nullable": true, - "description": "Date the appointment ended, when reported.", - "format": "date" - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", - "format": "date" - }, - "date_of_birth_year": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 9999, - "description": "Registry-reported birth year, including when the full date is unavailable." - }, - "date_of_birth_month": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 12, - "description": "Registry-reported birth month. Null when only the year is known." - } - } + "$ref": "#/components/schemas/CaseBlueprintDetail" + } + } + } + }, + "409": { + "description": "The review policy changed since expected_version. Review the latest version before retrying." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "delete": { + "summary": "Delete blueprint", + "description": "Delete a blueprint. Deletion always succeeds and does not touch the blueprint's cases: existing cases keep their blueprint reference and its settings continue to apply to them. A deleted blueprint no longer appears in blueprint lists and can no longer be chosen as a transfer target; if a maker blueprint is deleted while a resolution is pending 4-eyes approval, a rejected resolution leaves the case on the checker blueprint.", + "operationId": "delete_case_blueprint", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "delete", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Blueprints" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "blueprint_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "b1de0000-0000-4000-8000-000000000002" + }, + "description": "Blueprint UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/b1de0000-0000-4000-8000-000000000002/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "204": { + "description": "Blueprint deleted." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/presets/": { + "get": { + "summary": "List blueprint presets", + "description": "List the four built-in presets (Screening hit, AML investigation, Suspicious activity, KYC review) with their content_config and whether each is already installed on this application.", + "operationId": "list_case_blueprint_presets", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "list_presets", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Blueprints" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/presets/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Preset catalogue.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "preset_key": { + "type": "string", + "enum": [ + "screening_hit", + "aml_investigation", + "suspicious_activity", + "kyc" + ] }, - "description": "Directors, secretaries, and other officers extracted from the registry." - }, - "beneficial_owners": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "first_name": { - "type": "string", - "nullable": true - }, - "last_name": { - "type": "string", - "nullable": true - }, - "entity_type": { - "type": "string", - "enum": [ - "person", - "company" - ], - "description": "Whether the owner is a natural person or a corporate entity." - }, - "roles": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Normalized roles (e.g. `ubo`, `shareholder`)." - }, - "ownership_min_shares": { - "type": "string", - "nullable": true, - "description": "Lower bound of the ownership band reported by the registry (e.g. `25%`)." - }, - "ownership_max_shares": { - "type": "string", - "nullable": true, - "description": "Upper bound of the ownership band reported by the registry." - }, - "is_active": { - "type": "boolean" - }, - "kyc_status": { - "type": "string", - "nullable": true, - "enum": [ - "Approved", - "Declined", - "Pending" - ] - }, - "kyc_session_url": { - "type": "string", - "nullable": true - }, - "effective_ownership_percent": { - "type": "number", - "nullable": true, - "description": "Parsed ownership percentage (max bound, falling back to min bound)." - }, - "company_name": { - "type": "string", - "nullable": true - }, - "registration_number": { - "type": "string", - "nullable": true - }, - "country_of_birth": { - "type": "string", - "nullable": true - }, - "email": { - "type": "string", - "nullable": true - }, - "nationality": { - "type": "string", - "nullable": true - }, - "designation": { - "type": "string", - "nullable": true - }, - "voting_min_shares": { - "type": "string", - "nullable": true - }, - "voting_max_shares": { - "type": "string", - "nullable": true - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete birth date when reported.", - "format": "date" - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Appointment date when reported.", - "format": "date" - }, - "shareholding": { - "$ref": "#/components/schemas/KYBRegistryShareholding" - } - } + "name": { + "type": "string" }, - "description": "Beneficial owners / persons with significant control extracted from the registry. Can be empty when the registry does not expose ownership." - }, - "addresses": { - "type": "array", - "nullable": true, - "description": "Every address the registry states, one canonical shape. Null when it states none.", - "items": { - "$ref": "#/components/schemas/KYBRegistryAddress" - } - }, - "industries": { - "type": "array", - "nullable": true, - "description": "The activities the registry classifies the company under, one canonical shape. Null when it states none.", - "items": { - "$ref": "#/components/schemas/KYBRegistryIndustry" + "description": { + "type": "string" + }, + "content_config": { + "$ref": "#/components/schemas/CaseContentConfig" + }, + "installed": { + "type": "boolean" } - }, - "accounts": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." - }, - "registry_data": { - "type": "object", - "description": "Raw record returned by the registry source, kept for reviewers. Its shape differs by country and source and is not a stable contract: read the canonical fields (`addresses`, `industries`, `ownership`, `beneficial_owners[].shareholding`) instead." - }, - "user_provided_data": { - "type": "object", - "nullable": true, - "description": "Always `null` on this endpoint (used by the hosted KYB flow when end users edit registry data)." - }, - "confirmed_by_user_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Always `null` on this endpoint." - }, - "last_console_edit_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Set when a console reviewer edits the record later." - }, - "is_editable": { - "type": "boolean", - "description": "Whether the registry-sourced data can still be edited in the hosted flow/console." - }, - "registry_tier_delivered": { - "type": "string", - "enum": [ - "basic", - "shareholders", - "ubo" - ], - "description": "Same as the top-level `registry_tier_delivered`." - }, - "registry_ownership_status": { - "type": "string", - "nullable": true, - "enum": [ - "not_requested", - "delivered", - "unavailable" - ], - "description": "`delivered` when the ownership data came back, `unavailable` when an ownership tier was requested but the registry had none for this company (see `registry_data.ownership_error`), `not_requested` on the Lite tier." - }, - "ownership": { - "$ref": "#/components/schemas/KYBRegistryOwnership" - }, - "registry_tier": { - "type": "string", - "nullable": true, - "description": "The registry tier the workflow ordered: `basic`, `shareholders` or `ubo`. Also in `ownership.tier_requested`." } } - }, - "vendor_data": { + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/presets/install/": { + "post": { + "summary": "Install blueprint presets", + "description": "Copy one or more presets into the application as editable blueprints. Idempotent per preset_key; omit preset_keys to install every preset not yet installed.", + "operationId": "install_case_blueprint_presets", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "install_presets", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Blueprints" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "preset_keys": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "description": "Subset of screening_hit, aml_investigation, suspicious_activity, kyc. Omit to install all." + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/presets/install/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"preset_keys\": [\"aml_investigation\", \"kyc\"]}'" + } + ], + "responses": { + "201": { + "description": "One or more presets installed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseBlueprintPresetInstallResult" + } + } + } + }, + "200": { + "description": "No new presets to install (all requested presets already exist).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseBlueprintPresetInstallResult" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/report-templates/": { + "get": { + "summary": "List report templates", + "description": "List FIU report templates for the application.", + "operationId": "list_report_templates", + "x-didit-sdk": { + "resource": "report_templates", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Report Templates" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "description": "Page size." + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Paginated templates.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Number of matching rows, capped at 100 for performance." + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReportTemplateDetail" + } + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "post": { + "summary": "Create report template", + "description": "Create a reusable per-jurisdiction, per-report-type goAML header template. Rejected with 400 if any of the template-level obligatory fields (rentity_id, submission_code, currency_code_local) is missing from field_config; an invalid template cannot be saved or used to create a report. Per-filing header fields (action, submission_date, transactions) are enforced at report validate/finalize time instead.", + "operationId": "create_report_template", + "x-didit-sdk": { + "resource": "report_templates", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Report Templates" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "name", + "jurisdiction", + "report_type" + ], + "properties": { + "name": { + "type": "string" + }, + "jurisdiction": { + "type": "string", + "minLength": 2, + "maxLength": 2, + "description": "ISO 3166-1 alpha-2 country code, upper-cased on save." + }, + "report_type": { + "type": "string", + "enum": [ + "SAR", + "STR", + "CTR", + "TTR", + "UTR", + "IFT", + "CBR", + "TFR" + ] + }, + "field_config": { + "type": "object", + "description": "Reusable goAML header defaults. Template-level obligatory fields: rentity_id, submission_code, and currency_code_local (3-letter). Optional: enabled_fields (which optional report blocks to include) plus reporting_person, location, reason, and action defaults. Per-filing header fields (action, submission_date, transactions, jurisdiction) are enforced when the report is validated or finalized, not on the template." + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"name\": \"US SAR default\", \"jurisdiction\": \"US\", \"report_type\": \"SAR\", \"field_config\": {\"rentity_id\": \"FIU-US-00123\", \"submission_code\": \"E\", \"currency_code_local\": \"USD\"}}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'name': 'US SAR default',\n 'jurisdiction': 'US',\n 'report_type': 'SAR',\n 'field_config': {'rentity_id': 'FIU-US-00123', 'submission_code': 'E', 'currency_code_local': 'USD'},\n },\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()['uuid'])" + } + ], + "responses": { + "201": { + "description": "Template created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportTemplateDetail" + } + } + } + }, + "400": { + "description": "Unrecognized jurisdiction, or field_config fails obligatory-field validation.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/report-templates/{template_uuid}/": { + "get": { + "summary": "Get report template", + "description": "Retrieve a report template.", + "operationId": "get_report_template", + "x-didit-sdk": { + "resource": "report_templates", + "method": "retrieve", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Report Templates" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "template_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "7ec00000-0000-4000-8000-000000000006" + }, + "description": "Report template UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/7ec00000-0000-4000-8000-000000000006/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Template detail.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportTemplateDetail" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "patch": { + "summary": "Update report template", + "description": "Partially update a report template. Re-validates obligatory goAML fields on save.", + "operationId": "update_report_template", + "x-didit-sdk": { + "resource": "report_templates", + "method": "update", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Report Templates" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "template_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "7ec00000-0000-4000-8000-000000000006" + }, + "description": "Report template UUID." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "jurisdiction": { + "type": "string", + "minLength": 2, + "maxLength": 2, + "description": "ISO 3166-1 alpha-2 country code, upper-cased on save." + }, + "report_type": { + "type": "string", + "enum": [ + "SAR", + "STR", + "CTR", + "TTR", + "UTR", + "IFT", + "CBR", + "TFR" + ] + }, + "field_config": { + "type": "object", + "description": "Reusable goAML header defaults. Template-level obligatory fields: rentity_id, submission_code, and currency_code_local (3-letter). Optional: enabled_fields (which optional report blocks to include) plus reporting_person, location, reason, and action defaults. Per-filing header fields (action, submission_date, transactions, jurisdiction) are enforced when the report is validated or finalized, not on the template." + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/7ec00000-0000-4000-8000-000000000006/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"field_config\": {\"rentity_id\": \"FIU-US-00124\"}}'" + } + ], + "responses": { + "200": { + "description": "Updated template.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportTemplateDetail" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "delete": { + "summary": "Delete report template", + "description": "Delete a report template. Reports already created from it are unaffected.", + "operationId": "delete_report_template", + "x-didit-sdk": { + "resource": "report_templates", + "method": "delete", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Report Templates" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "template_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "7ec00000-0000-4000-8000-000000000006" + }, + "description": "Report template UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/7ec00000-0000-4000-8000-000000000006/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "204": { + "description": "Template deleted." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/regulatory-reports/": { + "get": { + "summary": "List reports for case", + "description": "List FIU reports generated for the case.", + "operationId": "list_case_regulatory_reports", + "x-didit-sdk": { + "resource": "cases.regulatory_reports", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "description": "Page size." + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/regulatory-reports/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Paginated reports for the case.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegulatoryReportList" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "post": { + "summary": "Create report from case", + "description": "Create a draft FIU report for the case: DRAFT status, auto-populated from the optional template plus the case's subject, linked transactions, and narrative. Validate then finalize to produce the goAML XML and PDF.", + "operationId": "create_case_regulatory_report", + "x-didit-sdk": { + "resource": "cases.regulatory_reports", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "case_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "c1a5e000-0000-4000-8000-000000000001" + }, + "description": "Case UUID." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "report_type": { + "type": "string", + "enum": [ + "SAR", + "STR", + "CTR", + "TTR", + "UTR", + "IFT", + "CBR", + "TFR" + ], + "description": "Required unless a template is given; the template's report_type is used otherwise." + }, + "jurisdiction": { + "type": "string", + "minLength": 2, + "maxLength": 2, + "description": "Required unless a template is given." + }, + "template": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Report template UUID; its field_config seeds the draft before case-derived data is merged in." + }, + "transactions": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "nullable": true, + "description": "Transactions to attach; defaults to the case's linked transactions." + }, + "rentity_id": { + "type": "string", + "nullable": true + }, + "rentity_branch": { + "type": "string", + "nullable": true + }, + "submission_code": { + "type": "string", + "enum": [ + "E", + "M" + ], + "default": "E" + }, + "entity_reference": { + "type": "string", + "nullable": true + }, + "fiu_ref_number": { + "type": "string", + "nullable": true + }, + "currency_code_local": { + "type": "string", + "minLength": 3, + "maxLength": 3, + "nullable": true + }, + "submission_date": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reporting_person": { + "type": "object" + }, + "location": { + "type": "object" + }, + "reason": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + }, + "activity": { + "type": "string", + "nullable": true + }, + "narrative": { + "type": "object", + "properties": { + "who": { + "type": "string" + }, + "what": { + "type": "string" + }, + "when": { + "type": "string" + }, + "where": { + "type": "string" + }, + "why": { + "type": "string" + }, + "how": { + "type": "string" + } + } + }, + "report_indicators": { + "type": "array", + "items": { + "type": "string" + } + }, + "included_fields": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/regulatory-reports/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"template\": \"7ec00000-0000-4000-8000-000000000006\"}'" + } + ], + "responses": { + "201": { + "description": "Draft report created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegulatoryReportDetail" + } + } + } + }, + "400": { + "description": "The referenced template was not found.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/": { + "get": { + "summary": "List reports", + "description": "Application-wide FIU report list, optionally filtered by status, report_type, or case.", + "operationId": "list_regulatory_reports", + "x-didit-sdk": { + "resource": "regulatory_reports", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1 + }, + "description": "Page size." + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset." + }, + { + "name": "status", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "DRAFT", + "VALIDATED", + "FINALIZED" + ] + }, + "description": "Filter by status." + }, + { + "name": "report_type", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "SAR", + "STR", + "CTR", + "TTR", + "UTR", + "IFT", + "CBR", + "TFR" + ] + }, + "description": "Filter by report type." + }, + { + "name": "case", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Filter to reports on this case." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Paginated reports across the application.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegulatoryReportList" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "post": { + "summary": "Create standalone report", + "description": "Create a draft FIU report from the standalone Reports page: both case and template are required (unlike the case-scoped create, which can omit template).", + "operationId": "create_standalone_regulatory_report", + "x-didit-sdk": { + "resource": "regulatory_reports", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "case", + "template" + ], + "properties": { + "case": { + "type": "string", + "format": "uuid" + }, + "template": { + "type": "string", + "format": "uuid" + }, + "report_type": { + "type": "string", + "enum": [ + "SAR", + "STR", + "CTR", + "TTR", + "UTR", + "IFT", + "CBR", + "TFR" + ], + "description": "Required unless a template is given; the template's report_type is used otherwise." + }, + "jurisdiction": { + "type": "string", + "minLength": 2, + "maxLength": 2, + "description": "Required unless a template is given." + }, + "transactions": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "nullable": true, + "description": "Transactions to attach; defaults to the case's linked transactions." + }, + "rentity_id": { + "type": "string", + "nullable": true + }, + "rentity_branch": { + "type": "string", + "nullable": true + }, + "submission_code": { + "type": "string", + "enum": [ + "E", + "M" + ], + "default": "E" + }, + "entity_reference": { + "type": "string", + "nullable": true + }, + "fiu_ref_number": { + "type": "string", + "nullable": true + }, + "currency_code_local": { + "type": "string", + "minLength": 3, + "maxLength": 3, + "nullable": true + }, + "submission_date": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reporting_person": { + "type": "object" + }, + "location": { + "type": "object" + }, + "reason": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + }, + "activity": { + "type": "string", + "nullable": true + }, + "narrative": { + "type": "object", + "properties": { + "who": { + "type": "string" + }, + "what": { + "type": "string" + }, + "when": { + "type": "string" + }, + "where": { + "type": "string" + }, + "why": { + "type": "string" + }, + "how": { + "type": "string" + } + } + }, + "report_indicators": { + "type": "array", + "items": { + "type": "string" + } + }, + "included_fields": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"case\": \"c1a5e000-0000-4000-8000-000000000001\", \"template\": \"7ec00000-0000-4000-8000-000000000006\"}'" + } + ], + "responses": { + "201": { + "description": "Draft report created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegulatoryReportDetail" + } + } + } + }, + "400": { + "description": "The referenced template was not found.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/": { + "get": { + "summary": "Get report", + "description": "Retrieve a regulatory report.", + "operationId": "get_regulatory_report", + "x-didit-sdk": { + "resource": "regulatory_reports", + "method": "retrieve", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "report_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "5ec00000-0000-4000-8000-000000000007" + }, + "description": "Report UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Report detail.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegulatoryReportDetail" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "patch": { + "summary": "Update report", + "description": "Edit a draft or validated report's fields. Finalized reports cannot be edited (409).", + "operationId": "update_regulatory_report", + "x-didit-sdk": { + "resource": "regulatory_reports", + "method": "update", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "report_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "5ec00000-0000-4000-8000-000000000007" + }, + "description": "Report UUID." + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "jurisdiction": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "rentity_id": { + "type": "string", + "nullable": true + }, + "rentity_branch": { + "type": "string", + "nullable": true + }, + "submission_code": { + "type": "string", + "enum": [ + "E", + "M" + ], + "default": "E" + }, + "entity_reference": { + "type": "string", + "nullable": true + }, + "fiu_ref_number": { + "type": "string", + "nullable": true + }, + "currency_code_local": { + "type": "string", + "minLength": 3, + "maxLength": 3, + "nullable": true + }, + "submission_date": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reporting_person": { + "type": "object" + }, + "location": { + "type": "object" + }, + "reason": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + }, + "activity": { + "type": "string", + "nullable": true + }, + "narrative": { + "type": "object", + "properties": { + "who": { + "type": "string" + }, + "what": { + "type": "string" + }, + "when": { + "type": "string" + }, + "where": { + "type": "string" + }, + "why": { + "type": "string" + }, + "how": { + "type": "string" + } + } + }, + "report_indicators": { + "type": "array", + "items": { + "type": "string" + } + }, + "included_fields": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"rentity_id\": \"FIU-US-00123\", \"currency_code_local\": \"USD\"}'" + } + ], + "responses": { + "200": { + "description": "Updated report.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegulatoryReportDetail" + } + } + } + }, + "409": { + "description": "Report is already finalized.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "delete": { + "summary": "Delete report", + "description": "Delete a draft or validated report. Finalized reports cannot be deleted (409).", + "operationId": "delete_regulatory_report", + "x-didit-sdk": { + "resource": "regulatory_reports", + "method": "delete", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "report_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "5ec00000-0000-4000-8000-000000000007" + }, + "description": "Report UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "204": { + "description": "Report deleted." + }, + "409": { + "description": "Report is already finalized.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/validate/": { + "post": { + "summary": "Validate report", + "description": "Check the report against the obligatory goAML header fields (reporting entity ID, submission code, local currency, action taken, submission date), plus at least one attached transaction and a 2-letter jurisdiction, without generating any files. Also stores the result on the report: validation_errors and validated_at are updated, and a non-finalized report's status moves to VALIDATED (no errors) or back to DRAFT (errors).", + "operationId": "validate_regulatory_report", + "x-didit-sdk": { + "resource": "regulatory_reports", + "method": "validate", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "report_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "5ec00000-0000-4000-8000-000000000007" + }, + "description": "Report UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/validate/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Validation result.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent." + "enum": [ + "DRAFT", + "VALIDATED", + "FINALIZED" + ] }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Echo of the `metadata` you sent." + "is_valid": { + "type": "boolean" }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - }, - "examples": { - "Resolved company": { - "summary": "Full registry profile (officer list truncated for brevity)", - "value": { - "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", - "kyb_registry": { - "uuid": "f1e2d3c4-b5a6-4789-9c0d-1e2f3a4b5c6d", - "node_id": "feature_kyb_registry", - "status": "In Review", - "registry_status": "active", - "data_resolved": true, - "company_name": "Tesco PLC", - "registration_number": "00445790", - "country_code": "GB", - "region": "", - "company_type": "Public Limited Company", - "incorporation_date": "1947-11-27", - "registered_address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", - "tax_number": "GB412 8925 93", - "risk_level": "low risk", - "verification_status": "verified", - "is_from_registry": true, - "fetch_status": "resolved", - "alternative_names": "TESCO STORES (HOLDINGS) PUBLIC LIMITED COMPANY", - "nature_of_business": null, - "registered_capital": null, - "registered_capital_amount": null, - "registered_capital_currency": null, - "website": "https://www.tesco.com/", - "email": "customer.service@tesco.co.uk", - "phone": "03301231688", - "legal_entity_identifier": null, - "location_of_registration": null, - "financial_summary": { - "currency": "Pound (£)", - "total_number_of_shares": "8174378721", - "meta_detail": { - "Class_Of_Share": "Ordinary" - } - }, - "officers": [ - { - "uuid": "a1b2c3d4-e5f6-4789-9a0b-1c2d3e4f5a6b", - "name": "CHRISTOPHER JON TAYLOR", - "designation": "Secretary", - "role": "secretary", - "nationality": null, - "is_active": true, - "kyc_status": null, - "kyc_session_url": null + "errors": { + "type": "array", + "description": "Empty when valid.", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" }, - { - "uuid": "b2c3d4e5-f6a7-4890-8b1c-2d3e4f5a6b7c", - "name": "MELISSA BETHELL", - "designation": "Director", - "role": "director", - "nationality": "British", - "is_active": true, - "kyc_status": null, - "kyc_session_url": null - } - ], - "beneficial_owners": [], - "addresses": [ - { - "address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", - "type": "legal_entity_registry_address", - "description": "registered office address" + "message": { + "type": "string" } - ], - "industries": null, - "accounts": { - "last_account": "26 February 2025", - "next_account": "26 February 2026", - "due_by_date": "26 August 2026" - }, - "registry_data": { - "name": "Tesco PLC", - "registration_number": "00445790", - "status": "active", - "type": "Public Limited Company", - "incorporation_date": "1947-11-27", - "tax_number": "GB412 8925 93", - "country_code": "GB", - "fetch_status": "resolved", - "industries_detail": [ - { - "code": "47110", - "name": "Retail Sale In Non-specialised Stores With Food, Beverages Or Tobacco Predominating", - "description": null, - "meta_detail": [] - } - ] - }, - "user_provided_data": null, - "confirmed_by_user_at": null, - "last_console_edit_at": null, - "is_editable": true - }, - "vendor_data": "company-1234", - "metadata": null, - "created_at": "2026-06-11T10:45:00.000000+00:00" + } + } + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/finalize/": { + "post": { + "summary": "Finalize report", + "description": "Re-validate, then render the goAML XML and PDF and upload both to S3. On SAR/STR/TFR reports, this also moves the case's transaction alerts from Pending SAR to SAR filed.", + "operationId": "finalize_regulatory_report", + "x-didit-sdk": { + "resource": "regulatory_reports", + "method": "finalize", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "report_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "5ec00000-0000-4000-8000-000000000007" + }, + "description": "Report UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/finalize/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/finalize/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=30,\n)\nif resp.status_code == 400:\n print('Validation failed:', resp.json()['errors'])\nelse:\n resp.raise_for_status()\n report = resp.json()\n print(report['status'], report['report_number'])" + } + ], + "responses": { + "200": { + "description": "Report finalized; xml_s3_key and pdf_s3_key are set.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegulatoryReportDetail" + } + } + } + }, + "400": { + "description": "Report failed validation.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } } - }, - "Pending (registry still resolving)": { - "summary": "Select stayed pending after polling — sparse fields, auto-completes server-side", - "value": { - "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", - "kyb_registry": { - "uuid": "7c0a4f7e-2b1d-4f6a-9c3e-8d5b2a1f0e9d", - "node_id": "feature_kyb_registry", - "status": "Not Finished", - "registry_status": null, - "data_resolved": false, - "company_name": "Tesco PLC", - "registration_number": "00445790", - "country_code": "GB", - "region": null, - "company_type": null, - "incorporation_date": null, - "registered_address": null, - "tax_number": null, - "risk_level": null, - "verification_status": null, - "is_from_registry": true, - "fetch_status": "pending", - "alternative_names": null, - "nature_of_business": null, - "registered_capital": null, - "registered_capital_amount": null, - "registered_capital_currency": null, - "website": null, - "email": null, - "phone": null, - "legal_entity_identifier": null, - "location_of_registration": null, - "financial_summary": {}, - "officers": [], - "beneficial_owners": [], - "addresses": null, - "industries": null, - "accounts": null, - "registry_data": {}, - "user_provided_data": null, - "confirmed_by_user_at": null, - "last_console_edit_at": null, - "is_editable": true - }, - "vendor_data": "company-1234", - "metadata": null, - "created_at": "2026-06-11T10:45:00.000000+00:00" + } + } + } + } + }, + "409": { + "description": "Report is not in a finalizable state.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" } } } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/download-url/": { + "get": { + "summary": "Get report download URL", + "description": "Mint a presigned URL for a finalized report's goAML XML or PDF.", + "operationId": "get_regulatory_report_download_url", + "x-didit-sdk": { + "resource": "regulatory_reports", + "method": "retrieve_download_url", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Regulatory Reports" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "400": { - "description": "Validation error. Field-level problems use DRF's standard envelope.", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "report_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "5ec00000-0000-4000-8000-000000000007" + }, + "description": "Report UUID." + }, + { + "name": "file", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "pdf", + "xml" + ], + "default": "pdf" + }, + "description": "Which file to download." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/download-url/?file=xml' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Presigned download URL.", "content": { "application/json": { - "examples": { - "Missing kyb_response_id": { - "summary": "`kyb_response_id` is required", - "value": { - "kyb_response_id": [ - "This field is required." + "schema": { + "type": "object", + "properties": { + "download_url": { + "type": "string", + "format": "uri" + }, + "file": { + "type": "string", + "enum": [ + "pdf", + "xml" ] } - }, - "save_api_request=false rejected": { - "summary": "Selects are always persisted", - "value": { - "save_api_request": [ - "KYB registry select must be saved so billing and retrieval can be tracked." - ] + } + } + } + } + }, + "409": { + "description": "Report is not finalized yet.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "status": { + "type": "string" } } } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/transactions/rules/": { + "get": { + "summary": "List rules", + "description": "List the application's transaction-monitoring rules: your custom rules plus any library presets you have installed. Filter by source, category, mode, bundle or free-text search; order with `ordering` (run_count, approved_pct, reviewed_pct, declined_pct, latest_triggered_at, title, created_at, source, each with an optional `-` prefix).", + "operationId": "listTransactionRules", + "x-didit-sdk": { + "resource": "transactions.rules", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "source", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "PRESET", + "CUSTOM" + ] + }, + "description": "PRESET or CUSTOM" }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", + { + "name": "category", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Filter by rule category" + }, + { + "name": "mode", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "ACTIVE", + "DISABLED", + "TEST" + ] + }, + "description": "ACTIVE, DISABLED or TEST" + }, + { + "name": "bundle", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Filter presets by bundle" + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Case-insensitive match against title and description" + }, + { + "name": "ordering", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Comma-separated ordering fields, default source,-run_count" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer" + }, + "description": "Page size" + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer" + }, + "description": "Page offset" + } + ], + "responses": { + "200": { + "description": "Paginated rules.", "content": { "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleDetail" + } + } + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/transactions/rules/?source=CUSTOM&ordering=-run_count' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ] + }, + "post": { + "summary": "Create rule", + "description": "Create a CUSTOM transaction-monitoring rule. Titles are unique per application (duplicate -> 400). A `change_status` action targeting `AWAITING_USER` must carry `workflow_id`. Rules created in ACTIVE mode apply to new transactions immediately - the recommended workflow is create in TEST mode, backtest, then switch mode to ACTIVE.", + "operationId": "createTransactionRule", + "x-didit-sdk": { + "resource": "transactions.rules", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionRuleCreateRequest" + }, + "example": { + "title": "Structuring - inbound (custom)", + "description": "20+ inbound EUR transfers, all under 10,000 EUR, 30 days", + "category": "finance", + "mode": "TEST", + "evaluation_mode": "ALL", + "scope": { + "transaction_types": [ + "finance" + ], + "directions": [ + "INBOUND" + ] + }, + "conditions": [ + { + "field": "currency", + "operator": "eq", + "value": "EUR" + }, + { + "field": "amount", + "operator": "lt", + "value": 10000 + } + ], + "aggregation": [ + { + "metric": "count", + "operator": "gte", + "value": 20, + "window": "30d", + "filters": { + "direction": "INBOUND", + "transaction_type": "finance", + "subject_vendor_data": "__current__", + "currency": "EUR" } }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + { + "metric": "max", + "field": "amount", + "operator": "lt", + "value": 10000, + "window": "30d", + "filters": { + "direction": "INBOUND", + "transaction_type": "finance", + "subject_vendor_data": "__current__", + "currency": "EUR" } } + ], + "actions": [ + { + "type": "add_score", + "value": 35 + } + ] + } + } + } + }, + "responses": { + "201": { + "description": "The created rule.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionRuleDetail" + } + } + } + }, + "400": { + "description": "Validation error - duplicate title, unknown operator/metric, malformed window, invalid scope direction, or AWAITING_USER without workflow_id.", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "example": { + "title": [ + "A rule with this name already exists." + ] + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/transactions/rules/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"title\": \"Structuring - inbound (custom)\", \"description\": \"20+ inbound EUR transfers, all under 10,000 EUR, 30 days\", \"category\": \"finance\", \"mode\": \"TEST\", \"evaluation_mode\": \"ALL\", \"scope\": {\"transaction_types\": [\"finance\"], \"directions\": [\"INBOUND\"]}, \"conditions\": [{\"field\": \"currency\", \"operator\": \"eq\", \"value\": \"EUR\"}, {\"field\": \"amount\", \"operator\": \"lt\", \"value\": 10000}], \"aggregation\": [{\"metric\": \"count\", \"operator\": \"gte\", \"value\": 20, \"window\": \"30d\", \"filters\": {\"direction\": \"INBOUND\", \"transaction_type\": \"finance\", \"subject_vendor_data\": \"__current__\", \"currency\": \"EUR\"}}, {\"metric\": \"max\", \"field\": \"amount\", \"operator\": \"lt\", \"value\": 10000, \"window\": \"30d\", \"filters\": {\"direction\": \"INBOUND\", \"transaction_type\": \"finance\", \"subject_vendor_data\": \"__current__\", \"currency\": \"EUR\"}}], \"actions\": [{\"type\": \"add_score\", \"value\": 35}]}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nrule = {\n \"title\": \"Structuring - inbound (custom)\",\n \"description\": \"20+ inbound EUR transfers, all under 10,000 EUR, 30 days\",\n \"category\": \"finance\",\n \"mode\": \"TEST\",\n \"evaluation_mode\": \"ALL\",\n \"scope\": {\n \"transaction_types\": [\n \"finance\"\n ],\n \"directions\": [\n \"INBOUND\"\n ]\n },\n \"conditions\": [\n {\n \"field\": \"currency\",\n \"operator\": \"eq\",\n \"value\": \"EUR\"\n },\n {\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000\n }\n ],\n \"aggregation\": [\n {\n \"metric\": \"count\",\n \"operator\": \"gte\",\n \"value\": 20,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n },\n {\n \"metric\": \"max\",\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n }\n ],\n \"actions\": [\n {\n \"type\": \"add_score\",\n \"value\": 35\n }\n ]\n}\n\nresp = requests.post(\n 'https://verification.didit.me/v3/transactions/rules/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json=rule,\n timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json()['uuid'], resp.json()['mode'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const rule = {\n \"title\": \"Structuring - inbound (custom)\",\n \"description\": \"20+ inbound EUR transfers, all under 10,000 EUR, 30 days\",\n \"category\": \"finance\",\n \"mode\": \"TEST\",\n \"evaluation_mode\": \"ALL\",\n \"scope\": {\n \"transaction_types\": [\n \"finance\"\n ],\n \"directions\": [\n \"INBOUND\"\n ]\n },\n \"conditions\": [\n {\n \"field\": \"currency\",\n \"operator\": \"eq\",\n \"value\": \"EUR\"\n },\n {\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000\n }\n ],\n \"aggregation\": [\n {\n \"metric\": \"count\",\n \"operator\": \"gte\",\n \"value\": 20,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n },\n {\n \"metric\": \"max\",\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n }\n ],\n \"actions\": [\n {\n \"type\": \"add_score\",\n \"value\": 35\n }\n ]\n};\n\nconst resp = await fetch('https://verification.didit.me/v3/transactions/rules/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(rule),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst created = await resp.json();\nconsole.log(created.uuid, created.mode);" + } + ] + } + }, + "/v3/transactions/rules/{rule_uuid}/": { + "parameters": [ + { + "name": "rule_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The rule's uuid from list or create." + } + ], + "get": { + "summary": "Get rule", + "description": "Retrieve one rule with its full configuration and execution counters.", + "operationId": "getTransactionRule", + "x-didit-sdk": { + "resource": "transactions.rules", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "The rule.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionRuleDetail" } } } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "404": { + "description": "No such rule on this application.", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } + "type": "object" } } } } - } - } - }, - "/v3/passive-liveness/": { - "post": { - "summary": "Passive Liveness", - "description": "Detect presentation attacks — printed photos, screen replays, masks, deepfakes — from a single face image. No video, motion, or user interaction required.\n\n**How it works.** The image is analyzed by a biometric model; the largest detected face is evaluated and a liveness `score` (0–100) is computed. `status` is `Declined` when any `error`-severity warning fires:\n- `NO_FACE_DETECTED` — the liveness model found no face,\n- `LOW_LIVENESS_SCORE` — score at or below `face_liveness_score_decline_threshold` (default `30`),\n- `LIVENESS_FACE_ATTACK` — the model detected a presentation attack,\n- `FACE_IN_BLOCKLIST` / `POSSIBLE_FACE_IN_BLOCKLIST` — the face matches an entry on your face blocklist.\n\n`MULTIPLE_FACES_DETECTED` is a `warning`-severity signal (does not decline), and `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` are `information`-severity signals that the same face already appeared in another approved session (they never decline). `face_quality` and `face_luminance` (0–100, nullable) describe capture quality.\n\n**Blocklist and duplicate screening.** Runs on **every** call — with or without `save_api_request` — by matching the detected face against your application's face search index. Prior faces with the same `vendor_data` are excluded from the entire index search — so re-running liveness for the same user does not trigger `DUPLICATED_FACE`, and a blocklisted face originating from a session with the caller's own `vendor_data` is also NOT flagged (`FACE_IN_BLOCKLIST` screening is bypassed for same-`vendor_data` faces).\n\n**Billing.** Each `200` response consumes one Passive Liveness API credit (standalone APIs have no free tier). Insufficient balance returns `403` before any image processing.\n\n**Session persistence and face enrollment (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session (Business Console, `GET /v3/session/{sessionId}/decision/` via the returned `request_id`, `status.updated` webhook), the reference image and face crop are stored, and the detected face is **enrolled into your face search index** so later Face Search calls and duplicate checks can match it. When `false`, the call is one-shot: nothing is stored and the face is not enrolled (blocklist/duplicate screening still runs).\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", - "operationId": "post_v3passive-liveness", + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/transactions/rules/RULE_UUID/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ] + }, + "patch": { + "summary": "Update rule", + "description": "Partially update a rule. Custom rules accept every configuration field. Library (PRESET) rules accept only `mode` - use it to activate, disable, or switch a preset to TEST; any other field returns 400. An installed copy of a withheld library rule is switched off: setting its `mode` to anything but `DISABLED` answers 400 (\"This library rule cannot run yet: its conditions read data the rules engine cannot evaluate.\").", + "operationId": "updateTransactionRule", + "x-didit-sdk": { + "resource": "transactions.rules", + "method": "update", + "auth": "api_key", + "server": true + }, "tags": [ - "Standalone APIs" + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } ], - "parameters": [], "requestBody": { "required": true, "content": { - "multipart/form-data": { + "application/json": { "schema": { - "type": "object", - "required": [ - "user_image" - ], - "properties": { - "user_image": { - "type": "string", - "format": "binary", - "description": "Front-facing face image (e.g. a selfie). Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.5 MB before processing, so very high resolutions do not improve accuracy. The image should contain a single subject — when several faces are present, the largest one is evaluated and a `MULTIPLE_FACES_DETECTED` warning is added." - }, - "face_liveness_score_decline_threshold": { - "type": "number", - "format": "float", - "default": 30, - "minimum": 0, - "maximum": 100, - "description": "Liveness score threshold (0–100). A computed score **at or below** this value emits a `LOW_LIVENESS_SCORE` warning and sets `status` to `Declined`. Default `30`; raise it for a stricter anti-spoofing posture. Values outside 0–100 return `400`.", - "example": 30 - }, - "rotate_image": { - "type": "boolean", - "default": false, - "description": "When `true`, the service tries 90-degree rotations of the input and uses the orientation that yields the best face detection. Useful when EXIF orientation is missing. Adds latency.", - "example": false - }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "When `true` (default), persists the call as an API-type session, stores the face images, emits a `status.updated` webhook, and enrolls the detected face into your face search index. When `false`, the call is one-shot — nothing is stored and the face is not enrolled. Blocklist and duplicate screening run either way.", - "example": true - }, - "vendor_data": { - "type": "string", - "description": "Optional opaque identifier (your user id, email, UUID…) stored on the persisted session and echoed back. Also used to exclude prior sessions with the same `vendor_data` from the duplicate-face check, so re-verifying the same user does not raise `DUPLICATED_FACE`.", - "example": "user-123" - }, - "metadata": { - "type": "object", - "additionalProperties": true, - "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back. In multipart requests, send it as a JSON-encoded string field — it is parsed into an object.", - "example": { - "flow": "withdrawal" - } - } - } + "$ref": "#/components/schemas/TransactionRuleUpdateRequest" }, "example": { - "user_image": "(binary JPEG/PNG selfie)", - "face_liveness_score_decline_threshold": 30, - "rotate_image": false, - "save_api_request": true, - "vendor_data": "user-123", - "metadata": { - "flow": "withdrawal" - } + "mode": "ACTIVE" } } } }, "responses": { "200": { - "description": "Passive liveness completed. `liveness.score` is the model's 0–100 confidence that the image shows a live person; `status` is `Approved` unless an `error`-severity warning fired. A spoofed or blocklisted face still returns `200` — inspect `liveness.status` and `liveness.warnings`, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.", + "description": "The updated rule.", "content": { "application/json": { - "examples": { - "Approved": { - "summary": "Live face — no warnings", - "value": { - "request_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef", - "liveness": { - "status": "Approved", - "method": "PASSIVE", - "score": 95.0, - "user_image": { - "entities": [ - { - "bbox": [ - 661, - 728, - 1688, - 2188 - ], - "confidence": 0.732973, - "age": 26.91, - "gender": "male", - "race": null - } - ], - "best_angle": 0 - }, - "warnings": [], - "face_quality": 84.21, - "face_luminance": 50.33 - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T01:04:42.763237+00:00" - } - }, - "Declined - presentation attack": { - "summary": "The model flagged a spoof (print, replay, mask…)", - "value": { - "request_id": "f1c8c9b2-d3e4-4f5a-9b8c-7d6e5f4a3b2c", - "liveness": { - "status": "Declined", - "method": "PASSIVE", - "score": 12.5, - "user_image": { - "entities": [ - { - "bbox": [ - 100, - 120, - 420, - 540 - ], - "confidence": 0.81, - "age": 30.1, - "gender": "female", - "race": null - } - ], - "best_angle": 0 - }, - "warnings": [ - { - "risk": "LOW_LIVENESS_SCORE", - "feature": "LIVENESS", - "additional_data": null, - "log_type": "error", - "short_description": "Low liveness score", - "long_description": "The liveness check resulted in a low score, indicating potential use of non-live facial representations or poor-quality biometric data." - }, - { - "risk": "LIVENESS_FACE_ATTACK", - "feature": "LIVENESS", - "additional_data": null, - "log_type": "error", - "short_description": "Liveness Face Attack", - "long_description": "The system detected a potential attempt to bypass the liveness check." - } - ], - "face_quality": 72.4, - "face_luminance": 48.2 - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T01:04:42.763237+00:00" - } - }, - "Declined - face on blocklist": { - "summary": "Live face, but it matches a blocklist entry", - "value": { - "request_id": "0b54a1de-22aa-4e0f-9c84-5a2b1f3c4d5e", - "liveness": { - "status": "Declined", - "method": "PASSIVE", - "score": 93.75, - "user_image": { - "entities": [ - { - "bbox": [ - 40, - 40, - 100, - 100 - ], - "confidence": 0.722082, - "age": 27.0, - "gender": "male", - "race": null - } - ], - "best_angle": 0 - }, - "warnings": [ - { - "risk": "FACE_IN_BLOCKLIST", - "feature": "LIVENESS", - "additional_data": { - "blocklisted_session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86", - "blocklisted_session_number": 3242, - "api_service": null - }, - "log_type": "error", - "short_description": "Face in blocklist", - "long_description": "The system identified a face in the blocklist, which means the face is not allowed to be verified." - } - ], - "face_quality": null, - "face_luminance": null - }, - "vendor_data": "user-123", - "metadata": null, - "created_at": "2026-06-12T01:04:42.763237+00:00" - } - } - }, "schema": { - "type": "object", - "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." - }, - "liveness": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined" - ], - "description": "`Declined` when at least one `error`-severity warning fired (no face, low score, presentation attack, or a blocklist hit); `Approved` otherwise. `warning`/`information` entries (multiple faces, duplicates) never decline on their own." - }, - "method": { - "type": "string", - "enum": [ - "PASSIVE" - ], - "description": "Always `PASSIVE` for this endpoint." - }, - "score": { - "type": "number", - "format": "float", - "nullable": true, - "minimum": 0, - "maximum": 100, - "description": "Liveness confidence (0–100, two decimals) that the image depicts a live person rather than a spoof. `null` when the model returned no confidence.", - "example": 95.0 - }, - "user_image": { - "type": "object", - "description": "Face-detection results for the submitted image.", - "properties": { - "entities": { - "type": "array", - "items": { - "type": "object", - "properties": { - "bbox": { - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 4, - "maxItems": 4, - "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.", - "example": [ - 661, - 728, - 1688, - 2188 - ] - }, - "confidence": { - "type": "number", - "format": "float", - "minimum": 0, - "maximum": 1, - "description": "Face-detection confidence (0–1).", - "example": 0.732973 - }, - "age": { - "type": "number", - "format": "float", - "description": "Model-estimated age of the detected face, in years. Informational only — it does not affect the liveness decision.", - "example": 26.91 - }, - "gender": { - "type": "string", - "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.", - "example": "male" - }, - "race": { - "type": "string", - "nullable": true, - "description": "Reserved field — always `null` in responses from this endpoint.", - "example": null - } - } - }, - "description": "One entry per detected face. The largest face is the one evaluated." - }, - "best_angle": { - "type": "integer", - "nullable": true, - "description": "Rotation (degrees) that produced the best face detection; non-zero only when `rotate_image=true` corrected the orientation.", - "example": 0 - } - } - }, - "warnings": { - "type": "array", - "description": "Risk signals. `error` severity (declines): `NO_FACE_DETECTED`, `LOW_LIVENESS_SCORE` (score at or below the threshold), `LIVENESS_FACE_ATTACK`, `FACE_IN_BLOCKLIST`, `POSSIBLE_FACE_IN_BLOCKLIST`. `warning` severity (review, no decline): `MULTIPLE_FACES_DETECTED`. `information` severity (no decline): `DUPLICATED_FACE`, `POSSIBLE_DUPLICATED_FACE` — the same face already appeared in another approved session.", - "items": { - "type": "object", - "properties": { - "risk": { - "type": "string", - "enum": [ - "NO_FACE_DETECTED", - "LOW_LIVENESS_SCORE", - "LIVENESS_FACE_ATTACK", - "MULTIPLE_FACES_DETECTED", - "FACE_IN_BLOCKLIST", - "POSSIBLE_FACE_IN_BLOCKLIST", - "DUPLICATED_FACE", - "POSSIBLE_DUPLICATED_FACE" - ], - "description": "Machine-readable risk code." - }, - "feature": { - "type": "string", - "enum": [ - "LIVENESS" - ], - "description": "Feature that raised the warning. Always `LIVENESS` on this endpoint." - }, - "additional_data": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "`null` for most warnings. Blocklist hits carry `{blocklisted_session_id, blocklisted_session_number, api_service}`; duplicate hits carry `{duplicated_session_id, duplicated_session_number, api_service}` pointing at the matching session." - }, - "log_type": { - "type": "string", - "enum": [ - "error", - "warning", - "information" - ], - "description": "Severity. `error` warnings drive `status` to `Declined`; `warning` and `information` entries are advisory and never decline on their own." - }, - "short_description": { - "type": "string", - "description": "Human-readable one-line summary of the risk." - }, - "long_description": { - "type": "string", - "description": "Human-readable explanation of the risk." - } - } - } - }, - "face_quality": { - "type": "number", - "format": "float", - "nullable": true, - "minimum": 0, - "maximum": 100, - "description": "Capture-quality score of the detected face, normalized to 0–100. `null` when the model could not produce a reliable value.", - "example": 84.21 - }, - "face_luminance": { - "type": "number", - "format": "float", - "nullable": true, - "minimum": 0, - "maximum": 100, - "description": "Brightness of the detected face, normalized to 0–100. `null` when unavailable.", - "example": 50.33 - } - } - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent, or `null`." - }, - "metadata": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Echo of the `metadata` object you sent, or `null`." - }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp (UTC) of when the response was generated, e.g. `2026-06-12T01:04:42.763237+00:00`." - } - } + "$ref": "#/components/schemas/TransactionRuleDetail" } } } }, "400": { - "description": "Validation error. Returned when `user_image` is missing, exceeds 5 MB, has an unsupported extension, fails server-side image decoding, or when an option is out of range. Field errors use DRF's `{field: [messages]}` envelope; image-decoding failures use `{\"error\": ...}`.", + "description": "Validation error, a non-mode field on a library rule, or a mode other than DISABLED on a withheld library rule.", "content": { "application/json": { - "examples": { - "Missing image": { - "summary": "`user_image` not included in the form data", - "value": { - "user_image": [ - "No file was submitted." - ] - } - }, - "Invalid image format": { - "summary": "Allowed extension but undecodable image content", - "value": { - "error": "Invalid user image format." - } - }, - "Unsupported file extension": { - "summary": "File extension outside tiff/jpg/jpeg/png/webp", - "value": { - "user_image": [ - "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp." - ] - } - }, - "File too large": { - "summary": "Upload exceeds the 5 MB limit", - "value": { - "user_image": [ - "File size should not exceed 5 MB" - ] - } - } + "schema": { + "type": "object" + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/transactions/rules/RULE_UUID/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"mode\": \"ACTIVE\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/transactions/rules/RULE_UUID/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'mode': 'ACTIVE'},\n timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json()['mode'])" + } + ] + }, + "delete": { + "summary": "Delete rule", + "description": "Delete a CUSTOM rule. Deletion is immediate and permanent: the rule stops evaluating for new transactions and disappears from listings, while past rule runs on already-evaluated transactions keep their recorded results. Library rules cannot be deleted - uninstall them via the install endpoint instead (they can always be reinstalled from the library).", + "operationId": "deleteTransactionRule", + "x-didit-sdk": { + "resource": "transactions.rules", + "method": "delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "204": { + "description": "Deleted." + }, + "400": { + "description": "The rule is a library preset - uninstall instead.", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "example": { + "detail": "Library rules cannot be deleted. Uninstall them via the rules install endpoint instead." } } } }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.", + "404": { + "description": "No such rule on this application.", "content": { "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." + "schema": { + "type": "object" + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/transactions/rules/RULE_UUID/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ] + } + }, + "/v3/transactions/rules/backtest/": { + "post": { + "summary": "Backtest rule configuration", + "description": "Evaluate a hypothetical rule configuration against your recent transactions (most recent first, up to the backtest cap) without creating a rule or touching any transaction. Use it before activating a rule to see how often it would have matched and how many subjects it would have affected.", + "operationId": "backtestTransactionRules", + "x-didit-sdk": { + "resource": "transactions.rules", + "method": "backtest", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionRuleBacktestRequest" + }, + "example": { + "conditions": [ + { + "field": "currency", + "operator": "eq", + "value": "EUR" + }, + { + "field": "amount", + "operator": "lt", + "value": 10000 + } + ], + "aggregation": [ + { + "metric": "count", + "operator": "gte", + "value": 20, + "window": "30d", + "filters": { + "direction": "INBOUND", + "transaction_type": "finance", + "subject_vendor_data": "__current__", + "currency": "EUR" } }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + { + "metric": "max", + "field": "amount", + "operator": "lt", + "value": 10000, + "window": "30d", + "filters": { + "direction": "INBOUND", + "transaction_type": "finance", + "subject_vendor_data": "__current__", + "currency": "EUR" } } - } + ], + "evaluation_mode": "ALL", + "scope": { + "transaction_types": [ + "finance" + ], + "directions": [ + "INBOUND" + ] + }, + "period_days": 90 } } - }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + } + }, + "responses": { + "200": { + "description": "Backtest statistics.", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } + "$ref": "#/components/schemas/TransactionRuleBacktestResult" + }, + "example": { + "evaluated": 4820, + "matched": 12, + "affected_entities": 4, + "period_days": 90 } } } } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", - "label": "cURL", - "source": "curl -X POST 'https://verification.didit.me/v3/passive-liveness/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'user_image=@./selfie.jpg' \\\n -F 'face_liveness_score_decline_threshold=30' \\\n -F 'save_api_request=true' \\\n -F 'vendor_data=user-123'" + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/transactions/rules/backtest/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"conditions\": [{\"field\": \"currency\", \"operator\": \"eq\", \"value\": \"EUR\"}, {\"field\": \"amount\", \"operator\": \"lt\", \"value\": 10000}], \"aggregation\": [{\"metric\": \"count\", \"operator\": \"gte\", \"value\": 20, \"window\": \"30d\", \"filters\": {\"direction\": \"INBOUND\", \"transaction_type\": \"finance\", \"subject_vendor_data\": \"__current__\", \"currency\": \"EUR\"}}, {\"metric\": \"max\", \"field\": \"amount\", \"operator\": \"lt\", \"value\": 10000, \"window\": \"30d\", \"filters\": {\"direction\": \"INBOUND\", \"transaction_type\": \"finance\", \"subject_vendor_data\": \"__current__\", \"currency\": \"EUR\"}}], \"evaluation_mode\": \"ALL\", \"scope\": {\"transaction_types\": [\"finance\"], \"directions\": [\"INBOUND\"]}, \"period_days\": 90}'" }, { "lang": "python", "label": "Python", - "source": "import requests\n\nurl = 'https://verification.didit.me/v3/passive-liveness/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as f:\n files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}\n data = {\n 'face_liveness_score_decline_threshold': 30,\n 'save_api_request': 'true',\n 'vendor_data': 'user-123',\n }\n resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nbody = resp.json()\nprint('status:', body['liveness']['status'])\nprint('score:', body['liveness']['score'])\nfor w in body['liveness']['warnings']:\n print('warning:', w['risk'], '-', w['log_type'])" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('face_liveness_score_decline_threshold', '30');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/passive-liveness/', {\n method: 'POST',\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n body: form,\n});\n\nif (!response.ok) throw new Error(`Passive liveness failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.liveness.status, 'score:', body.liveness.score);\nbody.liveness.warnings.forEach(w => console.log('warning:', w.risk, '-', w.log_type));" + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/transactions/rules/backtest/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n \"conditions\": [\n {\n \"field\": \"currency\",\n \"operator\": \"eq\",\n \"value\": \"EUR\"\n },\n {\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000\n }\n ],\n \"aggregation\": [\n {\n \"metric\": \"count\",\n \"operator\": \"gte\",\n \"value\": 20,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n },\n {\n \"metric\": \"max\",\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n }\n ],\n \"evaluation_mode\": \"ALL\",\n \"scope\": {\n \"transaction_types\": [\n \"finance\"\n ],\n \"directions\": [\n \"INBOUND\"\n ]\n },\n \"period_days\": 90\n},\n timeout=30,\n)\nresp.raise_for_status()\nstats = resp.json()\nprint(stats['matched'], 'matches across', stats['affected_entities'], 'subjects')" } ] } }, - "/v3/database-validation/": { - "post": { - "summary": "Database Validation (standalone)", - "operationId": "post_v3database-validation", + "/v3/transactions/rules/library/": { + "get": { + "summary": "List rule library", + "description": "Browse the full preset rule library (150+ rules) with each entry's complete configuration and an `is_installed` flag for this application. Filter by bundle, category, or free-text search. A library rule whose conditions read data the rules engine cannot evaluate yet is withheld until it can run. Withheld rules are not listed.", + "operationId": "listTransactionRuleLibrary", + "x-didit-sdk": { + "resource": "transactions.rules", + "method": "list_library", + "auth": "api_key", + "server": true + }, "tags": [ - "Standalone APIs" + "Transactions" ], "security": [ { "ApiKeyAuth": [] } ], - "parameters": [], - "description": "Validate a person's identity data against official government and registry sources — CPF in Brazil, RENAPER in Argentina, DNI registries in Spain and Peru, INE in Mexico, and 100+ more services across 60+ countries.\n\n**Service selection.** Each country exposes one or more catalog services identified by a `service_id` (e.g. `bra_cpf`, `arg_renaper`, `pan_cedula_sib`). Pin the services you want with the `services` array — if you omit it, exactly **one** default service runs (the longest-established live service for the country). Sending `services` also switches the response to the extended shape with `services_used` and `match_score`. Discover services, their required fields, and prices via `GET /v1/organization/database-validation-countries/` (a catalog endpoint not documented in this spec) or the Business Console workflow editor. Some services are flagged `requires_consent=true` in the catalog and demand `consent=true`; others require onboarding for your organization before they can be called.\n\n**Input fields.** `identification_number` is a universal field that maps to the right country-specific field automatically (see its description). Country format rules are enforced before any provider is called (e.g. BRA CPF must be exactly 11 digits) and format failures are never billed. Biometric services need a `selfie` upload (multipart only); Argentina RENAPER additionally requires `gender`.\n\n**Results.** Each service returns a field-by-field `validation` map plus a vendor-neutral `outcome_code` (`MATCH`, `PARTIAL_MATCH`, `NO_MATCH`, `DOCUMENT_NOT_FOUND`, `INVALID_DOCUMENT_FORMAT`, `INVALID_INPUT`, `MINOR_BLOCKED`, `DECEASED`, `BIOMETRIC_NO_MATCH`, `BIOMETRIC_IMAGE_UNUSABLE`, `INCONCLUSIVE`, `DOCUMENT_SUPERSEDED`, `REGISTRY_UNAVAILABLE`, `REGISTRY_ERROR`). Note the distinctions: `NO_MATCH` is a definitive mismatch, `INCONCLUSIVE` means the registry could not confirm either way (review, not decline), and `BIOMETRIC_IMAGE_UNUSABLE` is a technical selfie problem (retake), unlike the definitive `BIOMETRIC_NO_MATCH`. The overall `match_type` aggregates across services (any full → `full_match`, else any partial → `partial_match`, else `no_match`) and `no_match_action`/`partial_match_action` translate it into the final `status`. `validation_type` is derived: `two_by_two` when two or more distinct services produced a full match (on the identification number, on full name + date of birth together, or on the address without contradictions), else `one_by_one`.\n\n**Persistence.** `save_api_request` defaults to **true**: the result is stored as a session (`request_id` works with `GET /v3/session/{sessionId}/decision/`), appears in the console, and fires a `status.updated` webhook. It also controls the `validations` shape — per-service objects when saved, a merged `{field: match}` map when not.\n\n**Billing.** Per-service: each service that returns a billable result is charged its own catalog price. Failed or skipped services are not billed, and requests rejected with `400` cost nothing. The pre-flight balance check covers the sum of the selected services' prices (`403` when short).\n\n**Failures.** When no selected service returns a usable result, the API returns `400` with `validation_errors` if every unanswered attempt explicitly refused the input (`provider_rejected_input`, `retryable: false`). Correct the input before trying again. Otherwise it returns `502` with `validation_errors` (`empty_provider_response`, `retryable: true`), including mixed input rejection and unavailable fallback attempts; nothing is billed (a session is still recorded with status `Not Finished` when `save_api_request=true`). When only some services fail, the `200` response carries their failures in `database_validation.errors`.\n\n**Sandbox.** Keys from sandbox applications skip the registry calls and billing and return a static `full_match` response — the sandbox payload always includes `services_used` and `match_score: 100` regardless of how `services` was sent, so don't validate the live extended-shape gating or the 0.0–1.0 `match_score` fraction against sandbox responses.\n\nSend the request as `application/json`, or as `multipart/form-data` when uploading a `selfie`.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "issuing_state" - ], - "properties": { - "issuing_state": { - "type": "string", - "description": "ISO 3166-1 **alpha-3** country code of the registry to validate against (e.g. `BRA`, `ESP`, `COL`). Determines which services are available. Unsupported codes return `400` with the full list of valid options.", - "example": "BRA" - }, - "services": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Catalog `service_id`s to run for this country (e.g. `[\"bra_cpf\"]`). Also accepts a single string or a comma-separated/JSON-encoded string. **If omitted or empty, exactly one default service runs** — the longest-established live service for the country; newer, biometric, and pay-as-you-go services must be named explicitly. Sending `services` switches the response to the extended shape (adds `services_used` and `match_score`). Every id must exist and be live for the `issuing_state`; services that require onboarding return `400` until activated for your organization. Discover available services per country via `GET /v1/organization/database-validation-countries/` (a catalog endpoint not documented in this spec) or the Business Console workflow editor.", - "example": [ - "bra_cpf" - ] - }, - "validation_type": { - "type": "string", - "deprecated": true, - "description": "DEPRECATED. Accepted for backward compatibility but ignored — the response `validation_type` is now derived from how many services full-matched. Use `services` to pin specific services." - }, - "consent": { - "type": "boolean", - "default": false, - "description": "Set to `true` when the end user has explicitly consented to the selected validation services. Required for services flagged `requires_consent=true` in the catalog — without it those services return `400` with `services_requiring_consent`." - }, - "identification_number": { - "type": "string", - "description": "Universal identification number — automatically mapped to the correct country-specific field, so you can use it instead of `personal_number`/`tax_number`/`document_number`: ARG→document_number (DNI), BOL→document_number (CI), BRA→tax_number (CPF, 11 digits), CHL→personal_number (RUT), COL→personal_number (Cédula), CRI→personal_number (Cédula), DOM→personal_number (Cédula, 11 digits), ECU→personal_number (Cédula, 10 digits), ESP→personal_number (DNI/NIE), GTM→document_number (DPI), HND→document_number (DNI), MEX→personal_number (CURP, 18 chars), PAN→personal_number (Cédula), PER→personal_number (DNI, 8 digits), PRY→document_number (CI), SLV→document_number (DUI), URY→personal_number (CI), VEN→document_number (Cédula). It never overrides an explicitly provided country-specific field." - }, - "first_name": { - "type": "string", - "description": "The individual's first name. Required by some services/countries.", - "example": "John" - }, - "last_name": { - "type": "string", - "description": "The individual's last name. Required by some services/countries.", - "example": "Doe" - }, - "middle_name": { - "type": "string", - "description": "Middle name, used by some country services (AUS, NZL, …)." - }, - "full_name": { - "type": "string", - "description": "Full name — some services accept this in lieu of first/last name (e.g. CHN services, which match the native-script name)." - }, - "date_of_birth": { - "type": "string", - "format": "date", - "description": "Date of birth, `YYYY-MM-DD`. Required by many services.", - "example": "1980-01-01" - }, - "personal_number": { - "type": "string", - "description": "Government-issued unique personal identifier. Used by: CHL (RUT), COL (Cédula), CRI, DOM, ECU, ESP (DNI/NIE), MEX (CURP), PAN (Cédula), PER (DNI), URY. Consider `identification_number` instead." - }, - "tax_number": { - "type": "string", - "description": "Tax identification number. Used by: BRA (CPF, 11 digits). Consider `identification_number` instead." - }, - "document_number": { - "type": "string", - "description": "Document number. Used by: ARG (DNI), BOL (CI), GTM (DPI), HND (DNI), PRY (CI), SLV (DUI), VEN (Cédula). Consider `identification_number` instead." - }, - "document_type": { - "type": "string", - "enum": [ - "P", - "DL", - "ID", - "RP", - "SSC", - "HIC", - "WP", - "TC", - "VISA", - "PSC", - "BC", - "OTHER" - ], - "description": "Type of document being validated: `P` passport, `DL` driver license, `ID` national ID, `RP` residence permit (plus `SSC`, `HIC`, `WP`, `TC`, `VISA`, `PSC`, `BC`, `OTHER`). Required by some services (e.g. ESP)." - }, - "expiration_date": { - "type": "string", - "format": "date", - "description": "Document expiration date, `YYYY-MM-DD`. Required for ESP (Spain) validation.", - "example": "2030-01-15" - }, - "date_of_issue": { - "type": "string", - "format": "date", - "description": "Document issue date, `YYYY-MM-DD` (fecha de expedición). Required for COL (Colombia) cédula validation.", - "example": "2015-06-20" - }, - "nationality": { - "type": "string", - "description": "Nationality as ISO 3166-1 alpha-3. Required by some services." - }, - "gender": { - "type": "string", - "enum": [ - "M", - "F", - "X" - ], - "description": "Gender: `M`, `F`, or `X` (other/unknown). Required for Argentina's RENAPER validation (`arg_renaper`)." - }, - "address": { - "description": "Residential address. Prefer the structured object `{\"street_1\":\"123 Main St\",\"street_2\":\"Apt 4B\",\"city\":\"Springfield\",\"region\":\"IL\",\"postal_code\":\"62701\",\"country\":\"US\"}`; a complete single-line string is still accepted. Required by address-verification services; when a service defines address requirements, missing parts return `400` with `address_fields_required_by`.", - "oneOf": [ - { - "type": "object", - "properties": { - "street_1": { - "type": "string" - }, - "street_2": { - "type": "string" - }, - "city": { - "type": "string" - }, - "region": { - "type": "string" - }, - "postal_code": { - "type": "string" - }, - "country": { - "type": "string" - } - } - }, - { - "type": "string", - "description": "Complete single-line address (legacy)." - } - ] - }, - "address_element_1": { - "type": "string", - "description": "Street address including street number and type. If omitted, derived from `address`." - }, - "address_element_2": { - "type": "string", - "description": "Apartment, unit, building, floor, or extra address line. Only send when you have it explicitly." - }, - "address_element_3": { - "type": "string", - "description": "City, suburb, district, locality, or neighborhood. If omitted, derived from `address`." - }, - "address_element_4": { - "type": "string", - "description": "State, province, region, or town. If omitted, derived from `address`." - }, - "address_element_5": { - "type": "string", - "description": "Postcode or postal code. `postal_code` is accepted as an alias." - }, - "postal_code": { - "type": "string", - "description": "Postal code for address-based services (alias of `address_element_5`)." - }, - "driver_license_number": { - "type": "string", - "description": "Driver licence number for government document-verification services (AUS, NZL, IND)." - }, - "driver_license_card_number": { - "type": "string", - "description": "Physical driver licence card number, where required separately from the licence number (AUS)." - }, - "driver_license_state": { - "type": "string", - "description": "Driver licence state or territory of issue (AUS)." - }, - "driver_license_version": { - "type": "string", - "description": "Driver licence version code (NZL)." - }, - "passport_number": { - "type": "string", - "description": "Passport number for government passport-verification services (AUS, NZL, IND, CHN)." - }, - "passport_expiration_date": { - "type": "string", - "format": "date", - "description": "Passport expiry date, `YYYY-MM-DD`, for passport-verification services." - }, - "passport_file_number": { - "type": "string", - "description": "Passport file number (IND)." - }, - "passport_issue_country": { - "type": "string", - "description": "Issuing country of the passport (ISO 3166-1 alpha-3) for passport-verification services." - }, - "medicare_card_number": { - "type": "string", - "description": "Medicare card number (AUS)." - }, - "immi_card_number": { - "type": "string", - "description": "Australian ImmiCard number." - }, - "immi_card_expiry_date": { - "type": "string", - "format": "date", - "description": "Australian ImmiCard expiry date." - }, - "citizenship_certificate_number": { - "type": "string", - "description": "Citizenship certificate number (AUS)." - }, - "birth_registration_number": { - "type": "string", - "description": "Birth-certificate registration number (AUS)." - }, - "birth_registration_date": { - "type": "string", - "format": "date", - "description": "Birth-certificate registration date (AUS)." - }, - "birth_registration_state": { - "type": "string", - "description": "Birth-certificate registration state (AUS)." - }, - "marriage_certificate_number": { - "type": "string", - "description": "Marriage certificate number (AUS)." - }, - "change_of_name_certificate_number": { - "type": "string", - "description": "Change-of-name certificate number (AUS)." - }, - "first_partner_name": { - "type": "string", - "description": "First name of the partner on a marriage certificate (AUS)." - }, - "last_partner_name": { - "type": "string", - "description": "Last name of the partner on a marriage certificate (AUS)." - }, - "voter_id": { - "type": "string", - "description": "Voter registration number (IND EPIC, IRL)." - }, - "epic_card": { - "type": "string", - "description": "India EPIC voter card number." - }, - "pan": { - "type": "string", - "description": "India PAN (Permanent Account Number)." - }, - "national_id": { - "type": "string", - "description": "National ID number (CHN, MYS, KEN, KHM, NGA, ZAF, …)." - }, - "cic": { - "type": "string", - "description": "Mexican INE/IFE Código de Identificación de Credencial (CIC), 9 digits. Used by the MEX INE credential-validity service (`mex_ine_vigencia`)." - }, - "identificador_ciudadano": { - "type": "string", - "description": "Mexican INE Identificador del Ciudadano (9 digits). Used with `cic` for modern INE models (E/F/G/H) in `mex_ine_vigencia`." - }, - "ocr": { - "type": "string", - "description": "Mexican INE OCR number (13 digits, back of card). Used with `cic` for Model D in `mex_ine_vigencia`." - }, - "voter_number": { - "type": "string", - "description": "Mexican INE Clave de Elector (18 chars). Used with `emission_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`." - }, - "emission_number": { - "type": "string", - "description": "Mexican INE Número de Emisión. Used with `voter_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`." - }, - "bvn": { - "type": "string", - "description": "Nigerian Bank Verification Number." - }, - "bank_card_number": { - "type": "string", - "description": "Bank card number (CHN bank-card verification)." - }, - "ssn": { - "type": "string", - "description": "Social Security Number (USA)." - }, - "phone": { - "type": "string", - "description": "Phone number for phone-verification services." - }, - "landline": { - "type": "string", - "description": "Landline number for phone-verification services." - }, - "email": { - "type": "string", - "format": "email", - "description": "Email address for identity-verification services." - }, - "country_of_residence": { - "type": "string", - "description": "ISO 3166-1 alpha-2 country of residence. Used by global identity-enrichment services (e.g. `glb_identity_enrichment`) to focus the lookup.", - "example": "US" - }, - "partial_match_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "NO_ACTION", - "description": "What a `partial_match` does to `database_validation.status`. Defaults to `NO_ACTION` (status stays `Approved`, with a warning)." - }, - "no_match_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "DECLINE", - "description": "What a `no_match` does to `database_validation.status`. Defaults to `DECLINE`." - }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "Persist the validation as a session (console visibility, decision endpoint, `status.updated` webhook). Also changes the `validations` response shape: per-service objects when `true` (default), a merged field map when `false`." - }, - "vendor_data": { - "type": "string", - "description": "Your identifier for the validated user; echoed back and stored with the session." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Free-form JSON stored with the request and echoed back." + "parameters": [ + { + "name": "bundle", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Filter by bundle, e.g. finance, aml_ctf, crypto_monitoring, travel_rule" + }, + { + "name": "category", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Filter by category" + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Case-insensitive match against title and description" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer" + }, + "description": "Page size" + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer" + }, + "description": "Page offset" + } + ], + "responses": { + "200": { + "description": "Paginated library entries.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleLibraryEntry" + } + } } } } - }, - "multipart/form-data": { + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/transactions/rules/library/?bundle=finance' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ] + } + }, + "/v3/transactions/rules/install/": { + "post": { + "summary": "Install library rules", + "description": "Install one or more presets onto the application, by `library_keys` or a whole `bundle`. An installed preset is active at once while transaction monitoring is on, and disabled otherwise; already-installed keys are skipped. A library rule whose conditions read data the rules engine cannot evaluate yet is withheld until it can run. A request that names one in `library_keys` answers 400 and installs nothing (\"These library rules cannot run yet: their conditions read data the rules engine cannot evaluate.\" followed by the keys), and a `bundle` install skips it. A withheld rule comes back on its own once its conditions can be evaluated. A `library_keys` entry that is not in the library is skipped, not refused.", + "operationId": "installTransactionRuleLibrary", + "x-didit-sdk": { + "resource": "transactions.rules", + "method": "install_library", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { - "type": "object", - "required": [ - "issuing_state" - ], - "properties": { - "issuing_state": { - "type": "string", - "description": "ISO 3166-1 **alpha-3** country code of the registry to validate against (e.g. `BRA`, `ESP`, `COL`). Determines which services are available. Unsupported codes return `400` with the full list of valid options.", - "example": "BRA" - }, - "services": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Catalog `service_id`s to run for this country (e.g. `[\"bra_cpf\"]`). Also accepts a single string or a comma-separated/JSON-encoded string. **If omitted or empty, exactly one default service runs** — the longest-established live service for the country; newer, biometric, and pay-as-you-go services must be named explicitly. Sending `services` switches the response to the extended shape (adds `services_used` and `match_score`). Every id must exist and be live for the `issuing_state`; services that require onboarding return `400` until activated for your organization. Discover available services per country via `GET /v1/organization/database-validation-countries/` (a catalog endpoint not documented in this spec) or the Business Console workflow editor.", - "example": [ - "bra_cpf" - ] - }, - "validation_type": { - "type": "string", - "deprecated": true, - "description": "DEPRECATED. Accepted for backward compatibility but ignored — the response `validation_type` is now derived from how many services full-matched. Use `services` to pin specific services." - }, - "consent": { - "type": "boolean", - "default": false, - "description": "Set to `true` when the end user has explicitly consented to the selected validation services. Required for services flagged `requires_consent=true` in the catalog — without it those services return `400` with `services_requiring_consent`." - }, - "identification_number": { - "type": "string", - "description": "Universal identification number — automatically mapped to the correct country-specific field, so you can use it instead of `personal_number`/`tax_number`/`document_number`: ARG→document_number (DNI), BOL→document_number (CI), BRA→tax_number (CPF, 11 digits), CHL→personal_number (RUT), COL→personal_number (Cédula), CRI→personal_number (Cédula), DOM→personal_number (Cédula, 11 digits), ECU→personal_number (Cédula, 10 digits), ESP→personal_number (DNI/NIE), GTM→document_number (DPI), HND→document_number (DNI), MEX→personal_number (CURP, 18 chars), PAN→personal_number (Cédula), PER→personal_number (DNI, 8 digits), PRY→document_number (CI), SLV→document_number (DUI), URY→personal_number (CI), VEN→document_number (Cédula). It never overrides an explicitly provided country-specific field." - }, - "first_name": { - "type": "string", - "description": "The individual's first name. Required by some services/countries.", - "example": "John" - }, - "last_name": { - "type": "string", - "description": "The individual's last name. Required by some services/countries.", - "example": "Doe" - }, - "middle_name": { - "type": "string", - "description": "Middle name, used by some country services (AUS, NZL, …)." - }, - "full_name": { - "type": "string", - "description": "Full name — some services accept this in lieu of first/last name (e.g. CHN services, which match the native-script name)." - }, - "date_of_birth": { - "type": "string", - "format": "date", - "description": "Date of birth, `YYYY-MM-DD`. Required by many services.", - "example": "1980-01-01" - }, - "personal_number": { - "type": "string", - "description": "Government-issued unique personal identifier. Used by: CHL (RUT), COL (Cédula), CRI, DOM, ECU, ESP (DNI/NIE), MEX (CURP), PAN (Cédula), PER (DNI), URY. Consider `identification_number` instead." - }, - "tax_number": { - "type": "string", - "description": "Tax identification number. Used by: BRA (CPF, 11 digits). Consider `identification_number` instead." - }, - "document_number": { - "type": "string", - "description": "Document number. Used by: ARG (DNI), BOL (CI), GTM (DPI), HND (DNI), PRY (CI), SLV (DUI), VEN (Cédula). Consider `identification_number` instead." - }, - "document_type": { - "type": "string", - "enum": [ - "P", - "DL", - "ID", - "RP", - "SSC", - "HIC", - "WP", - "TC", - "VISA", - "PSC", - "BC", - "OTHER" - ], - "description": "Type of document being validated: `P` passport, `DL` driver license, `ID` national ID, `RP` residence permit (plus `SSC`, `HIC`, `WP`, `TC`, `VISA`, `PSC`, `BC`, `OTHER`). Required by some services (e.g. ESP)." - }, - "expiration_date": { - "type": "string", - "format": "date", - "description": "Document expiration date, `YYYY-MM-DD`. Required for ESP (Spain) validation.", - "example": "2030-01-15" - }, - "date_of_issue": { - "type": "string", - "format": "date", - "description": "Document issue date, `YYYY-MM-DD` (fecha de expedición). Required for COL (Colombia) cédula validation.", - "example": "2015-06-20" - }, - "nationality": { - "type": "string", - "description": "Nationality as ISO 3166-1 alpha-3. Required by some services." - }, - "gender": { - "type": "string", - "enum": [ - "M", - "F", - "X" - ], - "description": "Gender: `M`, `F`, or `X` (other/unknown). Required for Argentina's RENAPER validation (`arg_renaper`)." - }, - "address": { - "description": "Residential address. Prefer the structured object `{\"street_1\":\"123 Main St\",\"street_2\":\"Apt 4B\",\"city\":\"Springfield\",\"region\":\"IL\",\"postal_code\":\"62701\",\"country\":\"US\"}`; a complete single-line string is still accepted. Required by address-verification services; when a service defines address requirements, missing parts return `400` with `address_fields_required_by`.", - "oneOf": [ - { - "type": "object", - "properties": { - "street_1": { - "type": "string" - }, - "street_2": { - "type": "string" - }, - "city": { - "type": "string" - }, - "region": { - "type": "string" - }, - "postal_code": { - "type": "string" - }, - "country": { - "type": "string" - } + "$ref": "#/components/schemas/TransactionRuleInstallRequest" + }, + "example": { + "library_keys": [ + "structuring-inbound", + "structuring-outbound" + ] + } + } + } + }, + "responses": { + "200": { + "description": "What was installed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionRuleInstallResult" + }, + "example": { + "installed_library_keys": [ + "structuring-inbound", + "structuring-outbound" + ], + "installed_count": 2 + } + } + } + }, + "400": { + "description": "Neither library_keys nor bundle provided, `library_keys` is not a list, or a `library_keys` entry is withheld. An unknown entry is skipped and answers 200 without it.", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/transactions/rules/install/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"library_keys\": [\"structuring-inbound\", \"structuring-outbound\"]}'" + } + ] + }, + "delete": { + "summary": "Uninstall library rules", + "description": "Remove installed presets from the application, by `library_keys` or a whole `bundle`. Uninstalled presets stop evaluating immediately and can be reinstalled from the library at any time.", + "operationId": "uninstallTransactionRuleLibrary", + "x-didit-sdk": { + "resource": "transactions.rules", + "method": "uninstall_library", + "auth": "api_key", + "server": true + }, + "tags": [ + "Transactions" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionRuleInstallRequest" + }, + "example": { + "bundle": "responsible_gaming" + } + } + } + }, + "responses": { + "200": { + "description": "How many presets were removed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionRuleInstallResult" + }, + "example": { + "uninstalled_count": 5 + } + } + } + }, + "400": { + "description": "Neither library_keys nor bundle provided.", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/transactions/rules/install/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"bundle\": \"responsible_gaming\"}'" + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/networks/": { + "get": { + "summary": "List networks", + "description": "List fraud networks detected within this application's organization, highest risk first by default. By default dismissed networks are excluded; pass status to include them. Sandbox applications never have networks: clustering excludes sandbox data entirely. Every response carries the organization's `sharing_mode` and whether cross-organization signals apply to it (`cross_org_available`, `cross_org_unavailable_reason`). `kpis` aggregate the networks that match your filters and `counts` aggregate every network the application can see; pass `summary=false` to skip both when you only need the page. Requires a full-access API key, or `list:networks` for a console member.", + "operationId": "list_networks", + "x-didit-sdk": { + "resource": "networks", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "status", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Comma-separated statuses to include: active,in_review,resolved,dismissed. Omit to exclude dismissed." + }, + { + "name": "scope", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "identity", + "transactions" + ] + }, + "description": "`transactions` keeps networks whose members are all transaction parties; `identity` keeps every other network. Omit for both. It also narrows `counts.views` and `counts.facets`. Any other value returns 400." + }, + { + "name": "signal_type", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Comma-separated signal types: ip_address,device,face,document_number,phone,email,address." + }, + { + "name": "pattern_type", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Comma-separated pattern types, e.g. exact_same_device,same_email." + }, + { + "name": "risk_band", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Comma-separated bands: low,medium,high." + }, + { + "name": "min_size", + "in": "query", + "schema": { + "type": "integer" + }, + "description": "Minimum member count." + }, + { + "name": "max_size", + "in": "query", + "schema": { + "type": "integer" + }, + "description": "Maximum member count." + }, + { + "name": "assignee", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Comma-separated assignees: `unassigned`, member emails (matched case-insensitively), or `me` for the calling console member. A network matches any value. `me` needs a console member token: an API key gets 400 `{\"assignee\": [\"\\\"me\\\" needs a user token; an API key has no assignee identity.\"]}`." + }, + { + "name": "priority", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Comma-separated priorities: `low`, `medium`, `high`, or `none` for networks without a priority. Any other value returns 400 `{\"priority\": \"Unknown priority: .\"}`." + }, + { + "name": "risk_min", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "description": "Minimum risk score, a whole number from 0 to 100. A value that is not a whole number returns 400 `{\"risk_min\": \"Use a whole number from 0 to 100.\"}`." + }, + { + "name": "has_case", + "in": "query", + "schema": { + "type": "boolean" + }, + "description": "`true` keeps the networks your application opened a case for, `false` the others. Any other value is ignored." + }, + { + "name": "date_from", + "in": "query", + "schema": { + "type": "string", + "format": "date" + }, + "description": "Lower bound on last_activity_at (ISO 8601 date or datetime)." + }, + { + "name": "date_to", + "in": "query", + "schema": { + "type": "string", + "format": "date" + }, + "description": "Upper bound on first_activity_at (ISO 8601 date or datetime)." + }, + { + "name": "q", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Free-text search across network name, member names, the network_id (N-0001), and exact pattern_type tokens." + }, + { + "name": "ordering", + "in": "query", + "schema": { + "type": "string" + }, + "description": "One of risk_score, -risk_score, last_activity_at, -last_activity_at, first_activity_at, -first_activity_at, network_number, -network_number. Defaults to -risk_score,-last_activity_at." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "default": 100, + "minimum": 1, + "maximum": 500 + }, + "description": "Page size, 1-500. Defaults to 100." + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0 + }, + "description": "Zero-based offset of the first record." + }, + { + "name": "summary", + "in": "query", + "schema": { + "type": "boolean", + "default": true + }, + "description": "`false` returns the page and its `count` only, without `kpis` and `counts`. Defaults to `true`. Any value other than `true` or `false` returns 400." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/?status=active,in_review&ordering=-risk_score' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Paginated network list, with the organization's sharing mode and cross-organization availability, plus `kpis` and `counts` unless `summary=false`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "integer", + "nullable": true, + "description": "Offset of the next page, or null on the last page." + }, + "previous": { + "type": "integer", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkListItem" + } + }, + "kpis": { + "type": "object", + "description": "Each key is {value, delta} over the results matching the current filters. Omitted when `summary=false`.", + "properties": { + "active": { + "type": "object" + }, + "in_review": { + "type": "object" + }, + "applicants_involved": { + "type": "object" + }, + "detected_this_period": { + "type": "object" + }, + "false_positive_rate": { + "type": "object" } - }, - { - "type": "string", - "description": "Complete single-line address (legacy)." } - ] - }, - "address_element_1": { - "type": "string", - "description": "Street address including street number and type. If omitted, derived from `address`." - }, - "address_element_2": { - "type": "string", - "description": "Apartment, unit, building, floor, or extra address line. Only send when you have it explicitly." - }, - "address_element_3": { - "type": "string", - "description": "City, suburb, district, locality, or neighborhood. If omitted, derived from `address`." - }, - "address_element_4": { - "type": "string", - "description": "State, province, region, or town. If omitted, derived from `address`." - }, - "address_element_5": { - "type": "string", - "description": "Postcode or postal code. `postal_code` is accepted as an alias." - }, - "postal_code": { - "type": "string", - "description": "Postal code for address-based services (alias of `address_element_5`)." - }, - "driver_license_number": { - "type": "string", - "description": "Driver licence number for government document-verification services (AUS, NZL, IND)." - }, - "driver_license_card_number": { - "type": "string", - "description": "Physical driver licence card number, where required separately from the licence number (AUS)." - }, - "driver_license_state": { - "type": "string", - "description": "Driver licence state or territory of issue (AUS)." - }, - "driver_license_version": { - "type": "string", - "description": "Driver licence version code (NZL)." - }, - "passport_number": { - "type": "string", - "description": "Passport number for government passport-verification services (AUS, NZL, IND, CHN)." - }, - "passport_expiration_date": { - "type": "string", - "format": "date", - "description": "Passport expiry date, `YYYY-MM-DD`, for passport-verification services." - }, - "passport_file_number": { - "type": "string", - "description": "Passport file number (IND)." - }, - "passport_issue_country": { - "type": "string", - "description": "Issuing country of the passport (ISO 3166-1 alpha-3) for passport-verification services." - }, - "medicare_card_number": { - "type": "string", - "description": "Medicare card number (AUS)." - }, - "immi_card_number": { - "type": "string", - "description": "Australian ImmiCard number." - }, - "immi_card_expiry_date": { - "type": "string", - "format": "date", - "description": "Australian ImmiCard expiry date." - }, - "citizenship_certificate_number": { - "type": "string", - "description": "Citizenship certificate number (AUS)." - }, - "birth_registration_number": { - "type": "string", - "description": "Birth-certificate registration number (AUS)." - }, - "birth_registration_date": { - "type": "string", - "format": "date", - "description": "Birth-certificate registration date (AUS)." - }, - "birth_registration_state": { - "type": "string", - "description": "Birth-certificate registration state (AUS)." - }, - "marriage_certificate_number": { - "type": "string", - "description": "Marriage certificate number (AUS)." - }, - "change_of_name_certificate_number": { - "type": "string", - "description": "Change-of-name certificate number (AUS)." - }, - "first_partner_name": { - "type": "string", - "description": "First name of the partner on a marriage certificate (AUS)." - }, - "last_partner_name": { - "type": "string", - "description": "Last name of the partner on a marriage certificate (AUS)." - }, - "voter_id": { - "type": "string", - "description": "Voter registration number (IND EPIC, IRL)." - }, - "epic_card": { - "type": "string", - "description": "India EPIC voter card number." - }, - "pan": { - "type": "string", - "description": "India PAN (Permanent Account Number)." - }, - "national_id": { - "type": "string", - "description": "National ID number (CHN, MYS, KEN, KHM, NGA, ZAF, …)." - }, - "cic": { - "type": "string", - "description": "Mexican INE/IFE Código de Identificación de Credencial (CIC), 9 digits. Used by the MEX INE credential-validity service (`mex_ine_vigencia`)." - }, - "identificador_ciudadano": { - "type": "string", - "description": "Mexican INE Identificador del Ciudadano (9 digits). Used with `cic` for modern INE models (E/F/G/H) in `mex_ine_vigencia`." - }, - "ocr": { - "type": "string", - "description": "Mexican INE OCR number (13 digits, back of card). Used with `cic` for Model D in `mex_ine_vigencia`." - }, - "voter_number": { - "type": "string", - "description": "Mexican INE Clave de Elector (18 chars). Used with `emission_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`." - }, - "emission_number": { - "type": "string", - "description": "Mexican INE Número de Emisión. Used with `voter_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`." - }, - "bvn": { - "type": "string", - "description": "Nigerian Bank Verification Number." - }, - "bank_card_number": { - "type": "string", - "description": "Bank card number (CHN bank-card verification)." - }, - "ssn": { - "type": "string", - "description": "Social Security Number (USA)." + }, + "counts": { + "$ref": "#/components/schemas/NetworkListCounts" + }, + "sharing_mode": { + "type": "string", + "enum": [ + "off", + "private", + "global" + ], + "description": "The organization's cross-organization sharing mode. Only `global` compares networks across organizations; `private` and `off` keep them to the organization's own history.", + "example": "global" + }, + "cross_org_available": { + "type": "boolean", + "description": "Whether cross-organization signals apply to this organization. They need both the Identity Enterprise plan and the `global` sharing mode.", + "example": true + }, + "cross_org_unavailable_reason": { + "type": "string", + "enum": [ + "plan", + "sharing_mode" + ], + "nullable": true, + "description": "Why cross-organization signals do not apply, checked in this order. `plan`: the organization's Identity plan does not include the cross-organization fraud network (it needs Identity Enterprise), whatever the sharing mode. `sharing_mode`: the plan includes it, but the sharing mode is `private` or `off`. Null when `cross_org_available` is `true`.", + "example": null + } }, - "phone": { - "type": "string", - "description": "Phone number for phone-verification services." + "required": [ + "count", + "next", + "previous", + "results", + "sharing_mode", + "cross_org_available", + "cross_org_unavailable_reason" + ] + } + } + } + }, + "400": { + "description": "An invalid filter, ordering, pagination or `summary` value.", + "content": { + "application/json": { + "examples": { + "Invalid summary": { + "summary": "Invalid summary", + "value": { + "summary": [ + "Must be \"true\" or \"false\"." + ] + } }, - "landline": { - "type": "string", - "description": "Landline number for phone-verification services." + "Invalid scope": { + "summary": "Invalid scope", + "value": { + "scope": [ + "Use identity or transactions." + ] + } }, - "email": { - "type": "string", - "format": "email", - "description": "Email address for identity-verification services." + "Unsupported ordering": { + "summary": "Unsupported ordering", + "value": [ + "Unsupported ordering." + ] + } + } + } + } + }, + "401": { + "description": "Missing or invalid credentials.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "Missing or invalid credentials", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + } + } + } + } + }, + "403": { + "description": "The API key belongs to another application, or it is a key with restricted access: Networks endpoints need a full-access key. A console member token needs `list:networks`. Also returned with `code: networks_not_entitled` when Networks is not enabled for the organization.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, + "examples": { + "Networks not enabled": { + "summary": "Networks not enabled", + "value": { + "detail": "Networks is disabled for this organization. Contact Didit support if you believe this is an error.", + "code": "networks_not_entitled" + } }, - "country_of_residence": { - "type": "string", - "description": "ISO 3166-1 alpha-2 country of residence. Used by global identity-enrichment services (e.g. `glb_identity_enrichment`) to focus the lookup.", - "example": "US" + "Restricted API key": { + "summary": "Restricted API key", + "value": { + "detail": "This API key has no access to this endpoint." + } }, - "partial_match_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "NO_ACTION", - "description": "What a `partial_match` does to `database_validation.status`. Defaults to `NO_ACTION` (status stays `Approved`, with a warning)." + "No permission": { + "summary": "No permission", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/": { + "get": { + "summary": "Get network", + "description": "Retrieve a single network's full detail: stats, fraud tags and risk signals, plus the organization's `sharing_mode` and cross-organization availability, your application's `tags` on the network, member `outcomes` and pattern scores (`patterns`). Requires a full-access API key, or `read:networks` for a console member.", + "operationId": "get_network", + "x-didit-sdk": { + "resource": "networks", + "method": "retrieve", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "network_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "n3a5e000-0000-4000-8000-000000000001" + }, + "description": "Network UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Network detail.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetworkDetailResponse" + }, + "example": { + "uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "network_id": "N-0042", + "name": null, + "status": "in_review", + "risk": { + "score": 82, + "band": "high", + "bar": 0.82 + }, + "pattern_types": [ + "exact_same_device", + "same_email" + ], + "signal_types": [ + "device", + "email" + ], + "member_counts": { + "total": 3, + "users": 3, + "businesses": 0, + "transaction_parties": 0 + }, + "member_status": { + "approved": 1, + "declined": 1, + "in_review": 0, + "not_completed": 1 + }, + "signal_coverage": { + "device": { + "signals": 1, + "subjects": 3, + "coverage": 1.0 + }, + "email": { + "signals": 1, + "subjects": 2, + "coverage": 0.6667 + } + }, + "first_activity_at": "2026-10-01T09:12:44Z", + "last_activity_at": "2026-10-12T16:03:10Z", + "stats": { + "members": { + "total": 3, + "users": 3, + "businesses": 0, + "transaction_parties": 0 + }, + "member_status": { + "approved": 1, + "declined": 1, + "in_review": 0, + "not_completed": 1 + }, + "signals": { + "total": 2, + "by_type": { + "device": { + "signals": 1, + "subjects": 3, + "coverage": 1.0 + }, + "email": { + "signals": 1, + "subjects": 2, + "coverage": 0.6667 + } + } + } }, - "no_match_action": { - "type": "string", - "enum": [ - "DECLINE", - "NO_ACTION" - ], - "default": "DECLINE", - "description": "What a `no_match` does to `database_validation.status`. Defaults to `DECLINE`." + "fraud_tags": [], + "risk_signals": { + "residential_proxy_reuse": { + "detected": false, + "signal_count": 0, + "max_subject_count": 0 + }, + "email_intelligence": { + "enriched_signal_count": 1, + "high_risk_count": 0, + "social_profile_count": 0 + }, + "phone_intelligence": { + "enriched_signal_count": 0, + "high_risk_count": 0 + } }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "Persist the validation as a session (console visibility, decision endpoint, `status.updated` webhook). Also changes the `validations` response shape: per-service objects when `true` (default), a merged field map when `false`." + "reviewed_at": "2026-10-12T16:30:00Z", + "reviewed_by": "reviewer@example.com", + "review_reason": "Shared device across three new accounts.", + "notes_count": 0, + "sharing_mode": "global", + "cross_org_available": true, + "cross_org_unavailable_reason": null, + "tags": [ + { + "uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7", + "name": "SUSPICIOUS", + "color": "#6366F1", + "source": "custom", + "added_by": null, + "added_at": "2026-10-13T08:15:00Z" + } + ], + "outcomes": { + "approved": 1, + "declined": 1, + "in_review": 0, + "total": 3 }, - "vendor_data": { - "type": "string", - "description": "Your identifier for the validated user; echoed back and stored with the session." + "patterns": [ + { + "type": "background", + "score": 0.8125, + "members": 3, + "reason": null, + "computed_at": "2026-10-13T08:00:00Z" + } + ] + } + } + } + }, + "404": { + "description": "Network not found (or not visible to this application).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid credentials.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "Missing or invalid credentials", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + } + } + } + } + }, + "403": { + "description": "The API key belongs to another application, or it is a key with restricted access: Networks endpoints need a full-access key. A console member token needs `read:networks`. Also returned with `code: networks_not_entitled` when Networks is not enabled for the organization.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, + "examples": { + "Networks not enabled": { + "summary": "Networks not enabled", + "value": { + "detail": "Networks is disabled for this organization. Contact Didit support if you believe this is an error.", + "code": "networks_not_entitled" + } }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Free-form JSON stored with the request and echoed back." + "Restricted API key": { + "summary": "Restricted API key", + "value": { + "detail": "This API key has no access to this endpoint." + } }, - "selfie": { - "type": "string", - "format": "binary", - "description": "Selfie image (`jpg`, `jpeg`, `png`, `webp`; max 2 MB — images are compressed to ~1.5 MB before upload to the registry) for biometric validation services, e.g. Argentina RENAPER (`arg_renaper`) and Panama SIB (`pan_cedula_sib`, `pan_cedula_sib_plus`). Required whenever a selected service lists it as a required field. Only available via `multipart/form-data`." + "No permission": { + "summary": "No permission", + "value": { + "detail": "You do not have permission to perform this action." + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/graph/": { + "get": { + "summary": "Get network graph", + "description": "Retrieve the network as a graph of subject nodes (vendor users, vendor businesses, transaction parties) and signal nodes (the shared device/IP/face/document/phone/email/address values linking them), with edges representing memberships.", + "operationId": "get_network_graph", + "x-didit-sdk": { + "resource": "networks", + "method": "retrieve_graph", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "network_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "n3a5e000-0000-4000-8000-000000000001" + }, + "description": "Network UUID." + }, + { + "name": "depth", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "1", + "2", + "3", + "all" + ] + }, + "description": "Hops from the focus subject to include. Defaults to all (unbounded)." + }, + { + "name": "focus_kind", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "vendor_user", + "vendor_business", + "transaction_party" + ] + }, + "description": "Subject type to center the graph on. Must be supplied together with focus_id; defaults to an arbitrary member when depth is bounded and neither is supplied." + }, + { + "name": "focus_id", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Subject UUID to center the graph on." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/graph/?depth=2' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Network graph.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetworkGraph" + } + } + } + }, + "404": { + "description": "Network not found (or not visible to this application).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } } } } } } }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/members/": { + "get": { + "summary": "List network members", + "description": "List the subjects (vendor users, vendor businesses, or transaction parties) that belong to this network.", + "operationId": "list_network_members", + "x-didit-sdk": { + "resource": "networks", + "method": "list_members", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "network_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "n3a5e000-0000-4000-8000-000000000001" + }, + "description": "Network UUID." + } + ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/database-validation/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"issuing_state\": \"BRA\",\n \"services\": [\"bra_cpf\"],\n \"identification_number\": \"12345678900\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"date_of_birth\": \"1980-01-01\",\n \"vendor_data\": \"user-1234\"\n }'" + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/members/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Network members. Not paginated.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkMember" + } + } + } + } + } + } + }, + "404": { + "description": "Network not found (or not visible to this application).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/signals/": { + "get": { + "summary": "List network signals", + "description": "List the signals (shared device, IP, face, document number, phone, email, or address values) that link this network's members.", + "operationId": "list_network_signals", + "x-didit-sdk": { + "resource": "networks", + "method": "list_signals", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, { - "lang": "python", - "label": "Python", - "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/database-validation/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\n \"issuing_state\": \"BRA\",\n \"services\": [\"bra_cpf\"],\n \"identification_number\": \"12345678900\",\n \"first_name\": \"John\",\n \"last_name\": \"Doe\",\n \"date_of_birth\": \"1980-01-01\",\n \"vendor_data\": \"user-1234\",\n },\n timeout=45,\n)\nresp.raise_for_status()\ndv = resp.json()[\"database_validation\"]\nprint(dv[\"status\"], dv[\"match_type\"]) # e.g. Approved full_match\n\n# Biometric services need a selfie via multipart/form-data:\n# requests.post(..., data={\"issuing_state\": \"ARG\", \"services\": '[\"arg_renaper\"]',\n# \"identification_number\": \"12345678\", \"gender\": \"M\", ...},\n# files={\"selfie\": open(\"selfie.jpg\", \"rb\")})" + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/database-validation/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n issuing_state: 'BRA',\n services: ['bra_cpf'],\n identification_number: '12345678900',\n first_name: 'John',\n last_name: 'Doe',\n date_of_birth: '1980-01-01',\n vendor_data: 'user-1234',\n }),\n});\nconst data = await res.json();\nconsole.log(data.database_validation.status, data.database_validation.match_type);" + "name": "network_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "n3a5e000-0000-4000-8000-000000000001" + }, + "description": "Network UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/signals/' \\\n -H 'x-api-key: YOUR_API_KEY'" } ], "responses": { "200": { - "description": "Validation completed — at least one selected service returned a usable result. Inspect `database_validation.match_type` for the aggregate outcome, `validations` for the per-service field comparisons and `outcome_code`s, and `errors` (when present) for services that failed.", + "description": "Network signals. Not paginated.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID." - }, - "database_validation": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined", - "In Review" - ], - "description": "`Declined` when the overall `match_type` triggers a `DECLINE` action (`no_match_action` defaults to `DECLINE`, `partial_match_action` to `NO_ACTION`); `In Review` when no service produced a usable comparison, for example when the only selected service came back `REGISTRY_UNAVAILABLE` or `INCONCLUSIVE`; `Approved` otherwise. A request that selected several services can be `Approved` on one service's match while another was unavailable, so check `errors` and the per-service `outcome_code`s too." - }, - "issuing_state": { - "type": "string", - "description": "Echo of the ISO 3166-1 alpha-3 country validated against.", - "example": "BRA" - }, - "validation_type": { - "type": "string", - "enum": [ - "one_by_one", - "two_by_two" - ], - "description": "Derived from the results: `two_by_two` when ≥2 distinct services produced a full match (on the identification number, on full name + date of birth together, or on the address without contradictions); `one_by_one` otherwise. (The request field of the same name is deprecated and ignored.)" - }, - "screened_data": { - "type": "object", - "description": "Echo of the input fields that were sent to the selected services (dates normalized to `YYYY-MM-DD`; a `selfie`, when sent, is returned as a pre-signed URL; `consent_obtained: true` is added when consent was given)." - }, - "match_type": { - "type": "string", - "nullable": true, - "enum": [ - "full_match", - "partial_match", - "no_match" - ], - "description": "Overall result aggregated across services with field-level comparisons: any `full_match` → `full_match`; else any `partial_match` → `partial_match`; else `no_match`. Outcome-only services, such as biometric criminal screening, can return `null`; inspect each item in `validations` for its `outcome_code`." - }, - "validations": { - "description": "Shape depends on `save_api_request`. **`true` (default):** an array of per-service objects (`service_id`, `service_name`, `validation`, `outcome_code`, `outcome_detail`, `source_data`). **`false`:** a single flat object merging the field-level results of all services (`{\"full_name\": \"full_match\", ...}`).", - "oneOf": [ - { - "type": "array", - "items": { - "type": "object", - "description": "Per-service validation result (returned when `save_api_request=true`, the default).", - "properties": { - "service_id": { - "type": "string", - "description": "Catalog id of the service that produced this result (e.g. `bra_cpf`).", - "example": "bra_cpf" - }, - "service_name": { - "type": "string", - "description": "Human-readable catalog name of the service.", - "example": "Brazil - CPF status check" - }, - "validation": { - "type": "object", - "description": "Field-by-field comparison between your input and the registry record. Keys are canonical field names (`full_name`, `date_of_birth`, `identification_number`, `address`, …); values are `full_match`, `partial_match`, or `no_match`.", - "additionalProperties": { - "type": "string", - "enum": [ - "full_match", - "partial_match", - "no_match" - ] - } - }, - "outcome_code": { - "type": "string", - "enum": [ - "MATCH", - "PARTIAL_MATCH", - "NO_MATCH", - "DOCUMENT_NOT_FOUND", - "INVALID_DOCUMENT_FORMAT", - "INVALID_INPUT", - "MINOR_BLOCKED", - "DECEASED", - "BIOMETRIC_NO_MATCH", - "BIOMETRIC_IMAGE_UNUSABLE", - "INCONCLUSIVE", - "DOCUMENT_SUPERSEDED", - "REGISTRY_UNAVAILABLE", - "REGISTRY_ERROR" - ], - "description": "Vendor-neutral outcome of the registry lookup. `MATCH`/`PARTIAL_MATCH`/`NO_MATCH` describe the comparison; `DOCUMENT_NOT_FOUND` means the identifier is not in the registry; `INCONCLUSIVE` means the registry could not confirm either way (routed to review — NOT a no-match); `BIOMETRIC_NO_MATCH` is a definitive face mismatch while `BIOMETRIC_IMAGE_UNUSABLE` is a technical image problem (retake the selfie); `DECEASED`/`MINOR_BLOCKED` are registry-policy outcomes; `DOCUMENT_SUPERSEDED` means the identity matched but the edition of the document presented is not the one on file (a newer edition has been issued); `INVALID_DOCUMENT_FORMAT`/`INVALID_INPUT` mean the registry itself rejected the data it was sent; `REGISTRY_UNAVAILABLE`/`REGISTRY_ERROR` indicate upstream failures." - }, - "outcome_detail": { - "type": "string", - "nullable": true, - "description": "Raw upstream status detail backing the outcome code (e.g. an HTTP or registry status code)." - }, - "source_data": { - "type": "object", - "nullable": true, - "description": "Cleaned, normalized view of the registry record using canonical field names (`identification_number`, `first_name`, `last_name`, `date_of_birth`, plus registry-specific flags). Image fields, when present, are returned as pre-signed URLs." - } - } - } - }, - { - "type": "object", - "additionalProperties": { - "type": "string", - "enum": [ - "full_match", - "partial_match", - "no_match" - ] - }, - "description": "Merged field-level results (stateless `save_api_request=false` shape)." - } - ] - }, - "warnings": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "description": "Feature that raised the warning." - }, - "risk": { - "type": "string", - "description": "Machine-readable risk identifier." - }, - "additional_data": { - "type": "object", - "nullable": true, - "description": "Extra context for the warning, when available." - }, - "log_type": { - "type": "string", - "description": "`warning`, `information`, or `error` — how the risk affected the outcome." - }, - "short_description": { - "type": "string", - "description": "One-line human-readable summary." - }, - "long_description": { - "type": "string", - "description": "Full human-readable explanation." - } - } - }, - "description": "`DATABASE_VALIDATION_NO_MATCH` or `DATABASE_VALIDATION_PARTIAL_MATCH` when applicable; empty on a full match." - }, - "errors": { - "type": "array", - "description": "Present only when some (but not all) selected services failed. Each entry carries `service_id`, `code`, `message`, and `retryable`. A service reported here is absent from `validations` and `services_used`, and is not billed. This array is the only place it appears, so inspect it even when `status` is `Approved`.", - "items": { - "type": "object", - "properties": { - "service_id": { - "type": "string" - }, - "code": { - "type": "string", - "example": "empty_provider_response" - }, - "message": { - "type": "string" - }, - "retryable": { - "type": "boolean", - "description": "`true` when the service produced no answer and retrying it later can succeed; `false` when the source refused the data it was sent and the input must be corrected first.", - "example": true - } - } - } - }, - "services_used": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Catalog ids of the services that returned a billable result. **Only present when the request explicitly sent `services`.**" - }, - "match_score": { - "type": "number", - "description": "Fraction (0.0–1.0) of attempted services that produced a full match (on the identification number, on full name + date of birth together, or on the address without contradictions). **Only present when the request explicitly sent `services`.**" - } + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkSignal" } + } + } + } + } + } + }, + "404": { + "description": "Network not found (or not visible to this application).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/timeline/": { + "get": { + "summary": "Get network timeline", + "description": "Without `contract`: the network's own events (detection, each signal's latest observation and its review), oldest first and not paginated. `limit`, `cursor` and `event_type` are ignored. With `contract=2`: the typed timeline, newest first and paginated by cursor. It adds what the member accounts did (verification attempts, transactions, sign-ins you post through the events API and the live rules they hit) and the cases opened for the network. `note_added` events are returned to console members only, never to an API key. Requires a full-access API key, or `read:networks` for a console member.", + "operationId": "get_network_timeline", + "x-didit-sdk": { + "resource": "networks", + "method": "retrieve_timeline", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "network_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "n3a5e000-0000-4000-8000-000000000001" + }, + "description": "Network UUID." + }, + { + "name": "contract", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "2" + ] + }, + "description": "`2` returns the typed timeline. Omit it for the default timeline. Any other value returns 400." + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 500, + "default": 100 + }, + "description": "Typed timeline only. Events per page, 1 to 500. Defaults to 100." + }, + { + "name": "cursor", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Typed timeline only. The previous page's `next_cursor`. An invalid or empty cursor returns 400." + }, + { + "name": "event_type", + "in": "query", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "array", + "maxItems": 20, + "items": { + "type": "string", + "enum": [ + "network_detected", + "signal_observed", + "network_active", + "network_in_review", + "network_resolved", + "network_dismissed", + "verification_attempt", + "transaction", + "login", + "rule_hit", + "case_linked", + "note_added" + ] + } + }, + "description": "Typed timeline only. Event types to keep, repeated or comma-separated, at most 20 values. Defaults to every type." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/timeline/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "curl", + "label": "curl (typed timeline)", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/3fa85f64-5717-4562-b3fc-2c963f66afa6/timeline/?contract=2&limit=50&event_type=verification_attempt,login' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Without `contract`: `{results}`, oldest first, not paginated. With `contract=2`: `{results, next_cursor}`, newest first.", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/NetworkTimeline" }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Echo of the `metadata` you sent." - }, - "created_at": { - "type": "string", - "format": "date-time" + { + "$ref": "#/components/schemas/NetworkTimelinePage" } - } + ] }, "examples": { - "Full match (Approved)": { - "summary": "Default save_api_request=true — per-service `validations` objects", + "Default timeline": { + "summary": "Without contract", "value": { - "request_id": "7f9a1c0e-1f2b-4f6e-9d3a-2b1c0e7f9a1c", - "database_validation": { - "status": "Approved", - "issuing_state": "BRA", - "validation_type": "one_by_one", - "screened_data": { - "tax_number": "12345678900", - "first_name": "John", - "last_name": "Doe", - "date_of_birth": "1980-01-01" - }, - "match_type": "full_match", - "validations": [ - { - "validation": { - "full_name": "full_match", - "date_of_birth": "full_match", - "identification_number": "full_match" - }, - "outcome_code": "MATCH", - "outcome_detail": "200", - "service_id": "bra_cpf", - "service_name": "Brazil - CPF status check", - "source_data": { - "identification_number": "12345678900", - "first_name": "JOHN", - "last_name": "DOE", - "date_of_birth": "1980-01-01", - "lgpd_minor": false, - "minor_under_16": false, - "minor_under_18": false - } + "results": [ + { + "event_type": "network_detected", + "occurred_at": "2026-10-01T09:12:44Z", + "label": "Network detected", + "metadata": { + "network_id": "N-0042" } - ], - "warnings": [] - }, - "vendor_data": "user-1234", - "metadata": null, - "created_at": "2026-06-11T10:30:00.000000+00:00" - } - }, - "Explicit services (extended shape)": { - "summary": "Request sent `services` — response adds `services_used` and `match_score`", - "value": { - "request_id": "b3e7c1a2-9d4f-4f0b-8a6c-5e2d1f0a9b3e", - "database_validation": { - "status": "Approved", - "issuing_state": "BRA", - "validation_type": "one_by_one", - "screened_data": { - "tax_number": "12345678900", - "first_name": "John", - "last_name": "Doe", - "date_of_birth": "1980-01-01" }, - "match_type": "full_match", - "validations": [ - { - "validation": { - "full_name": "full_match", - "date_of_birth": "full_match", - "identification_number": "full_match" - }, - "outcome_code": "MATCH", - "outcome_detail": "200", - "service_id": "bra_cpf", - "service_name": "Brazil - CPF status check", - "source_data": { - "identification_number": "12345678900", - "first_name": "JOHN", - "last_name": "DOE", - "date_of_birth": "1980-01-01", - "lgpd_minor": false, - "minor_under_16": false, - "minor_under_18": false - } + { + "event_type": "signal_observed", + "occurred_at": "2026-10-12T16:03:10Z", + "label": "device observed", + "metadata": { + "signal_type": "device", + "subject_count": 3, + "is_hub": false } - ], - "warnings": [], - "services_used": [ - "bra_cpf" - ], - "match_score": 1.0 - }, - "vendor_data": "user-1234", - "metadata": null, - "created_at": "2026-06-11T10:30:00.000000+00:00" - } - }, - "No match (Declined)": { - "summary": "Registry record does not match — default `no_match_action=DECLINE`", - "value": { - "request_id": "0a1b2c3d-4e5f-6789-abcd-ef0123456789", - "database_validation": { - "status": "Declined", - "issuing_state": "BRA", - "validation_type": "one_by_one", - "screened_data": { - "tax_number": "12345678900", - "first_name": "John", - "last_name": "Doe", - "date_of_birth": "1980-01-01" }, - "match_type": "no_match", - "validations": [ - { - "validation": { - "full_name": "no_match", - "date_of_birth": "no_match", - "identification_number": "no_match" - }, - "outcome_code": "NO_MATCH", - "outcome_detail": "200", - "service_id": "bra_cpf", - "service_name": "Brazil - CPF status check", - "source_data": { - "identification_number": "00987654321", - "first_name": "TOTALLY", - "last_name": "DIFFERENT NAME", - "date_of_birth": "1999-01-01", - "lgpd_minor": false, - "minor_under_16": false, - "minor_under_18": false - } - } - ], - "warnings": [ - { - "feature": "DATABASE_VALIDATION", - "risk": "DATABASE_VALIDATION_NO_MATCH", - "additional_data": null, - "log_type": "error", - "short_description": "Database validation no match", - "long_description": "The system identified a no match in the database validation, requiring further investigation." + { + "event_type": "network_in_review", + "occurred_at": "2026-10-12T16:30:00Z", + "label": "Network in_review", + "metadata": { + "reason": "Shared device across three new accounts." } - ] - }, - "vendor_data": null, - "metadata": null, - "created_at": "2026-06-11T10:31:00.000000+00:00" + } + ] } }, - "Stateless (save_api_request=false)": { - "summary": "`validations` collapses to a merged field map; nothing is stored", + "Typed timeline": { + "summary": "With contract=2", "value": { - "request_id": "c4d5e6f7-8901-2345-6789-abcdef012345", - "database_validation": { - "status": "Approved", - "issuing_state": "BRA", - "validation_type": "one_by_one", - "screened_data": { - "tax_number": "12345678900", - "first_name": "John", - "last_name": "Doe", - "date_of_birth": "1980-01-01" + "results": [ + { + "id": "monitoring_event:9b2f1c3e-4d5a-4b6c-8d7e-0f1a2b3c4d5e", + "event_type": "login", + "occurred_at": "2026-10-12T18:41:02Z", + "ingested_at": "2026-10-12T18:41:03Z", + "label": "Login", + "metadata": {}, + "account": { + "node_id": "vendor_user:5f6e7d8c-9b0a-4c1d-8e2f-3a4b5c6d7e8f", + "node_type": "account", + "uuid": "5f6e7d8c-9b0a-4c1d-8e2f-3a4b5c6d7e8f", + "vendor_data": "user-1042", + "display_name": "Alex Example", + "ownership": "owned", + "route": { + "kind": "user", + "id": "5f6e7d8c-9b0a-4c1d-8e2f-3a4b5c6d7e8f", + "application_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + } + }, + "source_ref": { + "kind": "monitoring_event", + "id": "9b2f1c3e-4d5a-4b6c-8d7e-0f1a2b3c4d5e", + "ownership": "owned" + }, + "verification_attempt": null, + "transaction": null, + "login": { + "action": "auth.login", + "succeeded": true, + "status": "APPROVED", + "ip": { + "address": "203.0.113.24", + "country_code": "ES", + "city": "Madrid", + "connection_type": null, + "is_vpn_or_tor": false, + "is_data_center": false + }, + "device": { + "fingerprint": "fp_0a1b2c3d", + "label": "Chrome · Android" + }, + "is_first_login": false, + "is_new_device": true + }, + "rule_hit": null, + "case_linked": null, + "note_added": null }, - "match_type": "full_match", - "validations": { - "full_name": "full_match", - "date_of_birth": "full_match", - "identification_number": "full_match" + { + "id": "session:1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f", + "event_type": "verification_attempt", + "occurred_at": "2026-10-12T16:03:10Z", + "ingested_at": "2026-10-12T16:03:10Z", + "label": "Verification attempt 2 of 2", + "metadata": {}, + "account": { + "node_id": "vendor_user:5f6e7d8c-9b0a-4c1d-8e2f-3a4b5c6d7e8f", + "node_type": "account", + "uuid": "5f6e7d8c-9b0a-4c1d-8e2f-3a4b5c6d7e8f", + "vendor_data": "user-1042", + "display_name": "Alex Example", + "ownership": "owned", + "route": { + "kind": "user", + "id": "5f6e7d8c-9b0a-4c1d-8e2f-3a4b5c6d7e8f", + "application_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + } + }, + "source_ref": { + "kind": "session", + "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f", + "ownership": "owned", + "route": { + "kind": "session", + "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f", + "application_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + } + }, + "verification_attempt": { + "session_id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f", + "session_number": 1287, + "attempt": 2, + "attempt_count": 2, + "status": "Approved", + "status_changed_at": "2026-10-12T16:05:41Z", + "decline_reasons": [] + }, + "transaction": null, + "login": null, + "rule_hit": null, + "case_linked": null, + "note_added": null }, - "warnings": [] - }, - "vendor_data": null, - "metadata": null, - "created_at": "2026-06-11T10:32:00.000000+00:00" + { + "id": "signal_observed:8d7c6b5a-4e3f-4a2b-9c1d-0e9f8a7b6c5d", + "event_type": "signal_observed", + "occurred_at": "2026-10-12T16:03:10Z", + "ingested_at": "2026-10-01T09:12:44Z", + "label": "device observed", + "metadata": { + "signal_type": "device", + "subject_count": 3, + "is_hub": false + }, + "account": null, + "source_ref": null, + "verification_attempt": null, + "transaction": null, + "login": null, + "rule_hit": null, + "case_linked": null, + "note_added": null + } + ], + "next_cursor": "eyJ0IjoiMjAyNi0xMC0xMlQxNjowMzoxMCswMDowMCIsImMiOiIyMDI2LTEwLTAxVDA5OjEyOjQ0KzAwOjAwIiwiciI6MSwidSI6IjhkN2M2YjVhLTRlM2YtNGEyYi05YzFkLTBlOWY4YTdiNmM1ZCJ9" } } } @@ -20729,183 +42593,384 @@ } }, "400": { - "description": "Validation error - nothing is billed. Preflight field errors occur before any external call. If every unanswered attempt explicitly refuses the input, the response instead includes `validation_errors` with `code: provider_rejected_input` and `retryable: false`; correct the input before retrying. Field-level problems use DRF's standard envelope; catalog problems return a `services` array (plus `requires_onboarding` or `services_requiring_consent` when relevant); missing-field errors include `fields_required_by` mapping each missing field to the services that demand it; services that need at least one extra identifier (e.g. `usa_states_credit_bureau`, which cannot verify a name alone) return `any_field_required_by` mapping each service to the fields of which at least one must be present; format problems checked per service return `invalid_fields_by_service`.", + "description": "With `contract` present: an invalid `contract`, `limit`, `cursor` or `event_type` value.", "content": { "application/json": { "examples": { - "Invalid issuing_state": { - "summary": "Country not supported — message lists every valid alpha-3 code", + "Unsupported contract": { + "summary": "Unsupported contract", "value": { - "error": [ - "Invalid issuing state. Valid options are: ARE, ARG, AUS, AUT, BEL, BGD, BOL, BRA, CAN, CHE, CHL, CHN, CIV, COL, CRI, CZE, DEU, DNK, DOM, ECU, ESP, FIN, FRA, GBR, GHA, GLB, GRC, GTM, HKG, HND, IDN, IND, IRL, ITA, KEN, KHM, MAR, MEX, MYS, NGA, NLD, NOR, NZL, OMN, PAN, PER, PHL, POL, PRT, PRY, QAT, SGP, SLV, SVK, SWE, THA, UGA, URY, USA, VEN, ZAF, ZMB, ZWE" + "contract": [ + "\"3\" is not a valid choice." ] } }, - "Missing required fields": { - "summary": "Union of required fields across selected services, with the universal-field hint", - "value": { - "error": [ - "Missing required fields: tax_number (or use 'identification_number' which maps to 'tax_number' for BRA)" - ], - "fields_required_by": { - "tax_number": [ - "bra_cpf" - ] - } - } - }, - "Unknown service id": { - "summary": "`services` contains an id that is not in the catalog", + "Invalid cursor": { + "summary": "Invalid cursor", "value": { - "services": [ - "Unknown service_id(s): ['bogus_service']" + "cursor": [ + "This cursor is not valid. Reload the feed." ] } }, - "Service not live for country": { - "summary": "`services` names a service from another country (or a stub entry)", + "Limit too large": { + "summary": "Limit too large", "value": { - "services": [ - "Services not currently live for PER: ['bra_cpf']. Available services: ['per_dni', 'per_residential', 'per_tax_registration']" + "limit": [ + "Ensure this value is less than or equal to 500." ] } }, - "Service requires onboarding": { - "summary": "Service exists but is not activated for your organization yet", + "Unknown event type": { + "summary": "Unknown event type", "value": { - "services": [ - "Services require onboarding before they can be enabled for AUS: ['aus_australia_driver_licence']. Contact Didit support to activate them." - ], - "requires_onboarding": [ - "aus_australia_driver_licence" - ] + "event_type": { + "0": [ + "\"logout\" is not a valid choice." + ] + } } - }, - "Consent missing": { - "summary": "A selected service is flagged requires_consent=true and `consent` was not `true`", + } + } + } + } + }, + "401": { + "description": "Missing or invalid credentials.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "Missing or invalid credentials", "value": { - "consent": [ - "Explicit end-user consent is required for these database validation services: ['chn_national_id']. Send `consent=true`." - ], - "services_requiring_consent": [ - "chn_national_id" - ] + "detail": "Authentication credentials were not provided or are invalid." } - }, - "Country format rule violation": { - "summary": "Identifier fails the country's format rule (length/digits/regex)", + } + } + } + } + }, + "403": { + "description": "The API key belongs to another application, or it is a key with restricted access: Networks endpoints need a full-access key. A console member token needs `read:networks`. Also returned with `code: networks_not_entitled` when Networks is not enabled for the organization.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, + "examples": { + "Networks not enabled": { + "summary": "Networks not enabled", "value": { - "tax_number": [ - "Invalid tax number for BRA: Brazilian CPF (exactly 11 digits). Must be exactly 11 characters long (got 10)." - ] + "detail": "Networks is disabled for this organization. Contact Didit support if you believe this is an error.", + "code": "networks_not_entitled" } }, - "Service-specific format violation": { - "summary": "A field fails the selected service's own format requirements", + "Restricted API key": { + "summary": "Restricted API key", "value": { - "error": [ - "One or more fields do not match the format required by the selected service." - ], - "invalid_fields_by_service": { - "chn_national_id": [ - "national_id_format" - ] - } + "detail": "This API key has no access to this endpoint." } }, - "Input refused by every attempt": { - "summary": "Correct the input before retrying", + "No permission": { + "summary": "No permission", "value": { - "error": "The source refused to run this query with the supplied input.", - "validation_errors": [ - { - "service_id": "bra_cpf", - "code": "provider_rejected_input", - "message": "The source refused to run this query with the supplied input.", - "retryable": false - } - ] + "detail": "You do not have permission to perform this action." } } - }, + } + } + } + }, + "404": { + "description": "Network not found (or not visible to this application).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/map/": { + "get": { + "summary": "Get network map", + "description": "Geographic points where this network's members' addresses/locations converge, sourced from the sessions, business sessions, and transactions backing its memberships.", + "operationId": "get_network_map", + "x-didit-sdk": { + "resource": "networks", + "method": "retrieve_map", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "network_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "n3a5e000-0000-4000-8000-000000000001" + }, + "description": "Network UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/map/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Network map.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetworkMap" + } + } + } + }, + "404": { + "description": "Network not found (or not visible to this application).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/sessions/{session_id}/networks/": { + "get": { + "summary": "List networks for a session", + "description": "List the networks the session's vendor user belongs to. Resolves through the session's vendor_data-linked user.", + "operationId": "list_session_network_memberships", + "x-didit-sdk": { + "resource": "sessions", + "method": "list_networks", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Session UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/sessions/aaaaaaaa-0000-4000-8000-000000000001/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Networks this entity belongs to. Not paginated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EntityNetworkMembershipList" + } + } + } + }, + "404": { + "description": "Entity not found in this application, or has no linked subject to look up.", + "content": { + "application/json": { "schema": { "type": "object", - "additionalProperties": true, "properties": { - "error": { - "description": "Request-level explanation or preflight field errors.", - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "validation_errors": { - "type": "array", - "description": "Present when every unanswered attempt refused the input after preflight.", - "items": { - "type": "object", - "properties": { - "service_id": { - "type": "string" - }, - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "retryable": { - "type": "boolean", - "description": "Whether retrying later can resolve this service failure. False means correct the supplied input first." - } - } - } + "detail": { + "type": "string" } } } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/business-sessions/{business_session_id}/networks/": { + "get": { + "summary": "List networks for a business session", + "description": "List the networks the business session's vendor business belongs to.", + "operationId": "list_business_session_network_memberships", + "x-didit-sdk": { + "resource": "business_sessions", + "method": "list_networks", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "business_session_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Business session UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/business-sessions/aaaaaaaa-0000-4000-8000-000000000002/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Networks this entity belongs to. Not paginated.", "content": { "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" - } - } + "schema": { + "$ref": "#/components/schemas/EntityNetworkMembershipList" } } } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "404": { + "description": "Entity not found in this application, or has no linked subject to look up.", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { "type": "object", "properties": { @@ -20916,451 +42981,701 @@ } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/users/{vendor_user_uuid}/networks/": { + "get": { + "summary": "List networks for a user", + "description": "List the networks a vendor user belongs to.", + "operationId": "list_user_network_memberships", + "x-didit-sdk": { + "resource": "users", + "method": "list_networks", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "502": { - "description": "No selected service returned a usable result. Inspect each validation_errors entry before retrying; HTTP 502 alone does not establish a transient failure. An empty_provider_response with retryable: true indicates unavailable or unexplained results, including a refused primary followed by an unavailable fallback. Legacy provider_invalid_input and provider_validation_error entries instead require corrected input and may omit retryable. Nothing is billed. When save_api_request=true, the failed attempt is recorded as a session with status Not Finished. Partial failures return 200 with database_validation.errors.", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "vendor_user_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Vendor user UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/users/aaaaaaaa-0000-4000-8000-000000000003/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Networks this entity belongs to. Not paginated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EntityNetworkMembershipList" + } + } + } + }, + "404": { + "description": "Entity not found in this application, or has no linked subject to look up.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "error": { + "detail": { "type": "string" - }, - "validation_errors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "service_id": { - "type": "string" - }, - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "retryable": { - "type": "boolean", - "description": "Whether retrying later can resolve this service failure. False means correct the supplied input first." - } - } - } - } - } - }, - "examples": { - "Provider returned no result": { - "summary": "Every selected service failed", - "value": { - "error": "Database validation could not be performed. The external validation service did not return a result. Please try again later.", - "validation_errors": [ - { - "service_id": "bra_cpf", - "code": "empty_provider_response", - "message": "The provider did not return a database validation result.", - "retryable": true - } - ] } } } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/phone/send/": { - "post": { - "summary": "Send Phone Code", - "description": "Send a one-time passcode (OTP) to a phone number over WhatsApp, SMS, or another supported channel, then verify it with [`POST /v3/phone/check/`](/standalone-apis/phone-check).\n\n**How send and check pair up.** Verification state is keyed by your application plus the E.164 `phone_number` — 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 number and the code the user received before the window closes. Calling send again for the same number while a verification is pending re-sends a code for that same verification (`status: \"Retry\"`, same `request_id`). 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.\n\n**Delivery channels.** `options.preferred_channel` selects the channel (`whatsapp` by default; `sms`, `telegram`, `voice`, `rcs`, `viber`, and `zalo` are also supported). When the preferred channel is unavailable for the destination country or number, delivery automatically falls back to SMS — the send still reports `Success`, and the channel actually used is reported by the check response (`phone.verification_method` and the `lifecycle` events).\n\n**Branding.** SMS codes carry your application's SMS sender name, set in the Business Console, at no extra cost. Pass `options.sms_sender_name` to sign one code with another name, or `options.use_white_label_customization: true` to fall back to your white-label public name when no sender name is set. WhatsApp codes use a fixed message and are not branded.\n\n**Send statuses.** `Success` — OTP sent for a new verification; `Retry` — OTP re-sent for the pending verification; `Blocked` — the anti-fraud layer refused to send (see `reason`, e.g. `repeated_attempts`, `suspicious`, `spam`). A `Blocked` send immediately finalizes the verification as `Declined` with a high-risk warning, so a follow-up check returns `Expired or Not Found`.\n\n**Billing.** The price depends on the destination country and the channel that actually delivers the message — see [Phone Verification Pricing](/getting-started/phone-verification-pricing). Only `Blocked` sends are guaranteed free: any other unbilled send attempt is charged at verification finalization or expiration, even if delivery was never confirmed or the delivery provider reported the message undeliverable. Checks are always free. As an anti-abuse measure, phone verification is disabled until your organization's first top-up (`403`).\n\n**Session persistence.** Every new verification is persisted as an API-type session: `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, the verification appears in the Business Console, and `status.updated` webhooks fire as it progresses.\n\n**Sandbox.** Sandbox API keys skip delivery and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Success` payload with a random `request_id`; no message is sent and nothing is persisted. Use code `123456` on the sandbox check.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limits.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints, plus an anti-abuse cap of 4 send attempts per phone number per hour; exceeding either returns `429`.", - "operationId": "post_v3phone_send", + "/v3/organization/{organization_id}/application/{application_id}/businesses/{vendor_business_uuid}/networks/": { + "get": { + "summary": "List networks for a business", + "description": "List the networks a vendor business belongs to.", + "operationId": "list_business_network_memberships", + "x-didit-sdk": { + "resource": "businesses", + "method": "list_networks", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Standalone APIs" + "Networks" ], - "x-codeSamples": [ + "parameters": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/phone/send/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"phone_number\": \"+14155552671\",\n \"options\": {\n \"code_size\": 6,\n \"preferred_channel\": \"whatsapp\",\n \"locale\": \"en-US\"\n },\n \"vendor_data\": \"user-1234\"\n }'" + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, { - "lang": "python", - "label": "Python", - "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/phone/send/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"phone_number\": \"+14155552671\",\n \"options\": {\n \"code_size\": 6,\n \"preferred_channel\": \"whatsapp\",\n \"locale\": \"en-US\",\n },\n \"vendor_data\": \"user-1234\",\n },\n timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json()) # {request_id, status, reason, vendor_data, metadata}\n# Then verify with POST /v3/phone/check/ using the same phone_number" + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/phone/send/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n phone_number: '+14155552671',\n options: { code_size: 6, preferred_channel: 'whatsapp', locale: 'en-US' },\n vendor_data: 'user-1234',\n }),\n});\nif (!res.ok) throw new Error(`Phone send failed: ${res.status}`);\nconst data = await res.json();\nconsole.log(data); // { request_id, status, reason, vendor_data, metadata }\n// Then verify with POST /v3/phone/check/ using the same phone_number" + "name": "vendor_business_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Vendor business UUID." } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "phone_number": { - "type": "string", - "maxLength": 20, - "description": "Recipient phone number in E.164 format — leading `+` and country code (e.g., `+14155552671`). Numbers are validated and normalized to E.164; unparseable or invalid numbers return `400` with a `phone_number` field error.", - "example": "+14155552671" - }, - "options": { - "type": "object", - "description": "OTP delivery options. All fields are optional.", - "properties": { - "code_size": { - "type": "integer", - "minimum": 4, - "maximum": 8, - "default": 6, - "description": "Number of digits in the OTP." - }, - "locale": { - "type": "string", - "maxLength": 5, - "pattern": "^[a-z]{2,3}(-[A-Z]{2,3})?$", - "description": "BCP-47 locale used to localize the verification message (e.g., `en-US`, `fr`, `es`). Invalid formats return `400`.", - "example": "en-US" - }, - "preferred_channel": { - "type": "string", - "enum": [ - "whatsapp", - "sms", - "telegram", - "voice", - "rcs", - "viber", - "zalo" - ], - "default": "whatsapp", - "description": "Preferred delivery channel. If the channel is unavailable for the destination country or number, the message automatically falls back to SMS — the send still reports `Success`." - }, - "use_white_label_customization": { - "type": "boolean", - "default": false, - "description": "When `true`, the send is white-labelled: if your application has no SMS sender name, the SMS names your white-label public name instead (\"123456 is your verification code for Acme Bank\"). Your SMS sender name, when set, is used with or without this flag. Applies to SMS messages, including an SMS fallback from another channel; WhatsApp codes are sent with a fixed message and are not branded. Defaults to `false`." - }, - "sms_sender_name": { - "type": "string", - "nullable": true, - "maxLength": 30, - "example": "Acme Bank", - "description": "The name this code's SMS carries (\"123456 is your verification code for Acme Bank\"), instead of your application's SMS sender name. Free. 1 to 30 characters: letters, digits, spaces and ' & - ! only, not only digits, with single spaces between words; any other value returns `400` and no code is sent. Omitted, `null` or blank, your application's SMS sender name is used. Each application can start using up to two new names per day (UTC); past that, the code is sent with your application's name. Applies to SMS messages, including an SMS fallback from another channel; WhatsApp codes are not branded.", - "pattern": "^$|^(?![\\d ]+$)[\\p{L}\\p{N}'&!-]+( [\\p{L}\\p{N}'&!-]+)*$" - } - } - }, - "signals": { - "type": "object", - "description": "Optional device and network signals about the end user, forwarded to the anti-fraud layer to improve detection of abusive or automated traffic. All fields are optional.", - "properties": { - "ip": { - "type": "string", - "description": "IP address of the end user's device. IPv4 or IPv6.", - "example": "192.0.2.1" - }, - "device_id": { - "type": "string", - "maxLength": 255, - "description": "Unique identifier of the end user's device. Android: `ANDROID_ID`; iOS: `identifierForVendor`.", - "example": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2" - }, - "device_platform": { - "type": "string", - "enum": [ - "android", - "ios", - "ipados", - "tvos", - "web" - ], - "description": "Platform of the end user's device." - }, - "device_model": { - "type": "string", - "maxLength": 255, - "description": "Model of the end user's device.", - "example": "iPhone17,2" - }, - "os_version": { - "type": "string", - "maxLength": 64, - "description": "Operating-system version of the end user's device.", - "example": "18.0.1" - }, - "app_version": { - "type": "string", - "maxLength": 64, - "description": "Version of your application.", - "example": "1.2.34" - }, - "user_agent": { - "type": "string", - "maxLength": 512, - "description": "User agent of the end user's browser or app.", - "example": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1" - } - } - }, - "vendor_data": { - "type": "string", - "description": "Optional caller-controlled identifier (your internal user id, an email, a UUID, etc.) persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console. Use it to correlate Didit's `request_id` with your user record." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Optional free-form JSON object persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console." - } - }, - "required": [ - "phone_number" - ] - }, - "examples": { - "WhatsApp (default)": { - "summary": "6-digit OTP via WhatsApp, falling back to SMS automatically", - "value": { - "phone_number": "+14155552671", - "options": { - "preferred_channel": "whatsapp", - "locale": "en-US" - }, - "vendor_data": "user-1234" - } - }, - "SMS with custom code length": { - "summary": "Force SMS delivery with a 4-digit code", - "value": { - "phone_number": "+34699999999", - "options": { - "preferred_channel": "sms", - "code_size": 4, - "locale": "es" + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/businesses/aaaaaaaa-0000-4000-8000-000000000004/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Networks this entity belongs to. Not paginated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EntityNetworkMembershipList" + } + } + } + }, + "404": { + "description": "Entity not found in this application, or has no linked subject to look up.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" } } - }, - "With fraud signals": { - "summary": "Forward device signals to strengthen anti-fraud screening", - "value": { - "phone_number": "+14155552671", - "signals": { - "ip": "192.0.2.1", - "device_id": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2", - "device_platform": "ios", - "device_model": "iPhone17,2", - "os_version": "18.0.1", - "app_version": "1.2.34" - }, - "vendor_data": "user-1234" - } } } } } }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/networks/": { + "get": { + "summary": "List networks for a transaction", + "description": "List the networks the transaction's backing KYC/KYB subject belongs to (resolved through the transaction's session or business session).", + "operationId": "list_transaction_network_memberships", + "x-didit-sdk": { + "resource": "transactions", + "method": "list_networks", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "transaction_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Transaction UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/transactions/aaaaaaaa-0000-4000-8000-000000000005/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], "responses": { "200": { - "description": "Send acknowledged. Inspect `status`: `Success` and `Retry` mean a code is on its way; `Blocked` means the anti-fraud layer refused and the verification is already finalized as `Declined`. `request_id` is the persisted session id (same id on a `Retry`).", + "description": "Networks this entity belongs to. Not paginated.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PhoneVerificationSendResponse" - }, - "examples": { - "Success": { - "summary": "OTP sent for a new verification", - "value": { - "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", - "status": "Success", - "reason": null, - "vendor_data": "user-1234", - "metadata": null - } - }, - "Retry": { - "summary": "Second send for the same number inside the 5-minute window — same request_id", - "value": { - "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", - "status": "Retry", - "reason": null, - "vendor_data": "user-1234", - "metadata": null - } - }, - "Blocked": { - "summary": "Anti-fraud refusal — verification finalized as Declined, nothing billed", - "value": { - "request_id": "0d8af1f4-0c43-46d7-b75c-6e4e22c0a7b3", - "status": "Blocked", - "reason": "repeated_attempts", - "vendor_data": "user-1234", - "metadata": null - } - } + "$ref": "#/components/schemas/EntityNetworkMembershipList" } } } }, - "400": { - "description": "Validation error. Malformed input returns DRF's field-error envelope (one array of messages per offending field, nested under `options` for option errors). A number that parses but is rejected at send time (unreachable, or an ineligible line type such as a short code) returns a `{\"detail\": ...}` envelope instead.", + "404": { + "description": "Entity not found in this application, or has no linked subject to look up.", "content": { "application/json": { - "examples": { - "Invalid phone number (field error)": { - "summary": "Number is not valid E.164", - "value": { - "phone_number": [ - "Invalid phone number provided." - ] - } - }, - "Missing phone number": { - "summary": "`phone_number` not included in the body", - "value": { - "phone_number": [ - "This field is required." - ] - } - }, - "Invalid options": { - "summary": "Out-of-range `code_size`, malformed `locale`, unknown `preferred_channel`", - "value": { - "options": { - "code_size": [ - "Ensure this value is less than or equal to 8." - ], - "locale": [ - "Ensure this field has no more than 5 characters." - ], - "preferred_channel": [ - "\"carrier_pigeon\" is not a valid choice." - ] - } - } - }, - "Number rejected at send time": { - "summary": "Valid E.164 shape but the number cannot receive an OTP", - "value": { - "detail": "Invalid phone number provided." - } - }, - "Ineligible line type": { - "summary": "Line type cannot receive an OTP (e.g., short code)", - "value": { - "detail": "Invalid phone line type provided." + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" } } } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/parties/{transaction_party_uuid}/networks/": { + "get": { + "summary": "List networks for a transaction party", + "description": "List the networks a transaction party (remitter, beneficiary, or counterparty) belongs to, resolved through the party itself rather than through the transaction's backing session.", + "operationId": "list_transaction_party_network_memberships", + "x-didit-sdk": { + "resource": "transactions", + "method": "list_party_networks", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment (`{\"detail\": ...}`) — this API never returns `401`. Also returned before any send when the organization has never topped up (anti-abuse gate) or when its balance cannot cover the destination's send price (`{\"error\": ...}`).", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "transaction_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Transaction UUID." + }, + { + "name": "transaction_party_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Transaction party UUID." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/transactions/aaaaaaaa-0000-4000-8000-000000000005/parties/bbbbbbbb-0000-4000-8000-000000000006/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "Networks this entity belongs to. Not paginated.", "content": { "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Top-up required": { - "summary": "Phone verification stays disabled until the organization's first top-up", - "value": { - "detail": "To protect against abuse, phone verification is disabled until your first top-up." - } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the send price", - "value": { - "error": "You don't have enough credits to perform this request." + "schema": { + "$ref": "#/components/schemas/EntityNetworkMembershipList" + } + } + } + }, + "404": { + "description": "Entity not found in this application, or has no linked subject to look up.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" } } } } } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/biometric-templates/": { + "get": { + "summary": "List retained biometric templates", + "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nReturns a paginated list of the templates the application currently holds. Purged templates are not listed.", + "operationId": "list_biometric_templates", + "x-didit-sdk": { + "resource": "biometric_templates", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Biometric Templates" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "vendor_data", + "required": false, + "description": "Case-insensitive substring match on the owning User's current `vendor_data`.", + "schema": { + "type": "string" + } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. Additionally, an anti-abuse cap of 4 verification attempts per phone number per hour applies upstream. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers. Note: the `X-RateLimit-*`/`Retry-After` headers accompany only the 300/min write-limit 429; the per-number cap 429 carries no rate-limit headers.", + { + "in": "query", + "name": "vendor_user_uuid", + "required": false, + "description": "Exact match on the owning User.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "status", + "required": false, + "description": "Lifecycle status. Purged templates are never listed.", + "schema": { + "type": "string", + "enum": [ + "pending", + "active", + "expired", + "held_out_of_use", + "purge_pending", + "purge_failed" + ] + } + }, + { + "in": "query", + "name": "source_type", + "required": false, + "description": "How the template was created.", + "schema": { + "type": "string", + "enum": [ + "session_delete", + "automatic_retention", + "backfill" + ] + } + }, + { + "in": "query", + "name": "retained_from", + "required": false, + "description": "Templates retained at or after this time.", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "in": "query", + "name": "retained_to", + "required": false, + "description": "Templates retained at or before this time. Must not be earlier than `retained_from` (`400`).", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "in": "query", + "name": "ordering", + "required": false, + "description": "Sort order.", + "schema": { + "type": "string", + "enum": [ + "retained_at", + "-retained_at", + "expires_at", + "-expires_at" + ], + "default": "-retained_at" + } + }, + { + "in": "query", + "name": "limit", + "required": false, + "description": "Page size.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "offset", + "required": false, + "description": "Pagination offset.", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Paginated templates.", "content": { "application/json": { - "examples": { - "Per-number cap": { - "summary": "More than 4 verification attempts for the same number in the last hour", - "value": { - "detail": "Maximum verification attempts reached for this phone number. Only 4 authentication attempts are allowed per hour. Try again later or use a different number." - } - }, - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Template id. Randomly generated; not derived from the deleted session or its face." + }, + "vendor_user_uuid": { + "type": "string", + "format": "uuid", + "description": "The owning User." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "The User's current `vendor_data`, resolved when you read the template (never copied)." + }, + "source_type": { + "type": "string", + "enum": [ + "session_delete", + "automatic_retention", + "backfill" + ], + "description": "How the template was created: a session deletion (API or Console), the application's automatic data-retention window, or a Didit-operated backfill you instructed." + }, + "provenance_reference": { + "type": "string", + "format": "uuid", + "description": "Opaque reference tying the template to its audit trail. It is not the deleted session's id and cannot be resolved to it." + }, + "retention_policy": { + "type": "string", + "enum": [ + "delete_with_session", + "retain_until_user_deleted" + ], + "description": "The application policy at retention time." + }, + "retention_override": { + "type": "boolean", + "nullable": true, + "description": "The `retain_face_embeddings` value sent on the deletion, or `null` when the policy applied." + }, + "retained_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time", + "description": "When the template expires and is purged automatically. Always set." + }, + "retained_by": { + "type": "string", + "nullable": true, + "description": "Principal that gave the instruction: the application id for API keys, the Console member id for Console actions, or a `service:` identifier for automatic retention. Never an email address." + }, + "retained_by_type": { + "type": "string", + "enum": [ + "API_KEY", + "CONSOLE_USER", + "SYSTEM" + ] + }, + "instruction_class": { + "type": "string", + "enum": [ + "operational_session_delete", + "privacy_erasure" + ] + }, + "instruction_source": { + "type": "string", + "enum": [ + "api", + "console", + "automatic_retention", + "backfill", + "privacy_erasure", + "explicit_template_purge", + "vendor_user_delete", + "expiry", + "reconciliation", + "migration", + "system" + ] + }, + "instruction_id": { + "type": "string", + "description": "The `instruction_id` sent on the deletion, or a Didit-generated id." + }, + "held_out_of_use": { + "type": "boolean", + "description": "`true` only while a legal hold applies. A held template is excluded from all matching." + }, + "status": { + "type": "string", + "enum": [ + "pending", + "active", + "expired", + "held_out_of_use", + "purge_pending", + "purge_failed", + "purged" + ], + "description": "Only `active` templates that are not held, not past `expires_at`, and whose User still exists take part in matching." + }, + "purged_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + } } } }, - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "examples": { + "One active template": { + "value": { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", + "vendor_user_uuid": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f", + "vendor_data": "user-42", + "source_type": "session_delete", + "provenance_reference": "6b1e8d2f-4a3c-4f9e-8c7d-1a2b3c4d5e6f", + "retention_policy": "retain_until_user_deleted", + "retention_override": null, + "retained_at": "2026-08-28T10:15:42.318402Z", + "expires_at": "2027-08-28T10:15:42.318402Z", + "retained_by": "3c9e2b1f-7d4a-4e8b-9c1d-5f6a7b8c9d0e", + "retained_by_type": "API_KEY", + "instruction_class": "operational_session_delete", + "instruction_source": "api", + "instruction_id": "post-approval-cleanup-2026-08", + "held_out_of_use": false, + "status": "active", + "purged_at": null + } + ] } } } } } }, - "502": { - "description": "The phone verification provider is temporarily unavailable - a 5xx response, timeout, or connection failure that survived the built-in retries. Safe to retry; nothing is billed and no attempt is consumed.", + "400": { + "description": "Invalid filter value, or `retained_to` earlier than `retained_from`. Field-keyed validation errors.", "content": { "application/json": { "examples": { - "Provider outage": { - "summary": "The provider did not answer the send request", + "Bad range": { "value": { - "detail": "Phone verification is temporarily unavailable. Please try again shortly.", - "code": "phone_provider_unavailable" - } - } - }, - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" + "retained_to": [ + "Must be on or after retained_from." + ] } } } } } }, - "500": { - "description": "Unexpected failure while creating the verification that is not a known provider outage. Safe to retry.", + "403": { + "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", "content": { "application/json": { "examples": { - "Provider error": { - "summary": "OTP delivery could not be initiated", + "Forbidden": { "value": { - "detail": "Error creating phone verification" - } - } - }, - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "detail": "You do not have permission to perform this action." } } } @@ -21368,381 +43683,124 @@ } } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/biometric-templates/?vendor_data=user-42&status=active' \\\n -H 'x-api-key: YOUR_API_KEY'" } ] } }, - "/v3/phone/check/": { - "post": { - "summary": "Check Phone Code", - "description": "Verify the OTP delivered by [`POST /v3/phone/send/`](/standalone-apis/phone-send) and get the final verification result plus phone intelligence: carrier name and line type, virtual (VoIP) and disposable flags, and duplicate usage of the number across your sessions.\n\n**How the check finds the verification.** Matching is by your application plus the E.164 `phone_number` — `request_id` is not an input. The most recent pending verification created within the last **5 minutes** is checked. If there is none (never sent, already finalized, or older than 5 minutes) the endpoint returns `200` with `status: \"Expired or Not Found\"`.\n\n**Attempt budget.** Each verification allows **3 code attempts**. The first two wrong codes return `status: \"Failed\"` with the attempts remaining and `phone: null`; the third wrong code finalizes the verification as `Declined` with a `VERIFICATION_CODE_ATTEMPTS_EXCEEDED` warning and returns the full phone report.\n\n**Outcomes.** `Approved` — correct code and no declining risk. `Declined` — terminal: the code was correct but a declining risk matched (a `DECLINE` action below, a blocklisted or high-risk number), or the attempt budget was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — nothing to check. On `Approved`/`Declined` the `request_id` equals the send's `request_id` (the session id) and `phone` carries the full report (`carrier`, `is_virtual`, `is_disposable`, `warnings`, `lifecycle`, `matches`); on `Failed` and `Expired or Not Found` the `request_id` is a one-off random UUID.\n\n**Risk actions.** `duplicated_phone_number_action`, `disposable_number_action`, and `voip_number_action` decide what happens when the corresponding risk is detected on a correct code: `DECLINE` flips the final status to `Declined`; `NO_ACTION` (default) records the risk in `phone.warnings` without affecting the status.\n\n**Billing.** Checks are free — delivery is billed on the send side (see [Phone Verification Pricing](/getting-started/phone-verification-pricing)).\n\n**Session persistence.** A finalized check updates the session created by the send (visible in the Business Console, queryable via `GET /v3/session/{sessionId}/decision/`) and fires a `status.updated` webhook.\n\n**Sandbox.** Sandbox API keys skip all processing: any well-formed code (4–8 digits, `123456` included) returns a static `Approved` payload with a simplified flat `phone` object (`status`, `phone_number`, `country_code`, `carrier_name`, `line_type`, `flags`); malformed input still returns `400`. Nothing is persisted.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limits.** Shared write budget of 300 requests/min per API key, plus the upstream anti-abuse cap of 4 attempts per number per hour; exceeding either returns `429`.\n\n**Social footprint.** Set `enable_social` to `true` to add a `phone_social` block: the online platforms this number is registered on, with per-category counts and any profile details available. `profiles_registered: 0` means the platforms were checked and the number was found on none of them - a valid, billable answer. The block is absent when the add-on was not requested or the check could not be completed, and it is not billed in that case. Billing adds one `phone_social` unit per request on top of this endpoint's own price.", - "operationId": "post_v3phone_check", + "/v3/biometric-templates/count/": { + "get": { + "summary": "Count retained biometric templates", + "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nReturns the number of templates matching the same filters as the list endpoint. Use it to check what a filtered bulk purge with `delete_all: true` would touch.", + "operationId": "count_biometric_templates", + "x-didit-sdk": { + "resource": "biometric_templates", + "method": "count", + "auth": "api_key", + "server": true + }, "tags": [ - "Standalone APIs" + "Biometric Templates" ], - "x-codeSamples": [ + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/phone/check/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"phone_number\": \"+14155552671\",\n \"code\": \"123456\",\n \"voip_number_action\": \"DECLINE\"\n }'" + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "vendor_data", + "required": false, + "description": "Case-insensitive substring match on the owning User's current `vendor_data`.", + "schema": { + "type": "string" + } }, { - "lang": "python", - "label": "Python", - "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/phone/check/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"phone_number\": \"+14155552671\", # same number as the send call\n \"code\": \"123456\",\n \"voip_number_action\": \"DECLINE\",\n },\n timeout=15,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result[\"status\"]) # Approved / Declined / Failed / Expired or Not Found\nif result[\"status\"] in (\"Approved\", \"Declined\"):\n print(result[\"phone\"][\"carrier\"], result[\"phone\"][\"is_virtual\"])" + "in": "query", + "name": "vendor_user_uuid", + "required": false, + "description": "Exact match on the owning User.", + "schema": { + "type": "string", + "format": "uuid" + } }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/phone/check/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n phone_number: '+14155552671', // same number as the send call\n code: '123456',\n voip_number_action: 'DECLINE',\n }),\n});\nconst data = await res.json();\nif (data.status === 'Approved') {\n // full phone report is in data.phone (carrier, is_virtual, matches, ...)\n}" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "phone_number": { - "type": "string", - "maxLength": 20, - "description": "The same phone number used in the matching [`POST /v3/phone/send/`](/standalone-apis/phone-send) call, in E.164 format. This is what links the check to the send.", - "example": "+14155552671" - }, - "code": { - "type": "string", - "minLength": 4, - "maxLength": 8, - "pattern": "^[0-9]{4,8}$", - "description": "The OTP the end user received. Must be 4–8 numeric digits — anything else returns `400` without consuming an attempt. A well-formed but wrong code returns `200` with `status: \"Failed\"` and consumes one of the 3 attempts.", - "example": "123456" - }, - "duplicated_phone_number_action": { - "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "NO_ACTION", - "description": "What to do when the same number was already used by a different user (different `vendor_data`) of your application. `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `phone.warnings` and fills `phone.matches`." - }, - "disposable_number_action": { - "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "NO_ACTION", - "description": "What to do when the carrier lookup flags the number as a temporary/burner line (`phone.is_disposable`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `phone.warnings`." - }, - "voip_number_action": { - "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "NO_ACTION", - "description": "What to do when the carrier lookup reports a virtual line type — `voip`, `isp`, or `vpn` (`phone.is_virtual`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `phone.warnings`." - }, - "enable_social": { - "type": "boolean", - "default": false, - "description": "When true, also look up the social footprint of the number - the messaging, social, e-commerce and professional platforms it is registered on. Billed as one `phone_social` unit per request in addition to this endpoint's own price.", - "example": true - }, - "phone_no_social_presence_action": { - "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "NO_ACTION", - "description": "Action when the social footprint check finds the number registered on none of the platforms it covers. Only NO_ACTION and DECLINE are accepted on this endpoint; REVIEW is available on the workflow configuration key of the same name." - } - }, - "required": [ - "phone_number", - "code" - ] - }, - "examples": { - "Default": { - "summary": "Record any risks but never auto-decline", - "value": { - "phone_number": "+14155552671", - "code": "123456" - } - }, - "Strict risk policy": { - "summary": "Auto-decline VoIP and disposable lines", - "value": { - "phone_number": "+14155552671", - "code": "123456", - "voip_number_action": "DECLINE", - "disposable_number_action": "DECLINE" - } - } - } + "in": "query", + "name": "status", + "required": false, + "description": "Lifecycle status. Purged templates are never listed.", + "schema": { + "type": "string", + "enum": [ + "pending", + "active", + "expired", + "held_out_of_use", + "purge_pending", + "purge_failed" + ] + } + }, + { + "in": "query", + "name": "source_type", + "required": false, + "description": "How the template was created.", + "schema": { + "type": "string", + "enum": [ + "session_delete", + "automatic_retention", + "backfill" + ] + } + }, + { + "in": "query", + "name": "retained_from", + "required": false, + "description": "Templates retained at or after this time.", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "in": "query", + "name": "retained_to", + "required": false, + "description": "Templates retained at or before this time. Must not be earlier than `retained_from` (`400`).", + "schema": { + "type": "string", + "format": "date-time" } } - }, + ], "responses": { "200": { - "description": "Check completed — wrong codes and missing verifications also return `200`; inspect `status`, not the HTTP code. `phone` is populated only on finalized outcomes (`Approved`/`Declined`), `null` on `Failed`, and absent on `Expired or Not Found`.", + "description": "Count.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PhoneVerificationCheckResponse" - }, - "examples": { - "Approved": { - "summary": "Correct code — full phone report with carrier, lifecycle, and matches", - "value": { - "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", - "status": "Approved", - "message": "The verification code is correct.", - "phone": { - "status": "Approved", - "phone_number_prefix": "+1", - "phone_number": "4155552671", - "full_number": "+14155552671", - "country_code": "US", - "country_name": "United States", - "carrier": { - "name": "AT&T", - "type": "mobile" - }, - "is_disposable": false, - "is_virtual": false, - "verification_method": "whatsapp", - "verification_attempts": 1, - "verified_at": "2026-06-12T01:24:47.311323Z", - "warnings": [], - "lifecycle": [ - { - "type": "PHONE_VERIFICATION_MESSAGE_SENT", - "timestamp": "2026-06-12T01:23:39.580554+00:00", - "details": { - "status": "Success", - "reason": null, - "channel": "whatsapp", - "actual_channel": "whatsapp" - }, - "fee": 0.0709 - }, - { - "type": "PHONE_DELIVERY_DELIVERED", - "timestamp": "2026-06-12T01:23:43.118220+00:00", - "details": { - "channel": "whatsapp", - "status": "delivered" - }, - "fee": 0 - }, - { - "type": "VALID_CODE_ENTERED", - "timestamp": "2026-06-12T01:24:47.311201+00:00", - "details": { - "code_tried": "123456", - "status": "Approved" - }, - "fee": 0 - }, - { - "type": "PHONE_VERIFICATION_APPROVED", - "timestamp": "2026-06-12T01:24:47.384292+00:00", - "details": null, - "fee": 0 - } - ], - "matches": [] - }, - "vendor_data": "user-1234", - "metadata": null, - "created_at": "2026-06-12T01:24:47.401719+00:00" - } - }, - "Failed (attempts remaining)": { - "summary": "Wrong code — the user can retry; request_id here is a one-off UUID", - "value": { - "request_id": "11111111-2222-3333-4444-555555555555", - "status": "Failed", - "message": "The verification code is incorrect. Attempts remaining: 2", - "phone": null, - "vendor_data": "user-1234", - "metadata": null, - "created_at": "2026-06-12T01:24:21.512340+00:00" - } - }, - "Declined (max attempts)": { - "summary": "Third wrong code — verification finalized as Declined", - "value": { - "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", - "status": "Declined", - "message": "The verification code is incorrect. Maximum attempts reached.", - "phone": { - "status": "Declined", - "phone_number_prefix": "+1", - "phone_number": "4155552671", - "full_number": "+14155552671", - "country_code": "US", - "country_name": "United States", - "carrier": { - "name": "AT&T", - "type": "mobile" - }, - "is_disposable": false, - "is_virtual": false, - "verification_method": "whatsapp", - "verification_attempts": 1, - "verified_at": null, - "warnings": [ - { - "feature": "PHONE", - "risk": "VERIFICATION_CODE_ATTEMPTS_EXCEEDED", - "additional_data": null, - "log_type": "error", - "short_description": "Verification code attempts exceeded", - "long_description": "The system detected that the phone number has exceeded the maximum number of verification code attempts." - } - ], - "lifecycle": [ - { - "type": "PHONE_VERIFICATION_MESSAGE_SENT", - "timestamp": "2026-06-12T01:23:39.580554+00:00", - "details": { - "status": "Success", - "reason": null, - "channel": "whatsapp", - "actual_channel": "whatsapp" - }, - "fee": 0.0709 - }, - { - "type": "INVALID_CODE_ENTERED", - "timestamp": "2026-06-12T01:24:01.118220+00:00", - "details": { - "code_tried": "111111", - "status": "Failed" - }, - "fee": 0 - }, - { - "type": "INVALID_CODE_ENTERED", - "timestamp": "2026-06-12T01:24:21.512340+00:00", - "details": { - "code_tried": "222222", - "status": "Failed" - }, - "fee": 0 - }, - { - "type": "INVALID_CODE_ENTERED", - "timestamp": "2026-06-12T01:24:47.311201+00:00", - "details": { - "code_tried": "333333", - "status": "Failed" - }, - "fee": 0 - }, - { - "type": "PHONE_VERIFICATION_DECLINED", - "timestamp": "2026-06-12T01:24:47.384292+00:00", - "details": { - "reason": "VERIFICATION_CODE_ATTEMPTS_EXCEEDED" - }, - "fee": 0 - } - ], - "matches": [] - }, - "vendor_data": "user-1234", - "metadata": null, - "created_at": "2026-06-12T01:24:47.401719+00:00" - } - }, - "Declined (risk policy)": { - "summary": "Correct code, but the line is VoIP and voip_number_action=DECLINE", - "value": { - "request_id": "7f1c9d2e-44a1-4b6e-9a3e-5b8e6f1c2d3a", - "status": "Declined", - "message": "The verification code is correct.", - "phone": { - "status": "Declined", - "phone_number_prefix": "+1", - "phone_number": "5005550006", - "full_number": "+15005550006", - "country_code": "US", - "country_name": "United States", - "carrier": { - "name": "CLEC LLC", - "type": "voip" - }, - "is_disposable": false, - "is_virtual": true, - "verification_method": "sms", - "verification_attempts": 1, - "verified_at": "2026-06-12T01:24:47.311323Z", - "warnings": [ - { - "feature": "PHONE", - "risk": "VOIP_NUMBER_DETECTED", - "additional_data": null, - "log_type": "error", - "short_description": "VoIP number detected", - "long_description": "The system detected that the phone number is a VoIP number, which is not allowed." - } - ], - "lifecycle": [ - { - "type": "PHONE_VERIFICATION_MESSAGE_SENT", - "timestamp": "2026-06-12T01:23:39.580554+00:00", - "details": { - "status": "Success", - "reason": null, - "channel": "whatsapp", - "actual_channel": "sms" - }, - "fee": 0.0709 - }, - { - "type": "VALID_CODE_ENTERED", - "timestamp": "2026-06-12T01:24:47.311201+00:00", - "details": { - "code_tried": "123456", - "status": "Approved" - }, - "fee": 0 - }, - { - "type": "PHONE_VERIFICATION_DECLINED", - "timestamp": "2026-06-12T01:24:47.384292+00:00", - "details": { - "reason": "VOIP_NUMBER_DETECTED" - }, - "fee": 0 - } - ], - "matches": [] - }, - "vendor_data": "user-1234", - "metadata": null, - "created_at": "2026-06-12T01:24:47.401719+00:00" + "type": "object", + "properties": { + "count": { + "type": "integer", + "minimum": 0 } - }, - "Expired or Not Found": { - "summary": "No pending verification for this number in the last 5 minutes", + } + }, + "examples": { + "Count": { "value": { - "request_id": "69cc35c6-280a-47a6-ab8c-4f32abc02ed0", - "status": "Expired or Not Found", - "message": "No pending phone verification found in the last 5 minutes.", - "vendor_data": null, - "metadata": null, - "created_at": "2026-06-12T01:24:47.311323+00:00" + "count": 128 } } } @@ -21750,31 +43808,14 @@ } }, "400": { - "description": "Validation error — DRF's field-error envelope: one array of messages per offending field.", + "description": "Invalid filter value, or `retained_to` earlier than `retained_from`. Field-keyed validation errors.", "content": { "application/json": { "examples": { - "Invalid code format": { - "summary": "`code` is not 4–8 numeric digits", - "value": { - "code": [ - "Invalid code format. Enter 4 to 8 numeric digits." - ] - } - }, - "Invalid phone number": { - "summary": "`phone_number` is not valid E.164", - "value": { - "phone_number": [ - "Invalid phone number provided." - ] - } - }, - "Invalid action": { - "summary": "Risk actions accept only NO_ACTION or DECLINE", + "Bad range": { "value": { - "voip_number_action": [ - "\"REVIEW\" is not a valid choice." + "retained_to": [ + "Must be on or after retained_from." ] } } @@ -21783,12 +43824,11 @@ } }, "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — this API never returns `401`. Checks are free, so there is no credit check here.", + "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", "content": { "application/json": { "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", + "Forbidden": { "value": { "detail": "You do not have permission to perform this action." } @@ -21796,433 +43836,336 @@ } } } - }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. Additionally, an anti-abuse cap of 4 verification attempts per phone number per hour applies upstream. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers. Note: the `X-RateLimit-*`/`Retry-After` headers accompany only the 300/min write-limit 429; the per-number cap 429 carries no rate-limit headers.", + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/biometric-templates/count/?status=active' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ] + } + }, + "/v3/biometric-templates/{template_uuid}/": { + "get": { + "summary": "Get a retained biometric template", + "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nReturns one template. The response never includes an image, a vector, the deleted session's id, or identity data.", + "operationId": "get_biometric_template", + "x-didit-sdk": { + "resource": "biometric_templates", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Biometric Templates" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "template_uuid", + "required": true, + "description": "Template id, as returned in `biometric_template_uuid` by the session deletion endpoints, `biometric_template_id` on a Face Search match, or the list endpoint.", + "schema": { + "type": "string", + "format": "uuid", + "example": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b" + } + } + ], + "responses": { + "200": { + "description": "The template.", "content": { "application/json": { - "examples": { - "Per-number cap": { - "summary": "More than 4 verification attempts for the same number in the last hour", - "value": { - "detail": "Maximum verification attempts reached for this phone number. Only 4 authentication attempts are allowed per hour. Try again later or use a different number." - } - }, - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { "type": "object", "properties": { - "detail": { - "type": "string" + "uuid": { + "type": "string", + "format": "uuid", + "description": "Template id. Randomly generated; not derived from the deleted session or its face." + }, + "vendor_user_uuid": { + "type": "string", + "format": "uuid", + "description": "The owning User." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "The User's current `vendor_data`, resolved when you read the template (never copied)." + }, + "source_type": { + "type": "string", + "enum": [ + "session_delete", + "automatic_retention", + "backfill" + ], + "description": "How the template was created: a session deletion (API or Console), the application's automatic data-retention window, or a Didit-operated backfill you instructed." + }, + "provenance_reference": { + "type": "string", + "format": "uuid", + "description": "Opaque reference tying the template to its audit trail. It is not the deleted session's id and cannot be resolved to it." + }, + "retention_policy": { + "type": "string", + "enum": [ + "delete_with_session", + "retain_until_user_deleted" + ], + "description": "The application policy at retention time." + }, + "retention_override": { + "type": "boolean", + "nullable": true, + "description": "The `retain_face_embeddings` value sent on the deletion, or `null` when the policy applied." + }, + "retained_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time", + "description": "When the template expires and is purged automatically. Always set." + }, + "retained_by": { + "type": "string", + "nullable": true, + "description": "Principal that gave the instruction: the application id for API keys, the Console member id for Console actions, or a `service:` identifier for automatic retention. Never an email address." + }, + "retained_by_type": { + "type": "string", + "enum": [ + "API_KEY", + "CONSOLE_USER", + "SYSTEM" + ] + }, + "instruction_class": { + "type": "string", + "enum": [ + "operational_session_delete", + "privacy_erasure" + ] + }, + "instruction_source": { + "type": "string", + "enum": [ + "api", + "console", + "automatic_retention", + "backfill", + "privacy_erasure", + "explicit_template_purge", + "vendor_user_delete", + "expiry", + "reconciliation", + "migration", + "system" + ] + }, + "instruction_id": { + "type": "string", + "description": "The `instruction_id` sent on the deletion, or a Didit-generated id." + }, + "held_out_of_use": { + "type": "boolean", + "description": "`true` only while a legal hold applies. A held template is excluded from all matching." + }, + "status": { + "type": "string", + "enum": [ + "pending", + "active", + "expired", + "held_out_of_use", + "purge_pending", + "purge_failed", + "purged" + ], + "description": "Only `active` templates that are not held, not past `expires_at`, and whose User still exists take part in matching." + }, + "purged_at": { + "type": "string", + "format": "date-time", + "nullable": true } } - } - } - } - }, - "502": { - "description": "The phone verification provider is temporarily unavailable - a 5xx response, timeout, or connection failure that survived the built-in retries. Safe to retry; the attempt budget is not consumed.", - "content": { - "application/json": { + }, "examples": { - "Provider outage": { - "summary": "The provider did not answer the check request", + "Active template": { "value": { - "detail": "Phone verification is temporarily unavailable. Please try again shortly.", - "code": "phone_provider_unavailable" - } - } - }, - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" + "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", + "vendor_user_uuid": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f", + "vendor_data": "user-42", + "source_type": "session_delete", + "provenance_reference": "6b1e8d2f-4a3c-4f9e-8c7d-1a2b3c4d5e6f", + "retention_policy": "retain_until_user_deleted", + "retention_override": null, + "retained_at": "2026-08-28T10:15:42.318402Z", + "expires_at": "2027-08-28T10:15:42.318402Z", + "retained_by": "3c9e2b1f-7d4a-4e8b-9c1d-5f6a7b8c9d0e", + "retained_by_type": "API_KEY", + "instruction_class": "operational_session_delete", + "instruction_source": "api", + "instruction_id": "post-approval-cleanup-2026-08", + "held_out_of_use": false, + "status": "active", + "purged_at": null } } } } } }, - "500": { - "description": "Unexpected failure while verifying the code upstream that is not a known provider outage. Safe to retry; the attempt budget is only consumed by completed checks.", + "403": { + "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", "content": { "application/json": { "examples": { - "Provider error": { - "summary": "Code verification could not be completed", + "Forbidden": { "value": { - "detail": "Error checking phone verification" - } - } - }, - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "detail": "You do not have permission to perform this action." } } } } } - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - } - }, - "/v3/email/send/": { - "post": { - "summary": "Send Email Code", - "description": "Send a one-time passcode (OTP) to an email address, then verify it with [`POST /v3/email/check/`](/standalone-apis/email-check).\n\n**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.\n\n**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.\n\n**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`.\n\n**Billing.** One Email Verification API credit per successful send (`status: \"Success\"`), charged at send time. `Retry` and `Undeliverable` sends are free, and checks are free.\n\n**Session persistence.** Every new verification is persisted as an API-type session: `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, the verification appears in the Business Console, and `status.updated` webhooks fire as it progresses.\n\n**Sandbox.** Sandbox API keys skip delivery and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Success` payload with a random `request_id`; no email is sent and nothing is persisted. Use code `123456` on the sandbox check.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.", - "operationId": "post_v3email_send", - "tags": [ - "Standalone APIs" - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/email/send/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"email\": \"alice@example.com\",\n \"options\": { \"code_size\": 6, \"locale\": \"en\" },\n \"vendor_data\": \"user-1234\"\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/email/send/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"email\": \"alice@example.com\",\n \"options\": {\"code_size\": 6, \"locale\": \"en\"},\n \"vendor_data\": \"user-1234\",\n },\n timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json()) # {request_id, status, reason, vendor_data, metadata}\n# Then verify with POST /v3/email/check/ using the same email" }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/email/send/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n email: 'alice@example.com',\n options: { code_size: 6, locale: 'en' },\n vendor_data: 'user-1234',\n }),\n});\nif (!res.ok) throw new Error(`Email send failed: ${res.status}`);\nconst data = await res.json();\nconsole.log(data); // { request_id, status, reason, vendor_data, metadata }\n// Then verify with POST /v3/email/check/ using the same email" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email", - "description": "Recipient email address. Malformed addresses return `400`; syntactically valid addresses that cannot receive mail (failed DNS/MX validation) return `200` with `status: \"Undeliverable\"`.", - "example": "alice@example.com" - }, - "options": { - "type": "object", - "description": "OTP format, localization, and branding options. All fields are optional.", - "properties": { - "code_size": { - "type": "integer", - "minimum": 4, - "maximum": 8, - "default": 6, - "description": "Number of characters in the OTP (digits by default; uppercase letters and digits when `alphanumeric_code` is `true`)." - }, - "alphanumeric_code": { - "type": "boolean", - "default": false, - "description": "When `true`, the OTP is composed of uppercase letters `A–Z` and digits `0–9` instead of digits only. The `/v3/email/check/` comparison is case-insensitive." - }, - "locale": { - "type": "string", - "maxLength": 5, - "enum": [ - "en", - "ar", - "bn", - "bg", - "bs", - "ca", - "cs", - "da", - "de", - "el", - "es", - "et", - "fa", - "fi", - "fr", - "he", - "hi", - "hr", - "hu", - "hy", - "id", - "it", - "ja", - "ka", - "kk", - "ko", - "ky", - "lt", - "lv", - "cnr", - "mk", - "mn", - "ms", - "nl", - "no", - "pl", - "pt-BR", - "pt", - "ro", - "ru", - "sk", - "sl", - "so", - "sq", - "sr", - "sv", - "th", - "tr", - "uk", - "uz", - "vi", - "zh-CN", - "zh-TW", - "zh" - ], - "description": "Language of the OTP email template. Defaults to `en`. Unsupported values return `400` listing all supported locales.", - "example": "en" - }, - "use_white_label_customization": { - "type": "boolean", - "default": false, - "description": "When `true`, the OTP email uses your application's white-label customization (logo, colors, sender branding) instead of the default Didit template." - } - } - }, - "signals": { - "type": "object", - "description": "Optional device and network signals about the end user, forwarded to the anti-fraud layer to improve detection of abusive or automated traffic. All fields are optional.", - "properties": { - "ip": { - "type": "string", - "description": "IP address of the end user's device. IPv4 or IPv6.", - "example": "192.0.2.1" - }, - "device_id": { - "type": "string", - "maxLength": 255, - "description": "Unique identifier of the end user's device. Android: `ANDROID_ID`; iOS: `identifierForVendor`.", - "example": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2" - }, - "device_platform": { - "type": "string", - "enum": [ - "android", - "ios", - "ipados", - "tvos", - "web" - ], - "description": "Platform of the end user's device." - }, - "device_model": { - "type": "string", - "maxLength": 255, - "description": "Model of the end user's device.", - "example": "iPhone17,2" - }, - "os_version": { - "type": "string", - "maxLength": 64, - "description": "Operating-system version of the end user's device.", - "example": "18.0.1" - }, - "app_version": { - "type": "string", - "maxLength": 64, - "description": "Version of your application.", - "example": "1.2.34" - }, - "user_agent": { - "type": "string", - "maxLength": 512, - "description": "User agent of the end user's browser or app.", - "example": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1" - } - } - }, - "vendor_data": { - "type": "string", - "description": "Optional caller-controlled identifier (your internal user id, an email, a UUID, etc.) persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console. Use it to correlate Didit's `request_id` with your user record." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Optional free-form JSON object persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console." - } - }, - "required": [ - "email" - ] - }, - "examples": { - "Numeric 6-digit code": { - "summary": "Default — 6-digit numeric code in English", - "value": { - "email": "alice@example.com", - "options": { - "locale": "en" - }, - "vendor_data": "user-1234" - } - }, - "Alphanumeric 8-character code": { - "summary": "Uppercase letters + digits, Spanish template", - "value": { - "email": "bob@example.com", - "options": { - "code_size": 8, - "alphanumeric_code": true, - "locale": "es" - } - } - }, - "White-label branding": { - "summary": "Send the OTP email with your application's white-label customization", - "value": { - "email": "carol@example.com", - "options": { - "locale": "en", - "use_white_label_customization": true - }, - "vendor_data": "user-5678" + "404": { + "description": "Unknown template, a template of another application, or (on `GET`) a purged template.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "Biometric template not found." + } } } } } } }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET https://verification.didit.me/v3/biometric-templates/9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ] + }, + "delete": { + "summary": "Purge a retained biometric template", + "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nPurges one template: it is removed from every kind of matching and the stored template is deleted. Irreversible and recorded on the audit trail with the acting principal.\n\n**Retry semantics:** Didit deletes the stored template first and only then marks the template purged. If a biometric store is unavailable the call returns `503`, the template moves to `status: purge_failed` and is already excluded from matching; repeat the same call to finish. Repeating a purge of an already purged template returns `already_purged`.\n\nTemplates are also purged by `POST /v3/users/delete/` for the owning User, by any deletion of that User's sessions with `deletion_instruction: privacy_erasure`, and automatically at `expires_at`.", + "operationId": "purge_biometric_template", + "x-didit-sdk": { + "resource": "biometric_templates", + "method": "delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Biometric Templates" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "template_uuid", + "required": true, + "description": "Template id, as returned in `biometric_template_uuid` by the session deletion endpoints, `biometric_template_id` on a Face Search match, or the list endpoint.", + "schema": { + "type": "string", + "format": "uuid", + "example": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b" + } + } + ], "responses": { "200": { - "description": "Send acknowledged. Inspect `status`: `Success` and `Retry` mean a code is on its way; `Undeliverable` means the address cannot receive mail and the verification is already finalized as `Declined`. `request_id` is the persisted session id (same id on a `Retry`).", + "description": "Purge outcome.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmailVerificationSendResponse" + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "outcome": { + "type": "string", + "enum": [ + "purged", + "already_purged" + ] + } + } }, "examples": { - "Success": { - "summary": "OTP emailed for a new verification (billed)", - "value": { - "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", - "status": "Success", - "reason": null, - "vendor_data": "user-1234", - "metadata": null - } - }, - "Retry": { - "summary": "Second send inside the 5-minute window — fresh code, same request_id, free", + "Purged": { "value": { - "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", - "status": "Retry", - "reason": null, - "vendor_data": "user-1234", - "metadata": null + "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", + "outcome": "purged" } }, - "Undeliverable": { - "summary": "Address failed DNS/MX validation — verification finalized as Declined, nothing billed", + "Already purged": { "value": { - "request_id": "cb1a5011-344d-444d-bdce-218904745d7f", - "status": "Undeliverable", - "reason": "email_can_not_be_delivered", - "vendor_data": "user-1234", - "metadata": null + "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", + "outcome": "already_purged" } } } } } }, - "400": { - "description": "Validation error — DRF's field-error envelope: one array of messages per offending field, nested under `options` for option errors.", + "403": { + "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", "content": { "application/json": { "examples": { - "Invalid email": { - "summary": "Malformed email address", - "value": { - "email": [ - "Enter a valid email address." - ] - } - }, - "Unsupported locale": { - "summary": "`options.locale` outside the supported set", - "value": { - "options": { - "locale": [ - "Invalid locale. Supported locales are en, ar, bn, bg, bs, ca, cs, da, de, el, es, et, fa, fi, fr, he, hi, hr, hu, hy, id, it, ja, ka, kk, ko, ky, lt, lv, cnr, mk, mn, ms, nl, no, pl, pt-BR, pt, ro, ru, sk, sl, so, sq, sr, sv, th, tr, uk, uz, vi, zh-CN, zh-TW, zh." - ] - } - } - }, - "Code size out of range": { - "summary": "`options.code_size` outside 4–8", + "Forbidden": { "value": { - "options": { - "code_size": [ - "Ensure this value is less than or equal to 8." - ] - } + "detail": "You do not have permission to perform this action." } } } } } }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment (`{\"detail\": ...}`) — this API never returns `401`. Also returned before any send when the organization's balance cannot cover one Email Verification API credit (`{\"error\": ...}`).", + "404": { + "description": "Unknown template, a template of another application, or (on `GET`) a purged template.", "content": { "application/json": { "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the send price", + "Not found": { "value": { - "error": "You don't have enough credits to perform this request." + "detail": "Biometric template not found." } } } } } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "503": { + "description": "A biometric store was unavailable while retaining or purging a biometric template. Nothing is deleted; repeat the same request. Retries are idempotent and converge on a single template.", "content": { "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, "schema": { "type": "object", "properties": { @@ -22230,41 +44173,44 @@ "type": "string" } } + }, + "examples": { + "Retryable": { + "value": { + "detail": "The biometric-template operation could not finish because an external store is unavailable. The operation is retryable." + } + } } } } } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE https://verification.didit.me/v3/biometric-templates/9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b/ \\\n -H 'x-api-key: YOUR_API_KEY'" } ] } }, - "/v3/email/check/": { + "/v3/biometric-templates/delete/": { "post": { - "summary": "Check Email Code", - "description": "Verify the OTP delivered by [`POST /v3/email/send/`](/standalone-apis/email-send) and get the final verification result plus email intelligence: deliverability, breach exposure (`is_breached` and the `breaches` list from a breach-intelligence database), disposable-provider detection, and duplicate usage of the address across your sessions.\n\n**How the check finds the verification.** Matching is by your application plus the `email` address — `request_id` is not an input. The most recent pending verification created within the last **5 minutes** is checked. If there is none (never sent, already finalized, undeliverable at send time, or older than 5 minutes) the endpoint returns `200` with `status: \"Expired or Not Found\"`.\n\n**Attempt budget.** Each verification allows **3 code attempts**. The first two wrong codes return `status: \"Failed\"` with the attempts remaining and `email: null`; the third wrong code finalizes the verification as `Declined` with an `EMAIL_CODE_ATTEMPTS_EXCEEDED` warning and returns the full email report. Codes are compared case-insensitively (relevant for `alphanumeric_code` sends); only the most recently sent code is valid.\n\n**Outcomes.** `Approved` — correct code and no declining risk. `Declined` — terminal: the code was correct but a declining risk matched (a `DECLINE` action below, a blocklisted address, or the address found undeliverable at finalization), or the attempt budget was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — nothing to check. On `Approved`/`Declined` the `request_id` equals the send's `request_id` (the session id) and `email` carries the full report (`is_breached`, `breaches`, `is_disposable`, `is_undeliverable`, `warnings`, `lifecycle`, `matches`); on `Failed` and `Expired or Not Found` the `request_id` is a one-off random UUID.\n\n**Risk actions.** `duplicated_email_action`, `breached_email_action`, and `disposable_email_action` decide what happens when the corresponding risk is detected on a correct code: `DECLINE` flips the final status to `Declined`; `NO_ACTION` (default) records the risk in `email.warnings` without affecting the status.\n\n**Billing.** Checks are free — the credit is consumed by the successful send.\n\n**Session persistence.** A finalized check updates the session created by the send (visible in the Business Console, queryable via `GET /v3/session/{sessionId}/decision/`) and fires a `status.updated` webhook.\n\n**Sandbox.** Sandbox API keys skip all processing: any well-formed code (`123456` included) returns a static `Approved` payload with a simplified `email` object (`status`, `email`, `is_breached`, `is_disposable`, `is_undeliverable`); malformed input still returns `400`. Nothing is persisted.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\n**Social footprint.** Set `enable_social` to `true` to add a `email_social` block: the online platforms this address is registered on, with per-category counts and any profile details available. `profiles_registered: 0` means the platforms were checked and the address was found on none of them - a valid, billable answer. The block is absent when the add-on was not requested or the check could not be completed, and it is not billed in that case. Billing adds one `email_social` unit per request on top of this endpoint's own price.", - "operationId": "post_v3email_check", + "summary": "Bulk purge retained biometric templates", + "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nPurges many templates in one call, by explicit id list or with `delete_all: true` optionally narrowed by the same filters as the list endpoint. Templates are purged one by one and each result reports `purged` or `already_purged`. If a biometric store is unavailable the call returns `503` at the first failing template: templates purged earlier in the request stay purged, the failing one moves to `purge_failed` (already excluded from matching), and repeating the request finishes the job.\n\n**`delete_all: true` without filters purges every biometric template in the application and cannot be undone.** Run the count endpoint with the same filters first.", + "operationId": "bulk_purge_biometric_templates", + "x-didit-sdk": { + "resource": "biometric_templates", + "method": "batch_delete", + "auth": "api_key", + "server": true + }, "tags": [ - "Standalone APIs" + "Biometric Templates" ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/email/check/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"email\": \"alice@example.com\",\n \"code\": \"123456\",\n \"disposable_email_action\": \"DECLINE\"\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import os, requests\n\nresp = requests.post(\n \"https://verification.didit.me/v3/email/check/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"email\": \"alice@example.com\", # same address as the send call\n \"code\": \"123456\",\n \"disposable_email_action\": \"DECLINE\",\n },\n timeout=15,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result[\"status\"]) # Approved / Declined / Failed / Expired or Not Found\nif result[\"status\"] in (\"Approved\", \"Declined\"):\n print(result[\"email\"][\"is_breached\"], result[\"email\"][\"is_disposable\"])" - }, + "security": [ { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/email/check/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n email: 'alice@example.com', // same address as the send call\n code: '123456',\n disposable_email_action: 'DECLINE',\n }),\n});\nconst data = await res.json();\nif (data.status === 'Approved') {\n // full email report is in data.email (breaches, is_disposable, matches, ...)\n}" + "ApiKeyAuth": [] } ], "requestBody": { @@ -22274,81 +44220,80 @@ "schema": { "type": "object", "properties": { - "email": { - "type": "string", - "format": "email", - "description": "The same email address used in the matching [`POST /v3/email/send/`](/standalone-apis/email-send) call. This is what links the check to the send.", - "example": "alice@example.com" + "template_uuids": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "minItems": 1, + "description": "Templates to purge. Required unless `delete_all` is `true`. Already purged ids are reported as `already_purged`." }, - "code": { + "delete_all": { + "type": "boolean", + "default": false, + "description": "When `true`, purges every template matching the filters below (all of the application's templates when no filter is given). `template_uuids` is ignored. Irreversible." + }, + "vendor_data": { "type": "string", - "maxLength": 10, - "description": "The OTP the end user received: 4–8 digits, or 4–8 letters/digits when `alphanumeric_code: true` was used at send time. Comparison is case-insensitive.", - "example": "123456" + "description": "Only with `delete_all`: case-insensitive substring match on the owning User's `vendor_data`." }, - "duplicated_email_action": { + "vendor_user_uuid": { "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "NO_ACTION", - "description": "What to do when the same address was already used and approved by a different user (only previously Approved verifications count) (different `vendor_data`) of your application. `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `email.warnings` and fills `email.matches`." + "format": "uuid", + "description": "Only with `delete_all`: exact match on the owning User." }, - "breached_email_action": { + "status": { "type": "string", "enum": [ - "NO_ACTION", - "DECLINE" + "pending", + "active", + "expired", + "held_out_of_use", + "purge_pending", + "purge_failed" ], - "default": "NO_ACTION", - "description": "What to do when the address appears in known data breaches (`email.is_breached`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `email.warnings`." + "description": "Only with `delete_all`." }, - "disposable_email_action": { + "source_type": { "type": "string", "enum": [ - "NO_ACTION", - "DECLINE" + "session_delete", + "automatic_retention", + "backfill" ], - "default": "NO_ACTION", - "description": "What to do when the domain belongs to a disposable/temporary-mail provider (`email.is_disposable`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `email.warnings`." + "description": "Only with `delete_all`." }, - "enable_social": { - "type": "boolean", - "default": false, - "description": "When true, also look up the social footprint of the address - the email, social, professional, e-commerce and entertainment platforms it is registered on. Billed as one `email_social` unit per request in addition to this endpoint's own price.", - "example": true + "retained_from": { + "type": "string", + "format": "date-time", + "description": "Only with `delete_all`." }, - "email_no_social_presence_action": { + "retained_to": { "type": "string", - "enum": [ - "NO_ACTION", - "DECLINE" - ], - "default": "NO_ACTION", - "description": "Action when the social footprint check finds the address registered on none of the platforms it covers. Only NO_ACTION and DECLINE are accepted on this endpoint; REVIEW is available on the workflow configuration key of the same name." + "format": "date-time", + "description": "Only with `delete_all`. Must not be earlier than `retained_from`." } - }, - "required": [ - "email", - "code" - ] + } }, "examples": { - "Default": { - "summary": "Record any risks but never auto-decline", + "By id": { "value": { - "email": "alice@example.com", - "code": "123456" + "template_uuids": [ + "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", + "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d" + ] } }, - "Strict risk policy": { - "summary": "Auto-decline disposable and breached inboxes", + "Every template of one User": { "value": { - "email": "alice@example.com", - "code": "123456", - "disposable_email_action": "DECLINE", - "breached_email_action": "DECLINE" + "delete_all": true, + "vendor_user_uuid": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f" + } + }, + "Every template in the application": { + "value": { + "delete_all": true } } } @@ -22357,1339 +44302,1724 @@ }, "responses": { "200": { - "description": "Check completed — wrong codes and missing verifications also return `200`; inspect `status`, not the HTTP code. `email` is populated only on finalized outcomes (`Approved`/`Declined`), `null` on `Failed`, and absent on `Expired or Not Found`.", + "description": "Per-template purge outcomes.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmailVerificationCheckResponse" + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "outcome": { + "type": "string", + "enum": [ + "purged", + "already_purged" + ] + } + } + } + } + } }, "examples": { - "Approved": { - "summary": "Correct code — clean address, full email report", + "Mixed": { "value": { - "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24", - "status": "Approved", - "message": "The verification code is correct.", - "email": { - "status": "Approved", - "email": "alice@example.com", - "is_breached": false, - "breaches": [], - "is_disposable": false, - "is_undeliverable": false, - "verification_attempts": 1, - "verified_at": "2026-06-12T01:24:47.311323Z", - "warnings": [], - "lifecycle": [ - { - "type": "EMAIL_VERIFICATION_MESSAGE_SENT", - "timestamp": "2026-06-12T01:23:39.580554+00:00", - "details": { - "status": "Success", - "reason": null - }, - "fee": 0.03 - }, - { - "type": "VALID_CODE_ENTERED", - "timestamp": "2026-06-12T01:24:47.311201+00:00", - "details": { - "code_tried": "123456", - "status": "Approved" - }, - "fee": 0 - }, - { - "type": "EMAIL_VERIFICATION_APPROVED", - "timestamp": "2026-06-12T01:24:47.384292+00:00", - "details": null, - "fee": 0 - } - ], - "matches": [] - }, - "vendor_data": "user-1234", - "metadata": null, - "created_at": "2026-06-12T01:24:47.401719+00:00" + "results": [ + { + "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", + "outcome": "purged" + }, + { + "uuid": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", + "outcome": "already_purged" + } + ] } - }, - "Approved with breach exposure": { - "summary": "Correct code; breach exposure recorded as a warning (breached_email_action=NO_ACTION)", + } + } + } + } + }, + "400": { + "description": "Validation error — nothing is purged.", + "content": { + "application/json": { + "examples": { + "Missing selector": { "value": { - "request_id": "3a1f2b6c-9d4e-4f7a-8b2c-1e5d6f7a8b9c", - "status": "Approved", - "message": "The verification code is correct.", - "email": { - "status": "Approved", - "email": "bob@example.com", - "is_breached": true, - "breaches": [ - { - "name": "ExampleApp", - "domain": "exampleapp.com", - "breach_date": "2019-03-21", - "breach_emails_count": 763117, - "description": "In March 2019, the social platform ExampleApp suffered a data breach that exposed email addresses and passwords.", - "logo_path": "https:///logos/ExampleApp.png", - "data_classes": [ - "email_addresses", - "passwords" - ], - "is_verified": true - } - ], - "is_disposable": false, - "is_undeliverable": false, - "verification_attempts": 1, - "verified_at": "2026-06-12T01:24:47.311323Z", - "warnings": [ - { - "feature": "EMAIL", - "risk": "BREACHED_EMAIL_DETECTED", - "additional_data": null, - "log_type": "information", - "short_description": "Breached email detected", - "long_description": "This email address was found in one or more known data breaches." - } - ], - "lifecycle": [ - { - "type": "EMAIL_VERIFICATION_MESSAGE_SENT", - "timestamp": "2026-06-12T01:23:39.580554+00:00", - "details": { - "status": "Success", - "reason": null - }, - "fee": 0.03 - }, - { - "type": "VALID_CODE_ENTERED", - "timestamp": "2026-06-12T01:24:47.311201+00:00", - "details": { - "code_tried": "835126", - "status": "Approved" - }, - "fee": 0 - }, - { - "type": "EMAIL_VERIFICATION_APPROVED", - "timestamp": "2026-06-12T01:24:47.384292+00:00", - "details": null, - "fee": 0 - } - ], - "matches": [] - }, - "vendor_data": "user-5678", - "metadata": null, - "created_at": "2026-06-12T01:24:47.401719+00:00" + "template_uuids": [ + "This field is required unless delete_all is true." + ] } }, - "Failed (attempts remaining)": { - "summary": "Wrong code — the user can retry; request_id here is a one-off UUID", + "Bad range": { "value": { - "request_id": "11111111-2222-3333-4444-555555555555", - "status": "Failed", - "message": "The verification code is incorrect. Attempts remaining: 2", - "email": null, - "vendor_data": "user-1234", - "metadata": null, - "created_at": "2026-06-12T01:24:21.512340+00:00" + "retained_to": [ + "Must be on or after retained_from." + ] } - }, - "Declined (risk policy)": { - "summary": "Correct code, but the address is disposable and disposable_email_action=DECLINE", + } + } + } + } + }, + "403": { + "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", + "content": { + "application/json": { + "examples": { + "Forbidden": { "value": { - "request_id": "8c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f", - "status": "Declined", - "message": "The verification code is correct.", - "email": { - "status": "Declined", - "email": "tempuser42@mailinator.com", - "is_breached": false, - "breaches": [], - "is_disposable": true, - "is_undeliverable": false, - "verification_attempts": 1, - "verified_at": "2026-06-12T01:24:47.311323Z", - "warnings": [ - { - "feature": "EMAIL", - "risk": "DISPOSABLE_EMAIL_DETECTED", - "additional_data": null, - "log_type": "error", - "short_description": "Disposable email detected", - "long_description": "The system detected that the email is disposable, which is not allowed." - } - ], - "lifecycle": [ - { - "type": "EMAIL_VERIFICATION_MESSAGE_SENT", - "timestamp": "2026-06-12T01:23:39.580554+00:00", - "details": { - "status": "Success", - "reason": null - }, - "fee": 0.03 + "detail": "You do not have permission to perform this action." + } + } + } + } + } + }, + "503": { + "description": "A biometric store was unavailable while retaining or purging a biometric template. Nothing is deleted; repeat the same request. Retries are idempotent and converge on a single template.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Retryable": { + "value": { + "detail": "The biometric-template operation could not finish because an external store is unavailable. The operation is retryable." + } + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/biometric-templates/delete/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"delete_all\": true, \"vendor_user_uuid\": \"2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f\"}'" + } + ] + } + }, + "/v3/kyb/shareholders/": { + "post": { + "summary": "KYB registry company with shareholders (standalone)", + "operationId": "post_v3_kyb_shareholders", + "x-didit-sdk": { + "resource": "kyb", + "method": "list_shareholders", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [], + "description": "Retrieve the Lite profile **plus the direct shareholders** (natural persons and corporate entities, with their ownership bands) of one candidate returned by `POST /v3/kyb/search/`.\n\n**Pairing with search.** Pass the candidate's `kyb_response_id` exactly as returned by a search of the same application. The search runs on the Lite registry chain; when the country's ownership data lives at another registry Didit re-finds the company there by registration number, at no extra cost.\n\n**Billing.** Billed at the country's Shareholders price - see [KYB registry pricing](/getting-started/kyb-registry-pricing). When the registry holds no shareholder record for the company (or the country offers no ownership data at all) the Lite profile is delivered and billed instead: read `registry_tier_delivered` and `registry_ownership_status`. The balance is checked against the Shareholders price before any registry call (`403` when short).\n\n**Availability.** v3 only. Every call creates a business session (its id is the `request_id`).\n\n**Pending reports.** When a report is still being prepared, or the ownership section has not been retrieved from the registry yet, the endpoint returns 202 Accepted, kyb_registry.fetch_status=pending, kyb_registry.registry_ownership_status=pending, and registry_tier_delivered=null. Keep request_id and retrieve GET /v3/session/{request_id}/decision/ until registry_checks[0].company.fetch_status is resolved. Didit completes the report automatically and charges the delivered tier once it is ready. Do not repeat the purchase while it is pending; every new POST creates a separate request.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KYBRegistryCandidateRequest" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/kyb/shareholders/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"kyb_response_id\": \"69aeeb95febb0f1704042259\",\n \"vendor_data\": \"company-1234\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os, time, requests\n\nsearch = requests.post(\n \"https://verification.didit.me/v3/kyb/search/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n timeout=60,\n).json()\nkyb_response_id = search[\"kyb_registry\"][\"companies\"][0][\"kyb_response_id\"]\n\nresp = requests.post(\n \"https://verification.didit.me/v3/kyb/shareholders/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"kyb_response_id\": kyb_response_id, \"vendor_data\": \"company-1234\"},\n timeout=90,\n)\nresp.raise_for_status()\ndata = resp.json()\nrequest_id = data[\"request_id\"]\nprint(\"Keep this request ID:\", request_id)\ncompany = data[\"kyb_registry\"]\nif resp.status_code == 202 or company.get(\"fetch_status\") == \"pending\":\n # This client wait limit does not cancel the report or create a new purchase.\n deadline = time.monotonic() + 300\n while company.get(\"fetch_status\") != \"resolved\" and time.monotonic() < deadline:\n time.sleep(5)\n decision = requests.get(\n f\"https://verification.didit.me/v3/session/{request_id}/decision/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n timeout=30,\n )\n decision.raise_for_status()\n checks = decision.json().get(\"registry_checks\") or []\n if checks:\n company = checks[0].get(\"company\") or company\nif company.get(\"fetch_status\") == \"resolved\":\n print(company[\"company_name\"], company[\"registry_tier_delivered\"])\nelse:\n print(\"Still processing; retrieve this request later:\", request_id)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/kyb/shareholders/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n kyb_response_id: '69aeeb95febb0f1704042259',\n vendor_data: 'company-1234',\n }),\n});\nif (!res.ok) throw new Error(`Request failed: ${res.status}`);\nconst data = await res.json();\nconst requestId = data.request_id;\nconsole.log('Keep this request ID:', requestId);\nlet company = data.kyb_registry;\nif (res.status === 202 || company.fetch_status === 'pending') {\n // This client wait limit does not cancel the report or create a new purchase.\n const deadline = Date.now() + 300000;\n while (company.fetch_status !== 'resolved' && Date.now() < deadline) {\n await new Promise(resolve => setTimeout(resolve, 5000));\n const decisionResponse = await fetch(\n `https://verification.didit.me/v3/session/${requestId}/decision/`,\n { headers: { 'x-api-key': 'YOUR_API_KEY' }, signal: AbortSignal.timeout(30000) },\n );\n if (!decisionResponse.ok) throw new Error(`Retrieval failed: ${decisionResponse.status}`);\n const decision = await decisionResponse.json();\n company = decision.registry_checks?.[0]?.company ?? company;\n }\n}\nif (company.fetch_status === 'resolved') {\n console.log(company.company_name, company.registry_tier_delivered);\n} else {\n console.log('Still processing; retrieve this request later:', requestId);\n}" + } + ], + "responses": { + "200": { + "description": "Company report returned and billed at the delivered tier. Ownership reports still being prepared can return 202; keep request_id to retrieve their saved decision.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Id of the business session created for this retrieval. The stored record (visible in the console) keeps updating if the registry resolves after the response." + }, + "registry_tier_requested": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo" + ], + "description": "The tier this endpoint sells: `basic` (Lite) for select, `shareholders`, `ubo`." + }, + "registry_tier_delivered": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo", + null + ], + "description": "The tier actually delivered and billed. Lower than requested when the country's registries hold no ownership data, or the registry has no ownership record for this company.", + "nullable": true + }, + "kyb_registry": { + "type": "object", + "description": "Full registry company profile.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Didit's id for this company record." + }, + "node_id": { + "type": "string", + "example": "feature_kyb_registry" + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review" + ], + "description": "Didit's assessment of the registry check (e.g. `In Review` when the company is not active in the registry or ownership data could not be derived)." + }, + "registry_status": { + "type": "string", + "nullable": true, + "enum": [ + "active", + "dissolved", + "deregistered", + "see full details", + "authorised", + "appointed representative", + "unauthorised", + "inactive", + "no longer authorised", + "closed", + "struck off", + null + ], + "description": "Normalized company status as reported by the registry." + }, + "data_resolved": { + "type": "boolean", + "description": "`true` once the registry returned the full profile. `false` when the registry was still processing after polling — the stored record completes automatically once the registry resolves." + }, + "company_name": { + "type": "string", + "example": "Tesco PLC" + }, + "registration_number": { + "type": "string", + "nullable": true, + "example": "00445790" + }, + "country_code": { + "type": "string", + "description": "ISO 3166-1 alpha-2.", + "example": "GB" + }, + "region": { + "type": "string", + "nullable": true, + "description": "Subdivision code when the registry is regional (e.g. `CA` for `US-CA`); empty otherwise." + }, + "company_type": { + "type": "string", + "nullable": true, + "example": "Public Limited Company" + }, + "incorporation_date": { + "type": "string", + "format": "date", + "nullable": true, + "example": "1947-11-27" + }, + "registered_address": { + "type": "string", + "nullable": true, + "example": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA" + }, + "tax_number": { + "type": "string", + "nullable": true, + "example": "GB412 8925 93" + }, + "risk_level": { + "type": "string", + "nullable": true, + "description": "Registry-reported risk level, when available." + }, + "verification_status": { + "type": "string", + "nullable": true, + "description": "Registry verification status, when available." + }, + "is_from_registry": { + "type": "boolean", + "description": "Always `true` for this endpoint — the profile was retrieved from an official registry." + }, + "fetch_status": { + "type": "string", + "enum": [ + "pending", + "resolved" + ], + "description": "Registry fetch lifecycle. Mirrored by `data_resolved`." + }, + "alternative_names": { + "type": "string", + "nullable": true, + "description": "Alternative, trading, former or original-language company names as text, when reported." + }, + "nature_of_business": { + "type": "string", + "nullable": true, + "description": "Activity description or classification code, when reported." + }, + "registered_capital": { + "type": "string", + "nullable": true + }, + "registered_capital_amount": { + "type": "string", + "nullable": true, + "description": "Decimal amount as a string, when reported." + }, + "registered_capital_currency": { + "type": "string", + "nullable": true, + "description": "ISO 4217 code, when reported." + }, + "website": { + "type": "string", + "nullable": true, + "example": "https://www.tesco.com/" + }, + "email": { + "type": "string", + "nullable": true, + "example": "customer.service@tesco.co.uk" + }, + "phone": { + "type": "string", + "nullable": true, + "example": "03301231688" + }, + "legal_entity_identifier": { + "type": "string", + "nullable": true, + "description": "LEI, when reported." + }, + "location_of_registration": { + "type": "string", + "nullable": true, + "description": "Registry/jurisdiction the company is registered with, when reported." + }, + "vat_number": { + "type": "string", + "nullable": true, + "description": "EU VAT number, when collected during the hosted registry step or edited afterwards. At select time this is typically not yet set — VAT and tax ID validation runs when the end user submits the registry step in the hosted flow." + }, + "vat_validation_status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "format_valid", + "invalid_format", + "could_not_validate", + "not_applicable" + ], + "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." + }, + "vat_validated_number": { + "type": "string", + "nullable": true, + "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." + }, + "vat_validation_source": { + "type": "string", + "nullable": true, + "enum": [ + "eu_vies", + "pl_vat_white_list", + "no_brreg", + "ch_uid_register", + "br_receita_federal", + "co_rues", + "cr_hacienda", + "ec_sri", + "py_dnit", + "format_rules", + null + ], + "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." + }, + "vat_validated_name": { + "type": "string", + "nullable": true, + "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_validated_address": { + "type": "string", + "nullable": true, + "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_checked_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the check. `null` when no check ran." + }, + "financial_summary": { + "type": "object", + "nullable": true, + "description": "Registry-reported share/capital details (currency, total shares, share classes), when available." + }, + "officers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "example": "MELISSA BETHELL" + }, + "designation": { + "type": "string", + "nullable": true, + "description": "Raw registry designation (e.g. `Director`, `Secretary`)." + }, + "role": { + "type": "string", + "description": "Normalized role derived from the designation. Full 17-value model set; registry parsing on this endpoint typically emits director, non_executive_director, secretary, chairman, or company_officer.", + "enum": [ + "director", + "non_executive_director", + "secretary", + "chairman", + "ubo", + "shareholder", + "representative", + "founder", + "legal_advisor", + "authorized_signatory", + "trustee", + "beneficiary", + "company_officer", + "settlor", + "protector", + "investor", + "other" + ] + }, + "nationality": { + "type": "string", + "nullable": true + }, + "is_active": { + "type": "boolean", + "description": "`false` when the registry marks the appointment as resigned." + }, + "kyc_status": { + "type": "string", + "nullable": true, + "enum": [ + "Approved", + "Declined", + "Pending", + null + ], + "description": "Status of an associated KYC session, when one was started for this officer. `null` otherwise." + }, + "kyc_session_url": { + "type": "string", + "nullable": true, + "description": "Verification URL of the associated KYC session, when one exists." + }, + "first_name": { + "type": "string", + "nullable": true, + "description": "Given name when explicitly reported by the registry; not inferred from the full name." + }, + "last_name": { + "type": "string", + "nullable": true, + "description": "Family name when explicitly reported by the registry." + }, + "address": { + "type": "string", + "nullable": true, + "description": "Officer address reported by the registry." + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Date the officer was appointed.", + "format": "date" + }, + "termination_date": { + "type": "string", + "nullable": true, + "description": "Date the appointment ended, when reported.", + "format": "date" + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", + "format": "date" + }, + "date_of_birth_year": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 9999, + "description": "Registry-reported birth year, including when the full date is unavailable." + }, + "date_of_birth_month": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 12, + "description": "Registry-reported birth month. Null when only the year is known." + } + } }, - { - "type": "VALID_CODE_ENTERED", - "timestamp": "2026-06-12T01:24:47.311201+00:00", - "details": { - "code_tried": "123456", - "status": "Approved" - }, - "fee": 0 + "description": "Directors, secretaries, and other officers extracted from the registry." + }, + "beneficial_owners": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "first_name": { + "type": "string", + "nullable": true + }, + "last_name": { + "type": "string", + "nullable": true + }, + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ], + "description": "Whether the owner is a natural person or a corporate entity." + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Normalized roles (e.g. `ubo`, `shareholder`)." + }, + "ownership_min_shares": { + "type": "string", + "nullable": true, + "description": "Lower bound of the ownership band reported by the registry (e.g. `25%`)." + }, + "ownership_max_shares": { + "type": "string", + "nullable": true, + "description": "Upper bound of the ownership band reported by the registry." + }, + "is_active": { + "type": "boolean" + }, + "kyc_status": { + "type": "string", + "nullable": true, + "enum": [ + "Approved", + "Declined", + "Pending", + null + ] + }, + "kyc_session_url": { + "type": "string", + "nullable": true + }, + "effective_ownership_percent": { + "type": "number", + "nullable": true, + "description": "Parsed ownership percentage (max bound, falling back to min bound)." + }, + "company_name": { + "type": "string", + "nullable": true + }, + "registration_number": { + "type": "string", + "nullable": true + }, + "country_of_birth": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + }, + "nationality": { + "type": "string", + "nullable": true + }, + "designation": { + "type": "string", + "nullable": true + }, + "voting_min_shares": { + "type": "string", + "nullable": true + }, + "voting_max_shares": { + "type": "string", + "nullable": true + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete birth date when reported.", + "format": "date" + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Appointment date when reported.", + "format": "date" + }, + "shareholding": { + "$ref": "#/components/schemas/KYBRegistryShareholding" + } + } }, - { - "type": "EMAIL_VERIFICATION_DECLINED", - "timestamp": "2026-06-12T01:24:47.384292+00:00", - "details": { - "reason": "DISPOSABLE_EMAIL_DETECTED" - }, - "fee": 0 + "description": "Beneficial owners / persons with significant control extracted from the registry. Can be empty when the registry does not expose ownership." + }, + "addresses": { + "type": "array", + "nullable": true, + "description": "Every address the registry states, one canonical shape. Null when it states none.", + "items": { + "$ref": "#/components/schemas/KYBRegistryAddress" } - ], - "matches": [] - }, - "vendor_data": "user-1234", - "metadata": null, - "created_at": "2026-06-12T01:24:47.401719+00:00" - } - }, - "Expired or Not Found": { - "summary": "No pending verification for this address in the last 5 minutes", - "value": { - "request_id": "88808a77-06e9-4cb0-85f5-9c052ddfd987", - "status": "Expired or Not Found", - "message": "No pending email verification found in the last 5 minutes.", - "vendor_data": null, - "metadata": null, - "created_at": "2026-06-12T01:24:59.887904+00:00" - } - } - } - } - } - }, - "400": { - "description": "Validation error — DRF's field-error envelope: one array of messages per offending field.", - "content": { - "application/json": { - "examples": { - "Invalid email": { - "summary": "Malformed email address", - "value": { - "email": [ - "Enter a valid email address." - ] - } - }, - "Missing code": { - "summary": "`code` not included in the body", - "value": { - "code": [ - "This field is required." - ] - } - } - } - } - } - }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — this API never returns `401`. Checks are free, so there is no credit check here.", - "content": { - "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } - }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", - "content": { - "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - } - } - } - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - } - }, - "/v3/workflows/": { - "get": { - "summary": "List workflows", - "description": "List your workflows, newest first, in a limit/offset pagination envelope (`{count, next, previous, results}`, default page size 50). Each row is one version; `workflow_id` is the stable group ID and new sessions use the latest `published` version.", - "operationId": "list_workflows", - "tags": [ - "Workflows" - ], - "parameters": [ - { - "name": "limit", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "default": 50 - }, - "description": "Page size (number of workflows per page)." - }, - { - "name": "offset", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "default": 0 - }, - "description": "Number of rows to skip before the first returned workflow." - } - ], - "responses": { - "200": { - "description": "One page of workflows ordered by `created_at` descending, wrapped in the pagination envelope. Each item in `results` is a `WorkflowListItem`.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer", - "description": "Total number of workflow versions for the application." + }, + "industries": { + "type": "array", + "nullable": true, + "description": "The activities the registry classifies the company under, one canonical shape. Null when it states none.", + "items": { + "$ref": "#/components/schemas/KYBRegistryIndustry" + } + }, + "accounts": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." + }, + "registry_data": { + "type": "object", + "description": "Raw record returned by the registry source, kept for reviewers. Its shape differs by country and source and is not a stable contract: read the canonical fields (`addresses`, `industries`, `ownership`, `beneficial_owners[].shareholding`) instead." + }, + "user_provided_data": { + "type": "object", + "nullable": true, + "description": "Always `null` on this endpoint (used by the hosted KYB flow when end users edit registry data)." + }, + "confirmed_by_user_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Always `null` on this endpoint." + }, + "last_console_edit_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Set when a console reviewer edits the record later." + }, + "is_editable": { + "type": "boolean", + "description": "Whether the registry-sourced data can still be edited in the hosted flow/console." + }, + "registry_tier_delivered": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo", + null + ], + "description": "Same as the top-level `registry_tier_delivered`.", + "nullable": true + }, + "registry_ownership_status": { + "type": "string", + "nullable": true, + "enum": [ + "not_requested", + "delivered", + "unavailable", + "pending", + null + ], + "description": "`delivered` when the ownership data came back, `unavailable` when an ownership tier was requested but the registry had none for this company (see `registry_data.ownership_error`), `pending` when the ownership section has not been retrieved from the registry yet and Didit is still completing it, `not_requested` on the Lite tier." + }, + "ownership": { + "$ref": "#/components/schemas/KYBRegistryOwnership" + }, + "registry_tier": { + "type": "string", + "nullable": true, + "description": "The registry tier the workflow ordered: `basic`, `shareholders` or `ubo`. Also in `ownership.tier_requested`." + } + } }, - "next": { + "vendor_data": { "type": "string", - "format": "uri", "nullable": true, - "description": "URL of the next page, or `null` on the last page." + "description": "Echo of the `vendor_data` you sent." }, - "previous": { - "type": "string", - "format": "uri", + "metadata": { + "type": "object", "nullable": true, - "description": "URL of the previous page, or `null` on the first page." + "description": "Echo of the `metadata` you sent." }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WorkflowListItem" - } + "created_at": { + "type": "string", + "format": "date-time" } } }, "examples": { - "Two workflows": { - "summary": "A default KYC workflow and a draft KYC + AML workflow", + "Resolved company": { + "summary": "Full registry profile (officer list truncated for brevity)", "value": { - "count": 2, - "next": null, - "previous": null, - "results": [ - { - "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111", - "workflow_id": "a1b2c3d4-5678-90ab-cdef-111111111111", - "workflow_label": "Standard KYC", - "workflow_type": "kyc", - "is_default": true, - "is_archived": false, - "is_white_label_enabled": false, - "total_price": 0.15, - "min_price": 0.15, - "max_price": 0.15, - "features": "OCR + LIVENESS + FACE_MATCH", - "is_simple_workflow": true, - "is_editable": false, - "workflow_url": "https://verify.didit.me/u/_yljPOx8RWG3UAz5ZRQGVQ", - "max_retry_attempts": 3, - "retry_window_days": 7, - "session_expiration_time": 604800, - "version": 2, - "status": "published", - "has_draft": false, - "created_by": { - "name": "Alex Mateo", - "email": "alex@example.com", - "type": "user" - }, - "updated_at": "2026-04-12T10:30:00Z" + "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", + "kyb_registry": { + "uuid": "f1e2d3c4-b5a6-4789-9c0d-1e2f3a4b5c6d", + "node_id": "feature_kyb_registry", + "status": "In Review", + "registry_status": "active", + "data_resolved": true, + "company_name": "Tesco PLC", + "registration_number": "00445790", + "country_code": "GB", + "region": "", + "company_type": "Public Limited Company", + "incorporation_date": "1947-11-27", + "registered_address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", + "tax_number": "GB412 8925 93", + "risk_level": "low risk", + "verification_status": "verified", + "is_from_registry": true, + "fetch_status": "resolved", + "alternative_names": "TESCO STORES (HOLDINGS) PUBLIC LIMITED COMPANY", + "nature_of_business": null, + "registered_capital": null, + "registered_capital_amount": null, + "registered_capital_currency": null, + "website": "https://www.tesco.com/", + "email": "customer.service@tesco.co.uk", + "phone": "03301231688", + "legal_entity_identifier": null, + "location_of_registration": null, + "financial_summary": { + "currency": "Pound (£)", + "total_number_of_shares": "8174378721", + "meta_detail": { + "Class_Of_Share": "Ordinary" + } }, - { - "uuid": "b2c3d4e5-6789-01bc-defa-222222222222", - "workflow_id": "b2c3d4e5-6789-01bc-defa-222222222222", - "workflow_label": "Full Verification + AML", - "workflow_type": "kyc", - "is_default": false, - "is_archived": false, - "is_white_label_enabled": false, - "total_price": 0.25, - "min_price": 0.25, - "max_price": 0.25, - "features": "OCR + LIVENESS + FACE_MATCH + AML", - "is_simple_workflow": true, - "is_editable": true, - "workflow_url": "https://verify.didit.me/u/Aq3kPOx8RWG3UAz5ZRQGVQ", - "max_retry_attempts": 3, - "retry_window_days": 7, - "session_expiration_time": 604800, - "version": 1, - "status": "draft", - "has_draft": true, - "created_by": null, - "updated_at": "2026-04-15T18:05:21Z" - } - ] - } - } - } - } - } - }, - "403": { - "description": "The API key is missing, malformed, expired, or belongs to a different application. Body: `{\"detail\": \"You do not have permission to perform this action.\"}`. Refresh the token via the [Auth API](/auth-api/get-credentials) and retry.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, - "examples": { - "Invalid token": { - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } - }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff and honour any `Retry-After` header." - }, - "500": { - "description": "Unexpected server error while building the list. The request can be safely retried." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl -X GET 'https://verification.didit.me/v3/workflows/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Accept: application/json'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\nresp = requests.get(\n \"https://verification.didit.me/v3/workflows/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status()\npage = resp.json()\nfor workflow in page[\"results\"]:\n print(workflow[\"uuid\"], workflow[\"workflow_label\"], workflow[\"status\"])" - }, - { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(\"https://verification.didit.me/v3/workflows/\", {\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst page = await res.json();\npage.results.forEach((w) => console.log(w.uuid, w.workflow_label, w.status));" - } - ] - }, - "post": { - "summary": "Create workflow", - "description": "Create a simple workflow. Send `features` in execution order (ordering rules apply); defaults to `published` v1. The body accepts ONLY the fields listed in `SimpleWorkflowRequest` — any other key (including `workflow_type`) is rejected with a 400. Not idempotent — each call creates a new workflow. Maximum 50 workflows per application.", - "operationId": "create_workflow", - "tags": [ - "Workflows" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SimpleWorkflowRequest" - }, - "examples": { - "Simple KYC": { - "summary": "Linear ID, liveness, and face match workflow", - "value": { - "workflow_label": "Standard KYC", - "features": [ - { - "feature": "OCR", - "config": { - "duplicated_user_action": "review" - } - }, - { - "feature": "LIVENESS", - "config": { - "face_liveness_method": "passive" - } - }, - { - "feature": "FACE_MATCH", - "config": { - "face_match_score_decline_threshold": 40, - "face_match_score_review_threshold": 60 - } - } - ] - } - }, - "KYC + AML": { - "summary": "Run AML after ID verification", - "value": { - "workflow_label": "KYC with AML", - "features": [ - { - "feature": "OCR" + "officers": [ + { + "uuid": "a1b2c3d4-e5f6-4789-9a0b-1c2d3e4f5a6b", + "name": "CHRISTOPHER JON TAYLOR", + "designation": "Secretary", + "role": "secretary", + "nationality": null, + "is_active": true, + "kyc_status": null, + "kyc_session_url": null + }, + { + "uuid": "b2c3d4e5-f6a7-4890-8b1c-2d3e4f5a6b7c", + "name": "MELISSA BETHELL", + "designation": "Director", + "role": "director", + "nationality": "British", + "is_active": true, + "kyc_status": null, + "kyc_session_url": null + } + ], + "beneficial_owners": [], + "addresses": [ + { + "address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", + "type": "legal_entity_registry_address", + "description": "registered office address" + } + ], + "industries": null, + "accounts": { + "last_account": "26 February 2025", + "next_account": "26 February 2026", + "due_by_date": "26 August 2026" + }, + "registry_data": { + "name": "Tesco PLC", + "registration_number": "00445790", + "status": "active", + "type": "Public Limited Company", + "incorporation_date": "1947-11-27", + "tax_number": "GB412 8925 93", + "country_code": "GB", + "fetch_status": "resolved", + "industries_detail": [ + { + "code": "47110", + "name": "Retail Sale In Non-specialised Stores With Food, Beverages Or Tobacco Predominating", + "description": null, + "meta_detail": [] + } + ] + }, + "user_provided_data": null, + "confirmed_by_user_at": null, + "last_console_edit_at": null, + "is_editable": true }, - { - "feature": "AML", - "config": { - "aml_score_review_threshold": 70, - "aml_score_approve_threshold": 30 - } - } - ] - } - }, - "Questionnaire workflow": { - "summary": "Run a questionnaire created with POST /v3/questionnaires/", - "value": { - "workflow_label": "Source of Funds", - "features": [ - { - "feature": "QUESTIONNAIRE", - "config": { - "questionnaire_uuid": "11111111-2222-3333-4444-555555555555", - "review_questionnaire_manually": true - } - } - ] + "vendor_data": "company-1234", + "metadata": null, + "created_at": "2026-06-11T10:45:00.000000+00:00" + } } } } } - } - }, - "responses": { - "201": { - "description": "Workflow created. The response body is the FULL workflow configuration — the same raw `VerificationSettings` serialization returned by `GET /v3/workflows/{settings_uuid}/` (~190 fields: every feature toggle, threshold, action and the generated `workflow_graph`), not the compact list item. Store the `uuid` (also exposed as `workflow_id`) so you can reference it from later session-creation calls. Note: `features` is a `\" + \"`-joined string and prices are JSON numbers.", + }, + "400": { + "description": "Validation error. Field-level problems use DRF's standard envelope.", "content": { "application/json": { - "schema": { - "type": "object", - "description": "Full workflow configuration (abbreviated here — see `GET /v3/workflows/{settings_uuid}/`).", - "additionalProperties": true - }, "examples": { - "Created (abbreviated)": { - "summary": "Key fields of the full configuration object", + "Missing kyb_response_id": { + "summary": "`kyb_response_id` is required", "value": { - "uuid": "c3d4e5f6-7890-12cd-ef34-333333333333", - "workflow_id": "c3d4e5f6-7890-12cd-ef34-333333333333", - "workflow_label": "Standard KYC", - "workflow_type": null, - "is_default": false, - "is_archived": false, - "total_price": 0.15, - "min_price": 0.15, - "max_price": 0.15, - "features": "OCR + LIVENESS + FACE_MATCH", - "is_simple_workflow": false, - "is_editable": false, - "workflow_url": "https://verify.didit.me/u/w9TkPOx8RWG3UAz5ZRQGVQ", - "workflow_graph": { - "start_node": "ocr", - "nodes": "... one node per feature plus a final status node ..." - }, - "max_retry_attempts": 3, - "retry_window_days": 7, - "session_expiration_time": 604800, - "version": 1, - "status": "published", - "published_at": "2026-05-17T10:30:00Z", - "created_at": "2026-05-17T10:30:00Z", - "updated_at": "2026-05-17T10:30:00Z" + "kyb_response_id": [ + "This field is required." + ] + } + }, + "save_api_request=false rejected": { + "summary": "Selects are always persisted", + "value": { + "save_api_request": [ + "KYB registry select must be saved so billing and retrieval can be tracked." + ] } } } } } }, - "400": { - "description": "Validation error. Common causes: a top-level key outside the accepted whitelist (e.g. `workflow_type`), an unknown `feature` value, a feature placed before its dependency (e.g., `FACE_MATCH` before `OCR`), a passive feature (`AML`, `IP_ANALYSIS`, `DATABASE_VALIDATION`, `KYB_KEY_PEOPLE`) used as the first step, a `QUESTIONNAIRE` feature whose `config.questionnaire_uuid` does not exist for this application, or an out-of-range threshold. Body is a DRF validation error object — keys are field names, values are arrays of human-readable messages.", + "403": { + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", "content": { "application/json": { "examples": { - "Missing feature": { - "value": { - "features": [ - "At least one feature is required." - ] - } - }, - "Unknown top-level field": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", "value": { - "workflow_type": [ - "The v3 workflow API only accepts `workflow_label`, `is_default`, `status`, `features`, `is_white_label_enabled`, `is_desktop_allowed`, `max_retry_attempts`, `retry_window_days`, `session_expiration_time`, `face_liveness_max_attempts`, `face_match_max_attempts`." - ] + "detail": "You do not have permission to perform this action." } }, - "Unknown feature": { + "Not enough credits": { + "summary": "Organization balance cannot cover the call", "value": { - "features": [ - "Invalid feature 'FOO'. Allowed values: AGE_ESTIMATION, AML, DATABASE_VALIDATION, EMAIL_VERIFICATION, FACE_MATCH, IP_ANALYSIS, KYB_DOCUMENTS, KYB_KEY_PEOPLE, KYB_REGISTRY, LIVENESS, NFC, OCR, PHONE_VERIFICATION, PROOF_OF_ADDRESS, QUESTIONNAIRE." - ] + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" } - }, - "Ordering": { + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", "value": { - "features": [ - "Feature 'FACE_MATCH' must appear after one of: OCR." - ] + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." } - }, - "Questionnaire not found": { - "value": { - "features": "Questionnaire '11111111-2222-3333-4444-555555555555' was not found for this application. Create the questionnaire first, then use its questionnaire_id." + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" } } } } } }, - "403": { - "description": "The API key is missing, malformed, expired, or scoped to a different application. Refresh via the [Auth API](/auth-api/get-credentials) and retry.", + "202": { + "description": "Report accepted and still processing. When a report is still being prepared, the endpoint returns 202 Accepted, kyb_registry.fetch_status=pending, and registry_tier_delivered=null. Keep request_id and retrieve GET /v3/session/{request_id}/decision/ until registry_checks[0].company.fetch_status is resolved. Didit completes the report automatically and charges the delivered tier once it is ready. Do not repeat the purchase while it is pending; every new POST creates a separate request.", "content": { "application/json": { + "schema": { + "$ref": "#/paths/~1v3~1kyb~1shareholders~1/post/responses/200/content/application~1json/schema" + }, "examples": { - "Invalid token": { + "Pending (registry still resolving)": { + "summary": "Ownership report is still processing; retrieve the saved decision", "value": { - "detail": "You do not have permission to perform this action." + "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", + "kyb_registry": { + "uuid": "7c0a4f7e-2b1d-4f6a-9c3e-8d5b2a1f0e9d", + "node_id": "feature_kyb_registry", + "status": "Not Finished", + "registry_status": null, + "data_resolved": false, + "company_name": "Example Holdings Limited", + "registration_number": "12345678", + "country_code": "HK", + "region": null, + "company_type": null, + "incorporation_date": null, + "registered_address": null, + "tax_number": null, + "risk_level": null, + "verification_status": null, + "is_from_registry": true, + "fetch_status": "pending", + "alternative_names": null, + "nature_of_business": null, + "registered_capital": null, + "registered_capital_amount": null, + "registered_capital_currency": null, + "website": null, + "email": null, + "phone": null, + "legal_entity_identifier": null, + "location_of_registration": null, + "financial_summary": {}, + "officers": [], + "beneficial_owners": [], + "addresses": null, + "industries": null, + "accounts": null, + "registry_data": {}, + "user_provided_data": null, + "confirmed_by_user_at": null, + "last_console_edit_at": null, + "is_editable": true, + "registry_tier_delivered": null, + "registry_ownership_status": "pending" + }, + "vendor_data": "company-1234", + "metadata": null, + "created_at": "2026-06-11T10:45:00.000000+00:00", + "registry_tier_delivered": null, + "registry_tier_requested": "shareholders" } } } } } - }, - "415": { - "description": "Unsupported media type. Set `Content-Type: application/json` and send a JSON body." - }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." - }, - "500": { - "description": "Unexpected server error while creating the workflow. Safe to retry." } + } + } + }, + "/v3/kyb/ubo/": { + "post": { + "summary": "KYB registry company with ultimate beneficial owners (standalone)", + "operationId": "post_v3_kyb_ubo", + "x-didit-sdk": { + "resource": "kyb", + "method": "list_ubos", + "auth": "api_key", + "server": true }, + "tags": [ + "Standalone APIs" + ], "security": [ { "ApiKeyAuth": [] } ], + "parameters": [], + "description": "Retrieve the Lite profile, the direct shareholders **and the ultimate beneficial owners** resolved through the ownership chain (`registry_data.ubo_resolution` carries the corporate parent chain) of one candidate returned by `POST /v3/kyb/search/`.\n\n**Pairing with search.** Pass the candidate's `kyb_response_id` exactly as returned by a search of the same application. The search runs on the Lite registry chain; when the country's ownership data lives at another registry Didit re-finds the company there by registration number, at no extra cost.\n\n**Billing.** Billed at the country's UBO price - see [KYB registry pricing](/getting-started/kyb-registry-pricing). The tier delivered is the highest the country and the registry can serve: Shareholders when the resolved report names no natural person as a beneficial owner, Lite when there is no ownership record at all; `registry_tier_delivered` names what was billed. The balance is checked against the UBO price before any registry call (`403` when short).\n\n**Availability.** v3 only. Every call creates a business session (its id is the `request_id`).\n\n**Pending reports.** When a report is still being prepared, or the ownership section has not been retrieved from the registry yet, the endpoint returns 202 Accepted, kyb_registry.fetch_status=pending, kyb_registry.registry_ownership_status=pending, and registry_tier_delivered=null. Keep request_id and retrieve GET /v3/session/{request_id}/decision/ until registry_checks[0].company.fetch_status is resolved. Didit completes the report automatically and charges the delivered tier once it is ready. Do not repeat the purchase while it is pending; every new POST creates a separate request.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KYBRegistryCandidateRequest" + } + } + } + }, "x-codeSamples": [ { "lang": "curl", - "label": "cURL", - "source": "curl -X POST 'https://verification.didit.me/v3/workflows/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"workflow_label\": \"Standard KYC\",\n \"features\": [\n {\"feature\": \"OCR\"},\n {\"feature\": \"LIVENESS\", \"config\": {\"face_liveness_method\": \"passive\"}},\n {\"feature\": \"FACE_MATCH\", \"config\": {\"face_match_score_decline_threshold\": 40, \"face_match_score_review_threshold\": 60}}\n ]\n }'" + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/kyb/ubo/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"kyb_response_id\": \"69aeeb95febb0f1704042259\",\n \"vendor_data\": \"company-1234\"\n }'" }, { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\npayload = {\n \"workflow_label\": \"Standard KYC\",\n \"features\": [\n {\"feature\": \"OCR\"},\n {\"feature\": \"LIVENESS\", \"config\": {\"face_liveness_method\": \"passive\"}},\n {\"feature\": \"FACE_MATCH\"},\n ],\n}\nresp = requests.post(\n \"https://verification.didit.me/v3/workflows/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json=payload,\n timeout=10,\n)\nresp.raise_for_status()\nworkflow = resp.json()\nprint(\"Workflow id:\", workflow[\"uuid\"])" + "lang": "python", + "label": "Python", + "source": "import os, time, requests\n\nsearch = requests.post(\n \"https://verification.didit.me/v3/kyb/search/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n timeout=60,\n).json()\nkyb_response_id = search[\"kyb_registry\"][\"companies\"][0][\"kyb_response_id\"]\n\nresp = requests.post(\n \"https://verification.didit.me/v3/kyb/ubo/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"kyb_response_id\": kyb_response_id, \"vendor_data\": \"company-1234\"},\n timeout=90,\n)\nresp.raise_for_status()\ndata = resp.json()\nrequest_id = data[\"request_id\"]\nprint(\"Keep this request ID:\", request_id)\ncompany = data[\"kyb_registry\"]\nif resp.status_code == 202 or company.get(\"fetch_status\") == \"pending\":\n # This client wait limit does not cancel the report or create a new purchase.\n deadline = time.monotonic() + 300\n while company.get(\"fetch_status\") != \"resolved\" and time.monotonic() < deadline:\n time.sleep(5)\n decision = requests.get(\n f\"https://verification.didit.me/v3/session/{request_id}/decision/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n timeout=30,\n )\n decision.raise_for_status()\n checks = decision.json().get(\"registry_checks\") or []\n if checks:\n company = checks[0].get(\"company\") or company\nif company.get(\"fetch_status\") == \"resolved\":\n print(company[\"company_name\"], company[\"registry_tier_delivered\"])\nelse:\n print(\"Still processing; retrieve this request later:\", request_id)" }, { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(\"https://verification.didit.me/v3/workflows/\", {\n method: \"POST\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n workflow_label: \"Standard KYC\",\n features: [\n { feature: \"OCR\" },\n { feature: \"LIVENESS\", config: { face_liveness_method: \"passive\" } },\n { feature: \"FACE_MATCH\" },\n ],\n }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}: ${await res.text()}`);\nconst workflow = await res.json();\nconsole.log(\"Workflow id:\", workflow.uuid);" - } - ] - } - }, - "/v3/workflows/{settings_uuid}/": { - "get": { - "summary": "Get workflow", - "description": "Fetch full configuration for one workflow version by `settings_uuid`: feature toggles, thresholds, allowed documents, graph, retry policy, callbacks, and pricing.", - "operationId": "get_workflow", - "tags": [ - "Workflows" - ], - "parameters": [ - { - "name": "settings_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Per-version UUID of the workflow (the `uuid` value returned by the list endpoint). Each version of a workflow has its own `settings_uuid`." + "lang": "javascript", + "label": "JavaScript", + "source": "const res = await fetch('https://verification.didit.me/v3/kyb/ubo/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n kyb_response_id: '69aeeb95febb0f1704042259',\n vendor_data: 'company-1234',\n }),\n});\nif (!res.ok) throw new Error(`Request failed: ${res.status}`);\nconst data = await res.json();\nconst requestId = data.request_id;\nconsole.log('Keep this request ID:', requestId);\nlet company = data.kyb_registry;\nif (res.status === 202 || company.fetch_status === 'pending') {\n // This client wait limit does not cancel the report or create a new purchase.\n const deadline = Date.now() + 300000;\n while (company.fetch_status !== 'resolved' && Date.now() < deadline) {\n await new Promise(resolve => setTimeout(resolve, 5000));\n const decisionResponse = await fetch(\n `https://verification.didit.me/v3/session/${requestId}/decision/`,\n { headers: { 'x-api-key': 'YOUR_API_KEY' }, signal: AbortSignal.timeout(30000) },\n );\n if (!decisionResponse.ok) throw new Error(`Retrieval failed: ${decisionResponse.status}`);\n const decision = await decisionResponse.json();\n company = decision.registry_checks?.[0]?.company ?? company;\n }\n}\nif (company.fetch_status === 'resolved') {\n console.log(company.company_name, company.registry_tier_delivered);\n} else {\n console.log('Still processing; retrieve this request later:', requestId);\n}" } ], "responses": { "200": { - "description": "Full workflow configuration. The shape is the raw `VerificationSettings` serialization (~190 fields: every feature toggle, threshold, warning action, allowed-documents config, the `workflow_graph`, retry policy, callbacks and pricing). The example below is abbreviated. Notes: `features` is a `\" + \"`-joined string (e.g. `\"OCR + LIVENESS\"`), prices are JSON numbers, and `is_editable` is `true` only for `draft` versions.", + "description": "Company report returned and billed at the delivered tier. Ownership reports still being prepared can return 202; keep request_id to retrieve their saved decision.", "content": { "application/json": { - "examples": { - "KYC workflow with face match (abbreviated)": { - "value": { - "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111", - "workflow_id": "a1b2c3d4-5678-90ab-cdef-111111111111", - "workflow_label": "Standard KYC", - "workflow_type": "kyc", - "is_default": true, - "is_archived": false, - "is_liveness_enabled": true, - "face_liveness_method": "passive", - "face_liveness_score_decline_threshold": 50, - "face_liveness_score_review_threshold": 70, - "is_face_match_enabled": true, - "face_match_score_decline_threshold": 40, - "face_match_score_review_threshold": 60, - "is_aml_enabled": false, - "is_phone_verification_enabled": false, - "is_email_verification_enabled": false, - "is_database_validation_enabled": false, - "is_ip_analysis_enabled": false, - "is_nfc_enabled": false, - "duplicated_user_action": "no_action", - "documents_allowed": { - "ESP": { - "P": { - "sides": 1, - "enabled": 1, - "subtypes": [ - "EPASSPORT", - "PASSPORT_GENERIC" - ], - "preferred_characters": "latin", - "expiration_check_mode": "strict" + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Id of the business session created for this retrieval. The stored record (visible in the console) keeps updating if the registry resolves after the response." + }, + "registry_tier_requested": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo" + ], + "description": "The tier this endpoint sells: `basic` (Lite) for select, `shareholders`, `ubo`." + }, + "registry_tier_delivered": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo", + null + ], + "description": "The tier actually delivered and billed. Lower than requested when the country's registries hold no ownership data, when the registry has no ownership record for this company, or when the resolved report of a `ubo` request names no natural person as a beneficial owner.", + "nullable": true + }, + "kyb_registry": { + "type": "object", + "description": "Full registry company profile.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Didit's id for this company record." + }, + "node_id": { + "type": "string", + "example": "feature_kyb_registry" + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review" + ], + "description": "Didit's assessment of the registry check (e.g. `In Review` when the company is not active in the registry or ownership data could not be derived)." + }, + "registry_status": { + "type": "string", + "nullable": true, + "enum": [ + "active", + "dissolved", + "deregistered", + "see full details", + "authorised", + "appointed representative", + "unauthorised", + "inactive", + "no longer authorised", + "closed", + "struck off", + null + ], + "description": "Normalized company status as reported by the registry." + }, + "data_resolved": { + "type": "boolean", + "description": "`true` once the registry returned the full profile. `false` when the registry was still processing after polling — the stored record completes automatically once the registry resolves." + }, + "company_name": { + "type": "string", + "example": "Tesco PLC" + }, + "registration_number": { + "type": "string", + "nullable": true, + "example": "00445790" + }, + "country_code": { + "type": "string", + "description": "ISO 3166-1 alpha-2.", + "example": "GB" + }, + "region": { + "type": "string", + "nullable": true, + "description": "Subdivision code when the registry is regional (e.g. `CA` for `US-CA`); empty otherwise." + }, + "company_type": { + "type": "string", + "nullable": true, + "example": "Public Limited Company" + }, + "incorporation_date": { + "type": "string", + "format": "date", + "nullable": true, + "example": "1947-11-27" + }, + "registered_address": { + "type": "string", + "nullable": true, + "example": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA" + }, + "tax_number": { + "type": "string", + "nullable": true, + "example": "GB412 8925 93" + }, + "risk_level": { + "type": "string", + "nullable": true, + "description": "Registry-reported risk level, when available." + }, + "verification_status": { + "type": "string", + "nullable": true, + "description": "Registry verification status, when available." + }, + "is_from_registry": { + "type": "boolean", + "description": "Always `true` for this endpoint — the profile was retrieved from an official registry." + }, + "fetch_status": { + "type": "string", + "enum": [ + "pending", + "resolved" + ], + "description": "Registry fetch lifecycle. Mirrored by `data_resolved`." + }, + "alternative_names": { + "type": "string", + "nullable": true, + "description": "Alternative, trading, former or original-language company names as text, when reported." + }, + "nature_of_business": { + "type": "string", + "nullable": true, + "description": "Activity description or classification code, when reported." + }, + "registered_capital": { + "type": "string", + "nullable": true + }, + "registered_capital_amount": { + "type": "string", + "nullable": true, + "description": "Decimal amount as a string, when reported." + }, + "registered_capital_currency": { + "type": "string", + "nullable": true, + "description": "ISO 4217 code, when reported." + }, + "website": { + "type": "string", + "nullable": true, + "example": "https://www.tesco.com/" + }, + "email": { + "type": "string", + "nullable": true, + "example": "customer.service@tesco.co.uk" + }, + "phone": { + "type": "string", + "nullable": true, + "example": "03301231688" + }, + "legal_entity_identifier": { + "type": "string", + "nullable": true, + "description": "LEI, when reported." + }, + "location_of_registration": { + "type": "string", + "nullable": true, + "description": "Registry/jurisdiction the company is registered with, when reported." + }, + "vat_number": { + "type": "string", + "nullable": true, + "description": "EU VAT number, when collected during the hosted registry step or edited afterwards. At select time this is typically not yet set — VAT and tax ID validation runs when the end user submits the registry step in the hosted flow." + }, + "vat_validation_status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "format_valid", + "invalid_format", + "could_not_validate", + "not_applicable" + ], + "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." + }, + "vat_validated_number": { + "type": "string", + "nullable": true, + "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." + }, + "vat_validation_source": { + "type": "string", + "nullable": true, + "enum": [ + "eu_vies", + "pl_vat_white_list", + "no_brreg", + "ch_uid_register", + "br_receita_federal", + "co_rues", + "cr_hacienda", + "ec_sri", + "py_dnit", + "format_rules", + null + ], + "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." + }, + "vat_validated_name": { + "type": "string", + "nullable": true, + "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_validated_address": { + "type": "string", + "nullable": true, + "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_checked_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the check. `null` when no check ran." + }, + "financial_summary": { + "type": "object", + "nullable": true, + "description": "Registry-reported share/capital details (currency, total shares, share classes), when available." + }, + "officers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "example": "MELISSA BETHELL" + }, + "designation": { + "type": "string", + "nullable": true, + "description": "Raw registry designation (e.g. `Director`, `Secretary`)." + }, + "role": { + "type": "string", + "description": "Normalized role derived from the designation. Full 17-value model set; registry parsing on this endpoint typically emits director, non_executive_director, secretary, chairman, or company_officer.", + "enum": [ + "director", + "non_executive_director", + "secretary", + "chairman", + "ubo", + "shareholder", + "representative", + "founder", + "legal_advisor", + "authorized_signatory", + "trustee", + "beneficiary", + "company_officer", + "settlor", + "protector", + "investor", + "other" + ] + }, + "nationality": { + "type": "string", + "nullable": true + }, + "is_active": { + "type": "boolean", + "description": "`false` when the registry marks the appointment as resigned." + }, + "kyc_status": { + "type": "string", + "nullable": true, + "enum": [ + "Approved", + "Declined", + "Pending", + null + ], + "description": "Status of an associated KYC session, when one was started for this officer. `null` otherwise." + }, + "kyc_session_url": { + "type": "string", + "nullable": true, + "description": "Verification URL of the associated KYC session, when one exists." + }, + "first_name": { + "type": "string", + "nullable": true, + "description": "Given name when explicitly reported by the registry; not inferred from the full name." + }, + "last_name": { + "type": "string", + "nullable": true, + "description": "Family name when explicitly reported by the registry." + }, + "address": { + "type": "string", + "nullable": true, + "description": "Officer address reported by the registry." + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Date the officer was appointed.", + "format": "date" + }, + "termination_date": { + "type": "string", + "nullable": true, + "description": "Date the appointment ended, when reported.", + "format": "date" + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", + "format": "date" + }, + "date_of_birth_year": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 9999, + "description": "Registry-reported birth year, including when the full date is unavailable." + }, + "date_of_birth_month": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 12, + "description": "Registry-reported birth month. Null when only the year is known." + } + } + }, + "description": "Directors, secretaries, and other officers extracted from the registry." + }, + "beneficial_owners": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "first_name": { + "type": "string", + "nullable": true + }, + "last_name": { + "type": "string", + "nullable": true + }, + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ], + "description": "Whether the owner is a natural person or a corporate entity." + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Normalized roles (e.g. `ubo`, `shareholder`)." + }, + "ownership_min_shares": { + "type": "string", + "nullable": true, + "description": "Lower bound of the ownership band reported by the registry (e.g. `25%`)." + }, + "ownership_max_shares": { + "type": "string", + "nullable": true, + "description": "Upper bound of the ownership band reported by the registry." + }, + "is_active": { + "type": "boolean" + }, + "kyc_status": { + "type": "string", + "nullable": true, + "enum": [ + "Approved", + "Declined", + "Pending", + null + ] + }, + "kyc_session_url": { + "type": "string", + "nullable": true + }, + "effective_ownership_percent": { + "type": "number", + "nullable": true, + "description": "Parsed ownership percentage (max bound, falling back to min bound)." + }, + "company_name": { + "type": "string", + "nullable": true + }, + "registration_number": { + "type": "string", + "nullable": true + }, + "country_of_birth": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + }, + "nationality": { + "type": "string", + "nullable": true + }, + "designation": { + "type": "string", + "nullable": true + }, + "voting_min_shares": { + "type": "string", + "nullable": true + }, + "voting_max_shares": { + "type": "string", + "nullable": true + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete birth date when reported.", + "format": "date" + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Appointment date when reported.", + "format": "date" + }, + "shareholding": { + "$ref": "#/components/schemas/KYBRegistryShareholding" + } + } + }, + "description": "Beneficial owners / persons with significant control extracted from the registry. Can be empty when the registry does not expose ownership." + }, + "addresses": { + "type": "array", + "nullable": true, + "description": "Every address the registry states, one canonical shape. Null when it states none.", + "items": { + "$ref": "#/components/schemas/KYBRegistryAddress" } - } - }, - "max_retry_attempts": 3, - "retry_window_days": 7, - "session_expiration_time": 604800, - "version": 2, - "status": "published", - "published_at": "2026-04-12T10:30:00Z", - "is_simple_workflow": false, - "is_editable": false, - "workflow_url": "https://verify.didit.me/u/_yljPOx8RWG3UAz5ZRQGVQ", - "total_price": 0.15, - "min_price": 0.15, - "max_price": 0.15, - "features": "OCR + LIVENESS + FACE_MATCH", - "created_at": "2025-01-15T10:30:00Z", - "updated_at": "2026-04-12T10:30:00Z" - } - } - } - } - } - }, - "403": { - "description": "API key is missing, malformed, expired, or scoped to a different application.", - "content": { - "application/json": { - "examples": { - "Invalid token": { - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } - }, - "404": { - "description": "No workflow with this `settings_uuid` exists for the authenticated application. Either the UUID is wrong or it belongs to another application — Didit deliberately does not distinguish the two cases to avoid leaking the existence of cross-application records.", - "content": { - "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } - } - } - } - }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." - }, - "500": { - "description": "Unexpected server error. Safe to retry." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl -X GET 'https://verification.didit.me/v3/workflows/a1b2c3d4-5678-90ab-cdef-111111111111/' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\nresp = requests.get(\n f\"https://verification.didit.me/v3/workflows/{settings_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status()\nworkflow = resp.json()\nprint(workflow[\"workflow_label\"], workflow[\"status\"])" - }, - { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(`https://verification.didit.me/v3/workflows/${settingsUuid}/`, {\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst workflow = await res.json();\nconsole.log(workflow.workflow_label, workflow.status);" - } - ] - }, - "patch": { - "summary": "Update workflow", - "description": "Update a workflow. Versioning depends on the `status` field you send and the current state of the version: (1) no `status` on a **published** version → the published version is updated in place (same `uuid`, same `version`); (2) no `status` on a **draft** → the draft is updated and immediately published; (3) `status: \"published\"` on a **published** version → a NEW published version is created under the same `workflow_id` (new `uuid`, `version` + 1) and returned, and any existing draft in the group is deleted; (4) `status: \"draft\"` keeps a draft as a draft (autosave) but is rejected with 400 on a published version (`\"Cannot revert a published version to draft.\"`). The body accepts the same strict field whitelist as create — unknown keys are rejected with 400.", - "operationId": "update_workflow", - "tags": [ - "Workflows" - ], - "parameters": [ - { - "name": "settings_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Per-version UUID of the workflow to update." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SimpleWorkflowUpdateRequest" - }, - "examples": { - "Replace features": { - "summary": "Send the full desired feature order", - "value": { - "features": [ - { - "feature": "OCR" - }, - { - "feature": "AML" - } - ] - } - }, - "Rename": { - "summary": "Rename a workflow", - "value": { - "workflow_label": "Premium KYC" - } - }, - "Set as default": { - "summary": "Make this workflow the application's default", - "value": { - "is_default": true - } - }, - "Update config": { - "summary": "Change feature configuration", - "value": { - "features": [ - { - "feature": "OCR" - }, - { - "feature": "LIVENESS", - "config": { - "face_liveness_method": "passive" + }, + "industries": { + "type": "array", + "nullable": true, + "description": "The activities the registry classifies the company under, one canonical shape. Null when it states none.", + "items": { + "$ref": "#/components/schemas/KYBRegistryIndustry" + } + }, + "accounts": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." + }, + "registry_data": { + "type": "object", + "description": "Raw record returned by the registry source, kept for reviewers. Its shape differs by country and source and is not a stable contract: read the canonical fields (`addresses`, `industries`, `ownership`, `beneficial_owners[].shareholding`) instead." + }, + "user_provided_data": { + "type": "object", + "nullable": true, + "description": "Always `null` on this endpoint (used by the hosted KYB flow when end users edit registry data)." + }, + "confirmed_by_user_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Always `null` on this endpoint." + }, + "last_console_edit_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Set when a console reviewer edits the record later." + }, + "is_editable": { + "type": "boolean", + "description": "Whether the registry-sourced data can still be edited in the hosted flow/console." + }, + "registry_tier_delivered": { + "type": "string", + "enum": [ + "basic", + "shareholders", + "ubo", + null + ], + "description": "Same as the top-level `registry_tier_delivered`.", + "nullable": true + }, + "registry_ownership_status": { + "type": "string", + "nullable": true, + "enum": [ + "not_requested", + "delivered", + "unavailable", + "pending", + null + ], + "description": "`delivered` when the ownership data came back, `unavailable` when an ownership tier was requested but the registry had none for this company (see `registry_data.ownership_error`), `pending` when the ownership section has not been retrieved from the registry yet and Didit is still completing it, `not_requested` on the Lite tier." + }, + "ownership": { + "$ref": "#/components/schemas/KYBRegistryOwnership" + }, + "registry_tier": { + "type": "string", + "nullable": true, + "description": "The registry tier the workflow ordered: `basic`, `shareholders` or `ubo`. Also in `ownership.tier_requested`." } } - ] - } - }, - "Publish a draft": { - "summary": "Promote a draft to published", - "value": { - "status": "published" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Workflow updated. The body is the FULL configuration of the resulting version (same raw `VerificationSettings` shape as `GET /v3/workflows/{settings_uuid}/`). When the update created a new version (`status: \"published\"` sent on a published row) the returned `uuid` and `version` differ from the ones you patched — use the returned `uuid` for subsequent calls.", - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "Full workflow configuration (see `GET /v3/workflows/{settings_uuid}/`).", - "additionalProperties": true - } - } - } - }, - "400": { - "description": "Validation error — a top-level key outside the accepted whitelist, an unknown feature, invalid ordering, out-of-range threshold, referencing a non-existent questionnaire, or `status: \"draft\"` sent for a published version (`\"Cannot revert a published version to draft.\"`). The response body is a DRF validation error keyed by field name." - }, - "403": { - "description": "API key missing or invalid.", - "content": { - "application/json": { - "examples": { - "Invalid token": { - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } - }, - "404": { - "description": "No workflow with this `settings_uuid` exists for the authenticated application." - }, - "415": { - "description": "Unsupported media type. Set `Content-Type: application/json`." - }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." - }, - "500": { - "description": "Unexpected server error. Safe to retry." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl -X PATCH 'https://verification.didit.me/v3/workflows/a1b2c3d4-5678-90ab-cdef-111111111111/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"workflow_label\": \"Premium KYC\"}'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\nresp = requests.patch(\n f\"https://verification.didit.me/v3/workflows/{settings_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json={\"workflow_label\": \"Premium KYC\"},\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()[\"workflow_label\"])" - }, - { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(`https://verification.didit.me/v3/workflows/${settingsUuid}/`, {\n method: \"PATCH\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ workflow_label: \"Premium KYC\" }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst workflow = await res.json();\nconsole.log(workflow.workflow_label);" - } - ] - }, - "delete": { - "summary": "Delete workflow", - "description": "Permanently delete one workflow version (no undo). Existing sessions keep their verification results but their workflow reference is cleared; other versions in the same `workflow_id` group are not deleted.", - "operationId": "delete_workflow", - "tags": [ - "Workflows" - ], - "parameters": [ - { - "name": "settings_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Per-version UUID of the workflow to delete." - } - ], - "responses": { - "204": { - "description": "Workflow deleted. The response body is empty." - }, - "403": { - "description": "API key missing or invalid.", - "content": { - "application/json": { - "examples": { - "Invalid token": { - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } - }, - "404": { - "description": "No workflow with this `settings_uuid` exists for the authenticated application.", - "content": { - "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } - } - } - } - }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." - }, - "500": { - "description": "Unexpected server error. Safe to retry." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl -X DELETE 'https://verification.didit.me/v3/workflows/a1b2c3d4-5678-90ab-cdef-111111111111/' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\nresp = requests.delete(\n f\"https://verification.didit.me/v3/workflows/{settings_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status() # raises on 4xx/5xx; 204 is success" - }, - { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(`https://verification.didit.me/v3/workflows/${settingsUuid}/`, {\n method: \"DELETE\",\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (res.status !== 204) throw new Error(`Didit ${res.status}`);" - } - ] - } - }, - "/v3/questionnaires/": { - "get": { - "summary": "List questionnaires", - "description": "List your questionnaires, newest first, in a limit/offset pagination envelope (`{count, next, previous, results}`, default page size 50). Each row is one version; `questionnaire_group_id` is the stable group ID resolved to the latest `published` version.", - "operationId": "list_questionnaires", - "tags": [ - "Questionnaires" - ], - "parameters": [ - { - "name": "limit", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "default": 50 - }, - "description": "Page size (number of questionnaires per page)." - }, - { - "name": "offset", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "default": 0 - }, - "description": "Number of rows to skip before the first returned questionnaire." - } - ], - "responses": { - "200": { - "description": "One page of questionnaires ordered by `created_at` descending, wrapped in the pagination envelope. Each item in `results` is a `QuestionnaireListItem`.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer", - "description": "Total number of questionnaire versions for the application." }, - "next": { + "vendor_data": { "type": "string", - "format": "uri", "nullable": true, - "description": "URL of the next page, or `null` on the last page." + "description": "Echo of the `vendor_data` you sent." }, - "previous": { - "type": "string", - "format": "uri", + "metadata": { + "type": "object", "nullable": true, - "description": "URL of the previous page, or `null` on the first page." + "description": "Echo of the `metadata` you sent." }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QuestionnaireListItem" - } + "created_at": { + "type": "string", + "format": "date-time" } } }, "examples": { - "One questionnaire": { - "summary": "A simple bilingual questionnaire", - "value": { - "count": 1, - "next": null, - "previous": null, - "results": [ - { - "uuid": "11111111-2222-3333-4444-555555555555", - "title": "KYC Questionnaire", - "description": "Additional verification questions", - "languages": [ - "en", - "es" - ], - "default_language": "en", - "is_active": true, - "is_simple_questionnaire": true, - "created_at": "2026-04-23T09:55:00Z", - "updated_at": "2026-04-23T10:00:00Z", - "question_types": [ - "SHORT_TEXT", - "MULTIPLE_CHOICE", - "FILE_UPLOAD" - ], - "workflow_names": [], - "questionnaire_group_id": "11111111-2222-3333-4444-555555555555", - "version": 1, - "status": "published", - "published_at": "2026-04-23T10:00:00Z", - "is_editable": false - } - ] - } - } - } - } - } - }, - "403": { - "description": "API key missing, malformed, expired, or scoped to a different application.", - "content": { - "application/json": { - "examples": { - "Invalid token": { + "Resolved company": { + "summary": "Full registry profile (officer list truncated for brevity)", "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } - }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." - }, - "500": { - "description": "Unexpected server error while building the list. Safe to retry." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl -X GET 'https://verification.didit.me/v3/questionnaires/' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\nresp = requests.get(\n \"https://verification.didit.me/v3/questionnaires/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status()\npage = resp.json()\nfor q in page[\"results\"]:\n print(q[\"uuid\"], q[\"title\"], q[\"status\"])" - }, - { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(\"https://verification.didit.me/v3/questionnaires/\", {\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst page = await res.json();\npage.results.forEach((q) => console.log(q.uuid, q.title, q.status));" - } - ] - }, - "post": { - "summary": "Create questionnaire", - "description": "Create a simple linear questionnaire. Send `form_elements` in display order; branching and repeatable groups require the Console. Defaults to `published` v1.", - "operationId": "create_questionnaire", - "tags": [ - "Questionnaires" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SimpleQuestionnaireRequest" - }, - "examples": { - "Simple": { - "summary": "Basic questionnaire", - "value": { - "title": "Customer Onboarding", - "description": "Additional information needed for verification", - "default_language": "en", - "languages": [ - "en" - ], - "is_active": true, - "form_elements": [ - { - "id": "occupation", - "element_type": "short_text", - "label": { - "en": "What is your occupation?" - }, - "is_required": true, - "placeholder": { - "en": "Software engineer" - } - }, - { - "id": "source_of_funds", - "element_type": "multiple_choice", - "label": { - "en": "Source of funds" + "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", + "kyb_registry": { + "uuid": "f1e2d3c4-b5a6-4789-9c0d-1e2f3a4b5c6d", + "node_id": "feature_kyb_registry", + "status": "In Review", + "registry_status": "active", + "data_resolved": true, + "company_name": "Tesco PLC", + "registration_number": "00445790", + "country_code": "GB", + "region": "", + "company_type": "Public Limited Company", + "incorporation_date": "1947-11-27", + "registered_address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", + "tax_number": "GB412 8925 93", + "risk_level": "low risk", + "verification_status": "verified", + "is_from_registry": true, + "fetch_status": "resolved", + "alternative_names": "TESCO STORES (HOLDINGS) PUBLIC LIMITED COMPANY", + "nature_of_business": null, + "registered_capital": null, + "registered_capital_amount": null, + "registered_capital_currency": null, + "website": "https://www.tesco.com/", + "email": "customer.service@tesco.co.uk", + "phone": "03301231688", + "legal_entity_identifier": null, + "location_of_registration": null, + "financial_summary": { + "currency": "Pound (£)", + "total_number_of_shares": "8174378721", + "meta_detail": { + "Class_Of_Share": "Ordinary" + } }, - "is_required": true, - "options": [ - { - "value": "employment", - "label": { - "en": "Employment" - } - }, - { - "value": "business", - "label": { - "en": "Business" - } - }, + "officers": [ { - "value": "investments", - "label": { - "en": "Investments" - } + "uuid": "a1b2c3d4-e5f6-4789-9a0b-1c2d3e4f5a6b", + "name": "CHRISTOPHER JON TAYLOR", + "designation": "Secretary", + "role": "secretary", + "nationality": null, + "is_active": true, + "kyc_status": null, + "kyc_session_url": null }, { - "value": "other", - "label": { - "en": "Other" - } + "uuid": "b2c3d4e5-f6a7-4890-8b1c-2d3e4f5a6b7c", + "name": "MELISSA BETHELL", + "designation": "Director", + "role": "director", + "nationality": "British", + "is_active": true, + "kyc_status": null, + "kyc_session_url": null } - ] - } - ] - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Questionnaire created. The response includes the `questionnaire_id` — store it to reference this questionnaire from workflows or future update/delete calls.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QuestionnaireDetail" - }, - "examples": { - "Created": { - "value": { - "questionnaire_id": "11111111-2222-3333-4444-555555555555", - "title": "Customer Onboarding", - "description": "Additional information needed for verification", - "languages": [ - "en" - ], - "default_language": "en", - "is_active": true, - "is_simple_questionnaire": true, - "graph": { - "start_node": "occupation", - "nodes": { - "occupation": { - "element_type": "SHORT_TEXT", - "is_required": true, - "title": { - "en": "What is your occupation?" - }, - "next": "source_of_funds" - }, - "source_of_funds": { - "element_type": "MULTIPLE_CHOICE", - "is_required": true, - "title": { - "en": "Source of funds" - }, - "choices": [ - { - "value": "employment", - "label": { - "en": "Employment" - } - }, - { - "value": "business", - "label": { - "en": "Business" - } - } - ], - "next": null + ], + "beneficial_owners": [], + "addresses": [ + { + "address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", + "type": "legal_entity_registry_address", + "description": "registered office address" } - } + ], + "industries": null, + "accounts": { + "last_account": "26 February 2025", + "next_account": "26 February 2026", + "due_by_date": "26 August 2026" + }, + "registry_data": { + "name": "Tesco PLC", + "registration_number": "00445790", + "status": "active", + "type": "Public Limited Company", + "incorporation_date": "1947-11-27", + "tax_number": "GB412 8925 93", + "country_code": "GB", + "fetch_status": "resolved", + "industries_detail": [ + { + "code": "47110", + "name": "Retail Sale In Non-specialised Stores With Food, Beverages Or Tobacco Predominating", + "description": null, + "meta_detail": [] + } + ] + }, + "user_provided_data": null, + "confirmed_by_user_at": null, + "last_console_edit_at": null, + "is_editable": true }, - "sections": [], - "questionnaire_group_id": "11111111-2222-3333-4444-555555555555", - "version": 1, - "status": "published", - "published_at": "2026-04-23T10:00:00Z", - "is_editable": false + "vendor_data": "company-1234", + "metadata": null, + "created_at": "2026-06-11T10:45:00.000000+00:00" } } } @@ -23697,23 +46027,23 @@ } }, "400": { - "description": "Validation error. Common causes: an unknown `element_type`, a translatable field missing a translation for one of `languages`, `languages` missing `en`, `default_language` not in `languages`, an option without a `label`, a question that has `branches`/`required_if`/`repeatable_config` (not supported by this endpoint), or sending a raw `graph` instead of `form_elements`. The body is a DRF validation error keyed by field name.", + "description": "Validation error. Field-level problems use DRF's standard envelope.", "content": { "application/json": { "examples": { - "Graph not accepted": { + "Missing kyb_response_id": { + "summary": "`kyb_response_id` is required", "value": { - "graph": "The v3 questionnaire API only accepts simple `form_elements`, not `graph`." + "kyb_response_id": [ + "This field is required." + ] } }, - "Missing label": { + "save_api_request=false rejected": { + "summary": "Selects are always persisted", "value": { - "form_elements": [ - { - "label": [ - "This field is required." - ] - } + "save_api_request": [ + "KYB registry select must be saved so billing and retrieval can be tracked." ] } } @@ -23722,7283 +46052,4850 @@ } }, "403": { - "description": "API key missing, malformed, expired, or scoped to a different application.", + "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", "content": { "application/json": { "examples": { - "Invalid token": { + "Missing or invalid API key": { + "summary": "No `x-api-key` header, or the key is invalid/revoked", "value": { "detail": "You do not have permission to perform this action." } + }, + "Not enough credits": { + "summary": "Organization balance cannot cover the call", + "value": { + "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + } } } } } }, - "415": { - "description": "Unsupported media type. Set `Content-Type: application/json` and send a JSON body." - }, "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." + "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", + "content": { + "application/json": { + "examples": { + "Write rate limit exceeded": { + "summary": "More than 300 write requests in the current minute", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + }, + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + } + } + } }, - "500": { - "description": "Unexpected server error while creating the questionnaire. Safe to retry." + "202": { + "description": "Report accepted and still processing. When a report is still being prepared, the endpoint returns 202 Accepted, kyb_registry.fetch_status=pending, and registry_tier_delivered=null. Keep request_id and retrieve GET /v3/session/{request_id}/decision/ until registry_checks[0].company.fetch_status is resolved. Didit completes the report automatically and charges the delivered tier once it is ready. Do not repeat the purchase while it is pending; every new POST creates a separate request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/paths/~1v3~1kyb~1ubo~1/post/responses/200/content/application~1json/schema" + }, + "examples": { + "Pending (registry still resolving)": { + "summary": "Ownership report is still processing; retrieve the saved decision", + "value": { + "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", + "kyb_registry": { + "uuid": "7c0a4f7e-2b1d-4f6a-9c3e-8d5b2a1f0e9d", + "node_id": "feature_kyb_registry", + "status": "Not Finished", + "registry_status": null, + "data_resolved": false, + "company_name": "Example Holdings Limited", + "registration_number": "12345678", + "country_code": "HK", + "region": null, + "company_type": null, + "incorporation_date": null, + "registered_address": null, + "tax_number": null, + "risk_level": null, + "verification_status": null, + "is_from_registry": true, + "fetch_status": "pending", + "alternative_names": null, + "nature_of_business": null, + "registered_capital": null, + "registered_capital_amount": null, + "registered_capital_currency": null, + "website": null, + "email": null, + "phone": null, + "legal_entity_identifier": null, + "location_of_registration": null, + "financial_summary": {}, + "officers": [], + "beneficial_owners": [], + "addresses": null, + "industries": null, + "accounts": null, + "registry_data": {}, + "user_provided_data": null, + "confirmed_by_user_at": null, + "last_console_edit_at": null, + "is_editable": true, + "registry_tier_delivered": null, + "registry_ownership_status": "pending" + }, + "vendor_data": "company-1234", + "metadata": null, + "created_at": "2026-06-11T10:45:00.000000+00:00", + "registry_tier_delivered": null, + "registry_tier_requested": "ubo" + } + } + } + } + } } + } + } + }, + "/v3/id-verification/wallets/": { + "get": { + "operationId": "list_wallets", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "list", + "auth": "api_key", + "server": true }, + "description": "Every Digital ID wallet with its start endpoint, countries, required fields and whether this application can start it in this environment now. Listed does not mean live.", + "summary": "List Digital ID wallets", + "tags": [ + "Digital ID wallets" + ], "security": [ { "ApiKeyAuth": [] + }, + { + "BearerAuth": [] } ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletCatalogResponse" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" + } + } + }, + "description": "Missing or invalid credentials (x-api-key or Authorization: Bearer)." + } + }, "x-codeSamples": [ { "lang": "curl", "label": "cURL", - "source": "curl -X POST 'https://verification.didit.me/v3/questionnaires/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"title\": \"Customer Onboarding\",\n \"default_language\": \"en\",\n \"languages\": [\"en\"],\n \"form_elements\": [\n {\n \"id\": \"occupation\",\n \"element_type\": \"short_text\",\n \"label\": {\"en\": \"What is your occupation?\"},\n \"is_required\": true\n }\n ]\n }'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\npayload = {\n \"title\": \"Customer Onboarding\",\n \"default_language\": \"en\",\n \"languages\": [\"en\"],\n \"form_elements\": [\n {\n \"id\": \"occupation\",\n \"element_type\": \"short_text\",\n \"label\": {\"en\": \"What is your occupation?\"},\n \"is_required\": True,\n },\n ],\n}\nresp = requests.post(\n \"https://verification.didit.me/v3/questionnaires/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json=payload,\n timeout=10,\n)\nresp.raise_for_status()\nquestionnaire = resp.json()\nprint(\"Questionnaire id:\", questionnaire[\"questionnaire_id\"])" + "source": "curl --request GET \\\n --url https://verification.didit.me/v3/id-verification/wallets/ \\\n --header 'x-api-key: YOUR_API_KEY'" }, { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(\"https://verification.didit.me/v3/questionnaires/\", {\n method: \"POST\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n title: \"Customer Onboarding\",\n default_language: \"en\",\n languages: [\"en\"],\n form_elements: [\n {\n id: \"occupation\",\n element_type: \"short_text\",\n label: { en: \"What is your occupation?\" },\n is_required: true,\n },\n ],\n }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}: ${await res.text()}`);\nconst questionnaire = await res.json();\nconsole.log(\"Questionnaire id:\", questionnaire.questionnaire_id);" + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"]\n}\n\nresponse = requests.get(\n \"https://verification.didit.me/v3/id-verification/wallets/\",\n headers=headers,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] } }, - "/v3/questionnaires/{questionnaire_uuid}/": { + "/v3/id-verification/wallets/verifications/{request_id}/": { "get": { - "summary": "Get questionnaire", - "description": "Fetch full questionnaire detail for one version by `questionnaire_uuid`: title, `languages`, canonical `graph`, derived `sections`, and version metadata.", - "operationId": "get_questionnaire", - "tags": [ - "Questionnaires" - ], + "operationId": "get_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "retrieve_verification", + "auth": "api_key", + "server": true + }, + "description": "Status and result of a wallet verification: the normalized identity and the wallet's own attributes once verified, the error code once declined, the next action while pending. A verification past expires_at is decided timeout.", + "summary": "Get a wallet verification", "parameters": [ { - "name": "questionnaire_uuid", "in": "path", - "required": true, + "name": "request_id", "schema": { "type": "string", "format": "uuid" }, - "description": "Per-version UUID of the questionnaire (the `uuid` value returned by the list endpoint, also returned as `questionnaire_id` on detail endpoints)." + "description": "The request_id the start endpoint returned.", + "required": true + } + ], + "tags": [ + "Digital ID wallets" + ], + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] } ], "responses": { "200": { - "description": "Full questionnaire detail including the canonical `graph`, the derived `sections` view, version metadata, and `is_editable`.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QuestionnaireDetail" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "Published questionnaire": { - "value": { - "questionnaire_id": "11111111-2222-3333-4444-555555555555", - "title": "Customer Onboarding", - "description": "Additional information needed for verification", - "languages": [ - "en" - ], - "default_language": "en", - "is_active": true, - "is_simple_questionnaire": true, - "graph": { - "start_node": "occupation", - "nodes": { - "occupation": { - "element_type": "SHORT_TEXT", - "is_required": true, - "title": { - "en": "What is your occupation?" - }, - "next": "source_of_funds" - }, - "source_of_funds": { - "element_type": "MULTIPLE_CHOICE", - "is_required": true, - "title": { - "en": "Source of funds" - }, - "choices": [ - { - "value": "employment", - "label": { - "en": "Employment" - } - }, - { - "value": "business", - "label": { - "en": "Business" - } - } - ], - "next": null - } - } - }, - "sections": [ - { - "title": {}, - "description": {}, - "items": [ - { - "uuid": "11111111-2222-3333-4444-666666666666", - "value": "occupation", - "element_type": "SHORT_TEXT", - "is_required": true, - "title": { - "en": "What is your occupation?" - } - } - ] - } - ], - "questionnaire_group_id": "11111111-2222-3333-4444-555555555555", - "version": 1, - "status": "published", - "published_at": "2026-04-23T10:00:00Z", - "is_editable": false - } - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "ftn", + "name": "Finnish Trust Network", + "issuing_authority": "Finnish banks and mobile operators (FTN)" + }, + "country": "FIN", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, "403": { - "description": "API key missing, malformed, expired, or scoped to a different application.", "content": { "application/json": { - "examples": { - "Invalid token": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" } } - } + }, + "description": "Missing or invalid credentials (x-api-key or Authorization: Bearer)." }, "404": { - "description": "No questionnaire with this `questionnaire_uuid` exists for the authenticated application. Either the UUID is wrong or it belongs to another application — the API deliberately does not distinguish the two cases.", "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } - }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." - }, - "500": { - "description": "Unexpected server error. Safe to retry." + }, + "description": "error not_found: no wallet verification of this application has this request_id." } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", "label": "cURL", - "source": "curl -X GET 'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\nresp = requests.get(\n f\"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status()\nquestionnaire = resp.json()\nprint(questionnaire[\"title\"], questionnaire[\"status\"])" + "source": "curl --request GET \\\n --url https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/ \\\n --header 'x-api-key: YOUR_API_KEY'" }, { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`, {\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst questionnaire = await res.json();\nconsole.log(questionnaire.title, questionnaire.status);" + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"]\n}\n\nresponse = requests.get(\n \"https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/\",\n headers=headers,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] - }, - "patch": { - "summary": "Update questionnaire", - "description": "Update a questionnaire. Versioning depends on the `status` field you send and the current state of the version: (1) no `status` on a **published** version → the published version is updated in place (same `questionnaire_id`, same `version`); (2) no `status` on a **draft** → the draft is updated and immediately published; (3) `status: \"published\"` on a **published** version → a NEW published version is created under the same `questionnaire_group_id` (new `questionnaire_id`, `version` + 1) and returned, and any existing draft in the group is deleted; (4) `status: \"draft\"` keeps a draft as a draft (autosave) but is rejected with 400 on a published version (`\"Cannot revert a published version to draft.\"`).", - "operationId": "update_questionnaire", - "tags": [ - "Questionnaires" - ], + } + }, + "/v3/id-verification/wallets/verifications/{request_id}/poll/": { + "post": { + "operationId": "poll_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "poll_verification", + "auth": "api_key", + "server": true + }, + "description": "Smart-ID and Mobile-ID: ask the provider whether the user confirmed, refused or let the prompt lapse, and verify the signed identity when they confirmed. Redirect wallets complete through the user's browser, so for them this only reads the verification back. Answers the verification as it is afterwards.", + "summary": "Poll a wallet verification", "parameters": [ { - "name": "questionnaire_uuid", "in": "path", - "required": true, + "name": "request_id", "schema": { "type": "string", "format": "uuid" }, - "description": "Per-version UUID of the questionnaire to update." + "description": "The request_id the start endpoint returned.", + "required": true } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SimpleQuestionnaireUpdateRequest" - }, - "examples": { - "Rename": { - "summary": "Rename a questionnaire", - "value": { - "title": "Updated Customer Onboarding" - } + "tags": [ + "Digital ID wallets" + ], + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "Replace questions": { - "summary": "Replace the full linear question list", - "value": { - "form_elements": [ - { - "id": "occupation", - "element_type": "short_text", - "label": { - "en": "What is your occupation?" - }, - "is_required": true - }, - { - "id": "source_of_funds", - "element_type": "multiple_choice", - "label": { - "en": "Source of funds" - }, - "options": [ - { - "value": "employment", - "label": { - "en": "Employment" - } - }, - { - "value": "business", - "label": { - "en": "Business" - } - } - ] - } - ] - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "smart_id", + "name": "Smart-ID", + "issuing_authority": "SK ID Solutions" + }, + "country": "EST", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "in_app", + "simulated": true, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "confirm_in_app", + "verification_code": "1234", + "poll_interval_seconds": 2 + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" } } - } - } - }, - "responses": { - "200": { - "description": "Questionnaire updated. The body is the latest state of the resulting version — the in-place updated version, or the newly created published version when you sent `status: \"published\"` on a published row (in that case `questionnaire_id` and `version` differ from the ones you patched). `questionnaire_id` in the response is the per-version UUID; use `questionnaire_group_id` if you need the stable group identifier.", + }, + "description": "Missing or invalid credentials (x-api-key or Authorization: Bearer)." + }, + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QuestionnaireDetail" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } - }, - "400": { - "description": "Validation error — unknown `element_type`, missing translation, unsupported branching/conditional fields, `languages` missing `en`, `default_language` not in `languages`, option without a label, sending a raw `graph`, or `status: \"draft\"` on a published version (`\"Cannot revert a published version to draft.\"`). Response body is a DRF validation error keyed by field name." + }, + "description": "error not_found: no wallet verification of this application has this request_id." }, - "403": { - "description": "API key missing or invalid.", + "503": { "content": { "application/json": { - "examples": { - "Invalid token": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } - }, - "404": { - "description": "No questionnaire with this `questionnaire_uuid` exists for the authenticated application." - }, - "415": { - "description": "Unsupported media type. Set `Content-Type: application/json`." - }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." - }, - "500": { - "description": "Unexpected server error. Safe to retry." + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", "label": "cURL", - "source": "curl -X PATCH 'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"title\": \"Updated Customer Onboarding\"}'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\nresp = requests.patch(\n f\"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json={\"title\": \"Updated Customer Onboarding\"},\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()[\"questionnaire_id\"], resp.json()[\"status\"])" + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/poll/ \\\n --header 'x-api-key: YOUR_API_KEY'" }, { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`, {\n method: \"PATCH\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ title: \"Updated Customer Onboarding\" }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst q = await res.json();\nconsole.log(q.questionnaire_id, q.status);" + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"]\n}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/poll/\",\n headers=headers,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] - }, - "delete": { - "summary": "Delete questionnaire", - "description": "Permanently delete one questionnaire version (no undo). Stored questionnaire responses linked to this version are deleted with it, and sessions referencing it lose the link; other versions in the same group are not deleted. Workflows that referenced it keep running but the questionnaire step can no longer load it.", - "operationId": "delete_questionnaire", - "tags": [ - "Questionnaires" - ], + } + }, + "/v3/id-verification/wallets/verifications/{request_id}/cancel/": { + "post": { + "operationId": "cancel_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "cancel_verification", + "auth": "api_key", + "server": true + }, + "description": "Withdraw a pending verification: it is declined as cancelled and never billed. A decided one is returned unchanged. Smart-ID and Mobile-ID: a cancel that arrives while a poll is checking the sign-in waits a few seconds for that poll, then declines it, or returns it verified if the user had already confirmed. A verification the user is completing right now cannot be cancelled (409 verification_in_progress with Retry-After): retry after that many seconds to read its outcome.", + "summary": "Cancel a wallet verification", "parameters": [ { - "name": "questionnaire_uuid", "in": "path", - "required": true, + "name": "request_id", "schema": { "type": "string", "format": "uuid" }, - "description": "Per-version UUID of the questionnaire to delete." + "description": "The request_id the start endpoint returned.", + "required": true + } + ], + "tags": [ + "Digital ID wallets" + ], + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] } ], "responses": { - "204": { - "description": "Questionnaire deleted. The response body is empty." + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "ftn", + "name": "Finnish Trust Network", + "issuing_authority": "Finnish banks and mobile operators (FTN)" + }, + "country": "FIN", + "status": "Declined", + "outcome": "cancelled", + "error": "cancelled_by_customer", + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": null, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" + } + } + }, + "description": "" }, "403": { - "description": "API key missing or invalid.", "content": { "application/json": { - "examples": { - "Invalid token": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" } } - } + }, + "description": "Missing or invalid credentials (x-api-key or Authorization: Bearer)." }, "404": { - "description": "No questionnaire with this `questionnaire_uuid` exists for the authenticated application.", "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } - }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." + }, + "description": "error not_found: no wallet verification of this application has this request_id." }, - "500": { - "description": "Unexpected server error. Safe to retry." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error verification_in_progress: the user is completing this sign-in right now. The Retry-After header says when to retry; the retry returns the decided verification." } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", "label": "cURL", - "source": "curl -X DELETE 'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import requests\n\nresp = requests.delete(\n f\"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n timeout=10,\n)\nresp.raise_for_status() # raises on 4xx/5xx; 204 is success" + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/cancel/ \\\n --header 'x-api-key: YOUR_API_KEY'" }, { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const res = await fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`, {\n method: \"DELETE\",\n headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (res.status !== 204) throw new Error(`Didit ${res.status}`);" + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"]\n}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/cancel/\",\n headers=headers,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] } }, - "/v3/users/": { - "get": { - "summary": "List users", - "description": "List [users](/entities/users) Didit assembles from KYC sessions sharing the same `vendor_data` (free-form string, NOT a UUID), plus users created via the API. Paginated, ordered by `last_session_at` desc. This endpoint does not support filtering or search parameters — fetch pages and filter client-side.", - "operationId": "list_users", - "tags": [ - "Users" - ], + "/v3/id-verification/wallets/mitid/": { + "post": { + "operationId": "start_mitid_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_mitid", + "auth": "api_key", + "server": true + }, + "description": "Start a MitID sign-in (Danish Agency for Digital Government; DNK) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start MitID verification", "parameters": [ { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "default": 50, - "minimum": 1 - }, - "description": "Page size. Defaults to 50 when omitted. There is no enforced maximum, but keep pages small for latency." - }, - { - "name": "offset", - "in": "query", + "in": "header", + "name": "Idempotency-Key", "schema": { - "type": "integer", - "default": 0, - "minimum": 0 + "type": "string" }, - "description": "Zero-based offset of the first record to return. Prefer following the `next` URL from the previous page over computing this by hand." + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/users/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiMitidWalletVerificationStartRequest" + }, + "example": { + "country": "DNK", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiMitidWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiMitidWalletVerificationStartRequest" + } + } }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/users/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n params={'limit': 50},\n)\nresp.raise_for_status()\npage = resp.json()\nfor user in page['results']:\n print(user['vendor_data'], user['status'])" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const url = new URL('https://verification.didit.me/v3/users/');\nurl.searchParams.set('limit', '50');\n\nconst page = await fetch(url, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n}).then((r) => r.json());\n\nconsole.log(page.count, page.results.length);" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Paginated list of users with verification status, session counts, and feature breakdown.", + "201": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer", - "description": "Number of matching rows — exact up to 100, capped at 100 beyond that for performance. Do not use it to detect the last page; rely on `next` being `null` instead." - }, - "next": { - "type": "string", - "nullable": true, - "description": "Absolute URL of the next page, or `null` on the last page." + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "mitid", + "name": "MitID", + "issuing_authority": "Danish Agency for Digital Government" }, - "previous": { - "type": "string", - "nullable": true, - "description": "Absolute URL of the previous page, or `null` on the first page." + "country": "DNK", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserListItem" - } - } - } - }, - "examples": { - "Example": { - "value": { - "count": 2, - "next": null, - "previous": null, - "results": [ - { - "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", - "vendor_data": "user-abc-123", - "display_name": null, - "full_name": "John Michael Doe", - "date_of_birth": "1990-05-15", - "effective_name": "John Michael Doe", - "status": "ACTIVE", - "portrait_image_url": "https:///...", - "session_count": 3, - "approved_count": 2, - "declined_count": 0, - "in_review_count": 1, - "issuing_states": [ - "USA" - ], - "approved_emails": [ - "john@example.com" - ], - "approved_phones": [ - "+14155551234" - ], - "features": { - "ID_VERIFICATION": "Approved", - "LIVENESS": "Approved", - "FACE_MATCH": "Approved", - "AML": "Approved" - }, - "features_list": [ - { - "feature": "ID_VERIFICATION", - "status": "Approved" - }, - { - "feature": "LIVENESS", - "status": "Approved" - }, - { - "feature": "FACE_MATCH", - "status": "Approved" - }, - { - "feature": "AML", - "status": "Approved" - } - ], - "last_session_at": "2025-06-15T10:30:00Z", - "last_activity_at": "2025-06-15T10:30:00Z", - "first_session_at": "2025-06-01T08:00:00Z", - "tags": [ - { - "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", - "name": "VIP", - "color": "#2567FF" - } - ], - "created_at": "2025-06-01T08:00:00Z" - }, - { - "didit_internal_id": "6ed99d53-e8f5-4cf8-9ac1-4a506cb40f6b", - "vendor_data": null, - "display_name": "Jane S.", - "full_name": "Jane Elizabeth Smith", - "date_of_birth": "1985-11-22", - "effective_name": "Jane S.", - "status": "FLAGGED", - "portrait_image_url": null, - "session_count": 1, - "approved_count": 0, - "declined_count": 0, - "in_review_count": 1, - "issuing_states": [], - "approved_emails": [], - "approved_phones": [], - "features": { - "ID_VERIFICATION": "In Review" - }, - "features_list": [ - { - "feature": "ID_VERIFICATION", - "status": "In Review" - } - ], - "last_session_at": "2025-06-20T14:00:00Z", - "last_activity_at": "2025-06-20T14:05:00Z", - "first_session_at": "2025-06-20T14:00:00Z", - "tags": [], - "created_at": "2025-06-20T14:00:00Z" - } - ] - } - } + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, - "401": { - "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "400": { "content": { "application/json": { - "examples": { - "Missing credentials": { - "value": { - "detail": "You must be authenticated with a valid access token to access this endpoint." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "Invalid or revoked API key, or the key cannot list users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "409": { "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] - } - ] - } - }, - "/v3/users/{vendor_data}/": { - "get": { - "summary": "Get user", - "description": "Fetch one [user](/entities/users) by `vendor_data` (exact match). Adds `metadata`, `comments`, and `updated_at` to the list view, and switches `tags` to the detailed tag-link shape.", - "operationId": "get_user", - "tags": [ - "Users" + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/mitid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"DNK\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'DNK',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/mitid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + } ], + "x-didit-wallet-id": "mitid" + } + }, + "/v3/id-verification/wallets/bankid-se/": { + "post": { + "operationId": "start_bankid_se_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_bankid_se", + "auth": "api_key", + "server": true + }, + "description": "Start a BankID (SWE) sign-in (Finansiell ID-Teknik / bank consortium; SWE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start BankID (SWE) verification", "parameters": [ { - "name": "vendor_data", - "in": "path", - "required": true, + "in": "header", + "name": "Idempotency-Key", "schema": { "type": "string" }, - "description": "Your unique identifier for the user — a free-form string (NOT a UUID). This is the same value you passed as `vendor_data` when creating the session that first introduced this user, matched exactly as sent.", - "example": "user-abc-123" + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/users/user-abc-123/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBankidSeWalletVerificationStartRequest" + }, + "example": { + "country": "SWE", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBankidSeWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBankidSeWalletVerificationStartRequest" + } + } }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/users/user-abc-123/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n)\nresp.raise_for_status()\nuser = resp.json()\nprint(user['didit_internal_id'], user['status'], user['session_count'])" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const vendorData = encodeURIComponent('user-abc-123');\nconst user = await fetch(\n `https://verification.didit.me/v3/users/${vendorData}/`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n).then((r) => r.json());\n\nconsole.log(user.didit_internal_id, user.status);" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Full user detail including metadata, comments/activity log, and aggregated session data.", + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserDetailItem" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "Active user": { - "value": { - "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", - "vendor_data": "user-abc-123", - "display_name": null, - "full_name": "John Michael Doe", - "date_of_birth": "1990-05-15", - "effective_name": "John Michael Doe", - "status": "ACTIVE", - "metadata": { - "tier": "premium", - "source": "website" - }, - "portrait_image_url": "https:///...", - "session_count": 3, - "approved_count": 2, - "declined_count": 0, - "in_review_count": 1, - "issuing_states": [ - "USA" - ], - "approved_emails": [ - "john@example.com" - ], - "approved_phones": [ - "+14155551234" - ], - "features": { - "ID_VERIFICATION": "Approved", - "LIVENESS": "Approved", - "FACE_MATCH": "Approved", - "AML": "Approved" - }, - "features_list": [ - { - "feature": "ID_VERIFICATION", - "status": "Approved" - }, - { - "feature": "LIVENESS", - "status": "Approved" - }, - { - "feature": "FACE_MATCH", - "status": "Approved" - }, - { - "feature": "AML", - "status": "Approved" - } - ], - "last_session_at": "2025-06-15T10:30:00Z", - "last_activity_at": "2025-06-15T10:30:00Z", - "first_session_at": "2025-06-01T08:00:00Z", - "tags": [ - { - "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", - "tag": { - "uuid": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b", - "name": "VIP", - "color": "#2567FF", - "description": null, - "source": "custom", - "created_at": "2025-05-01T09:00:00Z", - "updated_at": "2025-05-01T09:00:00Z" - }, - "added_by_email": "analyst@acme.com", - "added_by_name": "Jane Analyst", - "created_at": "2025-06-02T11:00:00Z" - } - ], - "comments": [ - { - "uuid": "c1111111-2222-4333-8444-555555555555", - "comment_type": "STATUS_CHANGED", - "comment": null, - "actor_email": "analyst@acme.com", - "actor_name": "Jane Analyst", - "previous_status": "FLAGGED", - "new_status": "ACTIVE", - "previous_value": null, - "new_value": null, - "changed_fields": [], - "metadata": null, - "mentioned_emails": [], - "created_at": "2025-06-01T10:30:00Z" - } - ], - "created_at": "2025-06-01T08:00:00Z", - "updated_at": "2025-06-15T10:30:00Z" - } - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "bankid_se", + "name": "BankID", + "issuing_authority": "Finansiell ID-Teknik / bank consortium" + }, + "country": "SWE", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, - "401": { - "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "400": { "content": { "application/json": { - "examples": { - "Missing credentials": { - "value": { - "detail": "You must be authenticated with a valid access token to access this endpoint." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "Invalid or revoked API key, or the key cannot read this application's users. Note: on this endpoint an *invalid* key returns `403` (not `401`).", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "404": { - "description": "No user with the supplied `vendor_data` exists for this application.", + "409": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "502": { "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/bankid-se/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"SWE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'SWE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/bankid-se/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] - }, - "patch": { - "summary": "Update user", - "description": "Partial update of a user: `display_name`, `full_name`, `date_of_birth`, `status`, `metadata`, `approved_emails`/`approved_phones`/`issuing_states`. `metadata` fully replaces. **Note:** unlike [Update User Status](#patch-/v3/users/-vendor_data-/update-status/), changing `status` here does NOT record a status-change activity and does NOT sync the system blocklist — use the dedicated update-status endpoint for `BLOCKED`/unblock flows.", - "operationId": "update_user", - "tags": [ - "Users" ], + "x-didit-wallet-id": "bankid_se" + } + }, + "/v3/id-verification/wallets/bankid-no/": { + "post": { + "operationId": "start_bankid_no_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_bankid_no", + "auth": "api_key", + "server": true + }, + "description": "Start a BankID (NOR) sign-in (BankID BankAxept AS; NOR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start BankID (NOR) verification", "parameters": [ { - "name": "vendor_data", - "in": "path", - "required": true, + "in": "header", + "name": "Idempotency-Key", "schema": { "type": "string" }, - "description": "Your unique identifier for the user (free-form string, NOT a UUID).", - "example": "user-abc-123" + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/users/user-abc-123/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"display_name\": \"Jane S.\", \"status\": \"FLAGGED\"}'" + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBankidNoWalletVerificationStartRequest" + }, + "example": { + "country": "NOR", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBankidNoWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBankidNoWalletVerificationStartRequest" + } + } }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/users/user-abc-123/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'display_name': 'Jane S.', 'status': 'FLAGGED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/users/user-abc-123/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ display_name: 'Jane S.', status: 'FLAGGED' }),\n});\nconst user = await resp.json();\nconsole.log(user.status);" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "User updated. The full updated user record (same shape as Get User) is returned.", + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserDetailItem" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "Updated": { - "value": { - "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", - "vendor_data": "user-abc-123", - "display_name": "Jane S.", - "full_name": "Jane Elizabeth Smith", - "date_of_birth": "1985-11-22", - "effective_name": "Jane S.", - "status": "FLAGGED", - "metadata": { - "tier": "premium" - }, - "portrait_image_url": "https:///...", - "session_count": 3, - "approved_count": 2, - "declined_count": 0, - "in_review_count": 1, - "issuing_states": [ - "USA" - ], - "approved_emails": [ - "john@example.com" - ], - "approved_phones": [ - "+14155551234" - ], - "features": { - "ID_VERIFICATION": "Approved", - "LIVENESS": "Approved", - "FACE_MATCH": "Approved", - "AML": "Approved" - }, - "features_list": [ - { - "feature": "ID_VERIFICATION", - "status": "Approved" - }, - { - "feature": "LIVENESS", - "status": "Approved" - }, - { - "feature": "FACE_MATCH", - "status": "Approved" - }, - { - "feature": "AML", - "status": "Approved" - } - ], - "last_session_at": "2025-06-15T10:30:00Z", - "last_activity_at": "2025-06-15T10:30:00Z", - "first_session_at": "2025-06-01T08:00:00Z", - "tags": [ - { - "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", - "tag": { - "uuid": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b", - "name": "VIP", - "color": "#2567FF", - "description": null, - "source": "custom", - "created_at": "2025-05-01T09:00:00Z", - "updated_at": "2025-05-01T09:00:00Z" - }, - "added_by_email": "analyst@acme.com", - "added_by_name": "Jane Analyst", - "created_at": "2025-06-02T11:00:00Z" - } - ], - "comments": [], - "created_at": "2025-06-01T08:00:00Z", - "updated_at": "2025-06-15T10:30:00Z" - } - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "bankid_no", + "name": "BankID", + "issuing_authority": "BankID BankAxept AS" + }, + "country": "NOR", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, "400": { - "description": "Invalid body — typically an unknown `status` or a `date_of_birth` not in `YYYY-MM-DD`.", "content": { "application/json": { - "examples": { - "Bad status": { - "value": { - "status": [ - "\"INVALID\" is not a valid choice." - ] - } - }, - "Bad date_of_birth": { - "value": { - "date_of_birth": [ - "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." - ] - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, - "401": { - "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "403": { "content": { "application/json": { - "examples": { - "Missing credentials": { - "value": { - "detail": "You must be authenticated with a valid access token to access this endpoint." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "403": { - "description": "Invalid or revoked API key, or the key cannot update users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).", + "409": { "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "404": { - "description": "No user with the supplied `vendor_data` exists for this application.", + "502": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "503": { "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/bankid-no/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"NOR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'NOR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/bankid-no/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "full_name": { - "type": "string", - "nullable": true, - "maxLength": 512, - "description": "Full name of the user" - }, - "display_name": { - "type": "string", - "nullable": true, - "description": "Custom display name for this user" - }, - "date_of_birth": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Date of birth in YYYY-MM-DD format" - }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED" - ], - "description": "User lifecycle status. Changing it via this generic PATCH does NOT sync the system blocklist or record a status-change activity; prefer the update-status endpoint." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Arbitrary JSON object you attach to the user. **Fully replaces** the existing metadata on update — merge client-side if you only want to add keys." - }, - "approved_emails": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Verified email addresses for this user, e.g. `[\"john@example.com\"]`. Fully replaces the stored list." - }, - "approved_phones": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Verified phone numbers for this user. Fully replaces the stored list." - }, - "issuing_states": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Issuing countries (ISO 3166-1 alpha-3), e.g. `[\"USA\"]`. Fully replaces the stored list." - } - } - }, - "examples": { - "Rename": { - "summary": "Set display name", - "value": { - "display_name": "Jane S." - } - }, - "Override status": { - "summary": "Flag the user for review", - "value": { - "status": "FLAGGED" - } - }, - "Add metadata": { - "summary": "Replace metadata JSON", - "value": { - "metadata": { - "tier": "premium", - "risk_level": "low" - } - } - }, - "Fix extracted DOB": { - "summary": "Correct extracted profile fields", - "value": { - "full_name": "Jane Elizabeth Smith", - "date_of_birth": "1985-11-22" - } - } - } - } - } - } + "x-didit-wallet-id": "bankid_no" } }, - "/v3/users/{vendor_data}/generate-pdf/": { - "get": { - "summary": "Download the full user history PDF report", - "description": "Render and download one compliance-ready PDF containing the full verification history of the user identified by `vendor_data`. It is the same document the Console produces from **User detail → Actions → Download PDF**, so periodic compliance exports no longer need a person clicking in the Console.\n\n**What the report contains.** A cover page with the profile summary (name, date of birth, issuing states, approved emails and phones), the latest status of every feature, the session counters and an index of the sessions included — followed by the standard report of every included session, in chronological order (oldest first). Each session report is the same document `GET /v3/session/{sessionId}/generate-pdf/` returns for that session.\n\n**Eligible sessions.** Only User Verification (KYC) sessions in `Approved`, `Declined`, `In Review` or `Kyc Expired` are included; sessions in any other status are skipped. If the user has no session in one of those statuses the endpoint returns `403` — see below.\n\n**Cap.** The report is capped at the **20 most recent** reportable sessions. When a user has more, the newest 20 are included and the cover page states how many older sessions were left out. Use `GET /v3/sessions/?vendor_data=…` for the complete list as JSON.\n\n**Branding.** If your application has white-label customization, the cover and every session report are rendered with your logo and privacy-policy URL; otherwise they carry Didit branding. Reports are rendered in English — there is no language parameter.\n\n**No caching.** Every call re-renders the PDF from the current data, so a report generated after a manual review reflects the reviewer's decision. Two calls for the same user can produce byte-different files — archive the downloaded file if you need an immutable copy.\n\n**Latency.** Rendering is synchronous and downloads every stored image of every included session, so a user with many media-heavy sessions can take tens of seconds. Use a generous client read timeout (120 s recommended) and stream the body to disk. Generation stops at a server-side time budget instead of running until your client gives up: if that budget is reached, the response is still a valid PDF containing the most recent sessions, and the cover states how many were omitted.\n\n**Scope.** The user is resolved inside the application that owns the API key. A key can never export a user of another application — such a user does not exist for it and the endpoint returns `404`.\n\n**Trailing slash.** The canonical route ends with a trailing slash (`…/generate-pdf/`). A request without it is served directly with the same response — there is no `301` redirect, so no client has to follow redirects and `curl` does not need `-L`. Use the slashed URL as in the samples.", - "operationId": "get_user_generate_pdf", - "tags": [ - "Users" - ], + "/v3/id-verification/wallets/vipps/": { + "post": { + "operationId": "start_vipps_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_vipps", + "auth": "api_key", + "server": true + }, + "description": "Start a Vipps sign-in (Vipps MobilePay / BankID NO; NOR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Vipps verification", "parameters": [ { - "name": "vendor_data", - "in": "path", - "required": true, + "in": "header", + "name": "Idempotency-Key", "schema": { "type": "string" }, - "description": "Your unique identifier for the user — a free-form string (NOT a UUID). This is the same value you passed as `vendor_data` when creating the user's sessions, matched exactly as sent. URL-encode it; a value containing `/` cannot be addressed by this route.", - "example": "user-abc-123" + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "responses": { - "200": { - "description": "The rendered PDF document, returned directly as binary `application/pdf` — there is no JSON wrapper and no download-URL indirection. The body starts with the `%PDF` magic bytes. Save it to a `.pdf` file or stream it through to your caller.", - "headers": { - "Content-Disposition": { - "description": "`attachment; filename=user_{didit_internal_id}.pdf` — the file is named after the user's stable Didit identifier (`didit_internal_id` on `GET /v3/users/{vendor_data}/`), not after `vendor_data`.", - "schema": { - "type": "string", - "example": "attachment; filename=user_f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418.pdf" - } + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiVippsWalletVerificationStartRequest" }, - "Content-Length": { - "description": "Total size of the PDF in bytes; the body is not chunked.", - "schema": { - "type": "integer", - "example": 148213 - } + "example": { + "country": "NOR", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" } }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiVippsWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiVippsWalletVerificationStartRequest" + } + } + }, + "required": true + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], + "responses": { + "201": { "content": { - "application/pdf": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "vipps", + "name": "Vipps", + "issuing_authority": "Vipps MobilePay / BankID NO" + }, + "country": "NOR", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, - "401": { - "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "400": { "content": { "application/json": { - "examples": { - "Missing credentials": { - "value": { - "detail": "You must be authenticated with a valid access token to access this endpoint." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "Authorization or eligibility failure. Distinguish the two by the `detail` string:\n\n- `\"You do not have permission to perform this action.\"` — the API key is invalid or revoked, or the request carries a Console user token instead of an application API key.\n- `\"This user has no sessions in review, declined, approved or kyc expired to report.\"` — the user exists in your application but none of its sessions is in a reportable status yet (for example every session is still `In Progress`, or the user was created through `POST /v3/users/create/` and has never verified). Wait for a `status.updated` webhook before requesting the report.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "This user has no sessions in review, declined, approved or kyc expired to report." - } - } - }, - "examples": { - "Invalid credentials": { - "summary": "Revoked or invalid API key, or a Console user token", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "No reportable sessions": { - "summary": "User exists but has no Approved / Declined / In Review / Kyc Expired session", - "value": { - "detail": "This user has no sessions in review, declined, approved or kyc expired to report." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "404": { - "description": "No user with the supplied `vendor_data` exists in the application behind the API key. Users of other applications are invisible to this key and return the same `404`.", + "409": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "User not found." - } - } - }, - "examples": { - "Unknown user": { - "summary": "vendor_data does not match any user of this application", - "value": { - "detail": "User not found." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "429": { - "description": "Rate limit exceeded. PDF generation has a dedicated limit of **50 requests per minute per credential** (API key, Bearer token, or source IP when unauthenticated), shared between this endpoint and `GET /v3/session/{sessionId}/generate-pdf/`, in addition to the global 600/min GET limit. Wait `Retry-After` seconds (also reflected in the `X-RateLimit-*` headers), then retry. Reports are not cached server-side, so download each report once and store the file instead of re-fetching it.", - "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying the request.", - "schema": { - "type": "integer", - "minimum": 1, - "example": 30 - } - }, - "X-RateLimit-Limit": { - "description": "Maximum number of PDF generations allowed in the current window (50).", - "schema": { - "type": "integer", - "example": 50 - } - }, - "X-RateLimit-Remaining": { - "description": "Requests remaining in the current window.", - "schema": { - "type": "integer", - "example": 0 - } - }, - "X-RateLimit-Reset": { - "description": "Unix timestamp (seconds) at which the current window resets.", + "502": { + "content": { + "application/json": { "schema": { - "type": "integer", - "example": 1750000000 + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "description": "Human-readable explanation of the rate-limit breach.", - "example": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute." - } - } - }, - "examples": { - "Throttled": { - "summary": "More than 50 PDF generations in one minute from the same credential", - "value": { - "detail": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", "label": "cURL", - "source": "curl --fail \\\n 'https://verification.didit.me/v3/users/user-abc-123/generate-pdf/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n --output user-history.pdf" + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/vipps/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"NOR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" }, { "lang": "python", "label": "Python", - "source": "import requests\n\nresponse = requests.get(\n \"https://verification.didit.me/v3/users/user-abc-123/generate-pdf/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n stream=True,\n timeout=120,\n)\nresponse.raise_for_status()\nwith open(\"user-history.pdf\", \"wb\") as fh:\n for chunk in response.iter_content(chunk_size=8192):\n fh.write(chunk)" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "import { writeFile } from 'node:fs/promises';\n\nconst vendorData = encodeURIComponent('user-abc-123');\nconst response = await fetch(\n `https://verification.didit.me/v3/users/${vendorData}/generate-pdf/`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);\nawait writeFile('user-history.pdf', Buffer.from(await response.arrayBuffer()));" + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'NOR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/vipps/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "vipps" } }, - "/v3/users/delete/": { + "/v3/id-verification/wallets/buypass/": { "post": { - "summary": "Batch delete users", - "description": "**Permanently delete** users — the user records are removed permanently. For everyday blocking prefer [Update User Status](#patch-/v3/users/-vendor_data-/update-status/) with `BLOCKED`. Select users with `vendor_data_list`, with `didit_internal_id_list`, or with both at once; or pass `delete_all: true`. The Console's `filter_*` batch params are rejected with a `400` rather than ignored, so a narrowed intent can never turn into a full wipe.\n\n**Biometric templates:** every image-free biometric template retained for these users after session deletion (see `/v3/biometric-templates/`) is purged before the user is deleted, so the person stops matching in duplicate detection and Face Search. If a template cannot be purged because a biometric store is unavailable, the call returns `503`, no user in the request is deleted, and repeating the request finishes the job.", - "operationId": "batch_delete_users", + "operationId": "start_buypass_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_buypass", + "auth": "api_key", + "server": true + }, + "description": "Start a Buypass ID sign-in (Buypass AS; NOR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Buypass ID verification", + "parameters": [ + { + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + } + ], "tags": [ - "Users" + "Digital ID wallets" ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "description": "Provide `vendor_data_list`, `didit_internal_id_list`, or `delete_all: true`. The two lists can be combined and are OR'd. When `delete_all` is `true` it takes precedence and both lists are ignored.", - "properties": { - "vendor_data_list": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Your own identifiers to delete, matched exactly as sent." - }, - "didit_internal_id_list": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "Didit internal user IDs to delete - the `didit_internal_id` returned by the create and get endpoints." - }, - "delete_all": { - "type": "boolean", - "default": false, - "description": "If `true`, deletes every user in the application." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiBuypassWalletVerificationStartRequest" }, - "examples": { - "Specific users": { - "summary": "Delete users by vendor_data", - "value": { - "vendor_data_list": [ - "user-123", - "user-456" - ] - } - }, - "By Didit internal ID": { - "summary": "Delete users by didit_internal_id", - "value": { - "didit_internal_id_list": [ - "3f1c0d3e-8f0a-4c2e-9a3d-9b1f5f4a2c11" - ] - } + "example": { + "country": "NOR", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "All users": { - "summary": "Wipe every user in this application", - "value": { - "delete_all": true - } - } + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBuypassWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBuypassWalletVerificationStartRequest" } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/users/delete/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"vendor_data_list\": [\"user-123\", \"user-456\"]}'" }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/users/delete/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'vendor_data_list': ['user-123', 'user-456']},\n)\nresp.raise_for_status()\nprint('deleted', resp.json()['deleted'])" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/users/delete/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ vendor_data_list: ['user-123', 'user-456'] }),\n});\nconst body = await resp.json();\nconsole.log('deleted', body.deleted);" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Users deleted. Returns the number of users actually deleted.", + "201": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "deleted": { - "type": "integer", - "description": "Number of users deleted (excludes vendor_data values that didn't match anything)." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "Deleted some": { - "value": { - "deleted": 2 - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "buypass", + "name": "Buypass ID", + "issuing_authority": "Buypass AS" + }, + "country": "NOR", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" }, - "Nothing matched": { - "value": { - "deleted": 0 - } - } + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, "400": { - "description": "No selector supplied (`vendor_data_list` missing/empty and `delete_all` not `true`). The error body is a JSON array.", "content": { "application/json": { - "examples": { - "Missing selector": { - "value": [ - "Provide vendor_data_list or set delete_all=true." - ] - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, - "401": { - "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", + "403": { "content": { "application/json": { - "examples": { - "Missing credentials": { - "value": { - "detail": "You must be authenticated with a valid access token to access this endpoint." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "403": { - "description": "Invalid or revoked API key, or the key cannot delete users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).", + "409": { "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "502": { "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, "503": { - "description": "A biometric store was unavailable while retaining or purging a biometric template. Nothing is deleted; repeat the same request. Retries are idempotent and converge on a single template.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, - "examples": { - "Retryable": { - "value": { - "detail": "The biometric-template operation could not finish because an external store is unavailable. The operation is retryable." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/buypass/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"NOR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'NOR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/buypass/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "buypass" } }, - "/v3/sessions/delete/": { + "/v3/id-verification/wallets/itsme/": { "post": { - "summary": "Bulk delete KYC sessions by session_number", - "description": "Bulk delete User Verification (KYC) sessions by their numeric `session_number`, or every KYC session in your application with `delete_all: true`.\n\n`session_number` is the human-friendly incrementing counter shown in the Console session list and returned as `session_number` by the create-session and decision endpoints; it is **not** the `session_id` UUID. To delete a session by UUID, or to delete a Business Verification (KYB) session, which this endpoint never touches, use `DELETE /v3/session/{sessionId}/delete/` instead.\n\n**Up to 20 sessions are deleted in the request, more run as a job.** The number of matching sessions decides, whether they are selected by `session_numbers` or with `delete_all`:\n- Up to 20 matching sessions: they are deleted before the response, which is `200` with one entry per session in `results[]`.\n- More than 20: the response is `202` with a deletion job, and the sessions are deleted in the background. Poll `GET /v3/sessions/delete/{job_id}/` until `status` is `COMPLETED` or `FAILED`. A job reports counts per outcome, never which sessions it deleted. A `delete_all` job deletes only the sessions created before the request; sessions started while it runs are not deleted.\n\nEach deleted session gets exactly the same treatment as the single-session delete endpoint: the session, every verification attempt it holds, its decision and extracted data, its related feature records (ID verification, face, liveness, face match, and so on) and all of its stored files are deleted, it disappears from every read endpoint, and previously issued media URLs stop resolving. Webhook deliveries still queued for a deleted session, and their retries, are dropped; webhooks already delivered are not affected. Blocklist entries are **not** removed, credits already consumed are **not** refunded, and the parent User entities are **not** deleted (use `POST /v3/users/delete/` for those); no webhook is emitted for the deletion. Deletion is permanent and irreversible.\n\n**Selection semantics:**\n- Entries in `session_numbers` that do not match a live KYC session in your application (unknown numbers, already-deleted sessions, or sessions owned by another application) are **silently skipped**: they do not appear in `results` and are not counted in a job's `total`.\n- Validation failures (a non-numeric entry, an empty list, neither `session_numbers` nor `delete_all` provided, or a template retention the application cannot honour) reject the **whole request** with `400` and delete nothing.\n- Sessions are deleted in groups. If a group cannot be deleted (for example because a storage service is unavailable), every session in that group is reported as `face_retention_outcome: failed_retryable` and stays in place; the other groups are unaffected. Repeat the request for those sessions. A job retries such a group by itself.\n- There is no documented cap on the list length; the shared write rate limit (below) is the practical bound.\n\n**Face biometric data:** the optional `retain_face_embeddings`, `face_retention_days`, `face_retention_deadline`, `deletion_instruction`, and `instruction_id` fields have the same meaning as on `DELETE /v3/session/{sessionId}/delete/` and apply to every matched session. Each result reports its own `face_retention_outcome`.\n\n**Response:** `200 OK` with `results[]` for up to 20 matching sessions, or `202 Accepted` with a deletion job for more. Every result confirms deletion except `face_retention_outcome: failed_retryable`, which reports a session that was NOT deleted and should be retried. Earlier versions of this endpoint returned `204 No Content`; update clients that assert on `204`.\n\n**Idempotency:** repeating the same request deletes nothing more: numbers that no longer match a session are skipped.\n\n**`delete_all: true` (destructive):** deletes **every** remaining KYC session in the application; `session_numbers` is ignored when set. There is no confirmation step and no undo.\n\n**Authentication:** API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`); this API never returns `401`.\n\n**Rate limit:** shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\nThis endpoint accepts API keys only. Business Console user Bearer tokens are rejected with `403` (unlike the single-session delete, which accepts both).", - "operationId": "batch_delete_sessions", - "tags": [ - "Sessions" - ], - "security": [ + "operationId": "start_itsme_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_itsme", + "auth": "api_key", + "server": true + }, + "description": "Start a itsme sign-in (Belgian Mobile ID; BEL, LUX, NLD) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start itsme verification", + "parameters": [ { - "ApiKeyAuth": [] + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], + "tags": [ + "Digital ID wallets" + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "session_numbers": { - "type": "array", - "description": "Numeric `session_number` values of the KYC sessions to delete, as digit-only strings (JSON numbers are also accepted and coerced). Required unless `delete_all` is `true`; an empty list returns `400`. Any non-digit entry rejects the whole request with `400` (unless `delete_all` is `true`, in which case validation is skipped and the entries are ignored). Numbers that do not match a live session in your application are silently skipped.", - "items": { - "type": "string", - "pattern": "^[0-9]+$", - "example": "1001" - }, - "minItems": 1, - "example": [ - "1001", - "1002", - "1003" - ] - }, - "delete_all": { - "type": "boolean", - "description": "When `true`, every remaining KYC session in the calling application is deleted and `session_numbers` is ignored. More than 20 matching sessions run as a deletion job (`202`) that covers only the sessions created before the request. Defaults to `false`. Irreversible; there is no confirmation step.", - "default": false, - "example": false - }, - "retain_face_embeddings": { - "type": "boolean", - "nullable": true, - "description": "Override the application's `face_retention_policy` for this deletion. `true` keeps one image-free face biometric template anchored to the session's User after the session is deleted; `false` deletes the face embedding with the session; omit or `null` to follow the application policy. Ignored for KYB sessions and for sessions without a face embedding.", - "example": true - }, - "face_retention_days": { - "type": "integer", - "minimum": 1, - "maximum": 3650, - "description": "Finite retention duration, in days, for a retained template. Required when a template is retained unless the application already sets `face_retention_days`. The template's `expires_at` is the earliest of this duration, the application's general data-retention window, and `face_retention_deadline`.", - "example": 365 - }, - "face_retention_deadline": { - "type": "string", - "format": "date-time", - "description": "Optional hard expiry for a retained template. Must be in the future.", - "example": "2027-08-28T00:00:00Z" - }, - "deletion_instruction": { - "type": "string", - "enum": [ - "operational_session_delete", - "privacy_erasure" - ], - "default": "operational_session_delete", - "description": "The class of instruction you are giving. `operational_session_delete` deletes the session and retains a template only when the policy or `retain_face_embeddings` says so. `privacy_erasure` purges every retained biometric template anchored to the session's User before deleting the session, cannot be overridden by the application policy, and cannot be combined with `retain_face_embeddings: true` (`400`).", - "example": "operational_session_delete" - }, - "instruction_id": { - "type": "string", - "maxLength": 255, - "description": "Your durable reference for this deletion instruction (for example an erasure-ticket id). Recorded on the audit trail and on any retained template. Generated by Didit when omitted.", - "example": "dsar-2026-0142" - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiItsmeWalletVerificationStartRequest" }, - "examples": { - "Specific sessions": { - "summary": "Delete specific sessions by number", - "value": { - "session_numbers": [ - "1001", - "1002", - "1003" - ] - } - }, - "All sessions": { - "summary": "Delete every KYC session in the application", - "value": { - "delete_all": true - } - }, - "Delete and retain templates": { - "summary": "Delete sessions but keep image-free biometric templates for 365 days", - "value": { - "session_numbers": [ - "1001", - "1002" - ], - "retain_face_embeddings": true, - "face_retention_days": 365, - "instruction_id": "post-approval-cleanup-2026-08" - } + "example": { + "country": "BEL", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "Privacy erasure": { - "summary": "Delete sessions and purge every retained template for their Users", - "value": { - "session_numbers": [ - "1001" - ], - "deletion_instruction": "privacy_erasure", - "instruction_id": "dsar-2026-0142" - } - } + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiItsmeWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiItsmeWalletVerificationStartRequest" } } - } + }, + "required": true }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], "responses": { - "200": { - "description": "Up to 20 sessions matched and were processed in the request. One result per matched session; numbers that matched nothing (unknown or already deleted) are skipped and do not appear. A result confirms deletion except when `face_retention_outcome` is `failed_retryable`: that session was NOT deleted and should be retried.", + "201": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "type": "object", - "properties": { - "session_id": { - "type": "string", - "format": "uuid", - "description": "The deleted session." - }, - "session_number": { - "type": "integer", - "nullable": true, - "description": "The deleted session's number." - }, - "face_retention_outcome": { - "type": "string", - "enum": [ - "retained_with_user", - "deleted", - "none", - "ineligible_no_vendor_user", - "failed_retryable" - ], - "description": "What happened to the session's face biometric data. `deleted`: the face embedding was deleted with the session (default). `retained_with_user`: the session is deleted and one image-free biometric template stays anchored to the User; see `biometric_template_uuid`. `none`: the session had no face embedding (for example a document-only KYC session without a liveness selfie; KYB sessions are never selected by this endpoint). `ineligible_no_vendor_user`: retention was requested but the session has no linked User, so the embedding was deleted with the session. `failed_retryable` (batch endpoint only): the group this session was deleted with could not be deleted (a storage service, a biometric store, or a monitoring provider was unavailable); this session is still in place, repeat the request for it." - }, - "biometric_template_uuid": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Id of the retained biometric template when `face_retention_outcome` is `retained_with_user`; `null` otherwise. Manage it through `/v3/biometric-templates/{template_uuid}/`." - }, - "biometric_template_expires_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the retained biometric template is deleted, when `face_retention_outcome` is `retained_with_user`; `null` when no template is retained." - }, - "deleted_child_verifications": { - "type": "integer", - "description": "Always `0` on this endpoint, which deletes KYC sessions only. Deleting a Business Verification (KYB) session with `DELETE /v3/session/{sessionId}/delete/` reports the verifications it created that were deleted with it." - } - } - } - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "Mixed outcomes": { - "value": { - "results": [ - { - "session_id": "4c5c7f3a-3b1d-4b7e-9c39-2b4f7e6a1d02", - "session_number": 1001, - "face_retention_outcome": "deleted", - "biometric_template_uuid": null, - "biometric_template_expires_at": null, - "deleted_child_verifications": 0 - }, - { - "session_id": "b8e1d2c3-4f5a-4b6c-8d7e-9f0a1b2c3d4e", - "session_number": 1002, - "face_retention_outcome": "retained_with_user", - "biometric_template_uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", - "biometric_template_expires_at": "2027-10-09T10:15:00Z", - "deleted_child_verifications": 0 - }, - { - "session_id": "7a1e9c2d-5f4b-4c3a-8e2d-1b0a9f8e7d6c", - "session_number": 1003, - "face_retention_outcome": "ineligible_no_vendor_user", - "biometric_template_uuid": null, - "biometric_template_expires_at": null, - "deleted_child_verifications": 0 - }, - { - "session_id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f", - "session_number": 1004, - "face_retention_outcome": "failed_retryable", - "biometric_template_uuid": null, - "biometric_template_expires_at": null, - "deleted_child_verifications": 0 - } - ] - } - } - } - } - } - }, - "202": { - "description": "More than 20 sessions matched. They are deleted in the background; poll `GET /v3/sessions/delete/{job_id}/` until `status` is `COMPLETED` or `FAILED`.", - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "A bulk session deletion running in the background: progress and counts per outcome, never which sessions it deleted.", - "properties": { - "job_id": { - "type": "string", - "format": "uuid", - "description": "Poll `GET /v3/sessions/delete/{job_id}/` with this id." - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ], - "description": "`PENDING`: accepted, not started yet. `PROCESSING`: deleting; `processed` and `outcomes` are updating. `COMPLETED`: every selected session is accounted for. `FAILED`: the job stopped early; see `error`." - }, - "total": { - "type": "integer", - "description": "Sessions selected when the deletion was requested." - }, - "processed": { - "type": "integer", - "description": "Sessions the job has finished with so far." - }, - "outcomes": { - "type": "object", - "description": "How many sessions ended in each outcome. Every key is always present.", - "properties": { - "deleted": { - "type": "integer", - "description": "Sessions deleted together with their face embedding." - }, - "none": { - "type": "integer", - "description": "Sessions deleted that had no face embedding." - }, - "retained_with_user": { - "type": "integer", - "description": "Sessions deleted while one image-free biometric template was retained for their User, as the request or the application policy asked." - }, - "ineligible_no_vendor_user": { - "type": "integer", - "description": "Sessions deleted, with their face embedding, because retention was requested but they had no linked User." - }, - "failed_retryable": { - "type": "integer", - "description": "Sessions reported as not deleted. A job retries a group it could not delete by itself instead of counting it here." - }, - "already_deleted": { - "type": "integer", - "description": "Selected sessions that were already deleted, for example by another request, when the job reached them." - } - } - }, - "deleted_child_verifications": { - "type": "integer", - "description": "Verifications that the deleted business verifications created, deleted with them. Always `0` for a job started by `POST /v3/sessions/delete/`, which deletes KYC sessions only." - }, - "error": { - "type": "string", - "nullable": true, - "description": "`null` unless `status` is `FAILED`; then why the deletion stopped. The sessions the job had not reached are still in place: repeat the request to delete them." + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "itsme", + "name": "itsme", + "issuing_authority": "Belgian Mobile ID" }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "When the deletion was requested." + "country": "BEL", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" }, - "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the job finished; `null` while it runs." - } - } - }, - "examples": { - "Job accepted": { - "value": { - "job_id": "5b8f2c1e-7d3a-4f6b-9e2c-1a0d8f7e6c5b", - "status": "PENDING", - "total": 250, - "processed": 0, - "outcomes": { - "deleted": 0, - "none": 0, - "retained_with_user": 0, - "ineligible_no_vendor_user": 0, - "failed_retryable": 0, - "already_deleted": 0 - }, - "deleted_child_verifications": 0, - "error": null, - "created_at": "2026-10-09T10:15:00Z", - "completed_at": null - } - } + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, "400": { - "description": "Validation error — nothing was deleted. Error values are arrays of message strings keyed by field name.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "session_numbers": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "examples": { - "Missing selector": { - "summary": "Neither session_numbers nor delete_all provided", - "value": { - "session_numbers": [ - "This field is required unless delete_all is true." - ] - } - }, - "Empty list": { - "summary": "session_numbers is an empty array", - "value": { - "session_numbers": [ - "This list may not be empty." - ] - } - }, - "Non-numeric entry": { - "summary": "session_numbers contains a non-digit string", - "value": { - "session_numbers": [ - "All session_numbers must be numeric." - ] - } - }, - "Privacy erasure cannot retain": { - "summary": "deletion_instruction privacy_erasure combined with retain_face_embeddings true", - "value": { - "retain_face_embeddings": [ - "Privacy erasure cannot retain biometric templates." - ] - } - }, - "No finite retention duration": { - "summary": "Retention requested without face_retention_days on the request or the application", - "value": { - "detail": "A finite biometric-template retention duration is required." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "403": { - "description": "Missing or invalid API key. This API returns `403` for authentication failures — never `401`.", + "409": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, - "examples": { - "Missing or invalid API key": { - "summary": "No x-api-key header, or the key is invalid", - "value": { - "detail": "You do not have permission to perform this action." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "429": { - "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", + "502": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, - "examples": { - "Rate limited": { - "summary": "Write budget exhausted", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, "x-codeSamples": [ { "lang": "curl", - "label": "curl", - "source": "curl -X POST \\\n https://verification.didit.me/v3/sessions/delete/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"session_numbers\": [\"1001\", \"1002\", \"1003\"]\n }'" + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/itsme/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"BEL\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" }, { "lang": "python", "label": "Python", - "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/sessions/delete/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\"session_numbers\": [\"1001\", \"1002\", \"1003\"]},\n)\nresponse.raise_for_status()\nif response.status_code == 202: # more than 20 sessions: poll the job\n job_id = response.json()[\"job_id\"]\nelse: # 200: one result per matched session\n print(response.json()[\"results\"])\n\n# Destructive: delete every KYC session in the application\n# requests.post(\n# \"https://verification.didit.me/v3/sessions/delete/\",\n# headers={\"x-api-key\": \"YOUR_API_KEY\"},\n# json={\"delete_all\": True},\n# )" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const response = await fetch(\n 'https://verification.didit.me/v3/sessions/delete/',\n {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ session_numbers: ['1001', '1002', '1003'] }),\n },\n);\nif (response.status !== 200 && response.status !== 202) throw new Error(`HTTP ${response.status}`);\nconst body = await response.json();\n// 200: body.results, one per matched session. 202: poll /v3/sessions/delete/${body.job_id}/" + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'BEL',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/itsme/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "itsme" } }, - "/v3/sessions/delete/{job_id}/": { - "get": { - "summary": "Get a bulk session deletion job", - "description": "Progress of a bulk session deletion that `POST /v3/sessions/delete/` answered with `202` (more than 20 matching sessions, selected by `session_numbers` or with `delete_all`). Poll every few seconds while `status` is `PENDING` or `PROCESSING`; stop when it is `COMPLETED` or `FAILED`.\n\nThe job reports how many sessions ended in each outcome and how many it has processed, never which sessions it deleted. A `FAILED` job carries the reason in `error`; the sessions it had not reached are still in place, so repeat the deletion request to delete them.\n\n**Authentication:** API key in the `x-api-key` header. A job is only visible to the application that created it; any other `job_id` returns `404`. Missing or invalid credentials return `403`; this API never returns `401`.", - "operationId": "get_session_deletion_job", - "tags": [ - "Sessions" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], + "/v3/id-verification/wallets/idin/": { + "post": { + "operationId": "start_idin_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_idin", + "auth": "api_key", + "server": true + }, + "description": "Start a iDIN sign-in (Dutch banks (Currence iDIN); NLD) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start iDIN verification", "parameters": [ { - "name": "job_id", - "in": "path", - "required": true, + "in": "header", + "name": "Idempotency-Key", "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, - "description": "The `job_id` returned with the `202` from `POST /v3/sessions/delete/`." + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl 'https://verification.didit.me/v3/sessions/delete/JOB_ID/' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "python", - "label": "Python", - "source": "import time\nimport requests\n\nurl = 'https://verification.didit.me/v3/sessions/delete/JOB_ID/'\nwhile True:\n job = requests.get(url, headers={'x-api-key': 'YOUR_API_KEY'}).json()\n if job['status'] in ('COMPLETED', 'FAILED'):\n break\n time.sleep(3)\nprint(job['status'], job['outcomes'], job['error'])" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const url = 'https://verification.didit.me/v3/sessions/delete/JOB_ID/';\nlet job;\ndo {\n await new Promise((r) => setTimeout(r, 3000));\n job = await (await fetch(url, { headers: { 'x-api-key': 'YOUR_API_KEY' } })).json();\n} while (job.status === 'PENDING' || job.status === 'PROCESSING');\nconsole.log(job.status, job.outcomes, job.error);" - } + "tags": [ + "Digital ID wallets" ], - "responses": { - "200": { - "description": "The job.", - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "A bulk session deletion running in the background: progress and counts per outcome, never which sessions it deleted.", - "properties": { - "job_id": { - "type": "string", - "format": "uuid", - "description": "Poll `GET /v3/sessions/delete/{job_id}/` with this id." - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ], - "description": "`PENDING`: accepted, not started yet. `PROCESSING`: deleting; `processed` and `outcomes` are updating. `COMPLETED`: every selected session is accounted for. `FAILED`: the job stopped early; see `error`." - }, - "total": { - "type": "integer", - "description": "Sessions selected when the deletion was requested." - }, - "processed": { - "type": "integer", - "description": "Sessions the job has finished with so far." - }, - "outcomes": { - "type": "object", - "description": "How many sessions ended in each outcome. Every key is always present.", - "properties": { - "deleted": { - "type": "integer", - "description": "Sessions deleted together with their face embedding." - }, - "none": { - "type": "integer", - "description": "Sessions deleted that had no face embedding." - }, - "retained_with_user": { - "type": "integer", - "description": "Sessions deleted while one image-free biometric template was retained for their User, as the request or the application policy asked." - }, - "ineligible_no_vendor_user": { - "type": "integer", - "description": "Sessions deleted, with their face embedding, because retention was requested but they had no linked User." - }, - "failed_retryable": { - "type": "integer", - "description": "Sessions reported as not deleted. A job retries a group it could not delete by itself instead of counting it here." - }, - "already_deleted": { - "type": "integer", - "description": "Selected sessions that were already deleted, for example by another request, when the job reached them." - } - } - }, - "deleted_child_verifications": { - "type": "integer", - "description": "Verifications that the deleted business verifications created, deleted with them. Always `0` for a job started by `POST /v3/sessions/delete/`, which deletes KYC sessions only." - }, - "error": { - "type": "string", - "nullable": true, - "description": "`null` unless `status` is `FAILED`; then why the deletion stopped. The sessions the job had not reached are still in place: repeat the request to delete them." - }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "When the deletion was requested." - }, - "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the job finished; `null` while it runs." - } - } + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiIdinWalletVerificationStartRequest" + }, + "example": { + "country": "NLD", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "examples": { - "Processing": { - "value": { - "job_id": "5b8f2c1e-7d3a-4f6b-9e2c-1a0d8f7e6c5b", - "status": "PROCESSING", - "total": 250, - "processed": 100, - "outcomes": { - "deleted": 82, - "none": 18, - "retained_with_user": 0, - "ineligible_no_vendor_user": 0, - "failed_retryable": 0, - "already_deleted": 0 - }, - "deleted_child_verifications": 0, - "error": null, - "created_at": "2026-10-09T10:15:00Z", - "completed_at": null - } - }, - "Completed": { - "value": { - "job_id": "5b8f2c1e-7d3a-4f6b-9e2c-1a0d8f7e6c5b", - "status": "COMPLETED", - "total": 250, - "processed": 250, - "outcomes": { - "deleted": 204, - "none": 44, - "retained_with_user": 0, - "ineligible_no_vendor_user": 0, - "failed_retryable": 0, - "already_deleted": 2 - }, - "deleted_child_verifications": 0, - "error": null, - "created_at": "2026-10-09T10:15:00Z", - "completed_at": "2026-10-09T10:16:40Z" - } - }, - "Failed": { - "value": { - "job_id": "5b8f2c1e-7d3a-4f6b-9e2c-1a0d8f7e6c5b", - "status": "FAILED", - "total": 250, - "processed": 100, - "outcomes": { - "deleted": 82, - "none": 18, - "retained_with_user": 0, - "ineligible_no_vendor_user": 0, - "failed_retryable": 0, - "already_deleted": 0 - }, - "deleted_child_verifications": 0, - "error": "The deletion stopped before it finished. The sessions it had not reached are still in place; repeat the request to delete them.", - "created_at": "2026-10-09T10:15:00Z", - "completed_at": "2026-10-09T10:15:52Z" - } - } - } + "return_url": "https://app.example.com/identity/complete" } - } - }, - "403": { - "description": "Missing or invalid API key. This API returns `403` for authentication failures, never `401`.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, - "examples": { - "Missing or invalid API key": { - "summary": "No x-api-key header, or the key is invalid", - "value": { - "detail": "You do not have permission to perform this action." - } - } - } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiIdinWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiIdinWalletVerificationStartRequest" } } }, - "404": { - "description": "No deletion job with this `job_id` exists for the calling application. Jobs are only visible to the application that created them." - } - } - } - }, - "/v3/sessions/{session_id}/reviews/": { - "get": { - "summary": "List session reviews and activity log", - "description": "Return the audit trail / activity feed for a User Verification (KYC) session, newest first, in a paginated envelope (`count` / `next` / `previous` / `results`, 50 entries per page by default).\n\nFeed entries are written by every actor that touches the session: manual decisions made through `PATCH /v3/session/{session_id}/update-status/` (which records `previous_status` → `new_status`), Console reviewer activity (comments with `@email` mentions, KYC/POA data edits, file uploads/removals, tag changes, detail-page views), AML ongoing-monitoring matches, and notes appended via `POST` on this same path. Reading this feed is the API-side equivalent of the **Activity** panel in the Didit Console session view.\n\nOnly User Verification (KYC) sessions are addressable here; Business Verification (KYB) session IDs return `404`.", - "operationId": "list_session_reviews", - "tags": [ - "Sessions" - ], - "parameters": [ - { - "name": "session_id", - "in": "path", - "required": true, - "description": "UUID of the User Verification (KYC) session. Must belong to the same application as the API key — cross-application UUIDs, deleted sessions, and Business Verification (KYB) session IDs all return `404`.", - "schema": { - "type": "string", - "format": "uuid", - "example": "8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d" - } - }, - { - "name": "limit", - "in": "query", - "required": false, - "description": "Page size. Defaults to `50`.", - "schema": { - "type": "integer", - "default": 50, - "example": 50 - } - }, + "required": true + }, + "security": [ { - "name": "offset", - "in": "query", - "required": false, - "description": "Number of entries to skip from the start of the (ordered) feed. Defaults to `0`. The `next` / `previous` URLs in the response carry the right values for walking the feed.", - "schema": { - "type": "integer", - "default": 0, - "example": 0 - } + "ApiKeyAuth": [] }, { - "name": "ordering", - "in": "query", - "required": false, - "description": "Sort order. The feed defaults to newest first (`-created_at`); pass `created_at` to read it oldest first. Unknown values are silently ignored (default order is used); other serializer field names are also accepted.", - "schema": { - "type": "string", - "default": "-created_at", - "example": "created_at" - } + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Paginated activity feed, newest first by default. `count` is the total number of entries; `next` / `previous` are ready-made page URLs (`null` at the ends). A `count` of `0` with empty `results` means no activity has been recorded yet.", + "201": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer", - "description": "Total number of feed entries for this session (across all pages).", - "example": 3 - }, - "next": { - "type": "string", - "nullable": true, - "description": "URL of the next page, or `null` on the last page.", - "example": null + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "idin", + "name": "iDIN", + "issuing_authority": "Dutch banks (Currence iDIN)" }, - "previous": { - "type": "string", - "nullable": true, - "description": "URL of the previous page, or `null` on the first page.", - "example": null + "country": "NLD", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReviewItem" - }, - "description": "Feed entries for this page." - } - } - }, - "examples": { - "DecisionTrail": { - "summary": "Manual approval plus API-added notes", - "value": { - "count": 3, - "next": null, - "previous": null, - "results": [ - { - "uuid": "de0561bc-5583-4409-9171-1a31a4c8a5f4", - "activity_type": "STATUS_UPDATED", - "actor_display": "API Client", - "actor_email": null, - "actor_type": "API_KEY", - "new_status": "Approved", - "previous_status": "In Review", - "comment": "Document re-checked manually; address matches.", - "mentioned_emails": [], - "previous_value": { - "status": "In Review" - }, - "new_value": { - "status": "Approved" - }, - "changed_fields": [ - "status" - ], - "metadata": null, - "created_at": "2026-06-12T00:19:31.918735Z" - }, - { - "uuid": "35370f58-5098-44ee-8a06-8c9af80b2455", - "activity_type": "STATUS_UPDATED", - "actor_display": "Unknown", - "actor_email": null, - "actor_type": "CONSOLE_USER", - "new_status": null, - "previous_status": null, - "comment": "Note without status.", - "mentioned_emails": [], - "previous_value": null, - "new_value": null, - "changed_fields": [], - "metadata": null, - "created_at": "2026-06-12T00:18:44.504157Z" - }, - { - "uuid": "00338f82-f1c5-4f57-8614-c49bb258af1a", - "activity_type": "STATUS_UPDATED", - "actor_display": "Unknown", - "actor_email": null, - "actor_type": "CONSOLE_USER", - "new_status": "In Review", - "previous_status": null, - "comment": "Flagging for manual document re-check.", - "mentioned_emails": [], - "previous_value": null, - "new_value": null, - "changed_fields": [], - "metadata": null, - "created_at": "2026-06-12T00:18:44.466537Z" - } - ] - } - }, - "PaginatedFirstPage": { - "summary": "First page with limit=2 — next holds the follow-up URL", - "value": { - "count": 4, - "next": "https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/?limit=2&offset=2", - "previous": null, - "results": [ - { - "uuid": "de0561bc-5583-4409-9171-1a31a4c8a5f4", - "activity_type": "STATUS_UPDATED", - "actor_display": "API Client", - "actor_email": null, - "actor_type": "API_KEY", - "new_status": "Approved", - "previous_status": "In Review", - "comment": "Document re-checked manually; address matches.", - "mentioned_emails": [], - "previous_value": { - "status": "In Review" - }, - "new_value": { - "status": "Approved" - }, - "changed_fields": [ - "status" - ], - "metadata": null, - "created_at": "2026-06-12T00:19:31.918735Z" - }, - { - "uuid": "bd4e6227-bdd2-42b2-a726-1d12f785a457", - "activity_type": "STATUS_UPDATED", - "actor_display": "Unknown", - "actor_email": null, - "actor_type": "CONSOLE_USER", - "new_status": null, - "previous_status": null, - "comment": null, - "mentioned_emails": [], - "previous_value": null, - "new_value": null, - "changed_fields": [], - "metadata": null, - "created_at": "2026-06-12T00:18:44.540238Z" - } - ] - } + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" }, - "Empty": { - "summary": "Session with no recorded activity yet", - "value": { - "count": 0, - "next": null, - "previous": null, - "results": [] - } - } + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "Missing, malformed, expired, or revoked `x-api-key`. Authentication is checked before the session lookup, so an invalid key returns `403` even when the session UUID does not exist.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "You do not have permission to perform this action." - } - } - }, - "examples": { - "InvalidKey": { - "summary": "Missing or invalid x-api-key header", - "value": { - "detail": "You do not have permission to perform this action." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "404": { - "description": "No User Verification (KYC) session with this UUID is visible to your application: the UUID does not exist, the session was deleted, or it belongs to a different application. Business Verification (KYB) session IDs also return `404` here — the reviews feed is only addressable for KYC sessions.", + "409": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "Session not found." - } - } - }, - "examples": { - "NotFound": { - "summary": "Unknown, deleted, or cross-application session UUID", - "value": { - "detail": "Session not found." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "429": { - "description": "Rate limit exceeded. Inspect `Retry-After` and `X-RateLimit-*` response headers. See [Rate limiting](/integration/rate-limiting)." + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", - "label": "curl", - "source": "curl -s 'https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import os, requests\n\nsession_id = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\"\nurl = f\"https://verification.didit.me/v3/sessions/{session_id}/reviews/\"\n\nwhile url: # walk every page of the feed\n resp = requests.get(url, headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]}, timeout=10)\n resp.raise_for_status()\n page = resp.json()\n for entry in page[\"results\"]:\n print(entry[\"created_at\"], entry[\"activity_type\"], entry[\"actor_display\"], entry.get(\"new_status\"))\n url = page[\"next\"]" + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/idin/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"NLD\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" }, { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "const sessionId = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\";\nconst res = await fetch(\n `https://verification.didit.me/v3/sessions/${sessionId}/reviews/`,\n { headers: { 'x-api-key': 'YOUR_API_KEY' } },\n);\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst { count, results } = await res.json();\nconsole.log(`${count} feed entries`);\nfor (const entry of results) {\n console.log(entry.created_at, entry.activity_type, entry.actor_display, entry.new_status ?? \"\");\n}" + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'NLD',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/idin/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] - }, - "post": { - "summary": "Add a review note to a session's audit trail", - "description": "Append a note to a User Verification (KYC) session's review / activity feed. **This endpoint only records an entry — it never changes the session.** The session's `status` is left untouched, no status-transition validation runs, and no webhook fires. To actually approve, decline, or re-open a session (which also writes a fully-attributed `STATUS_UPDATED` entry to this feed), use `PATCH /v3/session/{session_id}/update-status/` instead.\n\nBoth body fields are optional and an empty JSON object is accepted. `new_status` is stored purely as a label on the entry and accepts any of the ten session status values (case-sensitive). `@email` mentions inside `comment` are stored as plain text only — they trigger no notifications on this endpoint, and the entry's `mentioned_emails` stays empty.\n\nNote on attribution: entries created here carry no actor information. In the feed they show `actor_display: \"Unknown\"`, `actor_type: \"CONSOLE_USER\"`, and `activity_type: \"STATUS_UPDATED\"` (storage defaults), and the `201` body's `user` field is `\"Unknown\"`.", - "operationId": "create_session_review", - "tags": [ - "Sessions" ], + "x-didit-wallet-id": "idin" + } + }, + "/v3/id-verification/wallets/ftn/": { + "post": { + "operationId": "start_ftn_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_ftn", + "auth": "api_key", + "server": true + }, + "description": "Start a Finnish Trust Network sign-in (Finnish banks and mobile operators (FTN); FIN) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Finnish Trust Network verification", "parameters": [ { - "name": "Content-Type", "in": "header", - "required": false, - "description": "Set to `application/json` when sending a JSON body.", - "schema": { - "type": "string", - "example": "application/json" - } - }, - { - "name": "session_id", - "in": "path", - "required": true, - "description": "UUID of the User Verification (KYC) session. Must belong to the same application as the API key — cross-application UUIDs, deleted sessions, and Business Verification (KYB) session IDs all return `404`.", + "name": "Idempotency-Key", "schema": { - "type": "string", - "format": "uuid", - "example": "8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d" - } + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], + "tags": [ + "Digital ID wallets" + ], "requestBody": { - "required": false, "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "new_status": { - "type": "string", - "nullable": true, - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "description": "Optional status label to record on the entry. Accepts any session status value, case-sensitive (`\"approved\"` is rejected with `400`). Recording a status here does **not** change the session's actual status — use the update-status endpoint for that.", - "example": "In Review" - }, - "comment": { - "type": "string", - "nullable": true, - "description": "Optional free-text note (no enforced length limit). Stored verbatim; `@email` mentions are not processed on this endpoint.", - "example": "Flagging for manual document re-check." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiFtnWalletVerificationStartRequest" }, - "examples": { - "NoteWithStatusLabel": { - "summary": "Note carrying a status label (session status unchanged)", - "value": { - "new_status": "In Review", - "comment": "Flagging for manual document re-check." - } + "example": { + "country": "FIN", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "CommentOnly": { - "summary": "Plain note without a status label", - "value": { - "comment": "Customer emailed an updated utility bill; awaiting upload." - } - } + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiFtnWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiFtnWalletVerificationStartRequest" } } - } + }, + "required": true }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], "responses": { "201": { - "description": "Entry recorded on the session's audit trail. The session itself is unchanged — its `status` keeps its previous value and no webhook is sent. The new entry also appears at the top of `GET …/reviews/` (with `actor_display: \"Unknown\"`).", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "user": { - "type": "string", - "description": "Display name of the entry's author. Always `\"Unknown\"` for entries created through this endpoint, because it records no actor attribution.", - "example": "Unknown" - }, - "new_status": { - "type": "string", - "nullable": true, - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "description": "The status label recorded on the entry, or `null` if none was sent. The session's actual status is not affected.", - "example": "In Review" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "ftn", + "name": "Finnish Trust Network", + "issuing_authority": "Finnish banks and mobile operators (FTN)" }, - "comment": { - "type": "string", - "nullable": true, - "description": "The note recorded on the entry, or `null` if none was sent.", - "example": "Flagging for manual document re-check." + "country": "FIN", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "When the entry was written, ISO 8601 UTC.", - "example": "2026-06-12T00:18:44.466537Z" - } - } - }, - "examples": { - "NoteWithStatusLabel": { - "summary": "Note with a status label", - "value": { - "user": "Unknown", - "new_status": "In Review", - "comment": "Flagging for manual document re-check.", - "created_at": "2026-06-12T00:18:44.466537Z" - } + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" }, - "CommentOnly": { - "summary": "Plain note", - "value": { - "user": "Unknown", - "new_status": null, - "comment": "Note without status.", - "created_at": "2026-06-12T00:18:44.504157Z" - } - } + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, "400": { - "description": "Validation error, returned as a per-field map of messages. The usual cause is a `new_status` value that is not one of the ten allowed statuses — the check is case-sensitive.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "new_status": { - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "\"approved\" is not a valid choice." - ] - } - } - }, - "examples": { - "InvalidStatusChoice": { - "summary": "Lowercase / unknown status value", - "value": { - "new_status": [ - "\"approved\" is not a valid choice." - ] - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "Missing, malformed, expired, or revoked `x-api-key`. Authentication is checked before the session lookup, so an invalid key returns `403` even when the session UUID does not exist.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "You do not have permission to perform this action." - } - } - }, - "examples": { - "InvalidKey": { - "summary": "Missing or invalid x-api-key header", - "value": { - "detail": "You do not have permission to perform this action." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "404": { - "description": "No User Verification (KYC) session with this UUID is visible to your application: the UUID does not exist, the session was deleted, or it belongs to a different application. Business Verification (KYB) session IDs also return `404` here — the reviews feed is only addressable for KYC sessions.", + "409": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "example": "Session not found." - } - } - }, - "examples": { - "NotFound": { - "summary": "Unknown, deleted, or cross-application session UUID", - "value": { - "detail": "Session not found." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "429": { - "description": "Rate limit exceeded. Inspect `Retry-After` and `X-RateLimit-*` response headers. See [Rate limiting](/integration/rate-limiting)." + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], "x-codeSamples": [ { "lang": "curl", - "label": "curl", - "source": "# Records an audit-trail note only -- the session's status does NOT change.\ncurl -s -X POST https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"new_status\": \"In Review\",\n \"comment\": \"Flagging for manual document re-check.\"\n }'" - }, - { - "lang": "Python", - "label": "Python (requests)", - "source": "import os, requests\n\n# Records an audit-trail note only -- the session's status does NOT change.\n# To approve/decline a session use PATCH /v3/session/{session_id}/update-status/.\nsession_id = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\"\nresp = requests.post(\n f\"https://verification.didit.me/v3/sessions/{session_id}/reviews/\",\n headers={\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Content-Type\": \"application/json\",\n },\n json={\n \"new_status\": \"In Review\",\n \"comment\": \"Flagging for manual document re-check.\",\n },\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()) # {'user': 'Unknown', 'new_status': 'In Review', ...}" + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/ftn/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"FIN\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" }, { - "lang": "JavaScript", - "label": "Node.js (fetch)", - "source": "// Records an audit-trail note only -- the session's status does NOT change.\n// To approve/decline a session use PATCH /v3/session/{session_id}/update-status/.\nconst sessionId = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\";\nconst res = await fetch(\n `https://verification.didit.me/v3/sessions/${sessionId}/reviews/`,\n {\n method: \"POST\",\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n new_status: \"In Review\",\n comment: \"Flagging for manual document re-check.\",\n }),\n },\n);\nif (!res.ok) throw new Error(`Didit ${res.status}: ${await res.text()}`);\nconsole.log(await res.json()); // { user: 'Unknown', new_status: 'In Review', ... }" + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'FIN',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/ftn/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "ftn" } }, - "/v3/billing/balance/": { - "get": { - "summary": "Get credit balance", - "description": "Get current pre-paid credit balance in USD (organization-wide) plus auto-refill config. Amounts are decimal strings — parse with a decimal library.", - "operationId": "get_billing_balance", + "/v3/id-verification/wallets/personalausweis/": { + "post": { + "operationId": "start_personalausweis_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_personalausweis", + "auth": "api_key", + "server": true + }, + "description": "Start a Personalausweis sign-in (Bundesministerium des Innern (eID); DEU) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Personalausweis verification", + "parameters": [ + { + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + } + ], "tags": [ - "Billing" + "Digital ID wallets" ], - "x-codeSamples": [ + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiPersonalausweisWalletVerificationStartRequest" + }, + "example": { + "country": "DEU", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiPersonalausweisWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiPersonalausweisWalletVerificationStartRequest" + } + } + }, + "required": true + }, + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/billing/balance/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "ApiKeyAuth": [] }, { - "lang": "python", - "label": "Python", - "source": "import os\nfrom decimal import Decimal\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/billing/balance/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\ndata = resp.json()\nbalance = Decimal(data['balance'])\nif balance < Decimal('50'):\n print(f'Low balance: ${balance} - top up before launching new sessions.')\nelse:\n print(f'Balance OK: ${balance}')" + "BearerAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "personalausweis", + "name": "Personalausweis", + "issuing_authority": "Bundesministerium des Innern (eID)" + }, + "country": "DEU", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" + } + } + }, + "description": "" }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/billing/balance/', {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Billing balance failed: ${resp.status}`);\nconst { balance, auto_refill_enabled, auto_refill_threshold } = await resp.json();\nconsole.log({ balance, auto_refill_enabled, auto_refill_threshold });" - } - ], - "responses": { - "200": { - "description": "Current credit balance plus the organization's auto-refill configuration.", + "400": { "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "balance", - "auto_refill_enabled" - ], - "properties": { - "balance": { - "type": "string", - "description": "Current pre-paid credit balance in USD, as a fixed-point decimal string. Decreases as verifications are billed and increases when a top-up settles." - }, - "auto_refill_enabled": { - "type": "boolean", - "description": "When `true`, Didit will automatically charge the saved Stripe payment method to add credits once the balance crosses `auto_refill_threshold`. Configured from Console → Billing." - }, - "auto_refill_amount": { - "type": "string", - "nullable": true, - "description": "USD amount that will be charged on each auto-refill. `null` when auto-refill is disabled or unset." - }, - "auto_refill_threshold": { - "type": "string", - "nullable": true, - "description": "Trigger threshold in USD. Auto-refill fires when `balance` falls below this value. `null` when auto-refill is disabled or unset." - } - } - }, - "examples": { - "Healthy balance with auto-refill": { - "value": { - "balance": "1234.5600", - "auto_refill_enabled": true, - "auto_refill_amount": "500.00", - "auto_refill_threshold": "100.00" - } - }, - "Low balance, manual top-up only": { - "value": { - "balance": "12.0000", - "auto_refill_enabled": false, - "auto_refill_amount": null, - "auto_refill_threshold": null - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot read billing data for this organization. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "409": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/personalausweis/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"DEU\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'DEU',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/personalausweis/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "personalausweis" } }, - "/v3/billing/top-up/": { + "/v3/id-verification/wallets/frejaid/": { "post": { - "summary": "Top up credits", - "description": "Add credits via a hosted Stripe Checkout session (**$50 minimum**); not available on annual plans. Redirect the customer to the returned `checkout_session_url`.", - "operationId": "billing_top_up", + "operationId": "start_frejaid_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_frejaid", + "auth": "api_key", + "server": true + }, + "description": "Start a Freja eID sign-in (Freja eID Group; SWE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Freja eID verification", + "parameters": [ + { + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + } + ], "tags": [ - "Billing" + "Digital ID wallets" ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "amount_in_dollars" - ], - "properties": { - "amount_in_dollars": { - "type": "number", - "minimum": 50, - "description": "Amount to charge, in USD. **Must be at least 50.** Tax is added automatically at checkout based on the customer's billing address." - }, - "success_url": { - "type": "string", - "format": "uri", - "description": "URL Stripe redirects the customer to after a successful payment. Currently defaults to `https://console.didit.me`. Pass a working URL explicitly; the default host is unavailable. Append the literal placeholder `{CHECKOUT_SESSION_ID}` if you want Stripe to substitute the session ID." - }, - "cancel_url": { - "type": "string", - "format": "uri", - "description": "URL Stripe redirects the customer to when they abort or close the Checkout window. Currently defaults to `https://console.didit.me`. Pass a working URL explicitly; the default host is unavailable." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiFrejaidWalletVerificationStartRequest" }, - "examples": { - "Minimum top-up": { - "value": { - "amount_in_dollars": 50 - } + "example": { + "country": "SWE", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "Custom amount with redirects": { - "value": { - "amount_in_dollars": 500, - "success_url": "https://yourapp.com/billing/top-up/success", - "cancel_url": "https://yourapp.com/billing/top-up/cancel" - } - } + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiFrejaidWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiFrejaidWalletVerificationStartRequest" } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/billing/top-up/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"amount_in_dollars\": 500,\n \"success_url\": \"https://yourapp.com/billing/success\",\n \"cancel_url\": \"https://yourapp.com/billing/cancel\"\n }'" }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/billing/top-up/',\n headers={\n 'x-api-key': os.environ['DIDIT_API_KEY'],\n 'Content-Type': 'application/json',\n },\n json={\n 'amount_in_dollars': 500,\n 'success_url': 'https://yourapp.com/billing/success',\n 'cancel_url': 'https://yourapp.com/billing/cancel',\n },\n timeout=10,\n)\nresp.raise_for_status()\nsession = resp.json()\nprint('Redirect customer to:', session['checkout_session_url'])" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/billing/top-up/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n amount_in_dollars: 500,\n success_url: 'https://yourapp.com/billing/success',\n cancel_url: 'https://yourapp.com/billing/cancel',\n }),\n});\nif (!resp.ok) throw new Error(`Top-up failed: ${resp.status}`);\nconst { checkout_session_url } = await resp.json();\nwindow.location.assign(checkout_session_url);" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Stripe Checkout session created. Redirect the user to `checkout_session_url` to complete payment.", + "201": { "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "checkout_session_id", - "checkout_session_url" - ], - "properties": { - "checkout_session_id": { - "type": "string", - "description": "Stripe Checkout Session identifier (e.g. `cs_test_a1B2c3...`). Useful for server-side reconciliation." - }, - "checkout_session_url": { - "type": "string", - "format": "uri", - "description": "Single-use Stripe-hosted payment page URL. Redirect the customer here to complete payment." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "Created": { - "value": { - "checkout_session_id": "cs_test_a1B2c3D4e5F6g7H8i9J0", - "checkout_session_url": "https://checkout.stripe.com/c/pay/cs_test_a1B2c3D4e5F6g7H8i9J0" - } - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "frejaid", + "name": "Freja eID", + "issuing_authority": "Freja eID Group" + }, + "country": "SWE", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, "400": { - "description": "Invalid payload — the amount is missing, not a number, below the $50 minimum, or the organization is on an annual plan. The error body is a JSON array.", "content": { "application/json": { - "examples": { - "Missing amount": { - "value": [ - "amount_in_dollars is required." - ] - }, - "Not a number": { - "value": [ - "amount_in_dollars must be a number." - ] - }, - "Below minimum": { - "value": [ - "Amount must be at least $50." - ] - }, - "Annual plan": { - "value": [ - "You have an annual plan. Contact your account manager to top up." - ] - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot create top-up sessions for this organization. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "409": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - } - }, - "/v3/webhook/destinations/": { - "get": { - "summary": "List webhook destinations", - "description": "List all webhook destinations (unpaginated array, oldest first). Each row includes URL, subscribed events, and delivery health metrics. The signing secret is NOT included in the list — fetch a single destination (`GET /v3/webhook/destinations/{destination_uuid}/`) to read `secret_shared_key`.", - "operationId": "list_webhook_destinations", - "tags": [ - "Webhook" - ], "x-codeSamples": [ { "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/webhook/destinations/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/frejaid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"SWE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" }, { "lang": "python", "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/webhook/destinations/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nfor dest in resp.json():\n health = 'OK' if dest['error_rate_percentage'] < 5 else 'DEGRADED'\n print(f\"{dest['label']:30s} {dest['url']:50s} {health}\")" - }, + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'SWE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/frejaid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + } + ], + "x-didit-wallet-id": "frejaid" + } + }, + "/v3/id-verification/wallets/uae-pass/": { + "post": { + "operationId": "start_uae_pass_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_uae_pass", + "auth": "api_key", + "server": true + }, + "description": "Start a UAE PASS sign-in (UAE Digital Government Authority; ARE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start UAE PASS verification", + "parameters": [ { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/webhook/destinations/', {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nconst destinations = await resp.json();\nfor (const d of destinations) {\n console.log(d.label, d.url, `${d.error_rate_percentage}% errors`);\n}" + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "responses": { - "200": { - "description": "List of webhook destinations (unpaginated, oldest first).", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Stable destination identifier; use as `{destination_uuid}` for update/delete." - }, - "label": { - "type": "string", - "description": "Human-readable name shown in the Console." - }, - "url": { - "type": "string", - "format": "uri", - "description": "Destination URL that receives `POST` requests with the webhook payload." - }, - "enabled": { - "type": "boolean", - "description": "When `false`, Didit silently skips delivery to this destination." - }, - "webhook_version": { - "type": "string", - "enum": [ - "v1", - "v2", - "v3" - ], - "description": "Payload schema version sent to this destination. `v3` is the current/recommended format." - }, - "subscribed_events": { - "$ref": "#/components/schemas/WebhookSubscribedEvents" - }, - "subscribed_events_count": { - "type": "integer", - "description": "Cached length of `subscribed_events`, useful for UI rendering." - }, - "deliveries_count": { - "type": "integer", - "description": "Total delivery attempts (successful + failed) recorded for this destination." - }, - "failed_deliveries_count": { - "type": "integer", - "description": "Delivery attempts whose target responded with HTTP ≥ 400 (or did not respond)." - }, - "average_response_time_ms": { - "type": "integer", - "nullable": true, - "description": "Average target response time in milliseconds, rounded to an integer. `null` if no deliveries yet." - }, - "error_rate_percentage": { - "type": "integer", - "description": "`round(failed_deliveries_count / deliveries_count * 100)`; `0` when there are no deliveries." - }, - "last_delivery_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the most recent delivery attempt (success or failure). `null` if never delivered." - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - } - } - } - }, - "examples": { - "Two destinations": { - "value": [ - { - "uuid": "11111111-2222-3333-4444-555555555555", - "label": "Production session webhooks", - "url": "https://yourapp.com/webhooks/didit", - "enabled": true, - "webhook_version": "v3", - "subscribed_events": [ - "status.updated", - "data.updated" - ], - "subscribed_events_count": 2, - "deliveries_count": 12453, - "failed_deliveries_count": 14, - "average_response_time_ms": 187, - "error_rate_percentage": 0, - "last_delivery_at": "2026-05-17T09:21:18Z", - "created_at": "2026-01-04T08:00:00Z", - "updated_at": "2026-04-22T14:09:11Z" - }, - { - "uuid": "22222222-3333-4444-5555-666666666666", - "label": "Staging mirror", - "url": "https://staging.yourapp.com/webhooks/didit", - "enabled": false, - "webhook_version": "v3", - "subscribed_events": [ - "status.updated" - ], - "subscribed_events_count": 1, - "deliveries_count": 0, - "failed_deliveries_count": 0, - "average_response_time_ms": null, - "error_rate_percentage": 0, - "last_delivery_at": null, - "created_at": "2026-03-12T10:15:02Z", - "updated_at": "2026-03-12T10:15:02Z" - } - ] - } - } + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiUaePassWalletVerificationStartRequest" + }, + "example": { + "country": "ARE", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiUaePassWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiUaePassWalletVerificationStartRequest" } } }, + "required": true + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "uae_pass", + "name": "UAE PASS", + "issuing_authority": "UAE Digital Government Authority" + }, + "country": "ARE", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + }, "403": { - "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "429": { - "description": "Rate limit exceeded; back off and retry.", + "409": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - }, - "post": { - "summary": "Create webhook destination", - "description": "Create a webhook destination. The response includes the auto-generated `secret_shared_key` HMAC signing secret (also readable later via `GET /v3/webhook/destinations/{destination_uuid}/`). Always send `subscribed_events` with at least one event (no wildcards): an explicit empty list is rejected with 400, and omitting the field creates a destination subscribed to nothing, which never receives a webhook. The `(application, url)` pair must be unique among non-deleted destinations.", - "operationId": "create_webhook_destination", - "tags": [ - "Webhook" - ], "x-codeSamples": [ { "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/webhook/destinations/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"label\": \"Production session webhooks\",\n \"url\": \"https://yourapp.com/webhooks/didit\",\n \"webhook_version\": \"v3\",\n \"subscribed_events\": [\"status.updated\", \"data.updated\"]\n }'" + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/uae-pass/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"ARE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" }, { "lang": "python", "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/webhook/destinations/',\n headers={\n 'x-api-key': os.environ['DIDIT_API_KEY'],\n 'Content-Type': 'application/json',\n },\n json={\n 'label': 'Production session webhooks',\n 'url': 'https://yourapp.com/webhooks/didit',\n 'webhook_version': 'v3',\n 'subscribed_events': ['status.updated', 'data.updated'],\n },\n timeout=10,\n)\nresp.raise_for_status()\ndestination = resp.json()\n# Store secret_shared_key in your secret manager.\nprint('Destination UUID:', destination['uuid'])\nprint('Signing secret:', destination['secret_shared_key'])" - }, + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'ARE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/uae-pass/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + } + ], + "x-didit-wallet-id": "uae_pass" + } + }, + "/v3/id-verification/wallets/govbr/": { + "post": { + "operationId": "start_govbr_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_govbr", + "auth": "api_key", + "server": true + }, + "description": "Start a gov.br sign-in (Governo Federal do Brasil; BRA) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start gov.br verification", + "parameters": [ { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/webhook/destinations/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n label: 'Production session webhooks',\n url: 'https://yourapp.com/webhooks/didit',\n webhook_version: 'v3',\n subscribed_events: ['status.updated', 'data.updated'],\n }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst destination = await resp.json();\nconsole.log('Store this secret securely:', destination.secret_shared_key);" + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], + "tags": [ + "Digital ID wallets" + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "label", - "url" - ], - "properties": { - "label": { - "type": "string", - "maxLength": 255, - "description": "Human-readable name displayed in Console → Webhooks. Helps distinguish destinations when you have several (e.g. \"Prod\", \"Staging mirror\")." - }, - "url": { - "type": "string", - "format": "uri", - "description": "HTTPS URL that will receive `POST` requests. Must be unique per application among non-deleted destinations. Use a stable, hardened endpoint — Didit retries failed deliveries on a backoff schedule." - }, - "enabled": { - "type": "boolean", - "default": true, - "description": "When `false`, the destination is saved but Didit will not push any webhooks to it. Useful for staging configs you want to keep parked." - }, - "webhook_version": { - "type": "string", - "enum": [ - "v1", - "v2", - "v3" - ], - "description": "Payload schema version Didit will send. `v3` is the current/recommended format; older versions are kept for backward compatibility with legacy integrations." - }, - "subscribed_events": { - "allOf": [ - { - "$ref": "#/components/schemas/WebhookSubscribedEvents" - } - ], - "description": "Events to deliver. Effectively required: an explicit `[]` is rejected with 400, and omitting the field creates a destination with no subscriptions that receives nothing." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiGovbrWalletVerificationStartRequest" }, - "examples": { - "session_updates": { - "summary": "Session status and data updates", - "value": { - "label": "Production session webhooks", - "url": "https://yourapp.com/webhooks/didit", - "webhook_version": "v3", - "subscribed_events": [ - "status.updated", - "data.updated" - ] - } - }, - "transaction_updates": { - "summary": "Transaction monitoring updates", - "value": { - "label": "Transaction monitoring webhooks", - "url": "https://yourapp.com/webhooks/didit-transactions", - "webhook_version": "v3", - "subscribed_events": [ - "transaction.created", - "transaction.status.updated" - ] - } + "example": { + "country": "BRA", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "entity_updates": { - "summary": "User and Business entity updates", - "value": { - "label": "Entity lifecycle webhooks", - "url": "https://yourapp.com/webhooks/didit-entities", - "webhook_version": "v3", - "subscribed_events": [ - "user.status.updated", - "user.data.updated", - "business.status.updated", - "business.data.updated", - "activity.created" - ] - } - } + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiGovbrWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiGovbrWalletVerificationStartRequest" } } - } + }, + "required": true }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], "responses": { "201": { - "description": "Destination created. `secret_shared_key` is the HMAC signing secret — store it now.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Stable destination identifier. Use as `{destination_uuid}` in subsequent calls." - }, - "label": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "enabled": { - "type": "boolean" - }, - "webhook_version": { - "type": "string", - "enum": [ - "v1", - "v2", - "v3" - ] - }, - "secret_shared_key": { - "type": "string", - "description": "Auto-generated HMAC signing secret bound to this destination (43-character URL-safe token). Use it to verify the signature headers on incoming webhooks. Always returned in full to API-key callers; Console users need the `read:webhooks` permission, otherwise they see an empty string." - }, - "subscribed_events": { - "$ref": "#/components/schemas/WebhookSubscribedEvents" - }, - "created_at": { - "type": "string", - "format": "date-time" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "govbr", + "name": "gov.br", + "issuing_authority": "Governo Federal do Brasil" }, - "updated_at": { - "type": "string", - "format": "date-time" + "country": "BRA", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" }, - "summary": { - "type": "object", - "description": "Delivery health summary (all zero/null on a brand-new destination).", - "properties": { - "total_deliveries": { - "type": "integer" - }, - "failed_deliveries": { - "type": "integer" - }, - "error_rate_percentage": { - "type": "integer" - }, - "min_response_time_ms": { - "type": "integer", - "nullable": true - }, - "avg_response_time_ms": { - "type": "integer", - "nullable": true - }, - "max_response_time_ms": { - "type": "integer", - "nullable": true - }, - "last_delivery_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - } - } - }, - "examples": { - "Created": { - "value": { - "uuid": "11111111-2222-3333-4444-555555555555", - "label": "Production session webhooks", - "url": "https://yourapp.com/webhooks/didit", - "enabled": true, - "webhook_version": "v3", - "secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ", - "subscribed_events": [ - "status.updated", - "data.updated" - ], - "created_at": "2026-05-17T10:00:00Z", - "updated_at": "2026-05-17T10:00:00Z", - "summary": { - "total_deliveries": 0, - "failed_deliveries": 0, - "error_rate_percentage": 0, - "min_response_time_ms": null, - "avg_response_time_ms": null, - "max_response_time_ms": null, - "last_delivery_at": null - } - } - } + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, "400": { - "description": "Validation failed — usually a duplicate URL, missing/invalid `subscribed_events`, or unknown event name.", "content": { "application/json": { - "examples": { - "Duplicate URL": { - "value": { - "url": [ - "This webhook destination already exists for the application." - ] - } - }, - "Empty events list": { - "value": { - "subscribed_events": [ - "At least one subscribed event is required." - ] - } - }, - "Unknown event": { - "value": { - "subscribed_events": [ - "Unsupported webhook events: foo.bar" - ] - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "429": { - "description": "Rate limit exceeded; back off and retry.", + "409": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/govbr/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"BRA\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'BRA',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/govbr/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "govbr" } }, - "/v3/webhook/destinations/{destination_uuid}/": { - "get": { - "summary": "Get webhook destination", - "description": "Retrieve one webhook destination with its `secret_shared_key` signing secret and delivery health summary. API-key callers always receive the secret in full; Console users without `read:webhooks` see an empty string.", - "operationId": "get_webhook_destination", - "tags": [ - "Webhook" - ], + "/v3/id-verification/wallets/oneid/": { + "post": { + "operationId": "start_oneid_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_oneid", + "auth": "api_key", + "server": true + }, + "description": "Start a OneID sign-in (OneID (UK bank-verified identity); GBR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start OneID verification", "parameters": [ { - "name": "destination_uuid", - "in": "path", - "required": true, - "description": "UUID of the destination (the `uuid` returned from create / list).", + "in": "header", + "name": "Idempotency-Key", "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, - "example": "11111111-2222-3333-4444-555555555555" + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiOneidWalletVerificationStartRequest" + }, + "example": { + "country": "GBR", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiOneidWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiOneidWalletVerificationStartRequest" + } + } }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nuuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n f'https://verification.didit.me/v3/webhook/destinations/{uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json())" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const uuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${uuid}/`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Lookup failed: ${resp.status}`);\nconst destination = await resp.json();\nconsole.log(destination);" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Webhook destination details.", + "201": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "label": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "enabled": { - "type": "boolean" - }, - "webhook_version": { - "type": "string", - "enum": [ - "v1", - "v2", - "v3" - ] - }, - "secret_shared_key": { - "type": "string", - "description": "HMAC signing secret (43-character URL-safe token). Always returned in full to API-key callers; Console users need `read:webhooks`, otherwise an empty string." - }, - "subscribed_events": { - "$ref": "#/components/schemas/WebhookSubscribedEvents" - }, - "created_at": { - "type": "string", - "format": "date-time" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "oneid", + "name": "OneID", + "issuing_authority": "OneID (UK bank-verified identity)" }, - "updated_at": { - "type": "string", - "format": "date-time" + "country": "GBR", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" }, - "summary": { - "type": "object", - "description": "Delivery health metrics aggregated over the destination's lifetime.", - "properties": { - "total_deliveries": { - "type": "integer" - }, - "failed_deliveries": { - "type": "integer" - }, - "error_rate_percentage": { - "type": "integer" - }, - "min_response_time_ms": { - "type": "integer", - "nullable": true - }, - "avg_response_time_ms": { - "type": "integer", - "nullable": true - }, - "max_response_time_ms": { - "type": "integer", - "nullable": true - }, - "last_delivery_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - } - } - }, - "examples": { - "Healthy destination": { - "value": { - "uuid": "11111111-2222-3333-4444-555555555555", - "label": "Production session webhooks", - "url": "https://yourapp.com/webhooks/didit", - "enabled": true, - "webhook_version": "v3", - "secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ", - "subscribed_events": [ - "status.updated", - "data.updated" - ], - "created_at": "2026-01-04T08:00:00Z", - "updated_at": "2026-04-22T14:09:11Z", - "summary": { - "total_deliveries": 12453, - "failed_deliveries": 14, - "error_rate_percentage": 0, - "min_response_time_ms": 41, - "avg_response_time_ms": 187, - "max_response_time_ms": 2103, - "last_delivery_at": "2026-05-17T09:21:18Z" - } - } - } + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "404": { - "description": "No destination with this UUID exists, or it belongs to a different application, or it has been deleted.", + "409": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Webhook destination not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "429": { - "description": "Rate limit exceeded; back off and retry.", + "502": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/oneid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"GBR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'GBR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/oneid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] - }, - "patch": { - "summary": "Update webhook destination", - "description": "Update a webhook destination. **`subscribed_events` is REPLACED wholesale, not merged** — fetch first, append, resend. Secret is not rotated.", - "operationId": "update_webhook_destination", - "tags": [ - "Webhook" ], + "x-didit-wallet-id": "oneid" + } + }, + "/v3/id-verification/wallets/govuk-wallet/": { + "post": { + "operationId": "start_govuk_wallet_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_govuk_wallet", + "auth": "api_key", + "server": true + }, + "description": "Start a GOV.UK Wallet sign-in (UK Government Digital Service; GBR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start GOV.UK Wallet verification", "parameters": [ { - "name": "destination_uuid", - "in": "path", - "required": true, - "description": "UUID of the destination to update.", + "in": "header", + "name": "Idempotency-Key", "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, - "example": "11111111-2222-3333-4444-555555555555" + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], + "tags": [ + "Digital ID wallets" + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "label": { - "type": "string", - "description": "New human-readable name." - }, - "url": { - "type": "string", - "format": "uri", - "description": "New target URL. Must be unique within the application." - }, - "enabled": { - "type": "boolean", - "description": "Set to `false` to pause deliveries to this destination without deleting it." - }, - "webhook_version": { - "type": "string", - "enum": [ - "v1", - "v2", - "v3" - ], - "description": "New payload schema version for future deliveries." - }, - "subscribed_events": { - "$ref": "#/components/schemas/WebhookSubscribedEvents" - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiGovukWalletWalletVerificationStartRequest" }, - "examples": { - "Pause destination": { - "value": { - "enabled": false - } - }, - "Add transaction events": { - "value": { - "subscribed_events": [ - "status.updated", - "data.updated", - "transaction.created", - "transaction.status.updated" - ] - } + "example": { + "country": "GBR", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "Move target URL": { - "value": { - "label": "Production session webhooks v2", - "url": "https://api.yourapp.com/webhooks/didit/v2" - } - } + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiGovukWalletWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiGovukWalletWalletVerificationStartRequest" } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"subscribed_events\": [\n \"status.updated\",\n \"data.updated\",\n \"transaction.created\"\n ]\n }'" }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nuuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.patch(\n f'https://verification.didit.me/v3/webhook/destinations/{uuid}/',\n headers={\n 'x-api-key': os.environ['DIDIT_API_KEY'],\n 'Content-Type': 'application/json',\n },\n json={'enabled': False},\n timeout=10,\n)\nresp.raise_for_status()\nprint('Paused destination:', resp.json()['label'])" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const uuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${uuid}/`, {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ enabled: false }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconsole.log(await resp.json());" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Updated webhook destination. Body has the same shape as `GET /v3/webhook/destinations/{destination_uuid}/`.", + "201": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "label": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "enabled": { - "type": "boolean" - }, - "webhook_version": { - "type": "string", - "enum": [ - "v1", - "v2", - "v3" - ] - }, - "secret_shared_key": { - "type": "string" - }, - "subscribed_events": { - "$ref": "#/components/schemas/WebhookSubscribedEvents" - }, - "created_at": { - "type": "string", - "format": "date-time" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "govuk_wallet", + "name": "GOV.UK Wallet", + "issuing_authority": "UK Government Digital Service" }, - "updated_at": { - "type": "string", - "format": "date-time" + "country": "GBR", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" }, - "summary": { - "type": "object", - "properties": { - "total_deliveries": { - "type": "integer" - }, - "failed_deliveries": { - "type": "integer" - }, - "error_rate_percentage": { - "type": "integer" - }, - "min_response_time_ms": { - "type": "integer", - "nullable": true - }, - "avg_response_time_ms": { - "type": "integer", - "nullable": true - }, - "max_response_time_ms": { - "type": "integer", - "nullable": true - }, - "last_delivery_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - } - } - }, - "examples": { - "Paused": { - "value": { - "uuid": "11111111-2222-3333-4444-555555555555", - "label": "Production session webhooks", - "url": "https://yourapp.com/webhooks/didit", - "enabled": false, - "webhook_version": "v3", - "secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ", - "subscribed_events": [ - "status.updated", - "data.updated" - ], - "created_at": "2026-01-04T08:00:00Z", - "updated_at": "2026-05-17T11:02:55Z", - "summary": { - "total_deliveries": 12453, - "failed_deliveries": 14, - "error_rate_percentage": 0, - "min_response_time_ms": 41, - "avg_response_time_ms": 187, - "max_response_time_ms": 2103, - "last_delivery_at": "2026-05-17T09:21:18Z" - } - } - } - } - } - } - }, - "400": { - "description": "Validation failed (duplicate URL, empty/unknown events list, malformed body).", - "content": { - "application/json": { - "examples": { - "Duplicate URL": { - "value": { - "url": [ - "This webhook destination already exists for the application." - ] - } + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "Empty events": { - "value": { - "subscribed_events": [ - "At least one subscribed event is required." - ] - } - } - } - } - } - }, - "403": { - "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", - "content": { - "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, - "404": { - "description": "Destination with this UUID does not exist (or is deleted, or belongs to another application).", + "400": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Webhook destination not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, - "429": { - "description": "Rate limit exceeded; back off and retry.", + "403": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - }, - "delete": { - "summary": "Delete webhook destination", - "description": "Delete a webhook destination. Future events stop being pushed to its URL. Past delivery records are not affected and stay queryable from the Business Console, so deleting a destination is not a way to remove your delivery history. Calling again returns 404.", - "operationId": "delete_webhook_destination", - "tags": [ - "Webhook" - ], - "parameters": [ - { - "name": "destination_uuid", - "in": "path", - "required": true, - "description": "UUID of the destination to delete.", - "schema": { - "type": "string", - "format": "uuid" }, - "example": "11111111-2222-3333-4444-555555555555" - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X DELETE 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nuuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.delete(\n f'https://verification.didit.me/v3/webhook/destinations/{uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nassert resp.status_code == 204" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const uuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${uuid}/`, {\n method: 'DELETE',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (resp.status !== 204) throw new Error(`Delete failed: ${resp.status}`);" - } - ], - "responses": { - "204": { - "description": "Destination deleted. No response body." + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "403": { - "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "409": { "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "404": { - "description": "No destination with this UUID exists for the application (already deleted or never existed).", + "502": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Webhook destination not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, - "429": { - "description": "Rate limit exceeded; back off and retry.", + "503": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/govuk-wallet/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"GBR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'GBR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/govuk-wallet/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "govuk_wallet" } }, - "/v3/wallet-screening/": { + "/v3/id-verification/wallets/smart-id/": { "post": { - "summary": "Screen a wallet address", - "description": "Run on-demand AML screening for a single crypto wallet/address without creating a transaction. Resolves your application's configured blockchain analytics provider, screens the address synchronously, and returns the normalised risk result. LIGHTNING is available only with an Elliptic or Chainalysis BYOK connection added through Marketplace. Nothing is written to the transactions table. One AML monitoring usage is billed per successful screening (sandbox applications are not billed).", - "operationId": "screenWallet", - "tags": [ - "Transactions" - ], - "security": [ + "operationId": "start_smart_id_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_smart_id", + "auth": "api_key", + "server": true + }, + "description": "Start a Smart-ID sign-in (SK ID Solutions; EST, LVA, LTU, BEL) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=confirm_in_app with the comparison code to show the user; the user confirms it and enters their PIN in the app (never collect the PIN). Poll POST /v3/id-verification/wallets/verifications/{request_id}/poll/ every poll_interval_seconds, or rely on the webhook: Didit polls in the background too. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Smart-ID verification", + "parameters": [ { - "ApiKeyAuth": [] + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], + "tags": [ + "Digital ID wallets" + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "wallet_address", - "blockchain" - ], - "properties": { - "wallet_address": { - "type": "string", - "description": "The crypto address or network identifier to screen. Must match the selected network (e.g. a `0x`-prefixed 40-hex-char address for EVM chains). Alias: `address`.", - "example": "0x94f1b9b64e2932f6a2db338f616844400cd58e8a" - }, - "blockchain": { - "type": "string", - "description": "Network identifier. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism), and ARB (Arbitrum One). USDT and USDC are legacy Ethereum aliases, not multi-network searches. Connected-provider coverage varies. LIGHTNING requires a supported connected provider. Celo, Mode and Plume are not currently supported. Alias: `currency`.", - "enum": [ - "BTC", - "LIGHTNING", - "ETH", - "LTC", - "XRP", - "BCH", - "DOGE", - "TRX", - "SOL", - "MATIC", - "BNB", - "USDT", - "USDC", - "BASE", - "OP", - "ARB" - ], - "example": "ETH" - }, - "direction": { - "type": "string", - "description": "Optional. Whether the address is being screened as an inbound (deposit) or outbound (withdrawal) counterparty. Accepts `inbound`, `outbound`, `deposit`, or `withdrawal`. Defaults to a neutral pre-transfer screen when omitted. This is passed to the provider as context only; it does not change the returned `risk_score`, `severity`, or the `source_of_funds`/`destination_of_funds` breakdowns, which are always both returned regardless of `direction`. Changing or omitting `direction` therefore does not require re-screening an address.", - "enum": [ - "inbound", - "outbound", - "deposit", - "withdrawal" - ] - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiSmartIdWalletVerificationStartRequest" + }, + "example": { + "country": "EST", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "personal_code": "00000000000" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiSmartIdWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiSmartIdWalletVerificationStartRequest" } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/wallet-screening/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"wallet_address\": \"0x94f1b9b64e2932f6a2db338f616844400cd58e8a\",\n \"blockchain\": \"ETH\"\n }'" }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/wallet-screening/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'wallet_address': '0x94f1b9b64e2932f6a2db338f616844400cd58e8a',\n 'blockchain': 'ETH',\n },\n timeout=20,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result['risk_score'], result['severity'], result['sanctions_hit'])" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const result = await fetch('https://verification.didit.me/v3/wallet-screening/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n wallet_address: '0x94f1b9b64e2932f6a2db338f616844400cd58e8a',\n blockchain: 'ETH',\n }),\n}).then((r) => r.json());\nconsole.log(result.risk_score, result.severity, result.sanctions_hit);" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "The normalised wallet screening result.", + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WalletScreeningResult" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, "example": { - "provider": "merklescience", - "screening_type": "WALLET_SCREENING", - "risk_score": 95, - "severity": "CRITICAL", - "status": "SCREENED", - "summary": "Merkle Science risk: Critical (5/5). SANCTIONS HIT. Dominant risk: sanctioned. Driven by: the address is attributed to Sanctions / OFAC (Example Sanctions)", - "wallet_address": "0x94f1b9b64e2932f6a2db338f616844400cd58e8a", - "transaction_hash": null, - "blockchain": "ETH", - "block_timestamp": null, - "total_value_usd": null, - "fee_usd": null, - "sender_rows": [], - "receiver_rows": [], - "transaction_flows": [], - "transaction_flows_status": null, - "transaction_flows_error": null, - "source_of_funds": [ - { - "category": "sanctioned", - "percentage": 100.0, - "amount_usd": 9813695.0, - "entity_name": "Example Sanctions", - "entity_type": "Sanctions", - "entity_subtype": "OFAC", - "hops": null, - "description": "Sanctions / OFAC", - "is_direct": true, - "exposure_direction": "incoming", - "exposure_type": "direct", - "provider_entity_id": "118885", - "provider_entity_type_id": "30", - "provider_entity_subtype_id": "125", - "country": "", - "raw_amount": null, - "raw_currency": null - } - ], - "destination_of_funds": [ - { - "category": "defi", - "percentage": 84.824453, - "amount_usd": 7783414.38, - "entity_name": "Example DeFi 7", - "entity_type": "DeFi", - "entity_subtype": "Bridges", - "hops": null, - "description": "DeFi / Bridges", - "is_direct": false, - "exposure_direction": "outgoing", - "exposure_type": "indirect", - "provider_entity_id": "118459", - "provider_entity_type_id": "33", - "provider_entity_subtype_id": "143", - "country": "", - "raw_amount": null, - "raw_currency": null - }, - { - "category": "defi", - "percentage": 15.175547, - "amount_usd": 1392494.37, - "entity_name": "Example DeFi 2", - "entity_type": "DeFi", - "entity_subtype": "Yield Aggregator", - "hops": null, - "description": "DeFi / Yield Aggregator", - "is_direct": false, - "exposure_direction": "outgoing", - "exposure_type": "indirect", - "provider_entity_id": "90192", - "provider_entity_type_id": "33", - "provider_entity_subtype_id": "142", - "country": "", - "raw_amount": null, - "raw_currency": null - } - ], - "counterparty_connections": [ - { - "entity_name": "Example Sanctions", - "entity_type": "Sanctions", - "entity_subtype": "OFAC", - "risk_level": "critical", - "categories": [ - "sanctioned" - ], - "received_usd": null, - "sent_usd": null, - "received_hops": null, - "sent_hops": null, - "percentage": 0.0, - "is_direct": true, - "country": null, - "provider_entity_id": null - }, - { - "entity_name": "Example Sanctions", - "entity_type": "Sanctions", - "entity_subtype": "OFAC", - "risk_level": "critical", - "categories": [ - "sanctioned" - ], - "received_usd": null, - "sent_usd": null, - "received_hops": null, - "sent_hops": null, - "percentage": 0.0, - "is_direct": true, - "country": null, - "provider_entity_id": null - } - ], - "network_nodes": [], - "network_edges": [], - "sanctions_hit": true, - "pep_counterparty": null, - "dominant_risk_category": "sanctioned", - "risk_factors": [ - { - "source": "attribution", - "category": "sanctioned", - "label": "Sanctions / OFAC", - "entity_name": "Example Sanctions", - "direction": "attribution", - "exposure_type": "attribution", - "amount_usd": null, - "percentage": 0.0, - "is_high_risk": true, - "description": "the address is attributed to Sanctions / OFAC (Example Sanctions)" - }, - { - "source": "exposure", - "category": "sanctioned", - "label": "Sanctions / OFAC", - "entity_name": "Example Sanctions", - "direction": "incoming", - "exposure_type": "direct", - "amount_usd": 9813695.0, - "percentage": 51.679304, - "is_high_risk": true, - "description": "51.6793% direct incoming exposure to Sanctions / OFAC (Example Sanctions)" + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "smart_id", + "name": "Smart-ID", + "issuing_authority": "SK ID Solutions" }, - { - "source": "exposure", - "category": "defi", - "label": "DeFi / Bridges", - "entity_name": "Example DeFi 7", - "direction": "outgoing", - "exposure_type": "indirect", - "amount_usd": 7783414.38, - "percentage": 40.987766, - "is_high_risk": false, - "description": "40.9878% indirect outgoing exposure to DeFi / Bridges (Example DeFi 7)" + "country": "EST", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "in_app", + "simulated": true, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "confirm_in_app", + "verification_code": "1234", + "poll_interval_seconds": 2 }, - { - "source": "exposure", - "category": "defi", - "label": "DeFi / Yield Aggregator", - "entity_name": "Example DeFi 2", - "direction": "outgoing", - "exposure_type": "indirect", - "amount_usd": 1392494.37, - "percentage": 7.33293, - "is_high_risk": false, - "description": "7.33293% indirect outgoing exposure to DeFi / Yield Aggregator (Example DeFi 2)" - } - ], - "raw_response": { - "...": "provider-specific details" + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" }, - "report_signature": "" + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, "400": { - "description": "Validation error — malformed wallet address, missing `wallet_address`, unsupported `blockchain`, or LIGHTNING without an Elliptic or Chainalysis BYOK Marketplace connection." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, - "401": { - "description": "Missing or invalid `x-api-key`." + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" + } + } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, "409": { - "description": "No AML provider is configured for the application, or the configured provider does not support wallet screening yet. Configure transaction monitoring first." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/smart-id/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"EST\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"personal_code\": \"00000000000\"\n}'" }, - "502": { - "description": "The AML provider could not complete the screening. Retry later." + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'EST',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'personal_code': '00000000000'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/smart-id/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - } + ], + "x-didit-wallet-id": "smart_id" } }, - "/v3/wallet-screening/pdf/": { + "/v3/id-verification/wallets/mobile-id/": { "post": { - "summary": "Render a wallet-screening result as a PDF", - "description": "Stateless companion to `POST /v3/wallet-screening/`: repost the exact JSON body that endpoint returned — including the `report_signature` it added — and get back a Didit-branded PDF of the same result. No provider is called and nothing is billed or persisted; this only re-renders a result Didit already produced. The signature ties the PDF to an unmodified result: editing `risk_score`, `severity`, `sanctions_hit`, or any other signed field before reposting fails with `400`.", - "operationId": "screenWalletPdf", - "tags": [ - "Transactions" - ], - "security": [ + "operationId": "start_mobile_id_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_mobile_id", + "auth": "api_key", + "server": true + }, + "description": "Start a Mobile-ID sign-in (SK ID Solutions with the national mobile operators; EST, LTU) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=confirm_in_app with the comparison code to show the user; the user confirms it and enters their PIN in the app (never collect the PIN). Poll POST /v3/id-verification/wallets/verifications/{request_id}/poll/ every poll_interval_seconds, or rely on the webhook: Didit polls in the background too. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Mobile-ID verification", + "parameters": [ { - "ApiKeyAuth": [] + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], + "tags": [ + "Digital ID wallets" + ], "requestBody": { - "required": true, - "description": "The unmodified JSON object returned by `POST /v3/wallet-screening/`, including `report_signature`.", "content": { "application/json": { "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/WalletScreeningResult" - } - ], - "required": [ - "report_signature" - ], - "description": "Same shape as the `POST /v3/wallet-screening/` response — pass that response body back verbatim, including `report_signature`." + "$ref": "#/components/schemas/DigitalIdWalletApiMobileIdWalletVerificationStartRequest" + }, + "example": { + "country": "EST", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "personal_code": "00000000000", + "phone_number": "+37200000000" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiMobileIdWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiMobileIdWalletVerificationStartRequest" } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "# $RESULT is the saved JSON body from POST /v3/wallet-screening/\ncurl -X POST 'https://verification.didit.me/v3/wallet-screening/pdf/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d \"$RESULT\" \\\n -o wallet_screening_report.pdf" }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\n# `result` is the dict returned by POST /v3/wallet-screening/, unmodified\nresponse = requests.post(\n 'https://verification.didit.me/v3/wallet-screening/pdf/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n json=result,\n timeout=30,\n)\nresponse.raise_for_status()\nwith open('wallet_screening_report.pdf', 'wb') as fh:\n fh.write(response.content)" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "// `result` is the object returned by POST /v3/wallet-screening/, unmodified\nconst response = await fetch('https://verification.didit.me/v3/wallet-screening/pdf/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(result),\n});\nif (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);\nconst pdfBuffer = Buffer.from(await response.arrayBuffer());" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "The rendered PDF document, returned directly as binary `application/pdf` — there is no JSON wrapper.", - "headers": { - "Content-Disposition": { - "description": "`attachment; filename=wallet_screening_{address}.pdf`, derived from the `wallet_address` (or `transaction_hash`) in the reposted result.", + "201": { + "content": { + "application/json": { "schema": { - "type": "string", - "example": "attachment; filename=wallet_screening_0x28c6c06298d514db0899.pdf" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "mobile_id", + "name": "Mobile-ID", + "issuing_authority": "SK ID Solutions with the national mobile operators" + }, + "country": "EST", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "in_app", + "simulated": true, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "confirm_in_app", + "verification_code": "1234", + "poll_interval_seconds": 2 + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } }, + "description": "" + }, + "400": { "content": { - "application/pdf": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, - "400": { - "description": "The body is not a JSON object matching the wallet-screening result shape, or `report_signature` is missing or does not match the reposted fields (edited or truncated before repost).", + "403": { "content": { "application/json": { "schema": { - "type": "object" - }, - "examples": { - "Not an object": { - "value": { - "non_field_errors": [ - "Expected a JSON object matching a wallet-screening result." - ] - } - }, - "Missing or invalid signature": { - "value": { - "report_signature": [ - "This wallet-screening result is missing or fails its report signature - repost the exact JSON returned by the wallet-screening endpoint, unmodified." - ] - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } - }, - "401": { - "description": "Missing or invalid `x-api-key`." - } - } - } - }, - "/v3/transactions/": { - "get": { - "summary": "List transactions", - "description": "Paginated list of monitored transactions, newest first (`txn_date` desc). `count` is capped at 100; use `total_transactions` for the true row count. This endpoint does not support filtering or search parameters.", - "operationId": "listTransactions", - "tags": [ - "Transactions" - ], - "parameters": [ - { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "default": 50, - "minimum": 1 }, - "description": "Page size. Defaults to 50 when omitted. There is no enforced maximum, but keep pages small for latency." - }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "integer", - "default": 0, - "minimum": 0 - }, - "description": "Zero-based offset of the first record to return. Prefer following the `next` URL from the previous page." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/transactions/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" - }, - { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/transactions/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'limit': 50},\n timeout=10,\n)\nresp.raise_for_status()\npage = resp.json()\nfor tx in page['results']:\n print(\n tx['transaction_number'],\n tx['status'],\n tx['severity'],\n f\"{tx['amount']} {tx['currency']}\",\n tx.get('subject_name') or tx.get('subject_vendor_data'),\n )" + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const url = new URL('https://verification.didit.me/v3/transactions/');\nurl.searchParams.set('limit', '50');\nconst page = await fetch(url, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n}).then((r) => r.json());\nfor (const tx of page.results) {\n console.log(tx.transaction_number, tx.status, tx.severity, tx.amount, tx.currency);\n}" - } - ], - "responses": { - "200": { - "description": "Paginated list of transaction summaries, newest first.", + "409": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer", - "description": "Capped count of matching transactions (exact up to 100, capped thereafter). Use `total_transactions` for the true count when paginating." - }, - "next": { - "type": "string", - "nullable": true, - "description": "Absolute URL of the next page, or `null` on the last page." - }, - "previous": { - "type": "string", - "nullable": true, - "description": "Absolute URL of the previous page, or `null` on the first page." - }, - "total_transactions": { - "type": "integer", - "description": "Exact total count of (non-deleted) transactions for the application. Present on paginated responses." - }, - "results": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Stable Didit transaction identifier. Use as `{transaction_id}` in detail/notes/related calls." - }, - "transaction_number": { - "type": "integer", - "description": "Application-scoped sequential transaction number shown in Console (e.g. `4123`)." - }, - "txn_id": { - "type": "string", - "description": "Customer-provided identifier from the create call (max 128 chars). Unique per application." - }, - "transaction_type": { - "type": "string", - "description": "Top-level category as stored: `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`, `gamblingLimitChange`, `gamblingBonusChange`, `auditTrailEvent`. Note multi-word values are echoed back in camelCase even when submitted in snake_case." - }, - "action_type": { - "type": "string", - "description": "Sub-type within the category (e.g. `deposit`, `withdrawal`, `transfer`)." - }, - "status": { - "type": "string", - "enum": [ - "APPROVED", - "IN_REVIEW", - "DECLINED", - "AWAITING_USER" - ], - "description": "Current monitoring verdict." - }, - "direction": { - "type": "string", - "enum": [ - "INBOUND", - "OUTBOUND" - ], - "description": "Direction relative to the subject. Stored uppercase regardless of the casing submitted." - }, - "amount": { - "type": "string", - "description": "Transaction amount as a decimal string with trailing zeros stripped (e.g. `\"1500\"`, `\"0.5\"`, `\"0.123456789012345678\"`). Up to 18 decimal places." - }, - "currency": { - "type": "string", - "description": "Currency code of `amount`. Any ISO 4217 alphabetic code is accepted (e.g. `EUR`, `USD`, `NGN`), as is a crypto asset symbol (e.g. `BTC`). Prefer a currently-circulating code. Not every accepted code has an exchange rate: when none is available for the code (for example the ISO accounting unit `XSU`, or a crypto symbol Didit does not price), automatic conversion fails and `preferred_currency_amount` is returned null unless the request supplied a pre-converted `amount_in_default_currency`." - }, - "preferred_currency_amount": { - "type": "string", - "nullable": true, - "description": "`amount` converted to the application's preferred currency, when available." - }, - "preferred_currency_code": { - "type": "string", - "nullable": true, - "description": "Preferred currency code used for `preferred_currency_amount` - the application's base currency, any currently-circulating ISO 4217 code. A code with no available exchange rate leaves `preferred_currency_amount` null unless the request supplied a pre-converted `amount_in_default_currency`." - }, - "payment_details": { - "type": "string", - "nullable": true, - "description": "Free-text payment reference or memo from the submission." - }, - "score": { - "type": "integer", - "description": "Risk score (typically 0–100). Higher = riskier." - }, - "severity": { - "type": "string", - "nullable": true, - "enum": [ - "UNKNOWN", - "LOW", - "MEDIUM", - "HIGH", - "CRITICAL" - ], - "description": "Categorical risk severity set by rules/providers (`UNKNOWN`, `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`). `null` until something sets it." - }, - "subject_name": { - "type": "string", - "nullable": true, - "description": "Full name of the `APPLICANT` party." - }, - "subject_vendor_data": { - "type": "string", - "nullable": true, - "description": "Your internal user/business identifier for the applicant." - }, - "subject_country": { - "type": "string", - "nullable": true, - "description": "Country code of the applicant, from the submitted `address.country` (typically ISO 3166-1 alpha-3)." - }, - "counterparty_name": { - "type": "string", - "nullable": true, - "description": "Full name of the `COUNTERPARTY` party, if any." - }, - "counterparty_vendor_data": { - "type": "string", - "nullable": true, - "description": "Your internal identifier for the counterparty." - }, - "counterparty_country": { - "type": "string", - "nullable": true, - "description": "Country code of the counterparty, from the submitted `address.country`." - }, - "tags": { - "type": "array", - "description": "Tags attached to the transaction (manually or by rules).", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "color": { - "type": "string" - }, - "description": { - "type": "string", - "nullable": true - }, - "source": { - "type": "string", - "enum": [ - "didit", - "custom" - ], - "description": "Whether the tag is a Didit preset or a custom tag." - } - } - } - }, - "txn_date": { - "type": "string", - "format": "date-time", - "description": "When the transaction occurred (from `transaction_at` at create time)." - }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "When Didit recorded the submission." - }, - "decision_reason_code": { - "type": "string", - "nullable": true, - "description": "Machine-readable reason for the current `status` (e.g. `WALLET_HIGH_RISK`)." - }, - "decision_reason_label": { - "type": "string", - "nullable": true, - "description": "Human-readable label for `decision_reason_code`." - }, - "assigned_to_name": { - "type": "string", - "nullable": true, - "description": "Display name of the Console reviewer assigned to this transaction." - } - } - } - } - } - }, - "examples": { - "Single transaction": { - "value": { - "count": 1, - "next": null, - "previous": null, - "total_transactions": 1, - "results": [ - { - "uuid": "abcdef12-3456-4890-abcd-ef1234567890", - "transaction_number": 4123, - "txn_id": "your-txn-2026-05-17-001", - "transaction_type": "finance", - "action_type": "withdrawal", - "status": "IN_REVIEW", - "direction": "OUTBOUND", - "amount": "1500", - "currency": "EUR", - "preferred_currency_amount": "1620.45", - "preferred_currency_code": "USD", - "payment_details": "Withdrawal to crypto wallet", - "txn_date": "2026-05-17T08:42:00Z", - "created_at": "2026-05-17T08:42:11Z", - "score": 78, - "severity": "HIGH", - "decision_reason_code": "WALLET_HIGH_RISK", - "decision_reason_label": "Destination wallet flagged as high risk", - "tags": [ - { - "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", - "name": "Manual review", - "color": "#F59E0B", - "description": null, - "source": "custom" - } - ], - "subject_name": "Maria Garcia", - "subject_vendor_data": "user-12345", - "counterparty_name": null, - "counterparty_vendor_data": null, - "counterparty_country": null, - "subject_country": null, - "assigned_to_name": "Alex Reviewer" - } - ] - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot list transactions for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "502": { "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "503": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - }, - "post": { - "summary": "Create transaction", - "description": "Submit a transaction for synchronous AML/risk monitoring. The transaction is created `APPROVED`, then your rules (and crypto screening, when applicable) run inline and may flip the returned `status` to `IN_REVIEW` or `DECLINED`. **`transaction_id` is your idempotency key** (max 128 chars, unique per app); reusing it returns 400. A `transaction.created` webhook is dispatched on success (sandbox applications are not billed and do not emit webhooks).", - "operationId": "createTransaction", - "tags": [ - "Transactions" - ], + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." + } + }, "x-codeSamples": [ { "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/transactions/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"transaction_id\": \"your-txn-2026-05-17-001\",\n \"transaction_category\": \"finance\",\n \"transaction_details\": {\n \"direction\": \"outbound\",\n \"amount\": 1500,\n \"currency\": \"EUR\",\n \"action_type\": \"withdrawal\"\n },\n \"subject\": {\n \"entity_type\": \"individual\",\n \"vendor_data\": \"user-12345\",\n \"full_name\": \"Maria Garcia\"\n }\n }'" + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/mobile-id/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"EST\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"personal_code\": \"00000000000\",\n \"phone_number\": \"+37200000000\"\n}'" }, { "lang": "python", "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/transactions/',\n headers={\n 'x-api-key': os.environ['DIDIT_API_KEY'],\n 'Content-Type': 'application/json',\n },\n json={\n 'transaction_id': 'your-txn-2026-05-17-001',\n 'transaction_category': 'finance',\n 'transaction_details': {\n 'direction': 'outbound',\n 'amount': 1500,\n 'currency': 'EUR',\n 'action_type': 'withdrawal',\n },\n 'subject': {\n 'entity_type': 'individual',\n 'vendor_data': 'user-12345',\n 'full_name': 'Maria Garcia',\n },\n },\n timeout=15,\n)\nresp.raise_for_status()\ntx = resp.json()\nprint(tx['txn_id'], '->', tx['status'], tx['severity'])" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/transactions/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n transaction_id: 'your-txn-2026-05-17-001',\n transaction_category: 'finance',\n transaction_details: {\n direction: 'outbound',\n amount: 1500,\n currency: 'EUR',\n action_type: 'withdrawal',\n },\n subject: {\n entity_type: 'individual',\n vendor_data: 'user-12345',\n full_name: 'Maria Garcia',\n },\n }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst tx = await resp.json();\nconsole.log(tx.txn_id, tx.status, tx.severity);" + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'EST',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'personal_code': '00000000000',\n 'phone_number': '+37200000000'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/mobile-id/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "transaction_id": { - "type": "string", - "description": "Your unique identifier for this transaction (max 128 characters).", - "example": "your-txn-2026-05-17-001" - }, - "transaction_at": { - "type": "string", - "format": "date-time", - "description": "When the transaction occurred. Defaults to now if omitted." - }, - "time_zone": { - "type": "string", - "description": "IANA time zone identifier, e.g. `Europe/Madrid`." - }, - "transaction_category": { - "type": "string", - "description": "Category of the transaction. CamelCase aliases (`travelRule`, `userPlatformEvent`, `gamblingBet`, …) are also accepted; the stored/echoed value is camelCase for multi-word categories.", - "example": "finance", - "enum": [ - "finance", - "kyc", - "travel_rule", - "user_event", - "audit_trail_event", - "gambling_bet", - "gambling_limit_change", - "gambling_bonus_change", - "travelRule" - ] - }, - "transaction_details": { - "type": "object", - "description": "Core financial details of the transaction.", - "required": [ - "direction", - "amount", - "currency" - ], - "properties": { - "direction": { - "type": "string", - "description": "Direction of the transaction relative to the subject. Case-insensitive; `in`/`out` are accepted as aliases. Echoed back uppercase (`INBOUND`/`OUTBOUND`).", - "example": "outbound", - "enum": [ - "inbound", - "outbound", - "in", - "out" - ] - }, - "amount": { - "type": "number", - "description": "Transaction amount (up to 18 decimal places). Send it as a decimal string (e.g. `\"0.123456789012345678\"`) when exact precision matters: a JSON number is parsed as a binary float first, so digits beyond about 15 significant digits are rounded before the amount is stored.", - "example": 1500 - }, - "currency": { - "type": "string", - "description": "Currency code. Any ISO 4217 alphabetic code is accepted (e.g. `EUR`, `USD`, `NGN`), as is a crypto asset symbol (e.g. `BTC`). Prefer a currently-circulating code. Not every accepted code has an exchange rate: when none is available for the code (for example the ISO accounting unit `XSU`, or a crypto symbol Didit does not price), automatic conversion fails and `preferred_currency_amount` is returned null unless the request supplied a pre-converted `amount_in_default_currency`.", - "example": "EUR" - }, - "currency_kind": { - "type": "string", - "description": "Whether the currency is `fiat` or `crypto`." - }, - "amount_in_default_currency": { - "type": "number", - "nullable": true, - "description": "Pre-converted amount in the default currency. If omitted, automatic FX conversion is attempted." - }, - "default_currency": { - "type": "string", - "description": "The default currency code for the converted amount. Any currently-circulating ISO 4217 code." - }, - "payment_details": { - "type": "string", - "nullable": true, - "description": "Free-text payment reference or memo." - }, - "payment_reference_id": { - "type": "string", - "nullable": true, - "description": "External payment system reference. For crypto AML transaction-hash screening, send the blockchain transaction hash and also provide `subject.payment_method.account_id` with the related address." - }, - "action_type": { - "type": "string", - "nullable": true, - "description": "Sub-type of the transaction, e.g. `deposit`, `withdrawal`, `transfer`." - }, - "crypto_params": { - "type": "object", - "description": "Crypto network selection, separate from currency (the asset symbol). Recording a custom token symbol does not guarantee transaction-hash coverage for its contract.", - "properties": { - "crypto_chain": { - "type": "string", - "description": "Actual network used for screening. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism) and ARB (Arbitrum One). Always specify this for multi-network tokens. Connected-provider coverage varies; LIGHTNING requires a supported connected provider.", - "example": "BASE" - } - } - } - } - }, - "subject": { - "type": "object", - "description": "The subject (applicant) of the transaction.", - "required": [ - "vendor_data", - "full_name" - ], - "properties": { - "entity_type": { - "type": "string", - "description": "Entity type: `individual` or `company`." - }, - "vendor_data": { - "type": "string", - "description": "Your internal user/business identifier.", - "example": "user-12345" - }, - "full_name": { - "type": "string", - "description": "Full name of the subject.", - "example": "Maria Garcia" - }, - "first_name": { - "type": "string", - "description": "First name of the subject." - }, - "last_name": { - "type": "string", - "description": "Last name of the subject." - }, - "date_of_birth": { - "type": "string", - "format": "date", - "description": "Date of birth (YYYY-MM-DD)." - }, - "address": { - "type": "object", - "description": "Address object.", - "properties": { - "country": { - "type": "string", - "description": "Country code (ISO 3166-1 alpha-3 recommended). Copied to the party's `country_code`." - }, - "town": { - "type": "string" - }, - "state": { - "type": "string" - }, - "street": { - "type": "string" - }, - "post_code": { - "type": "string" - } - } - }, - "institution_details": { - "type": "object", - "description": "Institution details (for institutional subjects).", - "properties": { - "name": { - "type": "string" - }, - "code": { - "type": "string" - }, - "address": { - "type": "object" - } - } - }, - "device_context": { - "type": "object", - "description": "Device context captured at transaction time. browser_family and platform must be sent as explicit top-level keys — they are not derived from user_agent. The camelCase alias device is also accepted on the request.", - "properties": { - "fingerprint": { - "type": "string", - "description": "Unique device fingerprint." - }, - "persistent_device_id": { - "type": "string", - "description": "Stable cross-session device identifier (v2 fingerprint schema)." - }, - "user_agent": { - "type": "string", - "description": "Browser or app user agent string. CamelCase alias: userAgent." - }, - "browser_family": { - "type": "string", - "description": "Browser family (e.g. Chrome, Firefox, Safari). Must be sent explicitly — it is not derived from user_agent. CamelCase alias: browserFamily." - }, - "browser_version": { - "type": "string", - "description": "Browser version string. Must be sent explicitly. CamelCase alias: browserVersion." - }, - "platform": { - "type": "string", - "description": "Platform identifier (web, ios, android, macos, windows, linux). Must be sent explicitly — it is not derived from user_agent." - }, - "os_family": { - "type": "string", - "description": "Operating system family (e.g. iOS, Android, Windows). CamelCase alias: osFamily." - }, - "os_version": { - "type": "string", - "description": "Operating system version string. CamelCase alias: osVersion." - }, - "accept_language": { - "type": "string", - "description": "Accept-Language header value. CamelCase alias: acceptLang." - }, - "session_id": { - "type": "string", - "description": "Session identifier of the user current session. Stored in metadata. CamelCase alias: sessionId." - }, - "session_age_ms": { - "type": "number", - "description": "Age of the user session in milliseconds at transaction time. CamelCase alias: sessionAgeMs." - }, - "network_context": { - "type": "object", - "description": "Network context with IP address and geo-location. A top-level ipInfo block is also folded into this object.", - "properties": { - "ip_address": { - "type": "string", - "description": "Client IP address. CamelCase alias: ip." - }, - "ip_country_code": { - "type": "string", - "description": "Geo-resolved country of the IP (ISO 3166-1 alpha-3). CamelCase alias: countryCode3." - }, - "latitude": { - "type": "number", - "description": "Geographic latitude of the IP. CamelCase alias: lat." - }, - "longitude": { - "type": "number", - "description": "Geographic longitude of the IP. CamelCase alias: lon." - } - } - } - } - }, - "payment_method": { - "type": "object", - "description": "Payment method used by the subject.", - "properties": { - "method_type": { - "type": "string", - "description": "Type of payment method: `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`." - }, - "account_id": { - "type": "string", - "description": "Account identifier (e.g. IBAN, wallet address). For crypto screening, this should contain the wallet address for this participant. Inbound transaction-hash screening requires the service/customer deposit address on the subject. Outbound screening uses the destination wallet on the counterparty." - }, - "issuing_country": { - "type": "string", - "description": "ISO 3166-1 alpha-3 country code of the issuer." - } - } - } - } - }, - "counterparty": { - "type": "object", - "description": "The counterparty of the transaction (optional).", - "properties": { - "entity_type": { - "type": "string", - "description": "Entity type: `individual` or `company`." - }, - "vendor_data": { - "type": "string", - "description": "Your internal identifier for the counterparty." - }, - "full_name": { - "type": "string", - "description": "Full name of the counterparty." - }, - "first_name": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "date_of_birth": { - "type": "string", - "format": "date" - }, - "address": { - "type": "object", - "description": "Address object with `country` (ISO 3166-1 alpha-3)." - }, - "institution_details": { - "type": "object" - }, - "device_context": { - "type": "object", - "description": "Device context for the counterparty. browser_family and platform must be sent as explicit top-level keys — they are not derived from user_agent.", - "properties": { - "fingerprint": { - "type": "string", - "description": "Unique device fingerprint." - }, - "persistent_device_id": { - "type": "string", - "description": "Stable cross-session device identifier (v2 fingerprint schema)." - }, - "user_agent": { - "type": "string", - "description": "Browser or app user agent string. CamelCase alias: userAgent." - }, - "browser_family": { - "type": "string", - "description": "Browser family (e.g. Chrome, Firefox, Safari). Must be sent explicitly — it is not derived from user_agent. CamelCase alias: browserFamily." - }, - "browser_version": { - "type": "string", - "description": "Browser version string. Must be sent explicitly. CamelCase alias: browserVersion." - }, - "platform": { - "type": "string", - "description": "Platform identifier (web, ios, android, macos, windows, linux). Must be sent explicitly — it is not derived from user_agent." - }, - "os_family": { - "type": "string", - "description": "Operating system family (e.g. iOS, Android, Windows). CamelCase alias: osFamily." - }, - "os_version": { - "type": "string", - "description": "Operating system version string. CamelCase alias: osVersion." - }, - "accept_language": { - "type": "string", - "description": "Accept-Language header value. CamelCase alias: acceptLang." - }, - "session_id": { - "type": "string", - "description": "Session identifier for the counterparty session. Stored in metadata. CamelCase alias: sessionId." - }, - "session_age_ms": { - "type": "number", - "description": "Age of the user session in milliseconds at transaction time. CamelCase alias: sessionAgeMs." - }, - "network_context": { - "type": "object", - "description": "Network context with IP address and geo-location. A top-level ipInfo block is also folded into this object.", - "properties": { - "ip_address": { - "type": "string", - "description": "Client IP address. CamelCase alias: ip." - }, - "ip_country_code": { - "type": "string", - "description": "Geo-resolved country of the IP (ISO 3166-1 alpha-3). CamelCase alias: countryCode3." - }, - "latitude": { - "type": "number", - "description": "Geographic latitude of the IP. CamelCase alias: lat." - }, - "longitude": { - "type": "number", - "description": "Geographic longitude of the IP. CamelCase alias: lon." - } - } - } - } - }, - "payment_method": { - "type": "object", - "description": "Payment method used by the counterparty. On travelRule transactions, `method_type: \"unhosted_wallet\"` declares the counterparty wallet as self-hosted: the transfer skips counterparty-VASP routing, is created directly in `UNCONFIRMED_OWNERSHIP`, and (when `auto_wallet_verification` is enabled) a wallet-ownership widget session is auto-minted and returned in the transaction's `action_required` block.", - "properties": { - "method_type": { - "type": "string", - "description": "Type of payment method: `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`. Use `unhosted_wallet` to declare a self-hosted wallet for the Travel Rule." - }, - "account_id": { - "type": "string", - "description": "Account identifier (e.g. IBAN or wallet address)." - }, - "issuing_country": { - "type": "string" - } - } - } - } - }, - "custom_properties": { - "type": "object", - "description": "Arbitrary key-value pairs attached to the transaction. Each key can be referenced in rule conditions with the field path `custom_values.`.", - "additionalProperties": true, - "example": { - "merchant_id": "test", - "bank_name": "test_2" - } - }, - "travel_rule_details": { - "type": "object", - "description": "Travel Rule compliance metadata.", - "required": [ - "status" - ], - "properties": { - "status": { - "type": "string" - }, - "protocol": { - "type": "string" - }, - "required": { - "type": "boolean" - }, - "obligations_count": { - "type": "integer" - }, - "originator_data": { - "type": "object" - }, - "beneficiary_data": { - "type": "object" - }, - "metadata": { - "type": "object" - } - } - }, - "network_snapshot": { - "type": "object", - "description": "Pre-computed network graph snapshot.", - "properties": { - "nodes": { - "type": "array", - "items": { - "type": "object" - } - }, - "edges": { - "type": "array", - "items": { - "type": "object" - } - }, - "metrics": { - "type": "object" - } - } - }, - "include_crypto_screening": { - "type": "boolean", - "nullable": true, - "description": "Per-transaction override for crypto blockchain analytics screening. When `true`, crypto screening is performed regardless of the application default. When `false`, crypto screening is skipped even if enabled in the application settings. When omitted or `null`, the application-level default configured in the Console is used. Crypto screening requires `transaction_details.direction`, `currency_kind: \"crypto\"`, a chain/currency, and the wallet address selected by the direction: inbound pre-transfer screening uses `counterparty.payment_method.account_id`, inbound transaction-hash screening uses `subject.payment_method.account_id`, and outbound screening uses `counterparty.payment_method.account_id`. When `transaction_details.payment_reference_id` contains a blockchain transaction hash, Didit performs transaction screening and returns transaction summary enrichment when available." - } - }, - "required": [ - "transaction_id", - "transaction_category", - "transaction_details", - "subject" - ] - }, - "examples": { - "Minimal fiat withdrawal": { - "summary": "Smallest acceptable payload for a fiat outbound transfer", - "value": { - "transaction_id": "your-txn-2026-05-17-001", - "transaction_category": "finance", - "transaction_details": { - "direction": "outbound", - "amount": 1500, - "currency": "EUR", - "action_type": "withdrawal" - }, - "subject": { - "entity_type": "individual", - "vendor_data": "user-12345", - "full_name": "Maria Garcia" - } - } + "x-didit-wallet-id": "mobile_id" + } + }, + "/v3/id-verification/wallets/idcard-ee/": { + "post": { + "operationId": "start_idcard_ee_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_idcard_ee", + "auth": "api_key", + "server": true + }, + "description": "Start a Estonian ID-card sign-in (Police and Border Guard Board (Estonia); EST) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Estonian ID-card verification", + "parameters": [ + { + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + } + ], + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiIdcardEeWalletVerificationStartRequest" + }, + "example": { + "country": "EST", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "Crypto outbound with screening": { - "summary": "Outbound crypto withdrawal to an unhosted wallet, opt-in screening", - "value": { - "transaction_id": "your-txn-2026-05-17-002", - "transaction_category": "finance", - "include_crypto_screening": true, - "transaction_details": { - "direction": "outbound", - "amount": 0.5, - "currency": "BTC", - "currency_kind": "crypto", - "amount_in_default_currency": 32500, - "default_currency": "USD", - "action_type": "withdrawal" - }, - "subject": { - "entity_type": "individual", - "vendor_data": "user-12345", - "full_name": "Maria Garcia", - "payment_method": { - "method_type": "crypto_wallet", - "account_id": "bc1qexampleexchangewalletaddressxyz", - "issuing_country": "ESP" - } - }, - "counterparty": { - "entity_type": "individual", - "payment_method": { - "method_type": "unhosted_wallet", - "account_id": "bc1qexampleexternalwalletaddressabc" - } - } - } + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiIdcardEeWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiIdcardEeWalletVerificationStartRequest" + } + } + }, + "required": true + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "Travel Rule transfer": { - "summary": "Outbound crypto transfer with Travel Rule originator/beneficiary data", - "value": { - "transaction_id": "your-txn-2026-05-17-003", - "transaction_category": "travel_rule", - "transaction_details": { - "direction": "outbound", - "amount": 25000, - "currency": "USDT", - "currency_kind": "crypto", - "payment_reference_id": "0xabcdef1234567890", - "action_type": "transfer" - }, - "subject": { - "entity_type": "individual", - "vendor_data": "user-12345", - "full_name": "Maria Garcia", - "date_of_birth": "1992-03-14", - "address": { - "country": "ESP", - "town": "Madrid", - "street": "Calle Mayor 1", - "post_code": "28013" - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "idcard_ee", + "name": "Estonian ID-card", + "issuing_authority": "Police and Border Guard Board (Estonia)" }, - "counterparty": { - "entity_type": "individual", - "full_name": "John Beneficiary", - "institution_details": { - "name": "Beneficiary VASP, Inc.", - "code": "BVASP-US" - } + "country": "EST", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" }, - "travel_rule_details": { - "status": "PENDING", - "protocol": "IVMS101", - "required": true, - "originator_data": { - "name": "Maria Garcia" - }, - "beneficiary_data": { - "name": "John Beneficiary" - } - } - } + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" + } + } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/idcard-ee/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"EST\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'EST',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/idcard-ee/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + } + ], + "x-didit-wallet-id": "idcard_ee" + } + }, + "/v3/id-verification/wallets/eparaksts-mobile/": { + "post": { + "operationId": "start_eparaksts_mobile_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_eparaksts_mobile", + "auth": "api_key", + "server": true + }, + "description": "Start a eParaksts mobile sign-in (LVRTC (Latvian State Radio and Television Centre); LVA) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start eParaksts mobile verification", + "parameters": [ + { + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } + ], + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiEparakstsMobileWalletVerificationStartRequest" + }, + "example": { + "country": "LVA", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiEparakstsMobileWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiEparakstsMobileWalletVerificationStartRequest" + } + } + }, + "required": true }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], "responses": { "201": { - "description": "Transaction submitted and monitored. The body is the full transaction detail (same shape as `GET /v3/transactions/{transaction_id}/`), reflecting any rule outcomes already applied.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TransactionDetail" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "Approved with no rule hits": { - "value": { - "uuid": "abcdef12-3456-4890-abcd-ef1234567890", - "environment": "live", - "transaction_number": 4123, - "txn_id": "your-txn-2026-05-17-001", - "txn_date": "2026-05-17T08:42:00Z", - "zone_id": "Europe/Madrid", - "transaction_type": "finance", - "action_type": "withdrawal", - "direction": "OUTBOUND", - "status": "APPROVED", - "source": "LIVE", - "amount": "1500", - "currency": "EUR", - "currency_type": "fiat", - "amount_in_default_currency": "1620.45", - "default_currency_code": "USD", - "preferred_currency_amount": "1620.45", - "preferred_currency_code": "USD", - "payment_details": "Withdrawal to crypto wallet", - "payment_txn_id": null, - "score": 0, - "severity": null, - "decision_reason_code": null, - "decision_reason_label": null, - "vendor_data": "user-12345", - "metadata": { - "subject": { - "entity_type": "individual", - "vendor_data": "user-12345", - "full_name": "Maria Garcia", - "address": {}, - "institution_details": {}, - "device_context": {} - }, - "counterparty": {} - }, - "props": {}, - "tags": [], - "parties": [ - { - "uuid": "b1111111-2222-4333-8444-555555555555", - "role": "APPLICANT", - "entity_type": "individual", - "kind": "USER", - "vendor_data": "user-12345", - "full_name": "Maria Garcia", - "first_name": null, - "last_name": null, - "country_code": null, - "dob": null, - "address": {}, - "institution_info": {}, - "device": {}, - "external_party_snapshot": null - } - ], - "payment_methods": [], - "documents": [], - "activities": [ - { - "uuid": "d4e5f6a7-8901-4bcd-9ef0-123456789abc", - "activity_type": "TRANSACTION_CREATED", - "source": "TRANSACTION", - "status": "APPROVED", - "title": "withdrawal", - "description": "Transaction recorded.", - "metadata": {}, - "occurred_at": "2026-05-17T08:42:11Z" - } - ], - "alerts": [], - "rule_runs": [], - "provider_results": [], - "travel_rule": null, - "network_snapshot": null, - "remediation": null, - "cost_breakdown": null - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "eparaksts_mobile", + "name": "eParaksts mobile", + "issuing_authority": "LVRTC (Latvian State Radio and Television Centre)" + }, + "country": "LVA", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" }, - "Flagged for review": { - "value": { - "uuid": "abcdef12-3456-4890-abcd-ef1234567891", - "environment": "live", - "transaction_number": 4124, - "txn_id": "your-txn-2026-05-17-002", - "txn_date": "2026-05-17T08:42:00Z", - "zone_id": "Europe/Madrid", - "transaction_type": "finance", - "action_type": "withdrawal", - "direction": "OUTBOUND", - "status": "IN_REVIEW", - "source": "LIVE", - "amount": "0.5", - "currency": "BTC", - "currency_type": "crypto", - "amount_in_default_currency": "32500", - "default_currency_code": "USD", - "preferred_currency_amount": "32500", - "preferred_currency_code": "USD", - "payment_details": "Withdrawal to crypto wallet", - "payment_txn_id": null, - "score": 78, - "severity": "HIGH", - "decision_reason_code": "WALLET_HIGH_RISK", - "decision_reason_label": "Destination wallet flagged as high risk", - "vendor_data": "user-12345", - "metadata": { - "subject": { - "entity_type": "individual", - "vendor_data": "user-12345", - "full_name": "Maria Garcia", - "address": {}, - "institution_details": {}, - "device_context": {} - }, - "counterparty": {} - }, - "props": { - "order_id": "ord-9988" - }, - "tags": [], - "parties": [ - { - "uuid": "b1111111-2222-4333-8444-555555555555", - "role": "APPLICANT", - "entity_type": "individual", - "kind": "USER", - "vendor_data": "user-12345", - "full_name": "Maria Garcia", - "first_name": null, - "last_name": null, - "country_code": null, - "dob": null, - "address": {}, - "institution_info": {}, - "device": {}, - "external_party_snapshot": null - } - ], - "payment_methods": [], - "documents": [], - "activities": [], - "alerts": [ - { - "uuid": "a7b8c9d0-1234-4abc-9def-567890abcdef", - "title": "Destination wallet flagged as high risk", - "description": "Provider risk score exceeded the configured threshold.", - "severity": "HIGH", - "status": "OPEN", - "source": "RULE", - "metadata": {}, - "created_at": "2026-05-17T08:42:12Z", - "due_at": null - } - ], - "rule_runs": [ - { - "uuid": "e5f6a7b8-9012-4cde-af01-23456789abcd", - "rule": "f6a7b8c9-0123-4def-b012-3456789abcde", - "rule_title": "High-risk wallet screening", - "rule_description": null, - "rule_category": "crypto", - "matched": true, - "is_test": false, - "mode": "ACTIVE", - "severity": "HIGH", - "score_delta": 78, - "status_target": "IN_REVIEW", - "action_summary": "score +78; change_status:IN_REVIEW", - "metadata": {}, - "created_at": "2026-05-17T08:42:12Z" - } - ], - "provider_results": [], - "travel_rule": null, - "network_snapshot": null, - "remediation": null, - "cost_breakdown": null - } - } + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" + } + } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "400": { - "description": "Validation failed (missing required fields, duplicate `transaction_id`, unknown enum value, invalid currency, etc.).", + "409": { "content": { "application/json": { - "examples": { - "Missing required field": { - "value": { - "transaction_details": { - "amount": [ - "This field is required." - ] - } - } - }, - "Duplicate transaction_id": { - "value": { - "transaction_id": [ - "A transaction with this transaction_id already exists." - ] - } - }, - "Invalid category": { - "value": { - "non_field_errors": [ - "Unsupported transaction category." - ] - } - }, - "Missing subject identity": { - "value": { - "subject": [ - "subject.vendor_data and subject.full_name are required." - ] - } - }, - "Blocklisted participant": { - "value": { - "vendor_data": [ - "The individual 'user-12345' is in the blocklist and cannot participate in transactions." - ] - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot submit transactions for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "502": { "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "503": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/eparaksts-mobile/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"LVA\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'LVA',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/eparaksts-mobile/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "eparaksts_mobile" } }, - "/v3/transactions/{transaction_id}/": { - "get": { - "summary": "Get transaction", - "description": "Retrieve the full monitoring record for one transaction: parties, score/severity, rule runs, alerts, Travel Rule outcome, remediation session.", - "operationId": "getTransaction", - "tags": [ - "Transactions" - ], + "/v3/id-verification/wallets/bankid-cz/": { + "post": { + "operationId": "start_bankid_cz_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_bankid_cz", + "auth": "api_key", + "server": true + }, + "description": "Start a Bank iD sign-in (Bankovní identita, a.s.; CZE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Bank iD verification", "parameters": [ { - "name": "transaction_id", - "in": "path", - "required": true, - "description": "Didit-stable transaction UUID (the `uuid` returned from create / list, **not** your `txn_id`).", + "in": "header", + "name": "Idempotency-Key", "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, - "example": "abcdef12-3456-7890-abcd-ef1234567890" + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/transactions/abcdef12-3456-7890-abcd-ef1234567890/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBankidCzWalletVerificationStartRequest" + }, + "example": { + "country": "CZE", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBankidCzWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiBankidCzWalletVerificationStartRequest" + } + } }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nuuid = 'abcdef12-3456-7890-abcd-ef1234567890'\nresp = requests.get(\n f'https://verification.didit.me/v3/transactions/{uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\ntx = resp.json()\nfor alert in tx['alerts']:\n print(alert.get('code'), alert.get('severity'))" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const uuid = 'abcdef12-3456-7890-abcd-ef1234567890';\nconst resp = await fetch(`https://verification.didit.me/v3/transactions/${uuid}/`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Lookup failed: ${resp.status}`);\nconst tx = await resp.json();\nconsole.log(tx.status, tx.severity, tx.alerts.length, 'alerts');" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Full transaction detail.", + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TransactionDetail" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "In review with an alert": { - "value": { - "uuid": "abcdef12-3456-4890-abcd-ef1234567890", - "environment": "live", - "transaction_number": 4123, - "txn_id": "your-txn-2026-05-17-001", - "txn_date": "2026-05-17T08:42:00Z", - "zone_id": "Europe/Madrid", - "transaction_type": "finance", - "action_type": "withdrawal", - "direction": "OUTBOUND", - "status": "IN_REVIEW", - "source": "LIVE", - "amount": "1500", - "currency": "EUR", - "currency_type": "fiat", - "amount_in_default_currency": "1620.45", - "default_currency_code": "USD", - "preferred_currency_amount": "1620.45", - "preferred_currency_code": "USD", - "payment_details": "Withdrawal to crypto wallet", - "payment_txn_id": null, - "score": 78, - "severity": "HIGH", - "decision_reason_code": "WALLET_HIGH_RISK", - "decision_reason_label": "Destination wallet flagged as high risk", - "vendor_data": "user-12345", - "metadata": { - "subject": { - "entity_type": "individual", - "vendor_data": "user-12345", - "full_name": "Maria Garcia", - "address": {}, - "institution_details": {}, - "device_context": {} - }, - "counterparty": {} - }, - "props": { - "order_id": "ord-9988" - }, - "tags": [], - "parties": [ - { - "uuid": "b1111111-2222-4333-8444-555555555555", - "role": "APPLICANT", - "entity_type": "individual", - "kind": "USER", - "vendor_data": "user-12345", - "full_name": "Maria Garcia", - "first_name": null, - "last_name": null, - "country_code": null, - "dob": null, - "address": {}, - "institution_info": {}, - "device": {}, - "external_party_snapshot": null - } - ], - "payment_methods": [], - "documents": [], - "activities": [], - "alerts": [ - { - "uuid": "a7b8c9d0-1234-4abc-9def-567890abcdef", - "title": "Destination wallet flagged as high risk", - "description": "Provider risk score exceeded the configured threshold.", - "severity": "HIGH", - "status": "OPEN", - "source": "RULE", - "metadata": {}, - "created_at": "2026-05-17T08:42:12Z", - "due_at": null - } - ], - "rule_runs": [], - "provider_results": [], - "travel_rule": null, - "network_snapshot": null, - "remediation": null, - "cost_breakdown": null - } - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "bankid_cz", + "name": "Bank iD", + "issuing_authority": "Bankovní identita, a.s." + }, + "country": "CZE", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot read this application's transactions. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "404": { - "description": "No transaction with this UUID exists for the application (deleted, never created, belongs to another application, or the path segment is not a valid UUID).", + "409": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "502": { "content": { "application/json": { - "examples": { - "Throttled": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/bankid-cz/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"CZE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'CZE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/bankid-cz/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "bankid_cz" } }, - "/v3/transactions/sdk-token/": { + "/v3/id-verification/wallets/mojeid/": { "post": { - "summary": "Mint an SDK transaction token", - "description": "Mint a short-lived scoped token your app passes to the Didit SDKs so they can submit transactions directly from the end-user's device via `POST /v1/transactions/`. The token is bound to one `vendor_data` (your user id): every transaction submitted with it has its subject identity enforced server-side from that binding, so a tampered client cannot submit on behalf of another user. Mint tokens from your backend - never ship the API key itself in an app. camelCase aliases (`vendorData`, `ttlSeconds`, `maxUses`) are also accepted.", - "operationId": "createTransactionSdkToken", - "tags": [ - "Transactions" - ], - "security": [ + "operationId": "start_mojeid_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_mojeid", + "auth": "api_key", + "server": true + }, + "description": "Start a MojeID sign-in (CZ.NIC; CZE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start MojeID verification", + "parameters": [ { - "ApiKeyAuth": [] + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], + "tags": [ + "Digital ID wallets" + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "vendor_data": { - "type": "string", - "description": "Your internal identifier for the end user this token is scoped to. Enforced as the subject identity of every transaction submitted with the token.", - "example": "user-042" - }, - "ttl_seconds": { - "type": "integer", - "default": 900, - "maximum": 86400, - "description": "Token lifetime in seconds. Defaults to 900 (15 minutes); maximum 86400 (24 hours)." - }, - "max_uses": { - "type": "integer", - "nullable": true, - "description": "Maximum number of successful submissions allowed with this token. Omit or null for unlimited uses within the TTL." - } - }, - "required": [ - "vendor_data" - ] + "$ref": "#/components/schemas/DigitalIdWalletApiMojeidWalletVerificationStartRequest" }, "example": { - "vendor_data": "user-042", - "ttl_seconds": 900, - "max_uses": 3 + "country": "CZE", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiMojeidWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiMojeidWalletVerificationStartRequest" } } - } + }, + "required": true }, - "x-codeSamples": [ + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/transactions/sdk-token/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"vendor_data\": \"user-042\", \"ttl_seconds\": 900, \"max_uses\": 3}'" + "ApiKeyAuth": [] }, { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/transactions/sdk-token/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'vendor_data': 'user-042', 'ttl_seconds': 900, 'max_uses': 3},\n timeout=10,\n)\nresp.raise_for_status()\ntoken = resp.json()\nprint(token['sdk_token'], token['expires_at'])" + "BearerAuth": [] } ], "responses": { "201": { - "description": "The minted token. Hand `sdk_token` to your app; it authenticates the device-facing `/v1/transactions/` endpoints via the `X-Transaction-Token` header until `expires_at`.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "sdk_token": { - "type": "string", - "description": "The scoped transaction token to pass to the SDK." - }, - "expires_at": { - "type": "string", - "format": "date-time", - "description": "When the token stops being accepted." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, "example": { - "sdk_token": "q7Zt...urlsafe", - "expires_at": "2026-07-07T12:15:00Z" + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "mojeid", + "name": "MojeID", + "issuing_authority": "CZ.NIC" + }, + "country": "CZE", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" + } + } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "400": { - "description": "Validation failed: missing `vendor_data`, or `ttl_seconds` outside the allowed range.", + "502": { "content": { "application/json": { - "examples": { - "Missing vendor_data": { - "value": { - "vendor_data": [ - "This field is required." - ] - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, - "403": { - "description": "Missing, invalid, or revoked API key.", + "503": { "content": { "application/json": { - "examples": { - "Invalid API Key": { - "value": { - "detail": "Invalid API Key." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } - } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/mojeid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"CZE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'CZE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/mojeid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + } + ], + "x-didit-wallet-id": "mojeid" } }, - "/v1/transactions/": { + "/v3/id-verification/wallets/diia/": { "post": { - "summary": "Submit a transaction from an SDK", - "description": "Device-facing endpoint the Didit SDKs call to submit a transaction directly from the end-user's device. Authentication is a scoped token from `POST /v3/transactions/sdk-token/` sent in the `X-Transaction-Token` header instead of an API key, and the endpoint is CORS-enabled so browsers can call it cross-origin. The payload uses the camelCase wire aliases shown below (the SDK `submitTransaction` methods build it for you). Three server-side guarantees: the application is always taken from the token; the subject identity (`externalUserId`) is always enforced from the token's `vendor_data`, so spoofed payload values are ignored; and device intelligence (the SDK device fingerprint plus server-derived IP and user agent) is attached to the subject automatically.", - "operationId": "submitTransactionFromSdk", - "tags": [ - "Transactions" - ], - "security": [ + "operationId": "start_diia_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_diia", + "auth": "api_key", + "server": true + }, + "description": "Start a Diia sign-in (Ministry of Digital Transformation of Ukraine; UKR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Diia verification", + "parameters": [ { - "TransactionTokenAuth": [] + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], + "tags": [ + "Digital ID wallets" + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "txnId": { - "type": "string", - "description": "Your unique identifier for this transaction (maps to `transaction_id`, max 128 characters).", - "example": "wd-2026-07-07-0042" - }, - "txnDate": { - "type": "string", - "format": "date-time", - "description": "When the transaction occurred (maps to `transaction_at`). Defaults to now if omitted." - }, - "zoneId": { - "type": "string", - "description": "IANA time zone identifier (maps to `time_zone`), e.g. `Europe/Madrid`." - }, - "type": { - "type": "string", - "description": "Transaction category (maps to `transaction_category`), e.g. `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`.", - "example": "travelRule" - }, - "info": { - "type": "object", - "description": "Core financial details of the transaction (maps to `transaction_details`).", - "required": [ - "direction", - "amount", - "currency" - ], - "properties": { - "direction": { - "type": "string", - "description": "Direction relative to the subject. Case-insensitive; `in`/`out` are accepted as aliases. Echoed back uppercase (`INBOUND`/`OUTBOUND`).", - "example": "out" - }, - "amount": { - "type": "number", - "description": "Transaction amount (up to 18 decimal places). A decimal string is also accepted and is the only way to preserve more than about 15 significant digits: a JSON number is parsed as a binary float first, so `0.123456789012345678` is stored as `0.12345678901234568`.", - "example": 0.25 - }, - "currency": { - "type": "string", - "description": "Currency code. Any ISO 4217 alphabetic code is accepted (e.g. `EUR`, `USD`, `NGN`), as is a crypto asset symbol (e.g. `BTC`). Prefer a currently-circulating code. Not every accepted code has an exchange rate: when none is available for the code (for example the ISO accounting unit `XSU`, or a crypto symbol Didit does not price), automatic conversion fails and `preferred_currency_amount` is returned null unless the request supplied a pre-converted `amountInDefaultCurrency`.", - "example": "ETH" - }, - "currencyType": { - "type": "string", - "description": "Whether the currency is `fiat` or `crypto` (maps to `currency_kind`)." - }, - "amountInDefaultCurrency": { - "type": "number", - "nullable": true, - "description": "Pre-converted amount in the default currency (maps to `amount_in_default_currency`). If omitted, automatic FX conversion is attempted." - }, - "defaultCurrencyCode": { - "type": "string", - "description": "The default currency code for the converted amount (maps to `default_currency`). Any currently-circulating ISO 4217 code." - }, - "paymentDetails": { - "type": "string", - "nullable": true, - "description": "Free-text payment reference or memo (maps to `payment_details`)." - }, - "paymentTxnId": { - "type": "string", - "nullable": true, - "description": "External payment reference (maps to `payment_reference_id`); the on-chain transaction hash for crypto transactions." - }, - "type": { - "type": "string", - "nullable": true, - "description": "Sub-type of the transaction (maps to `action_type`), e.g. `deposit`, `withdrawal`, `transfer`." - }, - "cryptoParams": { - "type": "object", - "description": "Crypto-specific parameters, e.g. `{\"crypto_chain\": \"ETH\"}`." - } - } - }, - "subject": { - "type": "object", - "description": "The subject (applicant) of the transaction. `externalUserId` is ignored here: the subject identity is always enforced from the token's `vendor_data`.", - "required": [ - "fullName" - ], - "properties": { - "type": { - "type": "string", - "description": "Entity type (maps to `entity_type`): `individual` (default) or `company`." - }, - "externalUserId": { - "type": "string", - "description": "Ignored on the subject - enforced server-side from the token's `vendor_data`." - }, - "fullName": { - "type": "string", - "example": "Jane Doe" - }, - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "dob": { - "type": "string", - "format": "date", - "description": "Date of birth (YYYY-MM-DD; maps to `date_of_birth`)." - }, - "address": { - "type": "object", - "description": "Address object with `country` (ISO 3166-1 alpha-3), `town`, `state`, `street`, `post_code`." - }, - "institutionInfo": { - "type": "object", - "description": "Institution details for institutional participants (maps to `institution_details`)." - }, - "device": { - "type": "object", - "description": "Optional explicit device context. Merged with the automatically captured device intelligence; server-derived fields win on conflict." - }, - "paymentMethod": { - "type": "object", - "description": "Payment method used by the subject (maps to `payment_method`).", - "properties": { - "type": { - "type": "string", - "description": "Payment method kind (maps to `method_type`), e.g. `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`." - }, - "accountId": { - "type": "string", - "description": "Account identifier (maps to `account_id`): IBAN, card fingerprint, or wallet address." - }, - "issuingCountry": { - "type": "string", - "description": "ISO 3166-1 alpha-3 country code of the issuer (maps to `issuing_country`)." - } - } - } - } - }, - "counterparty": { - "type": "object", - "description": "The counterparty of the transaction (optional). Same shape as `subject`; `externalUserId` is meaningful here.", - "properties": { - "type": { - "type": "string", - "description": "Entity type (maps to `entity_type`): `individual` or `company`." - }, - "externalUserId": { - "type": "string", - "description": "Your internal identifier for the counterparty (maps to `vendor_data`)." - }, - "fullName": { - "type": "string" - }, - "firstName": { - "type": "string" - }, - "lastName": { - "type": "string" - }, - "dob": { - "type": "string", - "format": "date" - }, - "address": { - "type": "object" - }, - "institutionInfo": { - "type": "object" - }, - "device": { - "type": "object" - }, - "paymentMethod": { - "type": "object", - "description": "Payment method used by the counterparty (maps to `payment_method`). On travelRule transactions, `type: \"unhosted_wallet\"` declares the counterparty wallet as self-hosted: the transfer skips counterparty-VASP routing, is created directly in `UNCONFIRMED_OWNERSHIP`, and (when `auto_wallet_verification` is enabled) a wallet-ownership widget session is auto-minted and returned in `action_required` so the SDK can launch it.", - "properties": { - "type": { - "type": "string", - "description": "Payment method kind (maps to `method_type`), e.g. `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`. Use `unhosted_wallet` to declare a self-hosted wallet for the Travel Rule." - }, - "accountId": { - "type": "string", - "description": "Account identifier (maps to `account_id`): IBAN, card fingerprint, or wallet address." - }, - "issuingCountry": { - "type": "string" - } - } - } - } - }, - "props": { - "type": "object", - "additionalProperties": true, - "description": "Custom key-value pairs (maps to `custom_properties`). Each key can be referenced in rule conditions as `custom_values.`." - }, - "travelRule": { - "type": "object", - "description": "Travel Rule details (maps to `travel_rule_details`). When Travel Rule is enabled for the application, Didit ignores any `status` you send and drives the exchange itself.", - "required": [ - "status" - ], - "properties": { - "status": { - "type": "string" - }, - "protocol": { - "type": "string" - }, - "required": { - "type": "boolean" - }, - "obligationsCount": { - "type": "integer" - }, - "originatorData": { - "type": "object" - }, - "beneficiaryData": { - "type": "object" - }, - "metadata": { - "type": "object" - } - } - }, - "includeCryptoScreening": { - "type": "boolean", - "nullable": true, - "description": "Per-transaction override for crypto blockchain analytics screening (maps to `include_crypto_screening`)." - }, - "fingerprint_v2": { - "type": "object", - "description": "The `didit-fp-v2` device fingerprint payload. Injected automatically by the SDKs; only relevant when calling the wire API directly." - } - }, - "required": [ - "txnId", - "type", - "info", - "subject" - ] + "$ref": "#/components/schemas/DigitalIdWalletApiDiiaWalletVerificationStartRequest" }, "example": { - "txnId": "wd-2026-07-07-0042", - "type": "travelRule", - "info": { - "direction": "out", - "amount": "0.25", - "currency": "ETH", - "currencyType": "crypto", - "cryptoParams": { - "crypto_chain": "ETH" - } - }, - "subject": { - "fullName": "Jane Doe" - }, - "counterparty": { - "fullName": "Ana Diaz", - "paymentMethod": { - "type": "unhosted_wallet", - "accountId": "0xBeneficiaryWallet01" - } - }, - "travelRule": { - "status": "AWAITING_COUNTERPARTY", - "beneficiaryData": { - "wallet_address": "0xBeneficiaryWallet01", - "name": "Ana Diaz" - } + "country": "UKR", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "includeCryptoScreening": true + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiDiiaWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiDiiaWalletVerificationStartRequest" } } - } + }, + "required": true }, - "x-codeSamples": [ + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v1/transactions/ \\\n -H 'X-Transaction-Token: SDK_TOKEN' -H 'Content-Type: application/json' \\\n -d '{\"txnId\": \"wd-2026-07-07-0042\", \"type\": \"travelRule\", \"info\": {\"direction\": \"out\", \"amount\": \"0.25\", \"currency\": \"ETH\", \"currencyType\": \"crypto\"}, \"subject\": {\"fullName\": \"Jane Doe\"}, \"counterparty\": {\"fullName\": \"Ana Diaz\", \"paymentMethod\": {\"type\": \"unhosted_wallet\", \"accountId\": \"0xBeneficiaryWallet01\"}}, \"travelRule\": {\"status\": \"AWAITING_COUNTERPARTY\", \"beneficiaryData\": {\"wallet_address\": \"0xBeneficiaryWallet01\", \"name\": \"Ana Diaz\"}}, \"includeCryptoScreening\": true}'" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "Web SDK", - "source": "import { DiditSdk } from '@didit-protocol/sdk-web';\n\nconst result = await DiditSdk.shared.submitTransaction({\n transactionToken: sdkToken,\n transaction: {\n txnId: 'wd-2026-07-07-0042',\n category: 'travelRule',\n details: { direction: 'out', amount: '0.25', currency: 'ETH', currencyType: 'crypto' },\n subject: { fullName: 'Jane Doe' },\n counterparty: { fullName: 'Ana Diaz', paymentMethod: { type: 'unhosted_wallet', accountId: '0xBeneficiaryWallet01' } },\n travelRule: { status: 'AWAITING_COUNTERPARTY', beneficiaryData: { wallet_address: '0xBeneficiaryWallet01', name: 'Ana Diaz' } },\n includeCryptoScreening: true\n },\n onActionCompleted: (refreshed) => console.log(refreshed.status)\n});" + "BearerAuth": [] } ], "responses": { "201": { - "description": "Transaction submitted and monitored. Same detail shape as `GET /v3/transactions/{transaction_id}/`, including the `action_required` block when the end user must complete a follow-up action (the SDKs auto-launch it by default).", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TransactionDetail" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "Wallet ownership required": { - "value": { - "uuid": "22222222-3333-4444-5555-666666666666", - "txn_id": "wd-2026-07-07-0042", - "transaction_type": "travelRule", - "direction": "OUTBOUND", - "status": "AWAITING_USER", - "amount": "0.25", - "currency": "ETH", - "travel_rule": { - "uuid": "33333333-4444-5555-6666-777777777777", - "status": "ON_HOLD", - "required": true - }, - "action_required": { - "type": "wallet_ownership", - "url": "https://verification.didit.me/wallet-ownership/3s9x...urlsafe", - "widget_session_id": "77777777-8888-9999-aaaa-bbbbbbbbbbbb", - "expires_at": "2026-07-07T13:00:00Z" - } - } - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "diia", + "name": "Diia", + "issuing_authority": "Ministry of Digital Transformation of Ukraine" + }, + "country": "UKR", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, "400": { - "description": "Validation failed (missing required fields, duplicate `txnId`, unknown category).", "content": { "application/json": { - "examples": { - "Missing fields": { - "value": { - "info": { - "currency": [ - "This field is required." - ] - } - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, - "401": { - "description": "The transaction token was rejected: missing, unknown, revoked, over its `max_uses`, or expired. Expired-token responses always contain the word `expired` in the detail, which is how the SDKs distinguish `expired_token` from `invalid_token` errors.", + "403": { "content": { "application/json": { - "examples": { - "Invalid token": { - "value": { - "detail": "Invalid transaction token." - } - }, - "Expired token": { - "value": { - "detail": "Transaction token expired." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } - } - } - } - }, - "/v1/transactions/{transaction_id}/": { - "get": { - "summary": "Get a transaction from an SDK", - "description": "Device-facing read endpoint, authenticated with the same `X-Transaction-Token` used to submit. The SDKs poll it after an auto-launched action flow completes or its window closes, so the final state never depends on a single notification. Returns the same transaction detail as the submit response, including the current `action_required` block (`null` once the action is resolved).", - "operationId": "getTransactionFromSdk", - "tags": [ - "Transactions" - ], - "parameters": [ - { - "name": "transaction_id", - "in": "path", - "required": true, - "description": "Didit-stable transaction UUID (the `uuid` from the submit response, **not** your `txnId`).", - "schema": { - "type": "string", - "format": "uuid" }, - "example": "22222222-3333-4444-5555-666666666666" - } - ], - "security": [ - { - "TransactionTokenAuth": [] - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v1/transactions/22222222-3333-4444-5555-666666666666/' \\\n -H 'X-Transaction-Token: SDK_TOKEN'" - } - ], - "responses": { - "200": { - "description": "The full transaction detail, including `action_required` (`null` once the pending action is resolved).", + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TransactionDetail" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "401": { - "description": "The transaction token was rejected: missing, unknown, revoked, over its `max_uses`, or expired.", + "502": { "content": { "application/json": { - "examples": { - "Expired token": { - "value": { - "detail": "Transaction token expired." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, - "404": { - "description": "No transaction with this UUID exists for the token's application, or the path segment is not a valid UUID.", + "503": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } - } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/diia/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"UKR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'UKR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/diia/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + } + ], + "x-didit-wallet-id": "diia" } }, - "/v3/businesses/": { - "get": { - "summary": "List businesses", - "description": "List [businesses](/entities/businesses) Didit assembles from KYB sessions sharing the same `vendor_data` (free-form string, NOT a UUID), plus businesses created via the API. Paginated, ordered by `last_session_at` desc. This endpoint does not support filtering or search parameters — fetch pages and filter client-side.", - "operationId": "list_businesses", - "tags": [ - "Businesses" - ], + "/v3/id-verification/wallets/franceconnect/": { + "post": { + "operationId": "start_franceconnect_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_franceconnect", + "auth": "api_key", + "server": true + }, + "description": "Start a FranceConnect sign-in (DINUM (French state); FRA) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start FranceConnect verification", "parameters": [ { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "default": 50, - "minimum": 1 - }, - "description": "Page size. Defaults to 50 when omitted. There is no enforced maximum, but keep pages small for latency." - }, - { - "name": "offset", - "in": "query", + "in": "header", + "name": "Idempotency-Key", "schema": { - "type": "integer", - "default": 0, - "minimum": 0 + "type": "string" }, - "description": "Zero-based offset of the first record to return. Prefer following the `next` URL from the previous page over computing this by hand." + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/businesses/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiFranceconnectWalletVerificationStartRequest" + }, + "example": { + "country": "FRA", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiFranceconnectWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiFranceconnectWalletVerificationStartRequest" + } + } }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/businesses/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n params={'limit': 50},\n)\nresp.raise_for_status()\npage = resp.json()\nfor biz in page['results']:\n print(biz['vendor_data'], biz['status'])" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const url = new URL('https://verification.didit.me/v3/businesses/');\nurl.searchParams.set('limit', '50');\n\nconst page = await fetch(url, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n}).then((r) => r.json());\nconsole.log(page.count, page.results.length);" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Paginated list of businesses with status, session counts, and feature breakdown.", + "201": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer", - "description": "Number of matching rows — exact up to 100, capped at 100 beyond that for performance. Do not use it to detect the last page; rely on `next` being `null` instead." - }, - "next": { - "type": "string", - "nullable": true, - "description": "Absolute URL of the next page, or `null` on the last page." + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "franceconnect", + "name": "FranceConnect", + "issuing_authority": "DINUM (French state)" }, - "previous": { - "type": "string", - "nullable": true, - "description": "Absolute URL of the previous page, or `null` on the first page." + "country": "FRA", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BusinessListItem" - } - } - } - }, - "examples": { - "Example": { - "value": { - "count": 1, - "next": null, - "previous": null, - "results": [ - { - "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", - "vendor_data": "company-123", - "display_name": null, - "legal_name": "Acme Trading Ltd", - "registration_number": "12345678", - "country_code": "GB", - "effective_name": "Acme Trading Ltd", - "status": "ACTIVE", - "session_count": 2, - "approved_count": 1, - "declined_count": 0, - "in_review_count": 1, - "features": { - "KYB": "Approved" - }, - "features_list": [ - { - "feature": "KYB", - "status": "Approved" - } - ], - "last_session_at": "2026-03-15T10:30:00Z", - "last_activity_at": "2026-03-15T10:30:00Z", - "first_session_at": "2026-01-10T08:00:00Z", - "tags": [ - { - "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", - "name": "VIP", - "color": "#2567FF" - } - ], - "created_at": "2026-01-10T08:00:00Z" - } - ] - } - } + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" }, - "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot list businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "400": { "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "403": { "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - } - }, - "/v3/customization/": { - "get": { - "summary": "Get branding customization", - "description": "Returns the white-label branding for the application resolved from the API key: colors, fonts, border radii, logo URLs and UI/UX options. Custom domain and white-label email settings are managed only in the Business Console and are not part of this response.", - "operationId": "get_customization", - "tags": [ - "Customization" - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/customization/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/customization/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n)\nresp.raise_for_status()\nbranding = resp.json()\nprint(branding['font_family'], branding['color_primary'])" + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const branding = await fetch(\n 'https://verification.didit.me/v3/customization/',\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n).then((r) => r.json());\nconsole.log(branding.font_family, branding.color_primary);" - } - ], - "responses": { - "200": { - "description": "Current branding customization for the application.", + "502": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BrandingCustomization" - }, - "examples": { - "Default branding": { - "value": { - "color_primary": "#000000", - "color_secondary": "#9DA1A1", - "color_background": "#FFFFFF", - "color_panel": "#2567FF", - "color_panel_10": "#1A1A1A", - "color_on_panel_1": "#9DA1A1", - "color_on_panel_2": "#F4F4F6", - "color_on_background": "#000000", - "color_button_1": "#2567FF", - "color_button_2": "#F8F8F8", - "color_button_text_1": "#FFFFFF", - "color_button_text_2": "#000000", - "color_pill_text": "#111827", - "border_radius_panel": 10, - "border_radius_buttons": 12, - "font_family": "Inter", - "font_weight": "regular", - "font_url": "https://fonts.googleapis.com/css2?family=Inter", - "logo_square": null, - "logo_rectangular": null, - "favicon": null, - "app_public_name": "Acme Verify", - "privacy_policy_url": "https://acme.com/privacy", - "skip_welcome_screen": false, - "disable_login_with_didit": false, - "hide_progress_bar": false, - "callback_seconds": 3 - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, - "403": { - "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "503": { "content": { "application/json": { - "examples": { - "Invalid token": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/franceconnect/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"FRA\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'FRA',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/franceconnect/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } + ], + "x-didit-wallet-id": "franceconnect" + } + }, + "/v3/id-verification/wallets/audkenni/": { + "post": { + "operationId": "start_audkenni_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_audkenni", + "auth": "api_key", + "server": true }, - "security": [ + "description": "Start a Auðkenni sign-in (Auðkenni (Icelandic electronic ID); ISL) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start Auðkenni verification", + "parameters": [ { - "ApiKeyAuth": [] + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } - ] - }, - "patch": { - "summary": "Update branding customization", - "description": "Partial update of the application's white-label branding. Send `application/json` to change colors, fonts, radii and UI options, or `multipart/form-data` to upload logo images. Only the fields you include are changed. Custom domain and white-label email cannot be set through this endpoint — use the Business Console for those.", - "operationId": "update_customization", + ], "tags": [ - "Customization" + "Digital ID wallets" ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BrandingCustomizationUpdate" + "$ref": "#/components/schemas/DigitalIdWalletApiAudkenniWalletVerificationStartRequest" }, - "examples": { - "Update colors and font": { - "summary": "Change branding colors and font", - "value": { - "color_primary": "#123456", - "color_button_1": "#2567FF", - "font_family": "AR One Sans", - "border_radius_buttons": 16 - } - }, - "Update UI options": { - "summary": "Change public name and flow options", - "value": { - "app_public_name": "Acme Verify", - "privacy_policy_url": "https://acme.com/privacy", - "skip_welcome_screen": true, - "hide_progress_bar": true, - "callback_seconds": 5 - } + "example": { + "country": "ISL", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "Remove a logo": { - "summary": "Delete the current rectangular logo", - "value": { - "delete_logo_rectangular": true - } - } + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiAudkenniWalletVerificationStartRequest" } }, "multipart/form-data": { "schema": { - "type": "object", - "description": "Use multipart/form-data only to upload logo images. Branding fields above may also be sent as form fields in the same request.", - "properties": { - "image_square": { - "type": "string", - "format": "binary", - "description": "Square logo (1:1 aspect ratio). Max **1 MB**. Allowed: `png`, `jpg`, `jpeg`, `webp`." - }, - "image_rectangular": { - "type": "string", - "format": "binary", - "description": "Rectangular logo (2:1 aspect ratio). Max **1 MB**. Allowed: `png`, `jpg`, `jpeg`, `webp`." - }, - "image_favicon": { - "type": "string", - "format": "binary", - "description": "Favicon image. Max **500 KB**." - } - } - }, - "example": { - "image_rectangular": "(binary PNG logo, 2:1)", - "image_favicon": "(binary favicon)" + "$ref": "#/components/schemas/DigitalIdWalletApiAudkenniWalletVerificationStartRequest" } } - } + }, + "required": true }, - "x-codeSamples": [ + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/customization/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"color_primary\": \"#123456\", \"font_family\": \"AR One Sans\"}'" + "ApiKeyAuth": [] }, { - "lang": "curl", - "label": "curl (logo upload)", - "source": "curl -X PATCH 'https://verification.didit.me/v3/customization/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'image_rectangular=@logo.png' \\\n -F 'image_favicon=@favicon.ico'" + "BearerAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + }, + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "audkenni", + "name": "Auðkenni", + "issuing_authority": "Auðkenni (Icelandic electronic ID)" + }, + "country": "ISL", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" + } + } + }, + "description": "" }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/customization/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'color_primary': '#123456', 'font_family': 'AR One Sans'},\n)\nresp.raise_for_status()\nprint(resp.json()['color_primary'])\n\n# Upload a logo (multipart):\nwith open('logo.png', 'rb') as logo:\n requests.patch(\n 'https://verification.didit.me/v3/customization/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n files={'image_rectangular': logo},\n )" + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/customization/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ color_primary: '#123456', font_family: 'AR One Sans' }),\n});\nconst branding = await resp.json();\nconsole.log(branding.color_primary);" - } - ], - "responses": { - "200": { - "description": "Branding updated. The FULL branding object is returned (all fields, not just the ones you changed).", + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BrandingCustomization" - }, - "examples": { - "Updated": { - "value": { - "color_primary": "#123456", - "color_secondary": "#9DA1A1", - "color_background": "#FFFFFF", - "color_panel": "#2567FF", - "color_panel_10": "#1A1A1A", - "color_on_panel_1": "#9DA1A1", - "color_on_panel_2": "#F4F4F6", - "color_on_background": "#000000", - "color_button_1": "#2567FF", - "color_button_2": "#F8F8F8", - "color_button_text_1": "#FFFFFF", - "color_button_text_2": "#000000", - "color_pill_text": "#111827", - "border_radius_panel": 10, - "border_radius_buttons": 16, - "font_family": "AR One Sans", - "font_weight": "400", - "font_url": "https://fonts.googleapis.com/css2?family=AR+One+Sans", - "logo_square": null, - "logo_rectangular": "https://cdn.didit.me/white_label/logo_rectangular/...", - "favicon": null, - "app_public_name": "Acme Verify", - "privacy_policy_url": "https://acme.com/privacy", - "skip_welcome_screen": true, - "disable_login_with_didit": false, - "hide_progress_bar": false, - "callback_seconds": 3 - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "400": { - "description": "Invalid body — e.g. malformed hex color, unknown font family or weight, a logo failing the size/aspect-ratio check, or a Business-Console-only field (`domain`, `from_email`, `verify_domain`, `force_remove_domain`, `verify_email_domain`, `force_remove_email_domain`, and their read-only companions). Also returned when setting `disable_login_with_didit` to `false` while a custom domain is configured.", + "409": { "content": { "application/json": { - "examples": { - "Bad color": { - "value": { - "color_primary": [ - "Invalid color format. Use '#RRGGBB' or '#RRGGBBAA' format." - ] - } - }, - "Bad logo": { - "value": { - "image_rectangular": [ - "Image aspect ratio must be 2:1. Got 1024x1024 image dimensions" - ] - } - }, - "Console-only field": { - "value": { - "domain": [ - "Manage this field in the Business Console." - ] - } - }, - "Bad font weight": { - "value": { - "font_weight": [ - "Font weight 950 is not available for Inter. Available weights are: 100, 200, 300, 400, 500, 600, 700, 800, 900" - ] - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "403": { - "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.", + "502": { "content": { "application/json": { - "examples": { - "Invalid token": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, - "429": { - "description": "Rate limit exceeded. Retry with exponential backoff." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/audkenni/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"ISL\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'ISL',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/audkenni/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] + ], + "x-didit-wallet-id": "audkenni" } }, - "/v3/businesses/{vendor_data}/": { - "get": { - "summary": "Get business", - "description": "Fetch one [business](/entities/businesses) by `vendor_data` (exact match). Adds `metadata` and `updated_at` to the list view. Deleted businesses return 404.", - "operationId": "get_business", - "tags": [ - "Businesses" - ], + "/v3/id-verification/wallets/connectid/": { + "post": { + "operationId": "start_connectid_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_connectid", + "auth": "api_key", + "server": true + }, + "description": "Start a ConnectID sign-in (Australian Payments Plus; AUS) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start ConnectID verification", "parameters": [ { - "name": "vendor_data", - "in": "path", - "required": true, + "in": "header", + "name": "Idempotency-Key", "schema": { "type": "string" }, - "description": "Your unique identifier for the business — a free-form string (NOT a UUID), matched exactly as sent.", - "example": "company-123" + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/businesses/company-123/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "tags": [ + "Digital ID wallets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiConnectidWalletVerificationStartRequest" + }, + "example": { + "country": "AUS", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiConnectidWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiConnectidWalletVerificationStartRequest" + } + } }, + "required": true + }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/businesses/company-123/',\n headers={'x-api-key': 'YOUR_API_KEY'},\n)\nresp.raise_for_status()\nbiz = resp.json()\nprint(biz['didit_internal_id'], biz['status'])" + "ApiKeyAuth": [] }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const vendorData = encodeURIComponent('company-123');\nconst biz = await fetch(\n `https://verification.didit.me/v3/businesses/${vendorData}/`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n).then((r) => r.json());\nconsole.log(biz.didit_internal_id, biz.status);" + "BearerAuth": [] } ], "responses": { - "200": { - "description": "Full business detail including metadata and aggregated session data.", + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessDetailItem" + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "examples": { - "Active business": { - "value": { - "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", - "vendor_data": "company-123", - "display_name": null, - "legal_name": "Acme Trading Ltd", - "registration_number": "12345678", - "country_code": "GB", - "effective_name": "Acme Trading Ltd", - "status": "ACTIVE", - "session_count": 2, - "approved_count": 1, - "declined_count": 0, - "in_review_count": 1, - "features": { - "KYB": "Approved" - }, - "features_list": [ - { - "feature": "KYB", - "status": "Approved" - } - ], - "last_session_at": "2026-03-15T10:30:00Z", - "last_activity_at": "2026-03-15T10:30:00Z", - "first_session_at": "2026-01-10T08:00:00Z", - "tags": [ - { - "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", - "name": "VIP", - "color": "#2567FF" - } - ], - "created_at": "2026-01-10T08:00:00Z", - "metadata": { - "industry": "fintech" - }, - "updated_at": "2026-03-15T10:30:00Z" - } - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "connectid", + "name": "ConnectID", + "issuing_authority": "Australian Payments Plus" + }, + "country": "AUS", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" } } - } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + } + } + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot read this application's businesses. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "404": { - "description": "No (non-deleted) business with the supplied `vendor_data` exists for this application.", + "409": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "502": { "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." } }, - "security": [ + "x-codeSamples": [ { - "ApiKeyAuth": [] + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/connectid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"AUS\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'AUS',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/connectid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } - ] - }, - "patch": { - "summary": "Update business", - "description": "Partial update of a business: `display_name`, `legal_name`, `registration_number`, `country_code`, `status`, `metadata`. `metadata` fully replaces. Setting `status` to `BLOCKED` adds the `vendor_data` to the system blocklist; moving away from `BLOCKED` removes that entry.", - "operationId": "update_business", - "tags": [ - "Businesses" ], + "x-didit-wallet-id": "connectid" + } + }, + "/v3/id-verification/wallets/eudi/": { + "post": { + "operationId": "start_eudi_wallet_verification", + "x-didit-sdk": { + "resource": "id_wallets", + "method": "start_eudi", + "auth": "api_key", + "server": true + }, + "description": "Start a EUDI Wallet sign-in (The user's own member state; the issuer differs per country; AUT, BEL, BGR, HRV, CYP, CZE, DNK, EST, FIN, FRA, DEU, GRC, HUN, IRL, ITA, LVA, LTU, LUX, MLT, NLD, POL, PRT, ROU, SVK, SVN, ESP, SWE, ISL, LIE, NOR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", + "summary": "Start EUDI Wallet verification", "parameters": [ { - "name": "vendor_data", - "in": "path", - "required": true, + "in": "header", + "name": "Idempotency-Key", "schema": { "type": "string" }, - "description": "Your unique identifier for the business (free-form string, NOT a UUID).", - "example": "company-123" + "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." } ], + "tags": [ + "Digital ID wallets" + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "description": "Partial-update body. Provide only the fields you want to change.", - "properties": { - "display_name": { - "type": "string", - "nullable": true, - "description": "Friendly display name shown in the console (takes precedence over `legal_name` for UI display)." - }, - "legal_name": { - "type": "string", - "nullable": true, - "description": "Official legal name from registry or manual entry." - }, - "registration_number": { - "type": "string", - "nullable": true, - "description": "Company registration or incorporation number." - }, - "country_code": { - "type": "string", - "nullable": true, - "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `US`)." - }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED" - ], - "description": "Business lifecycle status. Setting `BLOCKED` automatically adds the `vendor_data` to the system blocklist; flipping away from `BLOCKED` removes that blocklist entry." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Arbitrary JSON object you attach to the business. **Fully replaces** the existing metadata on update — merge client-side if you only want to add keys." - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiEudiWalletVerificationStartRequest" }, - "examples": { - "Update name": { - "summary": "Update display name", - "value": { - "display_name": "Acme Corp" - } - }, - "Flag for review": { - "summary": "Flip status", - "value": { - "status": "FLAGGED" - } + "example": { + "country": "AUT", + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" }, - "Add metadata": { - "summary": "Replace metadata JSON", - "value": { - "metadata": { - "industry": "fintech", - "risk_level": "low" - } - } + "return_url": "https://app.example.com/identity/complete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiEudiWalletVerificationStartRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiEudiWalletVerificationStartRequest" + } + } + }, + "required": true + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" }, - "Fix registry data": { - "summary": "Correct extracted fields", - "value": { - "legal_name": "Acme Trading International Ltd", - "registration_number": "12345678", - "country_code": "GB" - } + "example": { + "request_id": "00000000-0000-4000-8000-000000000001", + "wallet_verification": { + "wallet": { + "id": "eudi", + "name": "EUDI Wallet", + "issuing_authority": "The user's own member state; the issuer differs per country" + }, + "country": "AUT", + "status": "Not Finished", + "outcome": "pending", + "error": null, + "interaction": "redirect", + "simulated": false, + "expires_at": "2026-01-01T10:10:00Z", + "next_action": { + "type": "redirect", + "url": "https://wallet.example.com/authorize" + }, + "identity": null, + "attributes": null, + "level_of_assurance": null, + "verified_at": null, + "signature_valid": null, + "credential_type": "person_identification" + }, + "vendor_data": "your-customer-reference", + "metadata": { + "reference": "example" + }, + "created_at": "2026-01-01T10:00:00Z" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" } } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/businesses/company-123/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"display_name\": \"Acme Corp\", \"status\": \"FLAGGED\"}'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/businesses/company-123/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'display_name': 'Acme Corp', 'status': 'FLAGGED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])" + }, + "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/company-123/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ display_name: 'Acme Corp', status: 'FLAGGED' }),\n});\nconst biz = await resp.json();\nconsole.log(biz.status);" - } - ], - "responses": { - "200": { - "description": "Business updated. The full updated business record is returned.", + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessDetailItem" - }, - "examples": { - "Updated": { - "value": { - "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", - "vendor_data": "company-123", - "display_name": "Acme Corp", - "legal_name": "Acme Trading Ltd", - "registration_number": "12345678", - "country_code": "GB", - "effective_name": "Acme Corp", - "status": "FLAGGED", - "session_count": 2, - "approved_count": 1, - "declined_count": 0, - "in_review_count": 1, - "features": { - "KYB": "Approved" - }, - "features_list": [ - { - "feature": "KYB", - "status": "Approved" - } - ], - "last_session_at": "2026-03-15T10:30:00Z", - "last_activity_at": "2026-03-15T10:30:00Z", - "first_session_at": "2026-01-10T08:00:00Z", - "tags": [ - { - "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", - "name": "VIP", - "color": "#2567FF" - } - ], - "created_at": "2026-01-10T08:00:00Z", - "metadata": { - "industry": "fintech", - "risk_level": "low" - }, - "updated_at": "2026-03-15T10:30:00Z" - } - } + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" } } - } + }, + "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." }, - "400": { - "description": "Invalid body — typically an unknown `status` or malformed `metadata`.", + "409": { "content": { "application/json": { - "examples": { - "Bad status": { - "value": { - "status": [ - "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" - ] - } - }, - "Bad metadata": { - "value": { - "metadata": [ - "Metadata must be a JSON object." - ] - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." }, - "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot update businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", + "502": { "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } - } + }, + "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." }, - "404": { - "description": "No (non-deleted) business with the supplied `vendor_data` exists for this application.", + "503": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } + "schema": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" } } + }, + "description": "error wallet_not_configured: this environment cannot reach the wallet." + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "cURL", + "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/eudi/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"AUT\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'AUT',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/eudi/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + } + ], + "x-didit-wallet-id": "eudi" + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/teams/": { + "get": { + "summary": "List review teams", + "description": "Requires `list:cases` for the requested application.", + "operationId": "list_case_teams", + "x-didit-sdk": { + "resource": "cases.teams", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Assignment" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 } }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 1000000, + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "Success", "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." + "schema": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseReviewTeam" + } + }, + "count": { + "type": "integer", + "description": "Matching count, capped at 100. Follow next to continue beyond the cap." + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true } } } } } + }, + "400": { + "description": "Invalid input." + }, + "403": { + "description": "Insufficient permission." } }, "security": [ @@ -31006,206 +50903,230 @@ "ApiKeyAuth": [] } ] - } - }, - "/v3/businesses/{vendor_data}/update-status/": { - "patch": { - "summary": "Update business status", - "description": "Flip only the lifecycle `status` of a business — VendorBusinessStatusChoices (`ACTIVE`/`FLAGGED`/`BLOCKED`, NOT session statuses). `BLOCKED` adds `vendor_data` to the system blocklist.", - "operationId": "update_business_status", + }, + "post": { + "summary": "Create review team", + "description": "Requires `write:cases` for the requested application.", + "operationId": "create_case_teams", + "x-didit-sdk": { + "resource": "cases.teams", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Businesses" + "Case Assignment" ], "parameters": [ { - "name": "vendor_data", + "name": "organization_id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, - "description": "Your unique identifier for the business (free-form string, NOT a UUID).", - "example": "company-123" + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." } ], + "responses": { + "201": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseReviewTeam" + } + } + } + }, + "400": { + "description": "Invalid input." + }, + "403": { + "description": "Insufficient permission." + } + }, "requestBody": { "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "status" - ], - "properties": { - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED" - ], - "description": "New lifecycle status. `BLOCKED` also adds the `vendor_data` to the system blocklist." - } - } - }, - "examples": { - "Flag": { - "summary": "Flag the business for review", - "value": { - "status": "FLAGGED" - } - }, - "Block": { - "summary": "Block the business (adds to system blocklist)", - "value": { - "status": "BLOCKED" - } - }, - "Reactivate": { - "summary": "Reactivate the business", - "value": { - "status": "ACTIVE" - } - } + "$ref": "#/components/schemas/CaseReviewTeamCreate" } } } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/businesses/company-123/update-status/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"status\": \"BLOCKED\"}'" - }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/businesses/company-123/update-status/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'status': 'BLOCKED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])" - }, + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/fraud/checks/{check_id}/": { + "get": { + "operationId": "root_retrieve", + "x-didit-sdk": { + "resource": "fraud_checks", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "description": "Fetch a previously created check by its `check_id`.", + "summary": "Retrieve a Fraud Check", + "parameters": [ { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/company-123/update-status/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'BLOCKED' }),\n});\nconst biz = await resp.json();\nconsole.log(biz.status);" + "in": "path", + "name": "check_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true } ], "responses": { "200": { - "description": "Business status updated. Full business record returned.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessDetailItem" - }, - "examples": { - "Blocked": { - "value": { - "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", - "vendor_data": "company-123", - "display_name": null, - "legal_name": "Acme Trading Ltd", - "registration_number": "12345678", - "country_code": "GB", - "effective_name": "Acme Trading Ltd", - "status": "BLOCKED", - "session_count": 2, - "approved_count": 1, - "declined_count": 0, - "in_review_count": 1, - "features": { - "KYB": "Approved" - }, - "features_list": [ - { - "feature": "KYB", - "status": "Approved" - } - ], - "last_session_at": "2026-03-15T10:30:00Z", - "last_activity_at": "2026-03-15T10:30:00Z", - "first_session_at": "2026-01-10T08:00:00Z", - "tags": [ - { - "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", - "name": "VIP", - "color": "#2567FF" - } - ], - "created_at": "2026-01-10T08:00:00Z", - "metadata": { - "industry": "fintech" - }, - "updated_at": "2026-03-15T10:30:00Z" - } - } + "$ref": "#/components/schemas/FraudCheckResponse" } } - } + }, + "description": "" }, - "400": { - "description": "Missing or invalid `status`.", + "401": { "content": { "application/json": { - "examples": { - "Missing status": { - "value": { - "status": [ - "This field is required." - ] - } - }, - "Bad status": { - "value": { - "status": [ - "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" - ] - } - } + "schema": { + "$ref": "#/components/schemas/FraudClientCredentialsErrorMessage" } } - } + }, + "description": "" }, "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot update businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/FraudClientCredentialsForbiddenErrorMessage" } } + }, + "description": "" + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ], + "tags": [ + "Fraud Check" + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/": { + "get": { + "operationId": "organization_application_cases_requests_list", + "x-didit-sdk": { + "resource": "cases.requests", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" } }, - "404": { - "description": "No (non-deleted) business with the supplied `vendor_data` exists for this application.", - "content": { - "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } - } - } + { + "name": "limit", + "required": false, + "in": "query", + "description": "Number of results to return per page.", + "schema": { + "type": "integer" } }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + { + "name": "offset", + "required": false, + "in": "query", + "description": "The initial index from which to return the results.", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Case Management" + ], + "responses": { + "200": { "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/CustomerMessagingPaginatedCaseRequestList" } } - } + }, + "description": "" + }, + "404": { + "description": "The request or case is unavailable for this caller." + }, + "403": { + "description": "The caller lacks the required cases permission." } }, "security": [ @@ -31213,1408 +51134,989 @@ "ApiKeyAuth": [] } ] - } - }, - "/v3/businesses/delete/": { + }, "post": { - "summary": "Batch delete businesses", - "description": "**Permanently delete** businesses — this cannot be undone. For blocking prefer [Update Business Status](#patch-/v3/businesses/-vendor_data-/update-status/) with `BLOCKED`.", - "operationId": "batch_delete_businesses", - "tags": [ - "Businesses" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "Provide `vendor_data_list`, `didit_internal_id_list`, or `delete_all: true`. Precedence when several are sent: `delete_all` > `vendor_data_list` > `didit_internal_id_list` (only the highest-precedence selector is used).", - "properties": { - "vendor_data_list": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Your own identifiers to delete, matched exactly as sent." - }, - "didit_internal_id_list": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "Didit's internal UUIDs (`didit_internal_id`) to delete." - }, - "delete_all": { - "type": "boolean", - "default": false, - "description": "If `true`, deletes every business in the application. Cannot be combined with the list selectors." - } - } - }, - "examples": { - "Specific businesses": { - "summary": "Delete by vendor_data", - "value": { - "vendor_data_list": [ - "company-123", - "company-456" - ] - } - }, - "Specific internal IDs": { - "summary": "Delete by didit_internal_id", - "value": { - "didit_internal_id_list": [ - "a1b2c3d4-e5f6-7890-abcd-ef1234567890", - "b2c3d4e5-f6a7-8901-bcde-f12345678901" - ] - } - }, - "All businesses": { - "summary": "Wipe every business in this application", - "value": { - "delete_all": true - } - } - } - } - } + "operationId": "organization_application_cases_requests_create", + "x-didit-sdk": { + "resource": "cases.requests", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" }, - "x-codeSamples": [ + "parameters": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/businesses/delete/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"vendor_data_list\": [\"company-123\", \"company-456\"]}'" + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/businesses/delete/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'vendor_data_list': ['company-123', 'company-456']},\n)\nresp.raise_for_status()\nprint('deleted', resp.json()['deleted'])" + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/delete/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ vendor_data_list: ['company-123', 'company-456'] }),\n});\nconst body = await resp.json();\nconsole.log('deleted', body.deleted);" + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true } ], - "responses": { - "200": { - "description": "Businesses deleted. Returns the number of rows actually removed.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "deleted": { - "type": "integer", - "description": "Number of businesses deleted (excludes vendor_data values that didn't match anything)." - } - } - }, - "examples": { - "Deleted some": { - "value": { - "deleted": 2 - } - }, - "Nothing matched": { - "value": { - "deleted": 0 - } - } - } + "tags": [ + "Case Management" + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequestConfirmRequest" } - } - }, - "400": { - "description": "No selector supplied (all of `vendor_data_list`, `didit_internal_id_list`, and `delete_all` missing or empty). The error body is a JSON array.", - "content": { - "application/json": { - "examples": { - "Missing selector": { - "value": [ - "Provide vendor_data_list, didit_internal_id_list, or set delete_all=true." - ] - } - } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequestConfirmRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequestConfirmRequest" } } }, + "required": true + }, + "responses": { + "404": { + "description": "The request or case is unavailable for this caller." + }, "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot delete businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", - "content": { - "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } + "description": "The caller lacks the required cases permission." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + "400": { + "description": "Invalid inputs, unsupported purpose, unavailable verified contact or an existing immutable response." + }, + "409": { + "description": "The preview changed or expired. Preview again before confirming." + }, + "201": { "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequest" } } - } + }, + "description": "" } }, "security": [ { "ApiKeyAuth": [] } - ] + ], + "description": "Confirm the exact signed preview within 15 minutes. Only fixed templates and an OTP-verified contact belonging to the case subject are accepted. The request ID makes retries idempotent. Delivery is queued; SENT, SIMULATED and UNKNOWN are distinct outcomes. An actionable request sets the case to Awaiting user. A review update leaves its status unchanged." } }, - "/v3/users/aml-monitoring/": { + "/v3/fraud/checks/": { "post": { - "summary": "Enable or disable AML ongoing monitoring for users", - "description": "Turns AML ongoing monitoring on or off for the selected users, with the same semantics as the Business Console. Exactly one monitored AML screening is kept per user, so a profile that verified several times is monitored and billed once; disabling turns monitoring off across all of the profile's screenings. Users without any AML screening are screened first (billed as a regular AML screening) when they have the full name, date of birth and country needed; otherwise they are reported as `skipped_no_identity_data`.\n\nSet `estimate_only: true` to preview counts and costs without writing anything. Selections of 20 profiles or fewer resolve inline and return `200` with the completed job; larger selections return `202` and run in the background - poll [Get AML monitoring job](#get-/v3/aml-monitoring/jobs/-job_id-/) with the returned `job_id`. Enabling is refused with `403` when the organization balance does not cover the estimate. Identifiers that match nothing are listed in `unmatched` and never fail the request: a selection that matches nothing completes as an empty job (`total` 0), the same convention as the batch delete endpoints.", - "operationId": "toggle_users_aml_monitoring", - "tags": [ - "AML Ongoing Monitoring" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], + "operationId": "create_fraud_check", + "x-didit-sdk": { + "resource": "fraud_checks", + "method": "create", + "auth": "api_key", + "server": true + }, + "description": "Submit one or more typed identifiers and receive normalization, fresh enrichment, authoritative validation where the country and identifier type are covered, independent risk dimensions and a recommendation in one response.\n\nThese profiles support identifier checks. Selfie-based identity binding (`biometric_identity`) requires profile version `2026-09-14.2` and a Brazilian CPF; it runs only with the user's consent and when your organization is entitled to it. Earlier versions report it as `not_available_in_profile_version`. Modules that cannot run are reported as skipped with a deterministic reason; a source that could not answer is never reported as an identity mismatch.", + "summary": "Run a Fraud Check", "requestBody": { - "required": true, "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/FraudCheckRequestRequest" + } + }, "application/json": { "schema": { - "type": "object", - "required": [ - "is_enabled" - ], - "description": "Select users with `vendor_data_list`, `didit_internal_id_list`, or both (the two lists are OR'd). At most 1000 identifiers per request across both lists.", - "properties": { - "is_enabled": { - "type": "boolean", - "description": "true to enable monitoring, false to disable it." - }, - "estimate_only": { - "type": "boolean", - "default": false, - "description": "When true, returns the cost estimate and performs no writes - no screening, no billing, no job." - }, - "vendor_data_list": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Your own identifiers of the users to toggle, matched exactly as sent." - }, - "didit_internal_id_list": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "Didit internal ids of the users to toggle - the `didit_internal_id` returned by the create, get and list endpoints." - } - } - }, - "examples": { - "Estimate first": { - "summary": "Preview the cost without writing", - "value": { - "is_enabled": true, - "estimate_only": true, - "vendor_data_list": [ - "user-123", - "user-456" - ] - } - }, - "Enable": { - "summary": "Enable monitoring for two users", - "value": { - "is_enabled": true, - "vendor_data_list": [ - "user-123", - "user-456" - ] - } - }, - "Disable": { - "summary": "Disable monitoring by Didit internal id", - "value": { - "is_enabled": false, - "didit_internal_id_list": [ - "3f1c0d3e-8f0a-4c2e-9a3d-9b1f5f4a2c11" - ] - } - } + "$ref": "#/components/schemas/FraudCheckRequestRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/FraudCheckRequestRequest" } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/users/aml-monitoring/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"is_enabled\": true, \"vendor_data_list\": [\"user-123\", \"user-456\"]}'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/users/aml-monitoring/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'is_enabled': True, 'vendor_data_list': ['user-123', 'user-456']},\n)\nresp.raise_for_status()\njob = resp.json()\nprint(job['status'], job['outcomes'])\nfor row in job['results']:\n print(row['vendor_data'], row['outcome'])" }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/users/aml-monitoring/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ is_enabled: true, vendor_data_list: ['user-123', 'user-456'] }),\n});\nconst job = await resp.json();\nconsole.log(job.status, job.outcomes);\nfor (const row of job.results) console.log(row.vendor_data, row.outcome);" - } - ], + "required": true + }, "responses": { "200": { - "description": "The estimate (when `estimate_only` is true) or the completed job for a selection processed inline.", "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "required": [ - "users_total", - "already_enabled", - "with_existing_aml", - "needing_screening", - "skipped_no_identity_data", - "skipped_blocked", - "estimated_screening_cost_usd", - "estimated_annual_monitoring_cost_usd", - "balance_sufficient", - "unmatched" - ], - "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", - "properties": { - "users_total": { - "type": "integer", - "description": "Profiles matched by the selection." - }, - "already_enabled": { - "type": "integer", - "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." - }, - "with_existing_aml": { - "type": "integer", - "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." - }, - "needing_screening": { - "type": "integer", - "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Profiles that cannot be screened because identity data is missing." - }, - "skipped_blocked": { - "type": "integer", - "description": "Blocked users that will be skipped (always 0 for businesses)." - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2, - "description": "One-time cost of the new screenings." - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07, - "description": "Yearly monitoring cost of the profiles that will become monitored." - }, - "balance_sufficient": { - "type": "boolean", - "description": "Whether the organization balance covers the screening plus the first year of monitoring." - }, - "unmatched": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Identifiers of the request that matched no profile in this application, exactly as sent." - } - } - }, - { - "type": "object", - "required": [ - "job_id", - "status", - "is_enabled", - "target_type", - "total", - "processed", - "outcomes", - "results", - "error", - "created_at", - "completed_at", - "estimate", - "unmatched" - ], - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean", - "description": "true when the job enables monitoring, false when it disables it." - }, - "target_type": { - "type": "string", - "enum": [ - "user", - "business" - ], - "description": "Which profile family the job toggles." - }, - "total": { - "type": "integer", - "description": "Profiles the job processes." - }, - "processed": { - "type": "integer", - "description": "Profiles processed so far." - }, - "outcomes": { - "type": "object", - "description": "Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled on it." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active; nothing changed and nothing was billed." - }, - "disabled": { - "type": "integer", - "description": "Monitoring turned off across every AML screening of the profile." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application, no screening is created." - }, - "failed_provider": { - "type": "integer", - "description": "The screening could not be completed; retry later." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out while the job was running." - } - } - }, - "results": { - "type": "array", - "description": "One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API.", - "items": { - "type": "object", - "properties": { - "didit_internal_id": { - "type": "string", - "format": "uuid", - "description": "Didit internal id of the profile." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own identifier for the profile." - }, - "outcome": { - "type": "string", - "enum": [ - "enabled", - "screened_and_enabled", - "already_enabled", - "disabled", - "skipped_no_identity_data", - "skipped_blocked", - "skipped_sandbox", - "failed_provider", - "failed_insufficient_balance" - ] - } - } - } - }, - "error": { - "type": "string", - "nullable": true, - "description": "Generic failure message when status is FAILED; the detailed cause is kept on the platform side." - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", - "properties": { - "users_total": { - "type": "integer", - "description": "Profiles matched by the selection." - }, - "already_enabled": { - "type": "integer", - "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." - }, - "with_existing_aml": { - "type": "integer", - "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." - }, - "needing_screening": { - "type": "integer", - "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Profiles that cannot be screened because identity data is missing." - }, - "skipped_blocked": { - "type": "integer", - "description": "Blocked users that will be skipped (always 0 for businesses)." - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2, - "description": "One-time cost of the new screenings." - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07, - "description": "Yearly monitoring cost of the profiles that will become monitored." - }, - "balance_sufficient": { - "type": "boolean", - "description": "Whether the organization balance covers the screening plus the first year of monitoring." - } - } - }, - "unmatched": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job." - } - } - } - ] + "$ref": "#/components/schemas/FraudCheckResponse" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FraudClientCredentialsErrorMessage" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FraudClientCredentialsForbiddenErrorMessage" } } - } + }, + "description": "" }, - "202": { - "description": "Larger selections run in the background. Poll the job endpoint with `job_id`; `results` fills in as profiles are processed.", + "409": { + "description": "Conflict, returned without a charge: the `client_reference` was already used with a different request body; a check with the same `client_reference` is still running (retry after `Retry-After` seconds with the same reference); or the verification this check records into was deleted before the check completed.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean", - "description": "true when the job enables monitoring, false when it disables it." - }, - "target_type": { - "type": "string", - "enum": [ - "user", - "business" - ], - "description": "Which profile family the job toggles." - }, - "total": { - "type": "integer", - "description": "Profiles the job processes." - }, - "processed": { - "type": "integer", - "description": "Profiles processed so far." - }, - "outcomes": { - "type": "object", - "description": "Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled on it." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active; nothing changed and nothing was billed." - }, - "disabled": { - "type": "integer", - "description": "Monitoring turned off across every AML screening of the profile." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application, no screening is created." - }, - "failed_provider": { - "type": "integer", - "description": "The screening could not be completed; retry later." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out while the job was running." - } - } - }, - "results": { - "type": "array", - "description": "One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API.", - "items": { - "type": "object", - "properties": { - "didit_internal_id": { - "type": "string", - "format": "uuid", - "description": "Didit internal id of the profile." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own identifier for the profile." - }, - "outcome": { - "type": "string", - "enum": [ - "enabled", - "screened_and_enabled", - "already_enabled", - "disabled", - "skipped_no_identity_data", - "skipped_blocked", - "skipped_sandbox", - "failed_provider", - "failed_insufficient_balance" - ] - } - } - } - }, "error": { - "type": "string", - "nullable": true, - "description": "Generic failure message when status is FAILED; the detailed cause is kept on the platform side." - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", - "properties": { - "users_total": { - "type": "integer", - "description": "Profiles matched by the selection." - }, - "already_enabled": { - "type": "integer", - "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." - }, - "with_existing_aml": { - "type": "integer", - "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." - }, - "needing_screening": { - "type": "integer", - "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Profiles that cannot be screened because identity data is missing." - }, - "skipped_blocked": { - "type": "integer", - "description": "Blocked users that will be skipped (always 0 for businesses)." - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2, - "description": "One-time cost of the new screenings." - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07, - "description": "Yearly monitoring cost of the profiles that will become monitored." - }, - "balance_sufficient": { - "type": "boolean", - "description": "Whether the organization balance covers the screening plus the first year of monitoring." - } - } - }, - "unmatched": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job." + "type": "string" + } + }, + "required": [ + "error" + ] + }, + "examples": { + "different_body": { + "summary": "Reference reused with a different body", + "value": { + "error": "client_reference has already been used with a different request body." + } + }, + "still_running": { + "summary": "A check with this reference is still running", + "value": { + "error": "A check with this client_reference is already running. Retry with the same reference later." + } + }, + "verification_deleted": { + "summary": "The verification was deleted before the check completed", + "value": { + "error": "This verification was deleted before the check completed." } } } } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying, sent when a check with the same `client_reference` is still running.", + "schema": { + "type": "integer" + } + } } + } + }, + "security": [ + { + "ApiKeyAuth": [] }, - "400": { - "description": "Validation error: `is_enabled` missing, no selector, or more than 1000 identifiers across both lists." + { + "BearerAuth": [] + } + ], + "tags": [ + "Fraud Check" + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/cancel/": { + "post": { + "operationId": "organization_application_cases_requests_cancel_create", + "x-didit-sdk": { + "resource": "cases.requests", + "method": "cancel", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, - "401": { - "description": "Missing or invalid API key." + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "request_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Management" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequest" + } + } + }, + "description": "" + }, + "404": { + "description": "The request or case is unavailable for this caller." }, "403": { - "description": "Insufficient balance to enable monitoring for this selection. The response carries the `estimate` so you can top up the right amount.", + "description": "The caller lacks the required cases permission." + }, + "400": { + "description": "Invalid inputs, unsupported purpose, unavailable verified contact or an existing immutable response." + }, + "409": { + "description": "The preview changed or expired. Preview again before confirming." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/options/": { + "get": { + "operationId": "organization_application_cases_requests_options_retrieve", + "x-didit-sdk": { + "resource": "cases.requests", + "method": "retrieve_options", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Management" + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "error": { - "type": "string" - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", - "properties": { - "users_total": { - "type": "integer", - "description": "Profiles matched by the selection." - }, - "already_enabled": { - "type": "integer", - "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." - }, - "with_existing_aml": { - "type": "integer", - "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." - }, - "needing_screening": { - "type": "integer", - "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Profiles that cannot be screened because identity data is missing." - }, - "skipped_blocked": { - "type": "integer", - "description": "Blocked users that will be skipped (always 0 for businesses)." - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2, - "description": "One-time cost of the new screenings." - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07, - "description": "Yearly monitoring cost of the profiles that will become monitored." - }, - "balance_sufficient": { - "type": "boolean", - "description": "Whether the organization balance covers the screening plus the first year of monitoring." - } - } - } - } + "$ref": "#/components/schemas/CustomerMessagingCaseRequestOptions" + } + } + }, + "description": "" + }, + "404": { + "description": "The request or case is unavailable for this caller." + }, + "403": { + "description": "The caller lacks the required cases permission." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/attachment/": { + "get": { + "operationId": "organization_application_cases_requests_attachment_retrieve", + "x-didit-sdk": { + "resource": "cases.requests", + "method": "retrieve_attachment", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "request_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Management" + ], + "responses": { + "200": { + "content": { + "image/jpeg": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "404": { + "description": "The request or case is unavailable for this caller." + }, + "403": { + "description": "The caller lacks the required cases permission." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/history/": { + "get": { + "operationId": "list_case_routing_rule_history", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "list_history", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "record_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "required": false, + "in": "query", + "description": "Number of results to return per page.", + "schema": { + "type": "integer" + } + }, + { + "name": "offset", + "required": false, + "in": "query", + "description": "The initial index from which to return the results.", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Case Assignment" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPaginatedPolicyRevisionList" } } - } + }, + "description": "" + }, + "403": { + "description": "Case access is required." } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/businesses/aml-monitoring/": { + "/v3/session/{sessionId}/resubmission-preview/": { "post": { - "summary": "Enable or disable AML ongoing monitoring for businesses", - "description": "Turns AML ongoing monitoring on or off for the selected businesses, with the same semantics as the Business Console. Exactly one monitored AML screening is kept per business, so a profile that verified several times is monitored and billed once; disabling turns monitoring off across all of the profile's screenings. Businesses without any AML screening are screened first (billed as a regular AML screening) when they have the legal or display name needed; otherwise they are reported as `skipped_no_identity_data`.\n\nSet `estimate_only: true` to preview counts and costs without writing anything. Selections of 20 profiles or fewer resolve inline and return `200` with the completed job; larger selections return `202` and run in the background - poll [Get AML monitoring job](#get-/v3/aml-monitoring/jobs/-job_id-/) with the returned `job_id`. Enabling is refused with `403` when the organization balance does not cover the estimate. Identifiers that match nothing are listed in `unmatched` and never fail the request: a selection that matches nothing completes as an empty job (`total` 0), the same convention as the batch delete endpoints.", - "operationId": "toggle_businesses_aml_monitoring", + "summary": "Preview a resubmission email", + "description": "Render the email that would accompany a KYC or KYB resubmission without changing the session, resetting checks, extending its expiry, running checks or sending mail. Requires the `write:sessions` privilege (a scoped API key needs write access to decisions) and a session in `Approved`, `Declined`, `In Review`, `Kyc Expired` or `Abandoned` status. The server validates the selected workflow steps and renders its fixed translated template with the applicable verified white-label sender and branding. The request cannot set a subject, message body or action URL.\n\n`email_address` is optional. Without it, the email goes to the person's own address: the one they verified in an email verification step of the flow, else the one given in `contact_details` when the session was created. An address you pass wins (`recipient_source: request`). When no address is known and you pass none, `recipient` and `preview_token` are empty strings and no email can be sent.\n\nThe response includes the recipient and where it comes from, the sender, subject, HTML, language, resolved steps, whether confirming sends the email (`will_be_sent`, `not_sent_reason`) and a signed `preview_token`. `requires_user_action` is `false` when every selected step runs without the person: the resubmission then finishes on its own and no email goes out (`not_sent_reason: no_user_action`).\n\nThis preview does not reserve session state or authorize delivery. After reviewing it, call [Update Session Status](/sessions-api/update-status) with `new_status: Resubmitted`, the same `nodes_to_resubmit` and `email_language` you previewed, `send_email: true`, `email_address` set to `recipient` and `email_preview_token` set to `preview_token`. The receipt expires after 15 minutes. When a receipt is supplied, changed inputs, message content, sender or session state return `409` before any mutation or send. Existing integrations may omit the receipt. Regenerate the preview after a conflict. Treat the response as sensitive verification content; responses use `Cache-Control: no-store`. Render HTML in a sandboxed frame, never directly in your application DOM.", + "operationId": "post_v3_session_resubmission_preview", + "x-didit-sdk": { + "resource": "sessions", + "method": "preview_resubmission", + "auth": "api_key", + "server": true + }, "tags": [ - "AML Ongoing Monitoring" + "Sessions" ], "security": [ { "ApiKeyAuth": [] } ], + "parameters": [ + { + "in": "path", + "name": "sessionId", + "required": true, + "description": "UUID of the verification session to update. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": [ - "is_enabled" - ], - "description": "Select businesses with `vendor_data_list`, `didit_internal_id_list`, or both (the two lists are OR'd). At most 1000 identifiers per request across both lists.", "properties": { - "is_enabled": { - "type": "boolean", - "description": "true to enable monitoring, false to disable it." - }, - "estimate_only": { - "type": "boolean", - "default": false, - "description": "When true, returns the cost estimate and performs no writes - no screening, no billing, no job." - }, - "vendor_data_list": { + "nodes_to_resubmit": { "type": "array", + "description": "Steps to preview, using the same validation, selection and workflow ordering as a resubmission. Omit to auto-select recorded attempts needing resubmission.", "items": { - "type": "string" - }, - "description": "Your own identifiers of the businesses to toggle, matched exactly as sent." + "type": "object", + "required": [ + "node_id", + "feature" + ], + "properties": { + "node_id": { + "type": "string", + "description": "Node identifier as it appears in the session's workflow definition (for example `feature_ocr`, `feature_liveness`).", + "example": "feature_ocr" + }, + "feature": { + "type": "string", + "description": "Feature type of the node. The aliases `ID_VERIFICATION`, `POA`, `PHONE`, and `EMAIL` are normalized server-side to `OCR`, `PROOF_OF_ADDRESS`, `PHONE_VERIFICATION`, and `EMAIL_VERIFICATION`. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and `KYB` pass schema validation but are always rejected with the business-rule `400` shown in the examples.", + "enum": [ + "OCR", + "OCR_BACK", + "NFC", + "AML", + "FACE", + "LIVENESS", + "FACE_MATCH", + "IP_ANALYSIS", + "AGE_ESTIMATION", + "PROOF_OF_ADDRESS", + "PHONE_VERIFICATION", + "EMAIL_VERIFICATION", + "FACE_SEARCH", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_DOCUMENTS", + "ID_VERIFICATION", + "POA", + "PHONE", + "EMAIL", + "KYB_REGISTRY", + "KYB_KEY_PEOPLE", + "KYB" + ], + "example": "OCR" + } + } + } }, - "didit_internal_id_list": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "Didit internal ids of the businesses to toggle - the `didit_internal_id` returned by the create, get and list endpoints." + "email_address": { + "type": "string", + "format": "email", + "description": "Optional. Send the email to this address instead of the person's own. Without it, the recipient is the address the person verified in the flow, else the one given in `contact_details`. No email is sent by this call.", + "example": "user@example.com" + }, + "email_language": { + "type": "string", + "description": "Language for the notification email. Accepts any string at schema level; unsupported codes silently fall back to English (`en`).", + "default": "en", + "example": "en" } } }, "examples": { - "Estimate first": { - "summary": "Preview the cost without writing", - "value": { - "is_enabled": true, - "estimate_only": true, - "vendor_data_list": [ - "company-123", - "company-456" - ] - } - }, - "Enable": { - "summary": "Enable monitoring for two businesses", + "Person's Own Address": { + "summary": "Preview a proof-of-address resubmission for the person's own address", "value": { - "is_enabled": true, - "vendor_data_list": [ - "company-123", - "company-456" - ] + "nodes_to_resubmit": [ + { + "node_id": "feature_poa_1", + "feature": "POA" + } + ], + "email_language": "en" } }, - "Disable": { - "summary": "Disable monitoring by Didit internal id", + "Named Recipient": { + "summary": "Preview the email for an address you name", "value": { - "is_enabled": false, - "didit_internal_id_list": [ - "3f1c0d3e-8f0a-4c2e-9a3d-9b1f5f4a2c11" - ] + "nodes_to_resubmit": [ + { + "node_id": "feature_ocr", + "feature": "OCR" + } + ], + "email_address": "person@example.com", + "email_language": "en" } } } } } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/businesses/aml-monitoring/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"is_enabled\": true, \"vendor_data_list\": [\"company-123\", \"company-456\"]}'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/businesses/aml-monitoring/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'is_enabled': True, 'vendor_data_list': ['company-123', 'company-456']},\n)\nresp.raise_for_status()\njob = resp.json()\nprint(job['status'], job['outcomes'])\nfor row in job['results']:\n print(row['vendor_data'], row['outcome'])" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/aml-monitoring/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ is_enabled: true, vendor_data_list: ['company-123', 'company-456'] }),\n});\nconst job = await resp.json();\nconsole.log(job.status, job.outcomes);\nfor (const row of job.results) console.log(row.vendor_data, row.outcome);" - } - ], "responses": { "200": { - "description": "The estimate (when `estimate_only` is true) or the completed job for a selection processed inline.", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "required": [ - "users_total", - "already_enabled", - "with_existing_aml", - "needing_screening", - "skipped_no_identity_data", - "skipped_blocked", - "estimated_screening_cost_usd", - "estimated_annual_monitoring_cost_usd", - "balance_sufficient", - "unmatched" - ], - "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", - "properties": { - "users_total": { - "type": "integer", - "description": "Profiles matched by the selection." - }, - "already_enabled": { - "type": "integer", - "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." - }, - "with_existing_aml": { - "type": "integer", - "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." - }, - "needing_screening": { - "type": "integer", - "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Profiles that cannot be screened because identity data is missing." - }, - "skipped_blocked": { - "type": "integer", - "description": "Blocked users that will be skipped (always 0 for businesses)." - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2, - "description": "One-time cost of the new screenings." - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07, - "description": "Yearly monitoring cost of the profiles that will become monitored." - }, - "balance_sufficient": { - "type": "boolean", - "description": "Whether the organization balance covers the screening plus the first year of monitoring." - }, - "unmatched": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Identifiers of the request that matched no profile in this application, exactly as sent." - } - } - }, - { - "type": "object", - "required": [ - "job_id", - "status", - "is_enabled", - "target_type", - "total", - "processed", - "outcomes", - "results", - "error", - "created_at", - "completed_at", - "estimate", - "unmatched" - ], - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean", - "description": "true when the job enables monitoring, false when it disables it." - }, - "target_type": { - "type": "string", - "enum": [ - "user", - "business" - ], - "description": "Which profile family the job toggles." - }, - "total": { - "type": "integer", - "description": "Profiles the job processes." - }, - "processed": { - "type": "integer", - "description": "Profiles processed so far." - }, - "outcomes": { - "type": "object", - "description": "Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled on it." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active; nothing changed and nothing was billed." - }, - "disabled": { - "type": "integer", - "description": "Monitoring turned off across every AML screening of the profile." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application, no screening is created." - }, - "failed_provider": { - "type": "integer", - "description": "The screening could not be completed; retry later." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out while the job was running." - } - } - }, - "results": { - "type": "array", - "description": "One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API.", - "items": { - "type": "object", - "properties": { - "didit_internal_id": { - "type": "string", - "format": "uuid", - "description": "Didit internal id of the profile." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own identifier for the profile." - }, - "outcome": { - "type": "string", - "enum": [ - "enabled", - "screened_and_enabled", - "already_enabled", - "disabled", - "skipped_no_identity_data", - "skipped_blocked", - "skipped_sandbox", - "failed_provider", - "failed_insufficient_balance" - ] - } - } - } - }, - "error": { - "type": "string", - "nullable": true, - "description": "Generic failure message when status is FAILED; the detailed cause is kept on the platform side." - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", - "properties": { - "users_total": { - "type": "integer", - "description": "Profiles matched by the selection." - }, - "already_enabled": { - "type": "integer", - "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." - }, - "with_existing_aml": { - "type": "integer", - "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." - }, - "needing_screening": { - "type": "integer", - "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Profiles that cannot be screened because identity data is missing." - }, - "skipped_blocked": { - "type": "integer", - "description": "Blocked users that will be skipped (always 0 for businesses)." - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2, - "description": "One-time cost of the new screenings." - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07, - "description": "Yearly monitoring cost of the profiles that will become monitored." - }, - "balance_sufficient": { - "type": "boolean", - "description": "Whether the organization balance covers the screening plus the first year of monitoring." - } - } - }, - "unmatched": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job." - } - } - } - ] - } - } - } - }, - "202": { - "description": "Larger selections run in the background. Poll the job endpoint with `job_id`; `results` fills in as profiles are processed.", + "description": "Preview only. The session is unchanged and no email is sent.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "recipient", + "recipient_source", + "sender", + "sender_name", + "sender_email", + "sender_kind", + "subject", + "html", + "language", + "will_be_sent", + "not_sent_reason", + "preview_token", + "nodes_to_resubmit", + "requires_user_action" + ], "properties": { - "job_id": { + "recipient": { "type": "string", - "format": "uuid" + "description": "The address the email goes to. An empty string when no address is known for the person: then no email can be sent and `preview_token` is empty too. Pass it as `email_address` when you confirm.", + "example": "person@example.com" }, - "status": { + "recipient_source": { "type": "string", "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] + "email_verification", + "contact_details", + "request", + "" + ], + "description": "Where `recipient` comes from. `request`: the `email_address` you passed. `email_verification`: the address the person verified in an email verification step of the flow. `contact_details`: the address given in `contact_details` when the session was created. An empty string when there is no address.", + "example": "email_verification" }, - "is_enabled": { - "type": "boolean", - "description": "true when the job enables monitoring, false when it disables it." + "sender": { + "type": "string", + "description": "The `From` header the email goes out with.", + "example": "Example App " }, - "target_type": { + "sender_name": { + "type": "string", + "description": "The display name part of `sender`.", + "example": "Example App" + }, + "sender_email": { + "type": "string", + "format": "email", + "description": "The address part of `sender`.", + "example": "no-reply@mail.example.com" + }, + "sender_kind": { "type": "string", "enum": [ - "user", - "business" + "customer", + "didit" ], - "description": "Which profile family the job toggles." + "description": "`customer`: the email goes out from your application's verified sender domain (white label). `didit`: it goes out from Didit's own sender.", + "example": "customer" }, - "total": { - "type": "integer", - "description": "Profiles the job processes." + "subject": { + "type": "string", + "description": "The subject line exactly as it is sent.", + "example": "Action Required: Complete your verification in Example App" }, - "processed": { - "type": "integer", - "description": "Profiles processed so far." + "html": { + "type": "string", + "description": "The email's HTML exactly as it is sent, with its logo embedded as a data URL. Render it in a sandboxed frame, never directly in your application DOM.", + "example": "..." }, - "outcomes": { - "type": "object", - "description": "Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled on it." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active; nothing changed and nothing was billed." - }, - "disabled": { - "type": "integer", - "description": "Monitoring turned off across every AML screening of the profile." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application, no screening is created." - }, - "failed_provider": { - "type": "integer", - "description": "The screening could not be completed; retry later." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out while the job was running." - } - } + "language": { + "type": "string", + "description": "The language the email is written in. English (`en`) when the `email_language` you asked for is not offered.", + "example": "en" }, - "results": { + "will_be_sent": { + "type": "boolean", + "description": "Whether confirming the change with `send_email: true` sends this email. `false` when `not_sent_reason` is set.", + "example": true + }, + "not_sent_reason": { + "type": "string", + "nullable": true, + "enum": [ + "no_user_action", + "no_recipient", + "sandbox", + null + ], + "description": "Why confirming would send no email, or `null` when it will be sent. `no_user_action`: every selected step runs without the person, so the resubmission finishes on its own and no email goes out. `no_recipient`: no address is known for the person and you passed none. `sandbox`: a sandbox session whose recipient has not opted into sandbox email delivery, so the send is only simulated.", + "example": null + }, + "nodes_to_resubmit": { "type": "array", - "description": "One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API.", "items": { "type": "object", + "required": [ + "node_id", + "feature" + ], "properties": { - "didit_internal_id": { - "type": "string", - "format": "uuid", - "description": "Didit internal id of the profile." - }, - "vendor_data": { + "node_id": { "type": "string", - "nullable": true, - "description": "Your own identifier for the profile." + "description": "Node identifier as it appears in the session's workflow definition (for example `feature_ocr`, `feature_liveness`).", + "example": "feature_ocr" }, - "outcome": { + "feature": { "type": "string", + "description": "Feature type of the node. The aliases `ID_VERIFICATION`, `POA`, `PHONE`, and `EMAIL` are normalized server-side to `OCR`, `PROOF_OF_ADDRESS`, `PHONE_VERIFICATION`, and `EMAIL_VERIFICATION`. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and `KYB` pass schema validation but are always rejected with the business-rule `400` shown in the examples.", "enum": [ - "enabled", - "screened_and_enabled", - "already_enabled", - "disabled", - "skipped_no_identity_data", - "skipped_blocked", - "skipped_sandbox", - "failed_provider", - "failed_insufficient_balance" - ] + "OCR", + "OCR_BACK", + "NFC", + "AML", + "FACE", + "LIVENESS", + "FACE_MATCH", + "IP_ANALYSIS", + "AGE_ESTIMATION", + "PROOF_OF_ADDRESS", + "PHONE_VERIFICATION", + "EMAIL_VERIFICATION", + "FACE_SEARCH", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_DOCUMENTS", + "ID_VERIFICATION", + "POA", + "PHONE", + "EMAIL", + "KYB_REGISTRY", + "KYB_KEY_PEOPLE", + "KYB" + ], + "example": "OCR" } } } }, - "error": { - "type": "string", - "nullable": true, - "description": "Generic failure message when status is FAILED; the detailed cause is kept on the platform side." - }, - "created_at": { - "type": "string", - "format": "date-time" + "requires_user_action": { + "type": "boolean", + "description": "Whether at least one selected check requires user interaction. This does not predict the final workflow outcome." }, - "completed_at": { + "preview_token": { "type": "string", - "format": "date-time", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", - "properties": { - "users_total": { - "type": "integer", - "description": "Profiles matched by the selection." - }, - "already_enabled": { - "type": "integer", - "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." - }, - "with_existing_aml": { - "type": "integer", - "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." - }, - "needing_screening": { - "type": "integer", - "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Profiles that cannot be screened because identity data is missing." - }, - "skipped_blocked": { - "type": "integer", - "description": "Blocked users that will be skipped (always 0 for businesses)." - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2, - "description": "One-time cost of the new screenings." - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07, - "description": "Yearly monitoring cost of the profiles that will become monitored." - }, - "balance_sufficient": { - "type": "boolean", - "description": "Whether the organization balance covers the screening plus the first year of monitoring." + "description": "Signed receipt for this exact email, valid for 15 minutes. Pass it as `email_preview_token` to [Update Session Status](/sessions-api/update-status). An empty string when there is no recipient.", + "example": "eyJkaWdlc3QiOiI0ZjJhIn0:1uExAm:Pl3x4mpleS1gnatureValue000000000000000" + } + } + }, + "examples": { + "Will Be Sent": { + "summary": "Default recipient: the address verified in the flow", + "value": { + "recipient": "person@example.com", + "recipient_source": "email_verification", + "sender": "Example App ", + "sender_name": "Example App", + "sender_email": "no-reply@mail.example.com", + "sender_kind": "customer", + "subject": "Action Required: Complete your verification in Example App", + "html": "...", + "language": "en", + "will_be_sent": true, + "not_sent_reason": null, + "nodes_to_resubmit": [ + { + "node_id": "feature_poa_1", + "feature": "PROOF_OF_ADDRESS" } - } - }, - "unmatched": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job." + ], + "requires_user_action": true, + "preview_token": "eyJkaWdlc3QiOiI0ZjJhIn0:1uExAm:Pl3x4mpleS1gnatureValue000000000000000" + } + }, + "No User Action": { + "summary": "Only backend steps are selected, so no email goes out", + "value": { + "recipient": "person@example.com", + "recipient_source": "contact_details", + "sender": "Example App ", + "sender_name": "Example App", + "sender_email": "no-reply@mail.example.com", + "sender_kind": "customer", + "subject": "Action Required: Complete your verification in Example App", + "html": "...", + "language": "en", + "will_be_sent": false, + "not_sent_reason": "no_user_action", + "nodes_to_resubmit": [ + { + "node_id": "feature_aml", + "feature": "AML" + } + ], + "requires_user_action": false, + "preview_token": "eyJkaWdlc3QiOiI0ZjJhIn0:1uExAm:Pl3x4mpleS1gnatureValue000000000000000" } } } } + }, + "headers": { + "Cache-Control": { + "description": "Always `no-store`: the preview carries verification content and must not be cached.", + "schema": { + "type": "string", + "example": "no-store" + } + } } }, "400": { - "description": "Validation error: `is_enabled` missing, no selector, or more than 1000 identifiers across both lists." + "description": "The session is not in a status this endpoint accepts, it is already `Resubmitted`, the selected steps cannot be resubmitted, or a field is invalid.", + "content": { + "application/json": { + "examples": { + "Wrong Current Status": { + "summary": "Session is not in a status this endpoint accepts", + "value": { + "detail": "Only sessions with status Approved, Declined, In Review, Kyc Expired, Abandoned, Resubmitted can be updated. Current status: In Progress" + } + }, + "Already Resubmitted": { + "summary": "The session is already Resubmitted", + "value": [ + "The new status is the same as the current status." + ] + }, + "Nothing To Resubmit": { + "summary": "Auto-select found no step that needs resubmission", + "value": { + "nodes_to_resubmit": "No features found that need resubmission. All features are already approved." + } + }, + "Invalid Email": { + "summary": "email_address is not an email address", + "value": { + "email_address": [ + "Enter a valid email address." + ] + } + } + } + } + } }, "401": { - "description": "Missing or invalid API key." + "description": "The scoped API key has expired.", + "content": { + "application/json": { + "examples": { + "Expired API Key": { + "summary": "Scoped API key past its expiry date", + "value": { + "detail": "This API key has expired." + } + } + } + } + } }, "403": { - "description": "Insufficient balance to enable monitoring for this selection. The response carries the `estimate` so you can top up the right amount.", + "description": "Missing or invalid credentials, a caller without the `write:sessions` privilege, a scoped API key without the access this endpoint needs, or a `session_id` that belongs to another application.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "error": { - "type": "string" - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", - "properties": { - "users_total": { - "type": "integer", - "description": "Profiles matched by the selection." - }, - "already_enabled": { - "type": "integer", - "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." - }, - "with_existing_aml": { - "type": "integer", - "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." - }, - "needing_screening": { - "type": "integer", - "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Profiles that cannot be screened because identity data is missing." - }, - "skipped_blocked": { - "type": "integer", - "description": "Blocked users that will be skipped (always 0 for businesses)." - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2, - "description": "One-time cost of the new screenings." - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07, - "description": "Yearly monitoring cost of the profiles that will become monitored." - }, - "balance_sufficient": { - "type": "boolean", - "description": "Whether the organization balance covers the screening plus the first year of monitoring." - } - } + "examples": { + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Missing Privilege": { + "summary": "Caller lacks write:sessions", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped Key Without Access": { + "summary": "Scoped API key without this access", + "value": { + "detail": "This API key has no write access to decisions." + } + } + } + } + } + }, + "404": { + "description": "No KYC or KYB session with this `session_id` exists.", + "content": { + "application/json": { + "examples": { + "Not Found": { + "summary": "Unknown session_id", + "value": { + "detail": "Not found." + } + } + } + } + } + }, + "429": { + "description": "The steps to resubmit are all backend-only (AML, DATABASE_VALIDATION, IP_ANALYSIS), and the session was already resubmitted for backend-only steps within the last 30 seconds, so Update Session Status would refuse the resubmission. Retry after the interval in `Retry-After`.", + "content": { + "application/json": { + "examples": { + "Backend-only resubmit throttled": { + "value": { + "detail": "This session was already resubmitted for backend-only features within the last 30 seconds; repeating it cannot change the outcome. Wait before resubmitting again." } } } @@ -32624,455 +52126,434 @@ } } }, - "/v3/aml-monitoring/jobs/{job_id}/": { + "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/": { "get": { - "summary": "Get AML ongoing monitoring job", - "description": "Status, outcome counters and per-record results of a monitoring toggle job started from `POST /v3/users/aml-monitoring/` or `POST /v3/businesses/aml-monitoring/`. Poll every couple of seconds while `status` is `PENDING` or `PROCESSING`; `results` is complete once it is `COMPLETED`.", - "operationId": "get_aml_monitoring_job", + "summary": "Retrieve routing rule", + "description": "Requires `read:cases` for the requested application.", + "operationId": "retrieve_case_routing_rules", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "retrieve", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "AML Ongoing Monitoring" + "Case Assignment" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "record_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseRoutingRule" + } + } + } + }, + "400": { + "description": "Invalid input." + }, + "403": { + "description": "Insufficient permission." + }, + "404": { + "description": "Record not found in this application." + } + }, "security": [ { "ApiKeyAuth": [] } + ] + }, + "patch": { + "summary": "Update routing rule", + "description": "Requires `write:cases` for the requested application.", + "operationId": "update_case_routing_rules", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "update", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Assignment" ], "parameters": [ { - "name": "job_id", + "name": "organization_id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, - "description": "The `job_id` returned by the toggle endpoint." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl 'https://verification.didit.me/v3/aml-monitoring/jobs/JOB_ID/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "description": "Organization UUID." }, { - "lang": "python", - "label": "Python", - "source": "import time\nimport requests\n\nurl = 'https://verification.didit.me/v3/aml-monitoring/jobs/JOB_ID/'\nwhile True:\n job = requests.get(url, headers={'x-api-key': 'YOUR_API_KEY'}).json()\n if job['status'] in ('COMPLETED', 'FAILED'):\n break\n time.sleep(2)\nprint(job['outcomes'])" + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const url = 'https://verification.didit.me/v3/aml-monitoring/jobs/JOB_ID/';\nlet job;\ndo {\n await new Promise((r) => setTimeout(r, 2000));\n job = await (await fetch(url, { headers: { 'x-api-key': process.env.DIDIT_API_KEY } })).json();\n} while (job.status === 'PENDING' || job.status === 'PROCESSING');\nconsole.log(job.outcomes);" + "name": "record_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } } ], "responses": { "200": { - "description": "The job.", + "description": "Success", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean", - "description": "true when the job enables monitoring, false when it disables it." - }, - "target_type": { - "type": "string", - "enum": [ - "user", - "business" - ], - "description": "Which profile family the job toggles." - }, - "total": { - "type": "integer", - "description": "Profiles the job processes." - }, - "processed": { - "type": "integer", - "description": "Profiles processed so far." - }, - "outcomes": { - "type": "object", - "description": "Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled on it." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active; nothing changed and nothing was billed." - }, - "disabled": { - "type": "integer", - "description": "Monitoring turned off across every AML screening of the profile." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application, no screening is created." - }, - "failed_provider": { - "type": "integer", - "description": "The screening could not be completed; retry later." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out while the job was running." - } - } - }, - "results": { - "type": "array", - "description": "One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API; a job started from the Business Console reports counters only.", - "items": { - "type": "object", - "properties": { - "didit_internal_id": { - "type": "string", - "format": "uuid", - "description": "Didit internal id of the profile." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own identifier for the profile." - }, - "outcome": { - "type": "string", - "enum": [ - "enabled", - "screened_and_enabled", - "already_enabled", - "disabled", - "skipped_no_identity_data", - "skipped_blocked", - "skipped_sandbox", - "failed_provider", - "failed_insufficient_balance" - ] - } - } - } - }, - "error": { - "type": "string", - "nullable": true, - "description": "Generic failure message when status is FAILED; the detailed cause is kept on the platform side." - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } + "$ref": "#/components/schemas/CaseRoutingRule" } } } }, - "401": { - "description": "Missing or invalid API key." + "400": { + "description": "Invalid input." + }, + "403": { + "description": "Insufficient permission." }, "404": { - "description": "No such job for this application." + "description": "Record not found in this application." + }, + "409": { + "description": "The routing rule changed since expected_version. Review the latest rule before retrying." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseRoutingRuleUpdate" + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/case-requests/{request_uuid}/": { + "get": { + "operationId": "case_requests_retrieve", + "x-didit-sdk": { + "resource": "case_requests", + "method": "retrieve", + "auth": "device_token", + "server": false + }, + "parameters": [ + { + "in": "path", + "name": "request_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Management" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequestPublic" + } + } + }, + "description": "" + }, + "404": { + "description": "The request or case is unavailable for this caller." + } + }, + "security": [ + { + "CaseRequestToken": [] } - } - } - }, - "/v3/users/{vendor_data}/update-status/": { - "patch": { - "summary": "Update user status", - "description": "Flip only the lifecycle `status` of a user — `ACTIVE`/`FLAGGED`/`BLOCKED` (NOT session statuses). `BLOCKED` adds the `vendor_data` to the system blocklist; moving away from `BLOCKED` removes that entry. Each change is recorded as a `STATUS_CHANGED` activity on the user.", - "operationId": "update_user_status", - "tags": [ - "Users" ], + "description": "Access one customer request using its request-specific token. No private case findings, original identity data or recipient address are returned. Actionable requests expire after seven days. Cancellation or case resolution invalidates the link. Responses are immutable and remain evidence for reviewer assessment; they never approve or update a source verification." + }, + "post": { + "operationId": "case_requests_create", + "x-didit-sdk": { + "resource": "case_requests", + "method": "respond", + "auth": "device_token", + "server": false + }, "parameters": [ { - "name": "vendor_data", "in": "path", - "required": true, + "name": "request_uuid", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, - "description": "Your unique identifier for the user." + "required": true } ], + "tags": [ + "Case Management" + ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "status" - ], - "properties": { - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED" - ], - "description": "New lifecycle status. `BLOCKED` also adds the `vendor_data` to the system blocklist." - } - } - }, - "examples": { - "Activate": { - "summary": "Reactivate the user", - "value": { - "status": "ACTIVE" - } - }, - "Flag": { - "summary": "Flag the user for review", - "value": { - "status": "FLAGGED" - } - }, - "Block": { - "summary": "Block the user (adds to system blocklist)", - "value": { - "status": "BLOCKED" - } - } + "$ref": "#/components/schemas/CustomerMessagingCaseCustomerResponseRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseCustomerResponseRequest" } } } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/users/user-abc-123/update-status/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"status\": \"BLOCKED\"}'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/users/user-abc-123/update-status/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'status': 'BLOCKED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/users/user-abc-123/update-status/', {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'BLOCKED' }),\n});\nconst user = await resp.json();\nconsole.log(user.status);" - } - ], "responses": { "200": { - "description": "User status updated. Full user record returned.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserDetailItem" - }, - "examples": { - "Blocked": { - "value": { - "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", - "vendor_data": "user-abc-123", - "display_name": null, - "full_name": "John Michael Doe", - "date_of_birth": "1990-05-15", - "effective_name": "John Michael Doe", - "status": "BLOCKED", - "metadata": { - "tier": "premium", - "source": "website" - }, - "portrait_image_url": "https:///...", - "session_count": 3, - "approved_count": 2, - "declined_count": 0, - "in_review_count": 1, - "issuing_states": [ - "USA" - ], - "approved_emails": [ - "john@example.com" - ], - "approved_phones": [ - "+14155551234" - ], - "features": { - "ID_VERIFICATION": "Approved", - "LIVENESS": "Approved", - "FACE_MATCH": "Approved", - "AML": "Approved" - }, - "features_list": [ - { - "feature": "ID_VERIFICATION", - "status": "Approved" - }, - { - "feature": "LIVENESS", - "status": "Approved" - }, - { - "feature": "FACE_MATCH", - "status": "Approved" - }, - { - "feature": "AML", - "status": "Approved" - } - ], - "last_session_at": "2025-06-15T10:30:00Z", - "last_activity_at": "2025-06-15T10:30:00Z", - "first_session_at": "2025-06-01T08:00:00Z", - "tags": [ - { - "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", - "tag": { - "uuid": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b", - "name": "VIP", - "color": "#2567FF", - "description": null, - "source": "custom", - "created_at": "2025-05-01T09:00:00Z", - "updated_at": "2025-05-01T09:00:00Z" - }, - "added_by_email": "analyst@acme.com", - "added_by_name": "Jane Analyst", - "created_at": "2025-06-02T11:00:00Z" - } - ], - "comments": [ - { - "uuid": "c1111111-2222-4333-8444-555555555555", - "comment_type": "STATUS_CHANGED", - "comment": null, - "actor_email": null, - "actor_name": null, - "previous_status": "ACTIVE", - "new_status": "BLOCKED", - "previous_value": null, - "new_value": null, - "changed_fields": [], - "metadata": null, - "mentioned_emails": [], - "created_at": "2025-06-15T10:30:00Z" - } - ], - "created_at": "2025-06-01T08:00:00Z", - "updated_at": "2025-06-15T10:30:00Z" - } - } + "$ref": "#/components/schemas/CustomerMessagingCaseCustomerResponseResult" } } - } + }, + "description": "" + }, + "404": { + "description": "The request or case is unavailable for this caller." }, "400": { - "description": "Missing or invalid `status`.", - "content": { - "application/json": { - "examples": { - "Missing status": { - "value": { - "status": [ - "This field is required." - ] - } - }, - "Bad status": { - "value": { - "status": [ - "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" - ] - } - } - } - } - } + "description": "Invalid inputs, unsupported purpose, unavailable verified contact or an existing immutable response." }, - "401": { - "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", - "content": { - "application/json": { - "examples": { - "Missing credentials": { - "value": { - "detail": "You must be authenticated with a valid access token to access this endpoint." - } - } - } + "409": { + "description": "The preview changed or expired. Preview again before confirming." + } + }, + "security": [ + { + "CaseRequestToken": [] + } + ], + "description": "Access one customer request using its request-specific token. No private case findings, original identity data or recipient address are returned. Actionable requests expire after seven days. Cancellation or case resolution invalidates the link. Responses are immutable and remain evidence for reviewer assessment; they never approve or update a source verification." + } + }, + "/v3/fraud/checks/plan/": { + "post": { + "operationId": "plan_create", + "x-didit-sdk": { + "resource": "fraud_checks", + "method": "plan", + "auth": "api_key", + "server": true + }, + "description": "Validate selected checks and estimate their price without executing, creating a session or billing.", + "summary": "Plan a Fraud Check", + "tags": [ + "Fraud Check" + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/FraudCheckRequestRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/FraudCheckRequestRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/FraudCheckRequestRequest" } } }, - "403": { - "description": "Invalid or revoked API key, or the key cannot update users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).", + "required": true + }, + "responses": { + "200": { "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/FraudCheckPlan" } } - } + }, + "description": "" }, - "404": { - "description": "No (non-deleted) user with the supplied `vendor_data` exists for this application.", + "400": { "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } + "schema": { + "$ref": "#/components/schemas/FraudBadRequestErrorMessage" } } + }, + "description": "" + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/": { + "get": { + "summary": "List routing rules", + "description": "Requires `list:cases` for the requested application.", + "operationId": "list_case_routing_rules", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Assignment" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 } }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 1000000, + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "Success", "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." + "schema": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseRoutingRule" + } + }, + "count": { + "type": "integer", + "description": "Matching count, capped at 100. Follow next to continue beyond the cap." + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true } } } } } + }, + "400": { + "description": "Invalid input." + }, + "403": { + "description": "Insufficient permission." } }, "security": [ @@ -33080,209 +52561,156 @@ "ApiKeyAuth": [] } ] - } - }, - "/v3/businesses/create/": { + }, "post": { - "summary": "Create business", - "description": "Pre-create a [business](/entities/businesses) *without* a KYB session. All fields are optional, but you should always send `vendor_data` — without it the record cannot be linked to sessions or transactions. When provided, `vendor_data` must be unique among non-deleted businesses (exact match; conflicts return 400). Not idempotent.", - "operationId": "create_business", + "summary": "Create routing rule", + "description": "Requires `write:cases` for the requested application. Set publish=false to create an unpublished draft; Publish creates its first live version. Discarding a never-published draft deletes it.", + "operationId": "create_case_routing_rules", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Businesses" + "Case Assignment" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } ], + "responses": { + "201": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseRoutingRule" + } + } + } + }, + "400": { + "description": "Invalid input." + }, + "403": { + "description": "Insufficient permission." + } + }, "requestBody": { "required": true, "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your unique identifier for this business (free-form string, NOT a UUID). Optional but strongly recommended. When provided, must be unique among non-deleted businesses for the application; matched exactly as sent." - }, - "display_name": { - "type": "string", - "nullable": true, - "maxLength": 255, - "description": "Friendly display name shown in the console (takes precedence over `legal_name` for UI display)." - }, - "legal_name": { - "type": "string", - "nullable": true, - "maxLength": 255, - "description": "Official legal name of the company." - }, - "registration_number": { - "type": "string", - "nullable": true, - "maxLength": 100, - "description": "Company registration or incorporation number." - }, - "country_code": { - "type": "string", - "nullable": true, - "maxLength": 10, - "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `US`)." - }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED" - ], - "default": "ACTIVE", - "description": "Initial lifecycle status. Defaults to `ACTIVE`." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Arbitrary JSON object you attach to the business. Defaults to `{}`." - } - } - }, - "examples": { - "Basic": { - "summary": "Recommended minimum", - "value": { - "vendor_data": "company-456", - "legal_name": "New Corp Ltd", - "country_code": "US" - } - }, - "Full": { - "summary": "All fields", - "value": { - "vendor_data": "company-789", - "display_name": "New Corp", - "legal_name": "New Corp International Ltd", - "registration_number": "98765432", - "country_code": "DE", - "status": "ACTIVE", - "metadata": { - "industry": "fintech", - "tier": "enterprise" - } - } - } + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseRoutingRuleCreate" } } } }, - "x-codeSamples": [ + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/businesses/create/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"vendor_data\": \"company-456\", \"legal_name\": \"New Corp Ltd\", \"country_code\": \"US\"}'" + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/publish/": { + "post": { + "operationId": "publish_case_routing_rule", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "publish", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/businesses/create/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={\n 'vendor_data': 'company-456',\n 'legal_name': 'New Corp Ltd',\n 'country_code': 'US',\n },\n)\nresp.raise_for_status()\nbiz = resp.json()\nprint(biz['didit_internal_id'])" + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/create/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n vendor_data: 'company-456',\n legal_name: 'New Corp Ltd',\n country_code: 'US',\n }),\n});\nconst biz = await resp.json();\nconsole.log(biz.didit_internal_id);" + "in": "path", + "name": "record_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true } ], - "responses": { - "201": { - "description": "Business created. Full business record returned (same shape as Get Business).", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessDetailItem" - }, - "examples": { - "Created": { - "value": { - "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890", - "vendor_data": "company-456", - "display_name": null, - "legal_name": "New Corp Ltd", - "registration_number": null, - "country_code": "US", - "effective_name": "New Corp Ltd", - "status": "ACTIVE", - "session_count": 0, - "approved_count": 0, - "declined_count": 0, - "in_review_count": 0, - "features": {}, - "features_list": [], - "last_session_at": null, - "last_activity_at": "2026-03-15T10:30:00Z", - "first_session_at": null, - "tags": [], - "created_at": "2026-03-15T10:30:00Z", - "metadata": {}, - "updated_at": "2026-03-15T10:30:00Z" - } - } - } + "tags": [ + "Case Assignment" + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraftPreconditionRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraftPreconditionRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraftPreconditionRequest" } } }, - "400": { - "description": "Invalid body — typically a duplicate `vendor_data`, an unknown `status`, or malformed `metadata`.", + "required": true + }, + "responses": { + "200": { "content": { "application/json": { - "examples": { - "Duplicate": { - "value": { - "vendor_data": [ - "A business with this vendor_data already exists." - ] - } - }, - "Bad status": { - "value": { - "status": [ - "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" - ] - } - }, - "Bad metadata": { - "value": { - "metadata": [ - "Metadata must be a JSON object." - ] - } - } + "schema": { + "$ref": "#/components/schemas/RoutingPublicationCaseRoutingRule" } } - } + }, + "description": "" }, "403": { - "description": "Missing, invalid, or revoked API key, or the key cannot create businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.", - "content": { - "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } + "description": "Case access is required." }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", - "content": { - "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } - } - } - } + "409": { + "description": "The published version or draft revision has changed. Reload before retrying." } }, "security": [ @@ -33292,267 +52720,274 @@ ] } }, - "/v3/users/create/": { + "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/publish/": { "post": { - "summary": "Create user", - "description": "Pre-create a [user](/entities/users) by `vendor_data` *without* a verification session. `vendor_data` must be unique among non-deleted users for the application (exact match; conflicts return 400). Not idempotent.", - "operationId": "create_user", + "operationId": "publish_case_blueprint", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "publish", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "blueprint_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "Users" + "Case Management" ], "requestBody": { - "required": true, "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CasePublicationPolicyDraftPreconditionRequest" + } + }, "application/json": { "schema": { - "type": "object", - "required": [ - "vendor_data" - ], - "properties": { - "vendor_data": { - "type": "string", - "description": "Your unique identifier for this user (free-form string, NOT a UUID). Must be unique among non-deleted users for the application; matched exactly as sent." - }, - "full_name": { - "type": "string", - "nullable": true, - "maxLength": 512, - "description": "Full legal name of the user." - }, - "display_name": { - "type": "string", - "nullable": true, - "description": "Friendly display name shown in the console (takes precedence over `full_name` for UI display).", - "maxLength": 255 - }, - "date_of_birth": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Date of birth in `YYYY-MM-DD` format." - }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED" - ], - "default": "ACTIVE", - "description": "Initial lifecycle status. Defaults to `ACTIVE`." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Arbitrary JSON object you attach to the user. Defaults to `{}`." - }, - "approved_emails": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Pre-trusted email addresses for this user, e.g. `[\"john@example.com\"]`." - }, - "approved_phones": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Pre-trusted phone numbers for this user, e.g. `[\"+14155551234\"]`." - }, - "issuing_states": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Pre-recorded issuing countries (ISO 3166-1 alpha-3), e.g. `[\"USA\"]`." - } - } - }, - "examples": { - "Basic": { - "summary": "Create with basic info", - "value": { - "vendor_data": "user-abc-123", - "full_name": "John Doe" - } - }, - "Full": { - "summary": "All fields", - "value": { - "vendor_data": "user-abc-456", - "full_name": "Jane Smith", - "display_name": "Jane S.", - "date_of_birth": "1990-05-15", - "status": "ACTIVE", - "metadata": { - "tier": "premium", - "source": "api" - }, - "approved_emails": [ - "jane@example.com" - ] - } - } + "$ref": "#/components/schemas/CasePublicationPolicyDraftPreconditionRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CasePublicationPolicyDraftPreconditionRequest" } } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CasePublicationCaseBlueprint" + } + } + }, + "description": "" + }, + "409": { + "description": "Published version or saved draft revision changed. Reload before retrying." + }, + "403": { + "description": "Required case permission is missing." } }, - "x-codeSamples": [ + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/users/create/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"vendor_data\": \"user-abc-123\", \"full_name\": \"John Doe\"}'" + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/preview/": { + "post": { + "operationId": "organization_application_cases_requests_preview_create", + "x-didit-sdk": { + "resource": "cases.requests", + "method": "preview", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/users/create/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={'vendor_data': 'user-abc-123', 'full_name': 'John Doe'},\n)\nresp.raise_for_status()\nuser = resp.json()\nprint(user['didit_internal_id'])" + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/users/create/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ vendor_data: 'user-abc-123', full_name: 'John Doe' }),\n});\nconst user = await resp.json();\nconsole.log(user.didit_internal_id);" + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true } ], + "tags": [ + "Case Management" + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequestPreviewRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequestPreviewRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequestPreviewRequest" + } + } + }, + "required": true + }, "responses": { - "201": { - "description": "User created. Full user record returned (same shape as Get User).", + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserDetailItem" - }, - "examples": { - "Created": { - "value": { - "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418", - "vendor_data": "user-abc-123", - "display_name": null, - "full_name": "John Doe", - "date_of_birth": null, - "effective_name": "John Doe", - "status": "ACTIVE", - "metadata": {}, - "portrait_image_url": null, - "session_count": 0, - "approved_count": 0, - "declined_count": 0, - "in_review_count": 0, - "issuing_states": [], - "approved_emails": [], - "approved_phones": [], - "features": {}, - "features_list": [], - "last_session_at": null, - "last_activity_at": "2025-06-15T10:30:00Z", - "first_session_at": null, - "tags": [], - "comments": [], - "created_at": "2025-06-15T10:30:00Z", - "updated_at": "2025-06-15T10:30:00Z" - } - } + "$ref": "#/components/schemas/CustomerMessagingCaseRequestPreviewResponse" } } - } + }, + "description": "" + }, + "404": { + "description": "The request or case is unavailable for this caller." + }, + "403": { + "description": "The caller lacks the required cases permission." }, "400": { - "description": "Invalid body — typically a duplicate `vendor_data`, a missing required field, or a malformed value.", - "content": { - "application/json": { - "examples": { - "Duplicate": { - "value": { - "vendor_data": [ - "A user with this vendor_data already exists." - ] - } - }, - "Missing vendor_data": { - "value": { - "vendor_data": [ - "This field is required." - ] - } - }, - "Bad metadata": { - "value": { - "metadata": [ - "Metadata must be a JSON object." - ] - } - }, - "Bad status": { - "value": { - "status": [ - "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED" - ] - } - } - } - } - } + "description": "Invalid inputs, unsupported purpose, unavailable verified contact or an existing immutable response." }, - "401": { - "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.", - "content": { - "application/json": { - "examples": { - "Missing credentials": { - "value": { - "detail": "You must be authenticated with a valid access token to access this endpoint." - } - } - } - } - } + "409": { + "description": "The preview changed or expired. Preview again before confirming." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Render the production email without sending or changing the case. Returns the exact recipient, sender, subject, body and a signed preview_token. Send the same contact_id, purpose, language, request_id, expires_at and preview_token to confirm within 15 minutes." + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/action-receipts/": { + "get": { + "operationId": "action_receipts_retrieve", + "x-didit-sdk": { + "resource": "cases", + "method": "list_action_receipts", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, - "403": { - "description": "Invalid or revoked API key, or the key cannot create users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).", - "content": { - "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } + { + "in": "query", + "name": "offset", + "schema": { + "type": "integer", + "maximum": 1000000, + "minimum": 0, + "default": 0 } - }, - "429": { - "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.", + } + ], + "tags": [ + "Case Management" + ], + "responses": { + "200": { "content": { "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } + "schema": { + "$ref": "#/components/schemas/CaseActionCaseActionReceiptPage" } } - } + }, + "description": "" } }, "security": [ { "ApiKeyAuth": [] } - ] + ], + "description": "List subject-action receipts, newest first, with at most 20 results per page. Use next_offset for the next page. Input note text is not exposed in the history." } }, - "/v3/organization/{organization_id}/application/{application_id}/vendor-users/by-id/{didit_internal_id}/faces/upload/": { - "post": { - "summary": "Upload user face", - "description": "Attach a trusted imported face image to an existing User profile. Use this after creating or retrieving a User by `vendor_data` and reading its `didit_internal_id`. The uploaded face is stored on the User profile and indexed for duplicate detection and face search. It is not added to a face blocklist.", - "operationId": "upload_user_face", + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/notes/{note_uuid}/": { + "delete": { + "operationId": "delete_network_note", + "x-didit-sdk": { + "resource": "networks.notes", + "method": "delete", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Users" + "Networks" ], + "summary": "Delete your network note", + "description": "Requires write:networks and the authenticated author of the note. Deletion is irreversible. Connected account and verification data are unaffected.", "parameters": [ { "name": "organization_id", @@ -33562,7 +52997,7 @@ "type": "string", "format": "uuid" }, - "description": "Organization UUID that owns the application." + "description": "Organization UUID." }, { "name": "application_id", @@ -33572,206 +53007,49 @@ "type": "string", "format": "uuid" }, - "description": "Application UUID that owns the User profile." + "description": "Application UUID." }, { - "name": "didit_internal_id", + "name": "network_uuid", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" }, - "description": "Didit's internal User UUID, returned as `didit_internal_id` by `GET /v3/users/{vendor_data}/` and `POST /v3/users/create/`." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "image" - ], - "properties": { - "image": { - "type": "string", - "format": "byte", - "description": "Raw base64-encoded JPG or PNG face image. Do not include a `data:image/...;base64,` prefix. Max decoded size: 2 MB." - }, - "comment": { - "type": "string", - "description": "Optional operator-visible note for the uploaded face.", - "default": "" - } - } - }, - "examples": { - "Imported face": { - "summary": "Attach a face imported from a previous provider", - "value": { - "image": "/9j/4AAQSkZJRgABAQEA...", - "comment": "Imported from previous KYC provider" - } - } - } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/vendor-users/by-id/f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418/faces/upload/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"image\": \"/9j/4AAQSkZJRgABAQEA...\", \"comment\": \"Imported from previous KYC provider\"}'" + "description": "Network UUID." }, { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/vendor-users/by-id/f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418/faces/upload/',\n headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n json={\n 'image': '/9j/4AAQSkZJRgABAQEA...',\n 'comment': 'Imported from previous KYC provider',\n },\n)\nresp.raise_for_status()\nface = resp.json()\nprint(face['uuid'], face['image_url'])" + "name": "note_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } }, { - "lang": "javascript", - "label": "JavaScript", - "source": "const resp = await fetch('https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/vendor-users/by-id/f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418/faces/upload/', {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({\n image: '/9j/4AAQSkZJRgABAQEA...',\n comment: 'Imported from previous KYC provider',\n }),\n});\nconst face = await resp.json();\nconsole.log(face.uuid, face.image_url);" + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + }, + "description": "Console user access token in the form Bearer ." } ], "responses": { - "201": { - "description": "Face uploaded and attached to the User profile.", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "uuid", - "image_url", - "comment", - "uploaded_by", - "created_at" - ], - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "UUID of the uploaded User face record." - }, - "image_url": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Signed URL for the uploaded face image." - }, - "comment": { - "type": "string", - "nullable": true, - "description": "Comment supplied when the face was uploaded." - }, - "uploaded_by": { - "type": "string", - "nullable": true, - "description": "Email or actor identifier of the uploader when available." - }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "Upload timestamp." - } - } - }, - "examples": { - "Uploaded": { - "value": { - "uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", - "image_url": "https://signed-url.example/face.jpg", - "comment": "Imported from previous KYC provider", - "uploaded_by": "api", - "created_at": "2026-05-18T12:00:00Z" - } - } - } - } - } - }, - "400": { - "description": "Invalid request body or image. Common causes: invalid base64, invalid image, no detectable face, image over 2 MB, missing embedding, or the User already has 5 uploaded faces.", - "content": { - "application/json": { - "examples": { - "No face": { - "value": { - "image": "No face detected in the image. Please upload a clear photo with a visible face." - } - }, - "Too many faces": { - "value": { - "detail": "Maximum of 5 faces per user reached." - } - }, - "Too large": { - "value": { - "image": "Image exceeds maximum size of 2 MB." - } - } - } - } - } + "204": { + "description": "Note deleted." }, "401": { - "description": "Missing or invalid API key.", - "content": { - "application/json": { - "examples": { - "Missing key": { - "value": { - "detail": "Authentication credentials were not provided." - } - } - } - } - } + "description": "Authentication is required." }, "403": { - "description": "The credential is valid but cannot write User profiles for this application.", - "content": { - "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } + "description": "Missing permission or Networks access." }, "404": { - "description": "No User exists for that `didit_internal_id` in the application.", - "content": { - "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Not found." - } - } - } - } - } - }, - "429": { - "description": "Rate limit exceeded; back off and retry.", - "content": { - "application/json": { - "examples": { - "Rate limited": { - "value": { - "detail": "Request was throttled. Expected available in 30 seconds." - } - } - } - } - } + "description": "Network or note is not visible to this application." } }, "security": [ @@ -33781,858 +53059,710 @@ ] } }, - "/v3/session/{sessionId}/update-data/": { - "patch": { - "summary": "Update KYC data extracted from the ID document on a verification session", - "description": "Correct the identity fields that were extracted (OCR / NFC) from the user's ID document — misread names, dates, document numbers, and so on — after the verification has finished. Typical callers are a compliance analyst working from your own back office, or an automated reconciliation job. Every field in the body is optional: only the keys you send are written, everything else is left untouched (`PATCH` semantics).\n\nThe session must belong to your application and be in `Approved`, `Declined`, `In Review`, or `Kyc Expired` status. Sessions in any other state (e.g. still `In Progress`) return `404` with `\"No Session matches the given query.\"` — distinct from the `\"Not found.\"` returned for an unknown or deleted `sessionId`. The session must also carry an ID-verification (KYC) record: sessions whose workflow never ran an ID-verification step return `404` (`\"The session does not have a KYC.\"`). For graph workflows with multiple ID-verification steps, target a specific step with `node_id` (query parameter or body field); when omitted, the most recent KYC record is patched.\n\nTwo derived fields are recomputed on every call: `full_name` (from `first_name` + `last_name`) and `issuing_state_name` (from `issuing_state`). If the free-text `address` changes, the server may also re-geocode it and refresh `parsed_address` (skipped for sandbox sessions). `extra_fields` and `parsed_address` are merged key-by-key rather than replaced — see the per-field descriptions for the `null`-to-delete semantics.\n\nSubmitting at least one valid field fires the `data.updated` [webhook](/integration/webhooks) (trigger `manual_review`) — even when the submitted values equal the stored ones. When values actually change, a `KYC_DATA_UPDATED` activity entry recording the changed fields with previous and new values is appended to the session's `reviews`, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and the console activity timeline. The session's status and decision are **not** re-evaluated — use [Update Status](#patch-/v3/session/-sessionId-/update-status/) to change the outcome. An empty JSON body (`{}`) is accepted and simply returns the current KYC data without firing the webhook.\n\nAccepts an API key or a console user token; either way the credential needs the `write:sessions` permission. Authentication and permission failures both return `403` — this API never responds `401`. Note that the session lookup runs before credential checks, so an unknown `sessionId` returns `404` even with missing credentials. This endpoint has a dedicated rate limit of 10 requests per minute per API key (invalid requests count too); the shared write budget of 300 requests per minute also applies.", - "operationId": "patch_v3_session_update_data", - "tags": [ - "Sessions" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/action-receipts/{receipt_uuid}/retry/": { + "post": { + "operationId": "action_receipts_retry_create", + "x-didit-sdk": { + "resource": "cases", + "method": "retry_action_receipt", + "auth": "api_key", + "server": true, + "scope": "application" + }, "parameters": [ { - "name": "sessionId", "in": "path", - "required": true, - "description": "UUID of the User Verification (KYC) session whose extracted document data you are correcting. Business (KYB) session IDs are not valid here — for company data use [Update KYB Company Data](#patch-/v3/session/-sessionId-/kyb/-companyUuid-/update-data/).", - "schema": { - "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" - } - }, - { - "name": "node_id", - "in": "query", - "required": false, - "description": "Workflow graph node whose KYC record to patch, for graph workflows with multiple ID-verification steps (also accepted as a `node_id` body field). When omitted, the most recent KYC record on the session is patched. If no KYC exists for the given node, the call returns `404` (`\"The session does not have a KYC for node ''.\"`).", - "schema": { - "type": "string", - "example": "feature_ocr" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "All fields optional — send only what you want to change.", - "properties": { - "document_type": { - "type": "string", - "description": "Document type code. Accepts the short codes listed in the enum, plus six long-form aliases normalized server-side before validation: `Passport` → `P`, `Identity Card` → `ID`, `Driver's License` → `DL`, `Residence Permit` → `RP`, `Health Insurance Card` → `HIC`, `Tax Card` → `TC`. Any other value (including `PASSPORT` in caps) returns `400`.", - "enum": [ - "P", - "DL", - "ID", - "RP", - "SSC", - "HIC", - "WP", - "TC", - "VISA", - "PSC", - "BC", - "OTHER" - ], - "nullable": true, - "example": "P" - }, - "document_subtype": { - "type": "string", - "maxLength": 128, - "nullable": true, - "description": "Document subtype from the document registry, including the region prefix when the registry distinguishes regional variants." - }, - "document_number": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Document number as printed on the ID.", - "example": "PAB123456" - }, - "personal_number": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Personal / national identification number where the document carries one (e.g. Spanish DNI on a passport).", - "example": "99999999R" - }, - "date_of_birth": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Date of birth in `YYYY-MM-DD`. Any other format returns `400`.", - "example": "1980-01-01" - }, - "date_of_issue": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Issue date of the document in `YYYY-MM-DD`." - }, - "expiration_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Expiration date of the document in `YYYY-MM-DD`." - }, - "issuing_state": { - "type": "string", - "nullable": true, - "description": "Issuing country as an ISO 3166-1 alpha-3 code. Case-insensitive (`esp` is stored as `ESP`); the human-readable `issuing_state_name` shown in [Get Decision](#get-/v3/session/-sessionId-/decision/) is recomputed from this value. Invalid codes return `400`.", - "example": "ESP" - }, - "first_name": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Given name(s). The stored `full_name` is recomputed as `first_name + ' ' + last_name` on every call to this endpoint.", - "example": "Carmen" - }, - "last_name": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Family name(s). Also feeds the recomputed `full_name`.", - "example": "Espanola Espanola" - }, - "gender": { - "type": "string", - "enum": [ - "M", - "F", - "U" - ], - "nullable": true, - "description": "Sex as printed on the document: `M` (male), `F` (female), or `U` (unknown). Any other value — including `X` — returns `400`." - }, - "address": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it synchronously before responding — the 200 body already reflects the refreshed `parsed_address`. The re-geocode also requires `issuing_state` to be present on the KYC record." - }, - "place_of_birth": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Place of birth as printed on the document." - }, - "nationality": { - "type": "string", - "maxLength": 32, - "nullable": true, - "description": "Nationality as printed on the document. Free text up to 32 characters — typically an ISO 3166-1 alpha-3 code (e.g. `ESP`), but not validated as one." - }, - "marital_status": { - "type": "string", - "enum": [ - "SINGLE", - "MARRIED", - "DIVORCED", - "WIDOWED", - "UNKNOWN" - ], - "description": "Marital status where the document carries it. Any other value returns `400`." - }, - "extra_fields": { - "type": "object", - "additionalProperties": true, - "nullable": true, - "description": "Per-key overrides for document-specific extra fields (e.g. `blood_group`, `dl_categories`, `mrz_string`). Keys are merged into the existing override map: send a key with a value to set it, or with `null` / `\"\"` to remove that key from the merged view. Send the whole field as `null` to clear all previously applied overrides. Must be a JSON object, otherwise `400`. The response always returns the merged view (OCR-extracted values with your overrides applied)." - }, - "parsed_address": { - "type": "object", - "nullable": true, - "description": "Structured address. Only these nested keys are accepted: `address_type`, `city`, `region`, `street_1`, `street_2`, `postal_code`, `country` — any other key returns `400`. Per-key semantics: a value sets the key, `null` removes it. `formatted_address` is recomputed server-side by joining `street_1`, `street_2`, `city`, `region`, `postal_code`, and `country` and is returned in the response — you cannot set it directly.", - "properties": { - "address_type": { - "type": "string", - "nullable": true, - "description": "Free-form address classification (e.g. `home`)." - }, - "city": { - "type": "string", - "nullable": true, - "example": "Madrid" - }, - "region": { - "type": "string", - "nullable": true, - "description": "State / province / autonomous community." - }, - "street_1": { - "type": "string", - "nullable": true, - "example": "Avda de Madrid 34" - }, - "street_2": { - "type": "string", - "nullable": true - }, - "postal_code": { - "type": "string", - "nullable": true, - "example": "28013" - }, - "country": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.", - "example": "ES" - } - } - } - } - }, - "examples": { - "Correct name and DOB": { - "summary": "Fix OCR-misread name, date of birth, and structured address", - "value": { - "first_name": "Carmen", - "last_name": "Espanola Espanola", - "date_of_birth": "1980-01-01", - "issuing_state": "ESP", - "marital_status": "MARRIED", - "extra_fields": { - "blood_group": "O+" - }, - "parsed_address": { - "street_1": "Avda de Madrid 34", - "city": "Madrid", - "postal_code": "28013", - "country": "ES" - } - } - }, - "Remove an extra field": { - "summary": "Delete one extra-field key, keep the rest", - "value": { - "extra_fields": { - "blood_group": null - } - } - }, - "Long-form document type": { - "summary": "Long-form alias is normalized to the short code (here P)", - "value": { - "document_type": "Passport" - } + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "receipt_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Management" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseActionCaseActionReceipt" } } - } + }, + "description": "" + } + }, + "security": [ + { + "ApiKeyAuth": [] } + ], + "description": "Retry only this failed subject action using its saved inputs. Current write permission is checked. Applied actions return their receipt without executing again. Case closure is independent." + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/draft/": { + "get": { + "operationId": "get_case_blueprint_draft", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "retrieve_draft", + "auth": "api_key", + "server": true, + "scope": "application" }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "blueprint_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Management" + ], "responses": { "200": { - "description": "KYC data updated. Returns the **full current KYC record** (every updatable field with its stored value, plus the merged `extra_fields` and the current `parsed_address`) — not just the fields you submitted. The `data.updated` webhook fires when at least one valid field was in the payload.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "document_type": { - "type": "string", - "description": "Document type code. Accepts the short codes listed in the enum, plus six long-form aliases normalized server-side before validation: `Passport` → `P`, `Identity Card` → `ID`, `Driver's License` → `DL`, `Residence Permit` → `RP`, `Health Insurance Card` → `HIC`, `Tax Card` → `TC`. Any other value (including `PASSPORT` in caps) returns `400`.", - "enum": [ - "P", - "DL", - "ID", - "RP", - "SSC", - "HIC", - "WP", - "TC", - "VISA", - "PSC", - "BC", - "OTHER" - ], - "nullable": true, - "example": "P" - }, - "document_subtype": { - "type": "string", - "maxLength": 128, - "nullable": true, - "description": "Document subtype from the document registry, including the region prefix when the registry distinguishes regional variants." - }, - "document_number": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Document number as printed on the ID.", - "example": "PAB123456" - }, - "personal_number": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Personal / national identification number where the document carries one (e.g. Spanish DNI on a passport).", - "example": "99999999R" - }, - "date_of_birth": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Date of birth in `YYYY-MM-DD`. Any other format returns `400`.", - "example": "1980-01-01" - }, - "date_of_issue": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Issue date of the document in `YYYY-MM-DD`." - }, - "expiration_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Expiration date of the document in `YYYY-MM-DD`." - }, - "issuing_state": { - "type": "string", - "nullable": true, - "description": "Issuing country as an ISO 3166-1 alpha-3 code. Case-insensitive (`esp` is stored as `ESP`); the human-readable `issuing_state_name` shown in [Get Decision](#get-/v3/session/-sessionId-/decision/) is recomputed from this value. Invalid codes return `400`.", - "example": "ESP" - }, - "first_name": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Given name(s). The stored `full_name` is recomputed as `first_name + ' ' + last_name` on every call to this endpoint.", - "example": "Carmen" - }, - "last_name": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Family name(s). Also feeds the recomputed `full_name`.", - "example": "Espanola Espanola" - }, - "gender": { - "type": "string", - "enum": [ - "M", - "F", - "U" - ], - "nullable": true, - "description": "Sex as printed on the document: `M` (male), `F` (female), or `U` (unknown). Any other value — including `X` — returns `400`." - }, - "address": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it in the background of the same request and may overwrite `parsed_address` with the geocoder result." - }, - "place_of_birth": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Place of birth as printed on the document." - }, - "nationality": { - "type": "string", - "maxLength": 32, - "nullable": true, - "description": "Nationality as printed on the document. Free text up to 32 characters — typically an ISO 3166-1 alpha-3 code (e.g. `ESP`), but not validated as one." - }, - "marital_status": { - "type": "string", - "enum": [ - "SINGLE", - "MARRIED", - "DIVORCED", - "WIDOWED", - "UNKNOWN" - ], - "description": "Marital status where the document carries it. Any other value returns `400`." - }, - "extra_fields": { - "type": "object", - "additionalProperties": true, - "nullable": true, - "description": "Merged view of the document's extra fields: the OCR-extracted values with all operator overrides applied. Keys removed via a `null` override are absent from this map." - }, - "parsed_address": { - "type": "object", - "nullable": true, - "description": "Current structured address (with internal bookkeeping keys stripped), or `null` when none exists. Includes the recomputed `formatted_address` alongside the seven editable keys.", - "properties": { - "address_type": { - "type": "string", - "nullable": true, - "description": "Free-form address classification (e.g. `home`)." - }, - "city": { - "type": "string", - "nullable": true, - "example": "Madrid" - }, - "region": { - "type": "string", - "nullable": true, - "description": "State / province / autonomous community." - }, - "street_1": { - "type": "string", - "nullable": true, - "example": "Avda de Madrid 34" - }, - "street_2": { - "type": "string", - "nullable": true - }, - "postal_code": { - "type": "string", - "nullable": true, - "example": "28013" - }, - "country": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.", - "example": "ES" - }, - "formatted_address": { - "type": "string", - "nullable": true, - "description": "Joined, human-readable form of the structured address. Recomputed server-side after every `parsed_address` edit.", - "example": "Avda de Madrid 34, Madrid, 28013, ES" - } - } - } - } - }, - "examples": { - "Updated KYC": { - "summary": "Full KYC record after the correction", - "value": { - "document_type": "P", - "document_subtype": null, - "document_number": "PAB123456", - "personal_number": "99999999R", - "date_of_birth": "1980-01-01", - "date_of_issue": "2021-02-09", - "expiration_date": "2031-02-08", - "issuing_state": "ESP", - "first_name": "Carmen", - "last_name": "Espanola Espanola", - "gender": "F", - "address": "AVDA DE MADRID 34, MADRID, MADRID 28013", - "place_of_birth": "MADRID", - "nationality": "ESP", - "marital_status": "MARRIED", - "extra_fields": { - "mrz_string": "P\": [\"\"]}`; errors on nested `parsed_address` / `extra_fields` keys use an object value: `{\"parsed_address\": {\"\": \"\"}}`.", + "required": true + }, + "responses": { + "200": { "content": { "application/json": { - "examples": { - "Invalid gender": { - "summary": "gender must be M, F, or U", - "value": { - "gender": [ - "\"X\" is not a valid choice." - ] - } - }, - "Invalid document type": { - "summary": "Not a short code or long-form alias", - "value": { - "document_type": [ - "\"PASSPORT\" is not a valid choice." - ] - } - }, - "Invalid issuing state": { - "summary": "issuing_state is not ISO 3166-1 alpha-3", - "value": { - "issuing_state": [ - "Invalid ISO 3166-1 alpha-3 country code for issuing_state." - ] - } - }, - "Invalid marital status": { - "summary": "marital_status not in the enum", - "value": { - "marital_status": [ - "\"ENGAGED\" is not a valid choice." - ] - } - }, - "Bad date format": { - "summary": "Dates must be YYYY-MM-DD", - "value": { - "date_of_birth": [ - "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." - ] - } - }, - "Unsupported parsed_address key": { - "summary": "Only the seven documented keys are allowed", - "value": { - "parsed_address": { - "zipcode": "Unsupported key. Allowed keys: address_type, city, region, street_1, street_2, postal_code, country" - } - } - }, - "Invalid parsed_address country": { - "summary": "Nested country must be alpha-2, not alpha-3", - "value": { - "parsed_address": { - "country": "Invalid ISO 3166-1 alpha-2 country code." - } - } - }, - "extra_fields not an object": { - "summary": "extra_fields must be a JSON object", - "value": { - "extra_fields": [ - "This field must be a JSON object." - ] - } - } + "schema": { + "$ref": "#/components/schemas/CasePublicationPolicyDraft" } } + }, + "description": "" + }, + "409": { + "description": "Published version or saved draft revision changed. Reload before retrying." + }, + "403": { + "description": "Required case permission is missing." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "delete": { + "operationId": "discard_case_blueprint_draft", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "discard_draft", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "blueprint_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Management" + ], + "responses": { + "204": { + "description": "No response body" + }, + "409": { + "description": "Published version or saved draft revision changed. Reload before retrying." + }, + "403": { + "description": "Required case permission is missing." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CasePublicationPolicyDraftPreconditionRequest" + } } + } + } + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/teams/{record_uuid}/": { + "get": { + "summary": "Retrieve review team", + "description": "Requires `read:cases` for the requested application.", + "operationId": "retrieve_case_teams", + "x-didit-sdk": { + "resource": "cases.teams", + "method": "retrieve", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Assignment" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "403": { - "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "record_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", "content": { "application/json": { - "examples": { - "Invalid Credentials": { - "summary": "Missing or invalid x-api-key", - "value": { - "detail": "Authentication credentials were not provided or are invalid." - } - }, - "Missing Privilege": { - "summary": "Credential lacks write:sessions", - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/CaseReviewTeam" } } } }, + "400": { + "description": "Invalid input." + }, + "403": { + "description": "Insufficient permission." + }, "404": { - "description": "Unknown session, session in a non-editable state, or no KYC record to patch. Each case has a distinct `detail` message (see examples). The unknown-session lookup runs before credential checks.", + "description": "Record not found in this application." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "patch": { + "summary": "Update review team", + "description": "Requires `write:cases` for the requested application.", + "operationId": "update_case_teams", + "x-didit-sdk": { + "resource": "cases.teams", + "method": "update", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Assignment" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "record_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", "content": { "application/json": { - "examples": { - "Unknown session": { - "summary": "No session with this ID in your application (or it was deleted)", - "value": { - "detail": "Not found." - } - }, - "Wrong session state": { - "summary": "Session exists but is not Approved / Declined / In Review / Kyc Expired", - "value": { - "detail": "No Session matches the given query." - } - }, - "No KYC record": { - "summary": "The workflow never ran an ID-verification step", - "value": { - "detail": "The session does not have a KYC." - } - }, - "No KYC for node": { - "summary": "node_id given but no KYC exists for it", - "value": { - "detail": "The session does not have a KYC for node 'feature_missing'." - } - } + "schema": { + "$ref": "#/components/schemas/CaseReviewTeam" } } } }, - "429": { - "description": "Dedicated rate limit exceeded — 10 KYC data updates per minute per API key (rejected requests count toward the budget). The shared 300/min write budget can also trigger with its own message. Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, - "examples": { - "Rate limited": { - "summary": "KYC update budget exhausted", - "value": { - "detail": "Session KYC data update rate limit exceeded. You can make up to 10 requests per minute." - } - } - } + "400": { + "description": "Invalid input." + }, + "403": { + "description": "Insufficient permission." + }, + "404": { + "description": "Record not found in this application." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseReviewTeamUpdate" } } } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-data/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"first_name\": \"Carmen\",\n \"last_name\": \"Espanola Espanola\",\n \"date_of_birth\": \"1980-01-01\",\n \"issuing_state\": \"ESP\",\n \"parsed_address\": {\n \"street_1\": \"Avda de Madrid 34\",\n \"city\": \"Madrid\",\n \"postal_code\": \"28013\",\n \"country\": \"ES\"\n }\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-data/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\n \"first_name\": \"Carmen\",\n \"last_name\": \"Espanola Espanola\",\n \"date_of_birth\": \"1980-01-01\",\n \"issuing_state\": \"ESP\",\n \"extra_fields\": {\"blood_group\": \"O+\"},\n \"parsed_address\": {\"city\": \"Madrid\", \"postal_code\": \"28013\", \"country\": \"ES\"},\n },\n)\nresponse.raise_for_status()\nkyc = response.json() # full current KYC record\nprint(kyc[\"first_name\"], kyc[\"parsed_address\"][\"formatted_address\"])" - }, + "security": [ { - "lang": "javascript", - "label": "JavaScript", - "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-data/',\n {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n first_name: 'Carmen',\n last_name: 'Espanola Espanola',\n date_of_birth: '1980-01-01',\n issuing_state: 'ESP',\n parsed_address: { city: 'Madrid', postal_code: '28013', country: 'ES' },\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst kyc = await response.json(); // full current KYC record" + "ApiKeyAuth": [] } ] } }, - "/v3/session/{sessionId}/features/{nodeId}/update-status/": { - "patch": { - "summary": "Update an individual feature (step) status", - "description": "Set the status of a single verification feature (step) within a session **without changing the overall session decision**. Target the feature by its workflow graph `nodeId` (use `default` for non-graph workflows where the step has no node).\n\nWorks for both User Verification (KYC) and Business Verification (KYB) sessions and every step type — ID/OCR, Liveness, Face Match, NFC, AML, Proof of Address, Phone, Email, Database Validation, Questionnaire, and the KYB registry / documents / key-people checks.\n\n`new_status` must be one of `Approved`, `Declined`, or `In Review`, and must differ from the feature's current status. A successful call fires a `data.updated` webhook and appends an activity entry (with actor attribution) to the session's review trail. This endpoint is **not** available for standalone API sessions (`session_type = API`).\n\nAuthenticate with your API key (`x-api-key`) or a user authorization header — either must carry the `write:sessions` privilege.", - "operationId": "patch_v3_session_update_feature_status", + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/report/": { + "get": { + "operationId": "download_network_report", + "x-didit-sdk": { + "resource": "networks", + "method": "download_report", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Sessions" + "Networks" + ], + "summary": "Download a network evidence report", + "description": "Download a PDF of the current organization network, its observed account connections, signal references and review status. Requires read:networks. Global intelligence and private analyst notes are not included. Each account and signal list is capped at 500 entries and the report states when it is incomplete. The request does not change network status. Errors are returned as JSON.", + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "network_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Network UUID." + }, + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + }, + "description": "Console user access token in the form Bearer ." + } ], + "responses": { + "200": { + "description": "Network evidence report. Cache-Control: no-store.", + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "Missing permission or Networks access." + }, + "404": { + "description": "Network or note is not visible to this application." + } + }, "security": [ { "ApiKeyAuth": [] } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/notes/": { + "get": { + "operationId": "list_network_notes", + "x-didit-sdk": { + "resource": "networks.notes", + "method": "list", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" ], + "summary": "List private network notes", + "description": "Console users with read:networks can read notes on a network visible to the application. Notes stay within the organization and are not contributed to shared intelligence. Newest notes appear first.", "parameters": [ { + "name": "organization_id", "in": "path", - "name": "sessionId", "required": true, - "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", "schema": { "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" - } + "format": "uuid" + }, + "description": "Organization UUID." }, { + "name": "application_id", "in": "path", - "name": "nodeId", "required": true, - "description": "Workflow graph node ID of the feature to update (for example `feature_ocr_1`). Use the literal value `default` for non-graph (single-step) workflows where the feature has no node ID.", "schema": { "type": "string", - "example": "feature_ocr_1" + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "network_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Network UUID." + }, + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + }, + "description": "Console user access token in the form Bearer ." + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 30 } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "new_status" - ], - "properties": { - "new_status": { - "type": "string", - "description": "Target status for this feature. Must differ from the current value.", - "enum": [ - "Approved", - "Declined", - "In Review" - ], - "example": "Approved" - }, - "comment": { - "type": "string", - "description": "Optional free-text note recorded on the session's review trail for this feature change.", - "example": "Document manually verified by compliance." - } - } - } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "maximum": 1000000, + "default": 0 } } - }, + ], "responses": { "200": { - "description": "Feature status updated. Returns the targeted feature, its node ID, and the new status.", + "description": "A page of notes. Count is capped at 100; use next to continue.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "session_id": { - "type": "string", - "format": "uuid" + "count": { + "type": "integer" }, - "node_id": { + "next": { "type": "string", - "example": "feature_ocr_1" + "nullable": true }, - "feature_type": { + "previous": { "type": "string", - "description": "Model name of the updated feature (e.g. `KYC`, `Face`, `FaceMatch`, `AML`, `POA`).", - "example": "KYC" + "nullable": true }, - "new_status": { - "type": "string", - "example": "Approved" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkNote" + } } } - }, - "example": { - "session_id": "11111111-2222-3333-4444-555555555555", - "node_id": "feature_ocr_1", - "feature_type": "KYC", - "new_status": "Approved" } } } }, - "400": { - "description": "Validation error. Returned when `new_status` is invalid or unchanged, or when the session is a standalone API session.", - "content": { - "application/json": { - "examples": { - "Same Status": { - "summary": "new_status equals the current status", - "value": { - "new_status": "The new status is the same as the current status." - } - }, - "API Session": { - "summary": "Feature status cannot be changed on standalone API sessions", - "value": { - "detail": "Cannot change feature status for API sessions." - } - } - } - } - } + "401": { + "description": "Authentication is required." }, "403": { - "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", - "content": { - "application/json": { - "examples": { - "Invalid Credentials": { - "summary": "Missing or invalid x-api-key", - "value": { - "detail": "Authentication credentials were not provided or are invalid." - } - }, - "Missing Privilege": { - "summary": "API key lacks write:sessions", - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } + "description": "Missing permission or Networks access." }, "404": { - "description": "The session was not found, or it has no feature with the given `nodeId`.", - "content": { - "application/json": { - "examples": { - "Session Not Found": { - "summary": "Unknown session_id", - "value": { - "detail": "Session not found." - } - }, - "Feature Not Found": { - "summary": "No feature for this node_id", - "value": { - "detail": "No feature found with node_id 'feature_ocr_1' in this session." - } - } - } - } - } + "description": "Network or note is not visible to this application." } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/features/feature_ocr_1/update-status/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"new_status\": \"Approved\",\n \"comment\": \"Document manually verified by compliance.\"\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/features/feature_ocr_1/update-status/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\", \"Content-Type\": \"application/json\"},\n json={\"new_status\": \"Approved\", \"comment\": \"Document manually verified by compliance.\"},\n)\nprint(response.json())" - } - ] - } - }, - "/v3/session/{sessionId}/update-aml-hit-status/": { - "patch": { - "summary": "Update an AML hit review status", - "description": "Set the review status of a **single AML hit** (a potential sanction / PEP / watchlist / adverse-media match) returned by AML screening, without changing the overall session decision. Identify the hit by its `hit_id` (the `id` of an entry in the AML check's `hits` array — see the session decision).\n\n`review_status` is one of `Unreviewed`, `Confirmed Match`, `False Positive`, or `Inconclusive`. Reviewer decisions are preserved across ongoing-monitoring updates, so a hit you mark `False Positive` stays that way when the provider re-screens.\n\nA successful call fires a `data.updated` webhook and appends an activity entry (with actor attribution) to the session's review trail. For graph workflows with multiple AML checks, pass `node_id` to target a specific check.\n\nAuthenticate with your API key (`x-api-key`) or a user authorization header — either must carry the `write:sessions` privilege.", - "operationId": "patch_v3_session_update_aml_hit_status", - "tags": [ - "Sessions" - ], "security": [ { "ApiKeyAuth": [] } + ] + }, + "post": { + "operationId": "create_network_note", + "x-didit-sdk": { + "resource": "networks.notes", + "method": "create", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Networks" ], + "summary": "Add a private network note", + "description": "Requires a console user with write:networks. The author is assigned from the authenticated user. Supply a new random UUID client_reference for each note; retry the same body and reference after an uncertain response to avoid duplicates. Reusing a reference for another body, author or network returns 409. Notes follow the surviving investigation when networks merge.", "parameters": [ { + "name": "organization_id", "in": "path", - "name": "sessionId", "required": true, - "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", "schema": { "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" - } + "format": "uuid" + }, + "description": "Organization UUID." + }, + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + }, + { + "name": "network_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Network UUID." }, { - "in": "query", - "name": "node_id", - "required": false, - "description": "Workflow graph node ID. Required only when the session has multiple AML checks (one per node); omit it for single-AML sessions.", + "name": "Authorization", + "in": "header", + "required": true, "schema": { - "type": "string", - "example": "feature_aml_1" - } + "type": "string" + }, + "description": "Console user access token in the form Bearer ." } ], "requestBody": { @@ -34642,30 +53772,18 @@ "schema": { "type": "object", "required": [ - "hit_id", - "review_status" + "comment", + "client_reference" ], "properties": { - "hit_id": { - "type": "string", - "description": "The `id` of the hit in the AML check's `hits` array.", - "example": "abc123" - }, - "review_status": { + "comment": { "type": "string", - "description": "The new review status for the hit.", - "enum": [ - "Unreviewed", - "Confirmed Match", - "False Positive", - "Inconclusive" - ], - "example": "False Positive" + "minLength": 1, + "maxLength": 10000 }, - "node_id": { + "client_reference": { "type": "string", - "description": "Optional graph node ID. Use it (here or as a query parameter) when the session has multiple AML checks.", - "example": "feature_aml_1" + "format": "uuid" } } } @@ -34673,579 +53791,473 @@ } }, "responses": { - "200": { - "description": "Hit review status updated.", + "201": { + "description": "Saved private note.", "content": { "application/json": { - "example": { - "message": "Hit status updated successfully", - "hit_id": "abc123", - "review_status": "False Positive" + "schema": { + "$ref": "#/components/schemas/NetworkNote" } } } }, - "400": { - "description": "Validation error — `hit_id` empty or `review_status` not one of the allowed values.", + "200": { + "description": "Saved private note.", "content": { "application/json": { - "example": { - "review_status": [ - "\"Maybe\" is not a valid choice." - ] + "schema": { + "$ref": "#/components/schemas/NetworkNote" } } } }, + "400": { + "description": "Invalid or blank note." + }, + "409": { + "description": "The reference was already used for another note." + }, + "401": { + "description": "Authentication is required." + }, "403": { - "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", + "description": "Missing permission or Networks access." + }, + "404": { + "description": "Network or note is not visible to this application." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/draft/": { + "get": { + "operationId": "get_case_routing_rule_draft", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "retrieve_draft", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "record_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Assignment" + ], + "responses": { + "200": { "content": { "application/json": { - "examples": { - "Invalid Credentials": { - "summary": "Missing or invalid x-api-key", - "value": { - "detail": "Authentication credentials were not provided or are invalid." - } - }, - "Missing Privilege": { - "summary": "API key lacks write:sessions", - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraftResponse" } } + }, + "description": "" + }, + "403": { + "description": "Case access is required." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "put": { + "operationId": "save_case_routing_rule_draft", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "save_draft", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "record_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Case Assignment" + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraftWriteRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraftWriteRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraftWriteRequest" + } } }, - "404": { - "description": "The session was not found, the session has no AML check (optionally for the given `node_id`), or no hit matches `hit_id`.", + "required": true + }, + "responses": { + "200": { "content": { "application/json": { - "examples": { - "No AML Check": { - "summary": "Session has no AML check", - "value": { - "detail": "The session does not have an AML check." - } - }, - "Hit Not Found": { - "summary": "Unknown hit_id", - "value": { - "detail": "Hit with ID 'abc123' not found in the AML check." - } - } + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraft" } } - } + }, + "description": "" + }, + "403": { + "description": "Case access is required." + }, + "409": { + "description": "The published version or draft revision has changed. Reload before retrying." } }, - "x-codeSamples": [ + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-aml-hit-status/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"hit_id\": \"abc123\",\n \"review_status\": \"False Positive\"\n }'" + "ApiKeyAuth": [] + } + ] + }, + "delete": { + "operationId": "discard_case_routing_rule_draft", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "discard_draft", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-aml-hit-status/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\", \"Content-Type\": \"application/json\"},\n json={\"hit_id\": \"abc123\", \"review_status\": \"False Positive\"},\n)\nprint(response.json())" + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "record_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true } - ] - } - }, - "/v3/session/{sessionId}/bulk-update-aml-hit-status/": { - "patch": { - "summary": "Bulk update AML hit review statuses", - "description": "Update the review status of **multiple AML hits** in one call. Provide `hit_updates`, a list of `{ hit_id, review_status }` pairs. `review_status` is one of `Unreviewed`, `Confirmed Match`, `False Positive`, or `Inconclusive`.\n\nThe update is all-or-nothing: if any `hit_id` is not found in the AML check, the request returns `404` and no hit is changed. A successful call fires a single `data.updated` webhook and records one activity entry covering all changes. For graph workflows with multiple AML checks, pass `node_id` to target a specific check.\n\nAuthenticate with your API key (`x-api-key`) or a user authorization header — either must carry the `write:sessions` privilege.", - "operationId": "patch_v3_session_bulk_update_aml_hit_status", + ], "tags": [ - "Sessions" + "Case Assignment" ], + "responses": { + "204": { + "description": "No response body" + }, + "403": { + "description": "Case access is required." + }, + "409": { + "description": "The published version or draft revision has changed. Reload before retrying." + } + }, "security": [ { "ApiKeyAuth": [] } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraftPreconditionRequest" + } + } + } + } + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/duplicates/": { + "post": { + "operationId": "duplicates_create", + "x-didit-sdk": { + "resource": "cases", + "method": "find_duplicates", + "auth": "api_key", + "server": true, + "scope": "application" + }, "parameters": [ { "in": "path", - "name": "sessionId", - "required": true, - "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", + "name": "organization_id", "schema": { "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" - } + "format": "uuid" + }, + "required": true }, { - "in": "query", - "name": "node_id", - "required": false, - "description": "Workflow graph node ID. Required only when the session has multiple AML checks (one per node); omit it for single-AML sessions.", + "in": "path", + "name": "application_id", "schema": { "type": "string", - "example": "feature_aml_1" - } + "format": "uuid" + }, + "required": true } ], + "tags": [ + "Case Management" + ], "requestBody": { - "required": true, "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CaseDuplicateCaseDuplicateQueryRequest" + } + }, "application/json": { "schema": { - "type": "object", - "required": [ - "hit_updates" - ], - "properties": { - "hit_updates": { - "type": "array", - "description": "Non-empty list of hit updates.", - "items": { - "type": "object", - "required": [ - "hit_id", - "review_status" - ], - "properties": { - "hit_id": { - "type": "string", - "example": "abc123" - }, - "review_status": { - "type": "string", - "enum": [ - "Unreviewed", - "Confirmed Match", - "False Positive", - "Inconclusive" - ], - "example": "Confirmed Match" - } - } - } - }, - "node_id": { - "type": "string", - "description": "Optional graph node ID. Use it (here or as a query parameter) when the session has multiple AML checks.", - "example": "feature_aml_1" - } - } + "$ref": "#/components/schemas/CaseDuplicateCaseDuplicateQueryRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CaseDuplicateCaseDuplicateQueryRequest" } } } }, "responses": { "200": { - "description": "All hits updated.", "content": { "application/json": { - "example": { - "message": "Updated 2 hit(s) successfully", - "updated_hits": [ - "abc123", - "def456" - ] + "schema": { + "$ref": "#/components/schemas/CaseDuplicateCaseDuplicatePage" } } - } + }, + "description": "" + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Review existing cases for a subject or owned source record before manual case creation. Both open and resolved investigations are returned. Each page contains at most 20 cases; use next_offset to request another page." + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/preview/": { + "post": { + "summary": "Test case routing", + "description": "Requires `read:cases` for the requested application. Evaluates current rules or a supplied draft against current reviewer access, availability, skills and capacity. It does not save the draft or reserve reviewer capacity.", + "operationId": "preview_case_routing", + "x-didit-sdk": { + "resource": "cases.routing_rules", + "method": "preview", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Assignment" + ], + "parameters": [ + { + "name": "organization_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Organization UUID." }, - "400": { - "description": "Validation error — `hit_updates` empty, or an entry is missing `hit_id` / `review_status` or carries an invalid value.", + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Application UUID." + } + ], + "responses": { + "200": { + "description": "Success", "content": { "application/json": { - "example": { - "hit_updates": [ - "hit_updates cannot be empty" - ] + "schema": { + "$ref": "#/components/schemas/CaseRoutingPreview" } } } }, - "403": { - "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", - "content": { - "application/json": { - "examples": { - "Invalid Credentials": { - "summary": "Missing or invalid x-api-key", - "value": { - "detail": "Authentication credentials were not provided or are invalid." - } - }, - "Missing Privilege": { - "summary": "API key lacks write:sessions", - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } - } + "400": { + "description": "Invalid input." }, - "404": { - "description": "The session was not found, the session has no AML check, or one or more `hit_id`s do not exist (no hit is changed in that case).", - "content": { - "application/json": { - "example": { - "detail": "Hits not found: zzz999" - } + "403": { + "description": "Insufficient permission." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CaseRoutingPreviewRequest" } } } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/bulk-update-aml-hit-status/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"hit_updates\": [\n { \"hit_id\": \"abc123\", \"review_status\": \"Confirmed Match\" },\n { \"hit_id\": \"def456\", \"review_status\": \"False Positive\" }\n ]\n }'" - }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/bulk-update-aml-hit-status/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\", \"Content-Type\": \"application/json\"},\n json={\n \"hit_updates\": [\n {\"hit_id\": \"abc123\", \"review_status\": \"Confirmed Match\"},\n {\"hit_id\": \"def456\", \"review_status\": \"False Positive\"},\n ]\n },\n)\nprint(response.json())" + "ApiKeyAuth": [] } ] } }, - "/v3/session/{sessionId}/update-poa-data/": { - "patch": { - "summary": "Update Proof of Address data extracted from the POA document on a verification session", - "description": "Correct the fields extracted from the user's Proof of Address document — utility bill, bank statement, or other address evidence — after the verification has finished: issuer, dates, the printed name, the address itself, and banking details. Every field in the body is optional: only the keys you send are written, everything else is left untouched (`PATCH` semantics).\n\nThe session must belong to your application and be in `Approved`, `Declined`, `In Review`, or `Kyc Expired` status. Sessions in any other state (e.g. still `In Progress`) return `404` with `\"No Session matches the given query.\"` — distinct from the `\"Not found.\"` returned for an unknown or deleted `sessionId`. The session must also carry a POA record: sessions whose workflow never ran a Proof of Address step return `404` (`\"The session does not have a POA.\"`). For graph workflows with multiple POA steps, target a specific step with `node_id` (query parameter or body field); when omitted, the most recent POA record is patched.\n\n`extra_fields` is merged key-by-key: nine known keys (`bank_iban`, `additional_names`, …) write through to dedicated POA columns, any other key is kept as a custom extra, and `null` / `\"\"` deletes a key. `poa_parsed_address` accepts only the seven documented keys and recomputes `formatted_address` / `poa_formatted_address` after every edit. If the free-text `poa_address` changes, the server may also re-geocode it and refresh the structured address (skipped for sandbox sessions).\n\nUnlike the KYC variant of this endpoint, the `data.updated` [webhook](/integration/webhooks) (trigger `manual_review`) fires only when at least one stored value **actually changed** — re-sending identical values is a silent no-op. When values change, a `POA_DATA_UPDATED` activity entry recording the changed fields with previous and new values is appended to the session's `reviews`, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and the console activity timeline. The session's status and decision are **not** re-evaluated — use [Update Status](#patch-/v3/session/-sessionId-/update-status/) to change the outcome.\n\nAccepts an API key or a console user token; either way the credential needs the `write:sessions` permission. Authentication and permission failures both return `403` — this API never responds `401`. Note that the session lookup runs before credential checks, so an unknown `sessionId` returns `404` even with missing credentials. This endpoint has a dedicated rate limit of 10 requests per minute per API key (invalid requests count too); the shared write budget of 300 requests per minute also applies.\n\nAn empty JSON body `{}` is a no-op: it returns `200` echoing the current POA record without firing the webhook (`has_changes` stays false).", - "operationId": "patch_v3_session_update_poa_data", - "tags": [ - "Sessions" - ], - "security": [ - { - "ApiKeyAuth": [] - } + "/v3/organization/{organization_id}/application/{application_id}/cases/reviewers/": { + "get": { + "summary": "List eligible case reviewers", + "description": "Requires `list:cases` for the requested application.", + "operationId": "list_case_reviewers", + "x-didit-sdk": { + "resource": "cases", + "method": "list_reviewers", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "tags": [ + "Case Assignment" ], "parameters": [ { - "name": "sessionId", + "name": "organization_id", "in": "path", "required": true, - "description": "UUID of the User Verification (KYC) session whose attached POA record you are correcting.", "schema": { "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" - } + "format": "uuid" + }, + "description": "Organization UUID." }, { - "name": "node_id", - "in": "query", - "required": false, - "description": "Workflow graph node whose POA record to patch, for graph workflows with multiple Proof of Address steps (also accepted as a `node_id` body field). When omitted, the most recent POA record on the session is patched. If no POA exists for the given node, the call returns `404` (`\"The session does not have a POA for node ''.\"`).", + "name": "application_id", + "in": "path", + "required": true, "schema": { "type": "string", - "example": "feature_poa" - } + "format": "uuid" + }, + "description": "Application UUID." } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "All fields optional — send only what you want to change.", - "properties": { - "issuing_state": { - "type": "string", - "nullable": true, - "description": "Country that issued the POA document, ISO 3166-1 alpha-3. Case-insensitive (normalized to uppercase); invalid codes return `400`.", - "example": "ESP" - }, - "document_type": { - "type": "string", - "enum": [ - "UTILITY_BILL", - "BANK_STATEMENT", - "GOVERNMENT_ISSUED_DOCUMENT", - "OTHER_POA_DOCUMENT", - "UNKNOWN" - ], - "description": "POA document category. Values are uppercase — lowercase variants like `utility_bill` return `400`.", - "example": "UTILITY_BILL" - }, - "document_language": { - "type": "string", - "maxLength": 50, - "nullable": true, - "description": "Language of the document, typically an ISO 639-1 code (e.g. `es`). Free text — not validated as a language code.", - "example": "es" - }, - "issuer": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Organization that issued the document (utility company, bank, government agency).", - "example": "Energy Provider S.A." - }, - "issue_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Issue date of the POA document in `YYYY-MM-DD`. Any other format returns `400`.", - "example": "2026-04-12" - }, - "poa_address": { - "type": "string", - "nullable": true, - "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it in the background of the same request and may overwrite `poa_parsed_address` / `poa_formatted_address` with the geocoder result. The re-geocode also requires `issuing_state` to be present on the POA record.", - "example": "Avda de Madrid 34, 28013 Madrid" - }, - "name_on_document": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Recipient name as printed on the document.", - "example": "Carmen Espanola" - }, - "extra_fields": { - "type": "object", - "additionalProperties": true, - "nullable": true, - "description": "Document-specific extras, merged key-by-key. Nine keys map to dedicated POA columns: `bank_account_number`, `bank_iban`, `bank_sort_code`, `bank_routing_number`, `bank_swift_bic`, `bank_branch_name`, `bank_branch_address`, `document_phone_number`, and `additional_names` (must be a list of strings or `null`, otherwise `400`). Any other key is stored as a custom extra field. Per-key semantics: a value sets the key; `null` or `\"\"` deletes it. Must be a JSON object, otherwise `400`." - }, - "poa_parsed_address": { - "type": "object", - "nullable": true, - "description": "Structured address. Only these nested keys are accepted: `address_type`, `city`, `region`, `street_1`, `street_2`, `postal_code`, `country` — any other key returns `400`. Per-key semantics: a value sets the key, `null` removes it. `formatted_address` (and the top-level `poa_formatted_address`) is recomputed server-side by joining `street_1`, `street_2`, `city`, `region`, `postal_code`, and `country` — you cannot set it directly.", - "properties": { - "address_type": { - "type": "string", - "nullable": true, - "description": "Free-form address classification (e.g. `home`)." - }, - "city": { - "type": "string", - "nullable": true, - "example": "Madrid" - }, - "region": { - "type": "string", - "nullable": true, - "description": "State / province / autonomous community." - }, - "street_1": { - "type": "string", - "nullable": true, - "example": "Avda de Madrid 34" - }, - "street_2": { - "type": "string", - "nullable": true - }, - "postal_code": { - "type": "string", - "nullable": true, - "example": "28013" - }, - "country": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.", - "example": "ES" - } - } - } - } - }, - "examples": { - "Correct issuer and address": { - "summary": "Fix document classification, issuer, and the structured address", - "value": { - "issuing_state": "ESP", - "document_type": "BANK_STATEMENT", - "issuer": "Banco Example S.A.", - "issue_date": "2026-05-02", - "name_on_document": "Carmen Espanola", - "poa_address": "Avda de Madrid 34, 28013 Madrid", - "extra_fields": { - "bank_iban": "ES9121000418450200051332", - "additional_names": [ - "Carmen E. Espanola" - ] - }, - "poa_parsed_address": { - "street_1": "Avda de Madrid 34", - "city": "Madrid", - "postal_code": "28013", - "country": "ES" - } - } - }, - "Remove a banking detail": { - "summary": "Delete one extra-field key, keep the rest", - "value": { - "extra_fields": { - "bank_iban": null - } - } - } - } - } - } - }, "responses": { "200": { - "description": "POA data updated. Returns the **full current POA record** (every updatable field with its stored value, plus the complete `extra_fields` map, `poa_parsed_address`, and `poa_formatted_address`) — not just the fields you submitted. The `data.updated` webhook fires only when at least one value actually changed.", + "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { - "issuing_state": { - "type": "string", - "nullable": true, - "description": "Country that issued the POA document, ISO 3166-1 alpha-3. Case-insensitive (normalized to uppercase); invalid codes return `400`.", - "example": "ESP" - }, - "document_type": { - "type": "string", - "enum": [ - "UTILITY_BILL", - "BANK_STATEMENT", - "GOVERNMENT_ISSUED_DOCUMENT", - "OTHER_POA_DOCUMENT", - "UNKNOWN" - ], - "description": "POA document category. Values are uppercase — lowercase variants like `utility_bill` return `400`.", - "example": "UTILITY_BILL" - }, - "document_language": { - "type": "string", - "maxLength": 50, - "nullable": true, - "description": "Language of the document, typically an ISO 639-1 code (e.g. `es`). Free text — not validated as a language code.", - "example": "es" - }, - "issuer": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Organization that issued the document (utility company, bank, government agency).", - "example": "Energy Provider S.A." - }, - "issue_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Issue date of the POA document in `YYYY-MM-DD`. Any other format returns `400`.", - "example": "2026-04-12" - }, - "poa_address": { - "type": "string", - "nullable": true, - "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it in the background of the same request and may overwrite `poa_parsed_address` / `poa_formatted_address` with the geocoder result.", - "example": "Avda de Madrid 34, 28013 Madrid" - }, - "name_on_document": { - "type": "string", - "maxLength": 255, - "nullable": true, - "description": "Recipient name as printed on the document.", - "example": "Carmen Espanola" - }, - "extra_fields": { - "type": "object", - "additionalProperties": true, - "nullable": true, - "description": "Always present in the response: the nine known keys (with `null` for unset ones, `additional_names` defaulting to `[]`) plus any custom keys previously stored." - }, - "poa_parsed_address": { - "type": "object", - "nullable": true, - "description": "Current structured address, or `null` when none exists. Includes the recomputed `formatted_address` alongside the seven editable keys. Returned as stored — after a server-side geocode it can contain bookkeeping keys (e.g. `raw_results`) alongside the editable keys.", - "properties": { - "address_type": { - "type": "string", - "nullable": true, - "description": "Free-form address classification (e.g. `home`)." - }, - "city": { - "type": "string", - "nullable": true, - "example": "Madrid" - }, - "region": { - "type": "string", - "nullable": true, - "description": "State / province / autonomous community." - }, - "street_1": { - "type": "string", - "nullable": true, - "example": "Avda de Madrid 34" - }, - "street_2": { - "type": "string", - "nullable": true - }, - "postal_code": { - "type": "string", - "nullable": true, - "example": "28013" - }, - "country": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.", - "example": "ES" - }, - "formatted_address": { - "type": "string", - "nullable": true, - "description": "Joined, human-readable form of the structured address. Recomputed server-side after every `poa_parsed_address` edit.", - "example": "Avda de Madrid 34, Madrid, 28013, ES" + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "format": "uuid" + }, + "email": { + "type": "string", + "format": "email" + }, + "name": { + "type": "string" + } } } - }, - "poa_formatted_address": { - "type": "string", - "nullable": true, - "description": "Top-level copy of the recomputed `formatted_address`, kept in sync when the structured address changes.", - "example": "Avda de Madrid 34, Madrid, 28013, ES" - } - } - }, - "examples": { - "Updated POA": { - "summary": "Full POA record after the correction", - "value": { - "issuing_state": "ESP", - "document_type": "BANK_STATEMENT", - "document_language": "es", - "issuer": "Banco Example S.A.", - "issue_date": "2026-05-02", - "poa_address": "Avda de Madrid 34, 28013 Madrid", - "name_on_document": "Carmen Espanola", - "extra_fields": { - "bank_account_number": null, - "bank_iban": "ES9121000418450200051332", - "bank_sort_code": null, - "bank_routing_number": null, - "bank_swift_bic": null, - "bank_branch_name": null, - "bank_branch_address": null, - "document_phone_number": null, - "additional_names": [ - "Carmen E. Espanola" - ] - }, - "poa_parsed_address": { - "city": "Madrid", - "street_1": "Avda de Madrid 34", - "postal_code": "28013", - "country": "ES", - "formatted_address": "Avda de Madrid 34, Madrid, 28013, ES" - }, - "poa_formatted_address": "Avda de Madrid 34, Madrid, 28013, ES" } } } @@ -35253,904 +54265,775 @@ } }, "400": { - "description": "Validation error. Field errors arrive as `{\"\": [\"\"]}`; errors on nested `poa_parsed_address` / `extra_fields` keys use an object value: `{\"extra_fields\": {\"\": \"\"}}`.", - "content": { - "application/json": { - "examples": { - "Invalid document type": { - "summary": "Enum is uppercase — lowercase variants are rejected", - "value": { - "document_type": [ - "\"utility_bill\" is not a valid choice." - ] - } - }, - "Invalid issuing state": { - "summary": "issuing_state is not ISO 3166-1 alpha-3", - "value": { - "issuing_state": [ - "Invalid ISO 3166-1 alpha-3 country code for issuing_state." - ] - } - }, - "additional_names not a list": { - "summary": "additional_names accepts a list of strings or null", - "value": { - "extra_fields": { - "additional_names": "Must be a list or null." - } - } - }, - "Unsupported poa_parsed_address key": { - "summary": "Only the seven documented keys are allowed", - "value": { - "poa_parsed_address": { - "zipcode": "Unsupported key. Allowed keys: address_type, city, region, street_1, street_2, postal_code, country" - } - } - }, - "Bad date format": { - "summary": "Dates must be YYYY-MM-DD", - "value": { - "issue_date": [ - "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." - ] - } - } - } - } - } + "description": "Invalid input." }, "403": { - "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", - "content": { - "application/json": { - "examples": { - "Invalid Credentials": { - "summary": "Missing or invalid x-api-key", - "value": { - "detail": "Authentication credentials were not provided or are invalid." - } - }, - "Missing Privilege": { - "summary": "Credential lacks write:sessions", - "value": { - "detail": "You do not have permission to perform this action." - } - } - } - } + "description": "Insufficient permission." + }, + "503": { + "description": "Reviewer access could not be verified. Retry later." + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/history/": { + "get": { + "operationId": "list_case_blueprint_history", + "x-didit-sdk": { + "resource": "cases.blueprints", + "method": "list_history", + "auth": "api_key", + "server": true, + "scope": "application" + }, + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "blueprint_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" } }, - "404": { - "description": "Unknown session, session in a non-editable state, or no POA record to patch. Each case has a distinct `detail` message (see examples). The unknown-session lookup runs before credential checks.", - "content": { - "application/json": { - "examples": { - "Unknown session": { - "summary": "No session with this ID in your application (or it was deleted)", - "value": { - "detail": "Not found." - } - }, - "Wrong session state": { - "summary": "Session exists but is not Approved / Declined / In Review / Kyc Expired", - "value": { - "detail": "No Session matches the given query." - } - }, - "No POA record": { - "summary": "The workflow never ran a Proof of Address step", - "value": { - "detail": "The session does not have a POA." - } - }, - "No POA for node": { - "summary": "node_id given but no POA exists for it", - "value": { - "detail": "The session does not have a POA for node 'feature_missing'." - } - } - } - } + { + "name": "limit", + "required": false, + "in": "query", + "description": "Number of results to return per page.", + "schema": { + "type": "integer" } }, - "429": { - "description": "Dedicated rate limit exceeded — 10 POA data updates per minute per API key (rejected requests count toward the budget). The shared 300/min write budget can also trigger with its own message. Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, - "examples": { - "Rate limited": { - "summary": "POA update budget exhausted", - "value": { - "detail": "Session POA data update rate limit exceeded. You can make up to 10 requests per minute." - } - } + { + "name": "offset", + "required": false, + "in": "query", + "description": "The initial index from which to return the results.", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Case Management" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CasePublicationPaginatedPolicyRevisionList" } } - } + }, + "description": "" + }, + "403": { + "description": "Required case permission is missing." } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-poa-data/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"issuing_state\": \"ESP\",\n \"document_type\": \"BANK_STATEMENT\",\n \"issuer\": \"Banco Example S.A.\",\n \"issue_date\": \"2026-05-02\",\n \"name_on_document\": \"Carmen Espanola\",\n \"poa_address\": \"Avda de Madrid 34, 28013 Madrid\",\n \"extra_fields\": {\n \"bank_iban\": \"ES9121000418450200051332\"\n },\n \"poa_parsed_address\": {\n \"city\": \"Madrid\",\n \"postal_code\": \"28013\",\n \"country\": \"ES\"\n }\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-poa-data/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\n \"issuing_state\": \"ESP\",\n \"document_type\": \"BANK_STATEMENT\",\n \"issuer\": \"Banco Example S.A.\",\n \"issue_date\": \"2026-05-02\",\n \"name_on_document\": \"Carmen Espanola\",\n \"poa_address\": \"Avda de Madrid 34, 28013 Madrid\",\n \"extra_fields\": {\"bank_iban\": \"ES9121000418450200051332\"},\n \"poa_parsed_address\": {\"city\": \"Madrid\", \"postal_code\": \"28013\", \"country\": \"ES\"},\n },\n)\nresponse.raise_for_status()\npoa = response.json() # full current POA record\nprint(poa[\"issuer\"], poa[\"poa_formatted_address\"])" - }, + "security": [ { - "lang": "javascript", - "label": "JavaScript", - "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-poa-data/',\n {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n issuing_state: 'ESP',\n document_type: 'BANK_STATEMENT',\n issuer: 'Banco Example S.A.',\n issue_date: '2026-05-02',\n name_on_document: 'Carmen Espanola',\n poa_address: 'Avda de Madrid 34, 28013 Madrid',\n poa_parsed_address: { city: 'Madrid', postal_code: '28013', country: 'ES' },\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst poa = await response.json(); // full current POA record" + "ApiKeyAuth": [] } ] } }, - "/v3/session/{sessionId}/kyb/{companyUuid}/update-data/": { - "patch": { - "summary": "Update registry-extracted company data on a Business Verification (KYB) session", - "description": "Correct the registry-extracted company data on a Business Verification (KYB) session — company name, registration number, addresses, status, contact details — identified by `companyUuid`. Typical callers are a compliance analyst fixing a registry misread, or your back office syncing corrected company data. Every field in the body is optional (`PATCH` semantics); an empty JSON body (`{}`) is a no-op that simply echoes the current company record.\n\nEdits are written in two places at once: fields with a canonical company column (name, registration number, country, incorporation date, tax number, type, statuses, capital fields (`registered_capital`, `registered_capital_amount`, `registered_capital_currency`), alternative names, nature of business, website, email, phone, LEI, location, and `legal_address` → `registered_address`) update that column, and **every** submitted key is also merged into the `registry_data` snapshot (dates as ISO strings) so the \"extracted from registry\" view reflects the correction. Fields without a column — `region`, `registration_date`, `dissolution_date`, `control_scheme` — land in `registry_data` only. `user_provided_data` (what the end user confirmed in the hosted flow) is never touched, and `last_console_edit_at` is stamped on every non-empty PATCH.\n\nUnlike the KYC/POA update endpoints, there is **no session-status gate**: the company can be edited at any point in the business session's lifecycle. The company must belong to the session in the path (`404` with `\"KYB company does not belong to the given session.\"` otherwise) and to your application. Every non-empty PATCH fires the `data.updated` [webhook](/integration/webhooks) on the business session (trigger `console_registry_edit`) — even when the submitted values equal the stored ones. The session's status and decision are **not** re-evaluated, and no review/audit entry is recorded.\n\nAccepts an API key or a console user token; either way the credential needs the `write:businesses` permission. Authentication and permission failures both return `403` — this API never responds `401`. Note that the session lookup runs before credential checks, so an unknown `sessionId` returns `404` even with missing credentials. The shared write budget of 300 requests per minute per API key applies.\n\nFor User Verification sessions, the KYC counterpart is `PATCH /v3/session/{sessionId}/update-data/`; to change the session decision itself use `PATCH /v3/session/{sessionId}/update-status/`.", - "operationId": "patch_v3_session_kyb_company_update_data", + "/v3/session/{sessionId}/signatures/{nodeId}/documents/{documentId}/file/": { + "get": { + "summary": "Download a signed document", + "description": "Download the signed PDF of one document of an Electronic Signature step. Use the `signed_file` path of `signatures[].documents[]` in the decision. The PDF holds the document the signer was shown with their field values stamped, the evidence page (unless `signature_evidence_page` is off) and the PAdES signature with its timestamp. Didit checks the file against `sha256_signed` before returning it.\n\nWorks for User Verification (KYC) and Business Verification (KYB) sessions. Needs `read:sessions`; your API key works. See the [Electronic Signature report](/core-technology/electronic-signature/report-electronic-signature#download-the-signed-documents).", + "operationId": "get_v3_session_signature_document_file", + "x-didit-sdk": { + "resource": "sessions.signatures", + "method": "download_document", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], - "security": [ - { - "ApiKeyAuth": [] - } - ], "parameters": [ { "name": "sessionId", "in": "path", "required": true, - "description": "UUID of the parent Business Verification (KYB) session.", + "description": "The User Verification (KYC) or Business Verification (KYB) session.", "schema": { "type": "string", "format": "uuid", - "example": "33333333-4444-5555-6666-777777777777" + "example": "11111111-2222-3333-4444-555555555555" } }, { - "name": "companyUuid", + "name": "nodeId", "in": "path", "required": true, - "description": "UUID of the company record to update (`registry_checks[].company.uuid` on the business session decision). Must belong to the session referenced by `sessionId`.", + "description": "The Electronic Signature step: `node_id` of the item in `signatures[]`.", "schema": { "type": "string", - "format": "uuid", - "example": "88888888-9999-aaaa-bbbb-cccccccccccc" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "All fields optional — send only what you want to change. Provided fields update the canonical company columns (where one exists) and are merged into the `registry_data` snapshot.", - "properties": { - "company_name": { - "type": "string", - "maxLength": 255, - "description": "Legal name of the company. Written to the canonical column and the registry snapshot.", - "example": "Docs Probe Corp" - }, - "registration_number": { - "type": "string", - "maxLength": 100, - "description": "Official registration number with the company registry.", - "example": "DPC-001" - }, - "country_code": { - "type": "string", - "maxLength": 2, - "description": "Country of incorporation, ISO 3166-1 alpha-2. Case-insensitive (`us` is stored as `US`). Alpha-3 codes fail the 2-character limit with `400`.", - "example": "US" - }, - "region": { - "type": "string", - "maxLength": 10, - "nullable": true, - "description": "Subdivision / region tag where the registry has one (e.g. US state). Stored in the registry snapshot only — it has no canonical column." - }, - "incorporation_date": { - "type": "string", - "format": "date", - "description": "Incorporation date in `YYYY-MM-DD`. Optional like every other field — the `PATCH` is partial. Any other date format returns `400`.", - "example": "2015-04-12" - }, - "registration_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Registration date where the registry distinguishes it from incorporation. Stored in the registry snapshot only." - }, - "dissolution_date": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Dissolution date for companies that are no longer active. Stored in the registry snapshot only." - }, - "tax_number": { - "type": "string", - "maxLength": 100, - "description": "Tax identification number." - }, - "legal_address": { - "type": "string", - "description": "Legal / registered address. Aliased server-side to the canonical `registered_address` column — the response returns it under `registered_address`, while the registry snapshot keeps the `legal_address` key you sent.", - "example": "1 Example Plaza, Wilmington, DE 19801" - }, - "company_type": { - "type": "string", - "maxLength": 100, - "description": "Legal entity type (e.g. `Limited Liability Company`, `PLC`, `GmbH`).", - "example": "Limited Liability Company" - }, - "registry_status": { - "type": "string", - "enum": [ - "active", - "dissolved", - "deregistered", - "see full details", - "authorised", - "appointed representative", - "unauthorised", - "inactive", - "no longer authorised", - "closed", - "struck off" - ], - "nullable": true, - "description": "Registry-reported company status. Lowercase, some values contain spaces (e.g. `struck off`, `no longer authorised`). Any other value returns `400`.", - "example": "active" - }, - "verification_status": { - "type": "string", - "enum": [ - "verified", - "failed", - "unknown" - ], - "nullable": true, - "description": "Outcome of the registry verification. Any other value returns `400`.", - "example": "verified" - }, - "alternative_names": { - "type": "string", - "description": "Trade names, former legal names, DBA names (free text)." - }, - "nature_of_business": { - "type": "string", - "description": "Short description of the company's activity.", - "example": "Software development" - }, - "registered_capital": { - "type": "string", - "maxLength": 100, - "description": "Human-readable registered capital (e.g. `USD 50000`). Use `registered_capital_amount` + `registered_capital_currency` for structured values, or this field for free-form text.", - "example": "USD 50000" - }, - "registered_capital_amount": { - "type": "number", - "format": "double", - "nullable": true, - "description": "Numeric registered-capital amount (up to 20 digits, 2 decimal places). Sets the `registered_capital_amount` column and is mirrored into `registry_data` as a decimal string; the response echoes it as a decimal string (e.g. `\"1500000.50\"`)." - }, - "registered_capital_currency": { - "type": "string", - "maxLength": 3, - "nullable": true, - "description": "ISO 4217 currency code for `registered_capital_amount`. Case-insensitive (`usd` is stored as `USD`); values that are not exactly 3 letters return `400` with the ISO 4217 message. Sets the canonical `registered_capital_currency` column and is mirrored into `registry_data`.", - "example": "USD" - }, - "website": { - "type": "string", - "maxLength": 500, - "description": "Company website. Bare domains are accepted and normalized with an `https://` prefix (`docsprobe.example.com` is stored as `https://docsprobe.example.com`); values that still fail URL validation return `400`.", - "example": "https://docsprobe.example.com" - }, - "email": { - "type": "string", - "format": "email", - "description": "Contact email on file with the registry.", - "example": "legal@docsprobe.example.com" - }, - "phone": { - "type": "string", - "maxLength": 50, - "description": "Contact phone. Must match `+`, digits, spaces, dashes, dots, or parentheses, with 7-15 digits total — otherwise `400`.", - "example": "+1 302 555 0143" - }, - "legal_entity_identifier": { - "type": "string", - "maxLength": 50, - "description": "LEI, where the company has one." - }, - "location_of_registration": { - "type": "string", - "maxLength": 255, - "description": "City / subdivision of the registry of record." - }, - "vat_number": { - "type": "string", - "maxLength": 64, - "description": "EU VAT number. Merged into the `registry_data` snapshot only — editing it here does not re-run VAT and tax ID validation, so `vat_validation_status` and the other `vat_*` result fields are unchanged." - }, - "control_scheme": { - "type": "string", - "description": "Control / governance scheme when the registry exposes it. Stored in the registry snapshot only." - } - } - }, - "examples": { - "Correct registry data": { - "summary": "Fix the company's registry-extracted fields", - "value": { - "company_name": "Docs Probe Corp", - "registration_number": "DPC-001", - "country_code": "US", - "incorporation_date": "2015-04-12", - "company_type": "Limited Liability Company", - "legal_address": "1 Example Plaza, Wilmington, DE 19801", - "registry_status": "active", - "verification_status": "verified", - "website": "docsprobe.example.com", - "email": "legal@docsprobe.example.com", - "phone": "+1 302 555 0143", - "registered_capital": "USD 50000", - "nature_of_business": "Software development" - } - }, - "Single field": { - "summary": "Only the keys you send are written", - "value": { - "registry_status": "dissolved" - } - }, - "No-op": { - "summary": "Empty body echoes the current record without firing the webhook", - "value": {} - } - } + "example": "feature_signature" } - } - }, - "responses": { - "200": { - "description": "Company data updated (or echoed unchanged for an empty body). Returns the full KYB company record — the same shape as `registry_checks[].company` on the business session decision. Note how `legal_address` comes back as `registered_address`. `registered_capital_amount`/`registered_capital_currency` populate their canonical columns and are mirrored into `registry_data`.", - "content": { - "application/json": { - "schema": { - "type": "object", - "description": "Full KYB company record — the same shape as the `company` object inside `registry_checks[]` on the business session decision.", - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Company record identifier (the `companyUuid` path parameter)." - }, - "node_id": { - "type": "string", - "nullable": true, - "description": "Workflow graph node this company record belongs to, when the workflow is graph-based." - }, - "status": { - "type": "string", - "description": "Feature lifecycle status of the registry check (e.g. `Not Finished`, `Approved`, `Declined`, `In Review`)." - }, - "registry_status": { - "type": "string", - "nullable": true, - "description": "Registry-reported company status (e.g. `active`, `dissolved`, `struck off`)." - }, - "data_resolved": { - "type": "boolean", - "description": "`true` when the company data is complete — always `true` for manual-entry companies; for registry-sourced companies, `true` once the registry fetch resolved." - }, - "company_name": { - "type": "string", - "nullable": true - }, - "registration_number": { - "type": "string", - "nullable": true - }, - "country_code": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-2 country of incorporation." - }, - "region": { - "type": "string", - "nullable": true - }, - "company_type": { - "type": "string", - "nullable": true - }, - "incorporation_date": { - "type": "string", - "format": "date", - "nullable": true - }, - "registered_address": { - "type": "string", - "nullable": true, - "description": "Canonical registered address — updated when you send `legal_address`." - }, - "tax_number": { - "type": "string", - "nullable": true - }, - "risk_level": { - "type": "string", - "nullable": true - }, - "verification_status": { - "type": "string", - "nullable": true, - "description": "`verified`, `failed`, or `unknown`." - }, - "is_from_registry": { - "type": "boolean", - "description": "`true` when the company was pre-filled from a registry search rather than entered manually." - }, - "fetch_status": { - "type": "string", - "nullable": true, - "description": "Registry fetch lifecycle for registry-sourced companies (`pending` / `resolved`); `null` for manual entries." - }, - "alternative_names": { - "type": "string", - "nullable": true, - "description": "Alternative, trading, former or original-language company names as text, when reported." - }, - "nature_of_business": { - "type": "string", - "nullable": true - }, - "registered_capital": { - "type": "string", - "nullable": true - }, - "registered_capital_amount": { - "type": "string", - "nullable": true, - "description": "Canonical column. Returned as a decimal string (e.g. `\"2750000.00\"`)." - }, - "registered_capital_currency": { - "type": "string", - "nullable": true, - "description": "Canonical column. Set by the registry flow or by console/API edits via this endpoint." - }, - "website": { - "type": "string", - "nullable": true - }, - "email": { - "type": "string", - "nullable": true - }, - "phone": { - "type": "string", - "nullable": true - }, - "legal_entity_identifier": { - "type": "string", - "nullable": true - }, - "location_of_registration": { - "type": "string", - "nullable": true - }, - "vat_number": { - "type": "string", - "nullable": true, - "description": "EU VAT number, when collected during the hosted registry step or edited afterwards." - }, - "vat_validation_status": { - "type": "string", - "enum": [ - "valid", - "invalid", - "format_valid", - "invalid_format", - "could_not_validate", - "not_applicable" - ], - "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." - }, - "vat_validated_number": { - "type": "string", - "nullable": true, - "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." - }, - "vat_validation_source": { - "type": "string", - "nullable": true, - "enum": [ - "eu_vies", - "pl_vat_white_list", - "no_brreg", - "ch_uid_register", - "br_receita_federal", - "co_rues", - "cr_hacienda", - "ec_sri", - "py_dnit", - "format_rules", - null - ], - "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." - }, - "vat_validated_name": { - "type": "string", - "nullable": true, - "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_validated_address": { - "type": "string", - "nullable": true, - "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_checked_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the check. `null` when no check ran." - }, - "financial_summary": { - "type": "object", - "nullable": true, - "additionalProperties": true - }, - "officers": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true - }, - "description": "Directors / officers attached to the company, including per-person KYC progress." - }, - "beneficial_owners": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true - }, - "description": "Beneficial owners attached to the company, including per-person KYC progress." - }, - "addresses": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "additionalProperties": true - }, - "description": "Registry address detail, when the registry returned any." - }, - "industries": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "additionalProperties": true - }, - "description": "Registry industry classification, when available." - }, - "accounts": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." - }, - "registry_data": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Raw registry payload snapshot. Every key you PATCH is merged into this map (dates serialized as ISO strings), so the \"extracted from registry\" view reflects operator corrections." - }, - "user_provided_data": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "What the end user typed during the hosted registry-confirmation step. Never modified by this endpoint." - }, - "confirmed_by_user_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the end user confirmed the company data in the hosted flow." - }, - "last_console_edit_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Stamped on every successful non-empty PATCH, so UIs can surface \"edited by operator\" context." + }, + { + "name": "documentId", + "in": "path", + "required": true, + "description": "`id` of the document in `signatures[].documents[]`.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "The signed PDF.", + "headers": { + "Content-Disposition": { + "description": "Attachment with the file name.", + "schema": { + "type": "string", + "example": "attachment; filename=\"agreement-signed.pdf\"" + } + } + }, + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "403": { + "description": "Missing or invalid credentials, or a credential that can't read this session. Didit's API-key authentication answers `403`, never `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "You do not have permission to perform this action." + } + } + } + }, + "404": { + "description": "No signed document matches: the session, step or document doesn't exist, was deleted, or isn't signed yet.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "code": { + "type": "string" }, - "is_editable": { - "type": "boolean", - "description": "`true` while the end user can still edit the registry-sourced data in the hosted flow (registry-sourced and not yet confirmed)." + "detail": { + "type": "string" } } }, "examples": { - "Updated company": { - "summary": "Full company record after the correction", + "Not signed yet": { + "summary": "The signer hasn't signed", "value": { - "uuid": "6b9479f5-6769-46c8-baf1-df63bbbc5f8c", - "node_id": null, - "status": "Not Finished", - "registry_status": "active", - "data_resolved": true, - "company_name": "Docs Probe Corp", - "registration_number": "DPC-001", - "country_code": "US", - "region": null, - "company_type": "Limited Liability Company", - "incorporation_date": "2015-04-12", - "registered_address": "1 Example Plaza, Wilmington, DE 19801", - "tax_number": null, - "risk_level": null, - "verification_status": "verified", - "is_from_registry": false, - "fetch_status": null, - "alternative_names": null, - "nature_of_business": "Software development", - "registered_capital": "USD 50000", - "registered_capital_amount": null, - "registered_capital_currency": "USD", - "website": "https://docsprobe.example.com", - "email": "legal@docsprobe.example.com", - "phone": "+1 302 555 0143", - "legal_entity_identifier": null, - "location_of_registration": null, - "financial_summary": null, - "officers": [], - "beneficial_owners": [], - "addresses": null, - "industries": null, - "accounts": null, - "registry_data": { - "company_name": "Docs Probe Corp", - "registration_number": "DPC-001", - "country_code": "US", - "incorporation_date": "2015-04-12", - "legal_address": "1 Example Plaza, Wilmington, DE 19801", - "company_type": "Limited Liability Company", - "registry_status": "active", - "verification_status": "verified", - "nature_of_business": "Software development", - "registered_capital": "USD 50000", - "registered_capital_currency": "USD", - "website": "https://docsprobe.example.com", - "email": "legal@docsprobe.example.com", - "phone": "+1 302 555 0143" - }, - "user_provided_data": null, - "confirmed_by_user_at": null, - "last_console_edit_at": "2026-06-12T00:02:34.276061Z", - "is_editable": false + "code": "signature_not_signed", + "detail": "signature_not_signed" + } + }, + "Unknown document": { + "summary": "No such document in this step", + "value": { + "code": "signature_not_found", + "detail": "signature_not_found" } } } } } }, - "400": { - "description": "Validation error. Field errors arrive as `{\"\": [\"\"]}`. Max-length checks run before format checks, so `country_code: \"USA\"` reports the 2-character limit rather than the ISO message.", + "409": { + "description": "The stored file no longer matches its recorded SHA-256. Nothing is returned.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" + } + } + }, "examples": { - "Alpha-3 country code": { - "summary": "country_code must be alpha-2 — length check fires first", + "Changed file": { + "summary": "Stored file doesn't match its hash", "value": { - "country_code": [ - "Ensure this field has no more than 2 characters." - ] + "code": "signature_document_changed", + "detail": "signature_document_changed" } - }, - "Non-alphabetic country code": { - "summary": "2 characters but not letters", - "value": { - "country_code": [ - "country_code must be a 2-letter ISO 3166-1 alpha-2 code." - ] + } + } + } + } + } + } + } + }, + "/v3/session/{sessionId}/signatures/{nodeId}/evidence/": { + "get": { + "summary": "Download the signature evidence", + "description": "Download a signer's evidence PDF: the level and format, the signer and their verified identity session, the code confirmation, the IP address, each document with its original SHA-256 and field values, the certificate, the timestamp and the SHA-256 of every signed file. Use the `evidence_file` path of the signer's item in `signatures[]`.\n\nAlways pass `signature_id` when a Business Verification step has several signers: without it, the route picks one of the step's signers. Needs `read:sessions`; your API key works.", + "operationId": "get_v3_session_signature_evidence", + "x-didit-sdk": { + "resource": "sessions.signatures", + "method": "download_evidence", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "description": "The User Verification (KYC) or Business Verification (KYB) session.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + }, + { + "name": "nodeId", + "in": "path", + "required": true, + "description": "The Electronic Signature step: `node_id` of the item in `signatures[]`.", + "schema": { + "type": "string", + "example": "feature_signature" + } + }, + { + "name": "signature_id", + "in": "query", + "required": false, + "description": "`id` of the signer's item in `signatures[]`.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "The evidence PDF.", + "headers": { + "Content-Disposition": { + "description": "Attachment with the file name.", + "schema": { + "type": "string", + "example": "attachment; filename=\"signature-evidence.pdf\"" + } + } + }, + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "403": { + "description": "Missing or invalid credentials, or a credential that can't read this session. Didit's API-key authentication answers `403`, never `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" } - }, - "Invalid currency": { - "summary": "3 characters but not an ISO 4217 code", - "value": { - "registered_capital_currency": [ - "Currency must be a 3-letter ISO 4217 code (e.g. USD, EUR, GBP)." - ] + } + }, + "example": { + "detail": "You do not have permission to perform this action." + } + } + } + }, + "404": { + "description": "No evidence matches: the session, step or signer doesn't exist, was deleted, or hasn't signed.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" } - }, - "Invalid phone": { - "summary": "Phone fails the format / digit-count check", + } + }, + "examples": { + "Not signed yet": { + "summary": "The signer hasn't signed", "value": { - "phone": [ - "Invalid phone number format." - ] + "code": "signature_not_signed", + "detail": "signature_not_signed" } }, - "Invalid registry status": { - "summary": "registry_status not in the enum", + "Unknown signer": { + "summary": "No such signature in this step", "value": { - "registry_status": [ - "\"liquidated\" is not a valid choice." - ] + "code": "signature_not_found", + "detail": "signature_not_found" } - }, - "Invalid email": { - "summary": "Malformed email address", - "value": { - "email": [ - "Enter a valid email address." - ] + } + } + } + } + }, + "409": { + "description": "The stored file no longer matches its recorded SHA-256. Nothing is returned.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" } - }, - "Invalid website": { - "summary": "Value fails URL validation even after https:// normalization", + } + }, + "examples": { + "Changed file": { + "summary": "Stored file doesn't match its hash", "value": { - "website": [ - "Enter a valid URL." - ] + "code": "signature_document_changed", + "detail": "signature_document_changed" } - }, - "Bad date format": { - "summary": "Dates must be YYYY-MM-DD", - "value": { - "incorporation_date": [ - "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." - ] + } + } + } + } + } + } + } + }, + "/v3/session/{sessionId}/signatures/{nodeId}/fields/{fieldId}/image/": { + "get": { + "summary": "Download a signature image", + "description": "Download the signature or initials a signer applied to a field, as PNG. Use the `image_url` of the field in `signatures[].documents[].fields[]`. Needs `read:sessions`; your API key works.", + "operationId": "get_v3_session_signature_field_image", + "x-didit-sdk": { + "resource": "sessions.signatures", + "method": "download_field_image", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "description": "The User Verification (KYC) or Business Verification (KYB) session.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + }, + { + "name": "nodeId", + "in": "path", + "required": true, + "description": "The Electronic Signature step: `node_id` of the item in `signatures[]`.", + "schema": { + "type": "string", + "example": "feature_signature" + } + }, + { + "name": "fieldId", + "in": "path", + "required": true, + "description": "The field on this signer's copy, as in the field's `image_url`.", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "The image.", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "The stored image no longer matches its recorded SHA-256.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" } } + }, + "example": { + "code": "signature_document_changed", + "detail": "signature_document_changed" } } } }, "403": { - "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", + "description": "Missing or invalid credentials, or a credential that can't read this session. Didit's API-key authentication answers `403`, never `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "You do not have permission to perform this action." + } + } + } + }, + "404": { + "description": "No image matches: the session, step or field doesn't exist, was deleted, or has no applied signature or initials.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" + } + } + }, "examples": { - "Invalid Credentials": { - "summary": "Missing or invalid x-api-key", + "Unknown field": { + "summary": "No such field", "value": { - "detail": "Authentication credentials were not provided or are invalid." + "code": "signature_field_not_found", + "detail": "signature_field_not_found" } - }, - "Missing Privilege": { - "summary": "Credential lacks write:businesses", - "value": { - "detail": "You do not have permission to perform this action." + } + } + } + } + } + } + } + }, + "/v3/session/{sessionId}/signatures/{nodeId}/validate/": { + "post": { + "summary": "Validate signatures again", + "description": "Re-check the signed documents of an Electronic Signature step. Didit re-reads every signed PDF, checks its SHA-256 and validates the PAdES signature, the certificate, the revocation data and the timestamp, then saves the result in each signer's `validation` and returns the updated items. Nothing is signed again. Send `signature_id` to check one signer; without it, every signer of the step is checked, and the call fails if any of them hasn't signed.\n\nNeeds `write:sessions`; your API key works. In the console it's the **Validate again** button.", + "operationId": "post_v3_session_signature_validate", + "x-didit-sdk": { + "resource": "sessions.signatures", + "method": "validate", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "description": "The User Verification (KYC) or Business Verification (KYB) session.", + "schema": { + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + }, + { + "name": "nodeId", + "in": "path", + "required": true, + "description": "The Electronic Signature step: `node_id` of the item in `signatures[]`.", + "schema": { + "type": "string", + "example": "feature_signature" + } + } + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "signature_id": { + "type": "string", + "format": "uuid", + "description": "`id` of one signer's item in `signatures[]`." + } + } + }, + "example": { + "signature_id": "0e6c2a8e-6c2f-4b8e-9a51-3f2f6a7d9b10" + } + } + } + }, + "responses": { + "200": { + "description": "The checked signers, with their updated `validation`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "signatures": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SignatureResult" + } } } } } } }, - "404": { - "description": "Unknown session, unknown company, or a company that belongs to a different session. Each case has a distinct `detail` message (see examples). The unknown-session lookup runs before credential checks.", + "400": { + "description": "A stored signed PDF no longer matches the SHA-256 recorded when it was signed.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" + } + } + }, "examples": { - "Unknown session": { - "summary": "No session with this ID in your application (or it was deleted)", + "Document changed": { + "summary": "A signed file no longer matches its hash", "value": { - "detail": "Not found." + "code": "signature_document_changed", + "detail": "signature_document_changed" } - }, - "Unknown company": { - "summary": "No company with this UUID in your application", - "value": { - "detail": "No KYBCompany matches the given query." + } + } + } + } + }, + "403": { + "description": "Missing or invalid credentials, or a credential that can't read this session. Didit's API-key authentication answers `403`, never `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "example": { + "detail": "You do not have permission to perform this action." + } + } + } + }, + "404": { + "description": "No signature matches: the session, step or signer doesn't exist or was deleted.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" } - }, - "Company / session mismatch": { - "summary": "companyUuid exists but belongs to another session", + } + }, + "examples": { + "Unknown signer": { + "summary": "No such signature in this step", "value": { - "detail": "KYB company does not belong to the given session." + "code": "signature_not_found", + "detail": "signature_not_found" } } } } } }, - "429": { - "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", + "409": { + "description": "A signer of the step hasn't signed, so there's nothing to validate.", "content": { "application/json": { "schema": { "type": "object", "properties": { + "code": { + "type": "string" + }, "detail": { "type": "string" } } }, "examples": { - "Rate limited": { - "summary": "Write budget exhausted", + "Not signed": { + "summary": "A signer hasn't signed yet", "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + "code": "signature_not_signed", + "detail": "signature_not_signed" } } } } } } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH \\\n https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/88888888-9999-aaaa-bbbb-cccccccccccc/update-data/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"company_name\": \"Docs Probe Corp\",\n \"registration_number\": \"DPC-001\",\n \"country_code\": \"US\",\n \"incorporation_date\": \"2015-04-12\",\n \"company_type\": \"Limited Liability Company\",\n \"legal_address\": \"1 Example Plaza, Wilmington, DE 19801\",\n \"registry_status\": \"active\"\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import requests\n\nresponse = requests.patch(\n \"https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/\"\n \"88888888-9999-aaaa-bbbb-cccccccccccc/update-data/\",\n headers={\n \"x-api-key\": \"YOUR_API_KEY\",\n \"Content-Type\": \"application/json\",\n },\n json={\n \"company_name\": \"Docs Probe Corp\",\n \"registration_number\": \"DPC-001\",\n \"country_code\": \"US\",\n \"incorporation_date\": \"2015-04-12\",\n \"legal_address\": \"1 Example Plaza, Wilmington, DE 19801\",\n \"registry_status\": \"active\",\n },\n)\nresponse.raise_for_status()\ncompany = response.json() # full company record\nprint(company[\"company_name\"], company[\"registered_address\"])" - }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const response = await fetch(\n 'https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/' +\n '88888888-9999-aaaa-bbbb-cccccccccccc/update-data/',\n {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n company_name: 'Docs Probe Corp',\n registration_number: 'DPC-001',\n country_code: 'US',\n incorporation_date: '2015-04-12',\n legal_address: '1 Example Plaza, Wilmington, DE 19801',\n registry_status: 'active',\n }),\n },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst company = await response.json(); // full company record" - } - ] + } } }, - "/v3/session/{sessionId}/kyb/parties/{partyUuid}/": { + "/v3/document-ai/{sessionId}/documents/{documentUuid}/": { "patch": { - "summary": "Skip (or re-require) a Key People party on a Business Verification (KYB) session", - "description": "Exempt a Key People party from verification, or put it back in scope. This is the operator escape hatch for a party that can never finish its own check - most often because the individual KYC session handed to a UBO or director was deleted, which freezes that child's status and leaves the KYB parked in `Awaiting User` with nothing left that could move it.\n\nSetting `is_skipped: true` drops the party out of the Key People aggregation and immediately recomputes the `KYBKeyPeopleCheck` and the parent session status, so the business session can settle and then be approved or declined. Sending `is_skipped: false` puts the party back in scope and the check goes back to waiting on it.\n\nUnlike the applicant-facing skip in the hosted Key People screen, this endpoint is **not** gated by the role's `allow_skip` setting - that flag limits what the applicant may decide, not what your analyst may override. `requires_verification` is deliberately left untouched, so an overridden party reads as `requires_verification: true` **and** `is_skipped: true` and the record still shows what the rules asked for.\n\nThe party must belong to the session in the path (`404` with `\"Party does not belong to the given session.\"` otherwise) and to your application. A PATCH that actually changes the flag fires the `data.updated` [webhook](/integration/webhooks) on the business session with trigger `console_key_people_skip`; a PATCH that sets the flag to its current value is a no-op that echoes the party back.\n\nAccepts an API key or a console user token; either way the credential needs the `write:businesses` permission.", - "operationId": "patch_v3_session_kyb_party_update", + "summary": "Update a Document AI document", + "description": "Correct the fields Document AI extracted from one uploaded document, set its review status, or both, on a KYC or KYB session. `extracted_data` must be a JSON object and is merged key by key: a key you send replaces the stored value (a key sent as `null` is stored as `null`) and keys you leave out keep their values. The document status feeds the Document AI result of its workflow node in the session decision; the session status itself is not recomputed. Every successful call, including one that changes nothing, sends a `data.updated` [webhook](https://docs.didit.me/integration/webhooks) with trigger `manual_review`. A scoped API key needs `decisions: write`; see [Document AI](https://docs.didit.me/core-technology/document-ai/overview).", + "operationId": "patch_v3_document_ai_document", + "x-didit-sdk": { + "resource": "sessions", + "method": "update_document_ai_data", + "auth": "api_key", + "server": true + }, "tags": [ "Sessions" ], - "security": [ - { - "ApiKeyAuth": [] - } - ], "parameters": [ { "name": "sessionId", "in": "path", "required": true, - "description": "UUID of the parent Business Verification (KYB) session.", + "description": "UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`.", "schema": { "type": "string", "format": "uuid", - "example": "33333333-4444-5555-6666-777777777777" + "example": "11111111-2222-3333-4444-555555555555" } }, { - "name": "partyUuid", + "name": "documentUuid", "in": "path", "required": true, - "description": "UUID of the Key People party to update (`key_people_checks[].submitted.parties[].uuid` on the business session decision). Must belong to the session referenced by `sessionId`.", + "description": "UUID of the Document AI document (`document_ai_documents[].items[].uuid` in `GET /v3/session/{sessionId}/decision/`). It must belong to the session in the path.", "schema": { "type": "string", "format": "uuid", - "example": "0a3f1e2c-7c2f-4a1b-9d55-2f8d2a5d6b41" + "example": "44444444-5555-6666-7777-888888888888" } } ], @@ -36160,104 +55043,166 @@ "application/json": { "schema": { "type": "object", - "required": [ - "is_skipped" - ], "properties": { - "is_skipped": { - "type": "boolean", - "description": "`true` exempts the party from verification so the Key People check can settle without it; `false` puts it back in scope.", - "example": true + "extracted_data": { + "type": "object", + "additionalProperties": true, + "description": "Field keys (the keys you configured for the document) and their corrected values. Must be a JSON object; merged key by key into the stored data. Leave it out rather than sending `null`, which returns `400`.", + "example": { + "invoice_number": "INV-2026-0042" + } + }, + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "In Review", + "Not Finished" + ], + "description": "New review status of the document.", + "example": "Approved" } } + }, + "example": { + "extracted_data": { + "invoice_number": "INV-2026-0042" + }, + "status": "Approved" } } } }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/document-ai/11111111-2222-3333-4444-555555555555/documents/44444444-5555-6666-7777-888888888888/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"extracted_data\": {\"invoice_number\": \"INV-2026-0042\"}, \"status\": \"Approved\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nsession_id = '11111111-2222-3333-4444-555555555555'\ndocument_uuid = '44444444-5555-6666-7777-888888888888'\nresp = requests.patch(\n f'https://verification.didit.me/v3/document-ai/{session_id}/documents/{document_uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'extracted_data': {'invoice_number': 'INV-2026-0042'}, 'status': 'Approved'},\n timeout=10,\n)\nresp.raise_for_status()\ndocument = resp.json()\nprint(document['status'], document['extracted_data'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const sessionId = '11111111-2222-3333-4444-555555555555';\nconst documentUuid = '44444444-5555-6666-7777-888888888888';\nconst resp = await fetch(`https://verification.didit.me/v3/document-ai/${sessionId}/documents/${documentUuid}/`, {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ extracted_data: { invoice_number: 'INV-2026-0042' }, status: 'Approved' }),\n});\nif (!resp.ok) throw new Error(`Update failed: ${resp.status}`);\nconst document = await resp.json();\nconsole.log(document.status, document.extracted_data);" + } + ], "responses": { "200": { - "description": "Party updated (or echoed unchanged when the flag already had this value). Returns the party - the same shape as an entry of `key_people_checks[].submitted.parties[]` on the business session decision.", + "description": "Document updated. Returns the document after the merge.", "content": { "application/json": { "schema": { "type": "object", - "description": "Key People party record - the same shape as an entry of `key_people_checks[].submitted.parties[]` on the business session decision.", + "required": [ + "uuid", + "document_key", + "original_filename", + "file_url", + "status", + "extracted_data" + ], "properties": { "uuid": { "type": "string", "format": "uuid", - "description": "Party identifier." - }, - "entity_type": { - "type": "string", - "enum": [ - "person", - "company" - ], - "description": "Whether the party is an individual or a company." + "description": "Document identifier." }, - "name": { + "document_key": { "type": "string", - "description": "Display name of the party." - }, - "is_skipped": { - "type": "boolean", - "description": "Whether the party is exempt from verification." - }, - "requires_verification": { - "type": "boolean", - "description": "What the role and threshold rules asked for. Left untouched by this endpoint." + "description": "The configured document this upload corresponds to." }, - "kyc_session_status": { + "original_filename": { "type": "string", "nullable": true, - "description": "Status of the linked individual KYC session, frozen at deletion time when the session was deleted." + "description": "File name the document was uploaded with." }, - "kyc_session_url": { + "file_url": { "type": "string", "nullable": true, - "description": "Link to the linked individual KYC session. `null` when there is none or when it was deleted." + "description": "Storage path of the uploaded file. It is not a download link: read the presigned `file_url` from the session decision to download the file." }, - "kyc_session_deleted": { - "type": "boolean", - "description": "The linked individual KYC session was deleted, so it can never settle." + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Review status of the document." }, - "kyb_sub_session_deleted": { - "type": "boolean", - "description": "Corporate-party counterpart of `kyc_session_deleted`." + "extracted_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "All extracted fields after the merge, keyed by field key." } } + }, + "example": { + "uuid": "44444444-5555-6666-7777-888888888888", + "document_key": "invoice", + "original_filename": "invoice-2026-09.pdf", + "file_url": "document_ai/11111111-2222-3333-4444-555555555555-66666666-7777-8888-9999-000000000000.pdf", + "status": "Approved", + "extracted_data": { + "invoice_number": "INV-2026-0042", + "total_amount": 1250.0, + "issue_date": "2026-09-01" + } } } } }, "400": { - "description": "Validation error on the request body. `is_skipped` is the only field this endpoint reads, and it is required. Field errors arrive as `{\"\": [\"\"]}`. Body validation runs *after* the party lookup, so a malformed `is_skipped` sent to a party that does not exist reports `404`, not `400`.", + "description": "Validation error, checked before the document is looked up: `status` not one of the allowed values, or `extracted_data` sent as `null`.", "content": { "application/json": { "examples": { - "Missing is_skipped": { - "summary": "Empty body, or a body that omits `is_skipped`", + "Invalid status": { + "summary": "status not in the enum", "value": { - "is_skipped": [ - "This field is required." + "status": [ + "\"Rejected\" is not a valid choice." ] } }, - "Non-boolean is_skipped": { - "summary": "Value is not a boolean — `\"maybe\"`, `2`, `[]`. Booleans plus `\"true\"`/`\"false\"`, `1`/`0` and `\"yes\"`/`\"no\"` are all accepted", + "Null extracted_data": { + "summary": "extracted_data is null", "value": { - "is_skipped": [ - "Must be a valid boolean." + "extracted_data": [ + "This field may not be null." ] } - }, - "Null is_skipped": { - "summary": "`is_skipped` is present but `null`", + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. This is the only case that returns `401`: a missing, invalid or revoked key returns `403`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "summary": "The key's expiry date has passed", "value": { - "is_skipped": [ - "This field may not be null." - ] + "detail": "This API key has expired." } } } @@ -36265,41 +55210,77 @@ } }, "403": { - "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.", + "description": "Authentication or authorization failure. Missing, invalid and revoked keys return `403` (not `401`); so do a key of another application, a scoped key without the permission this endpoint needs, and a request from outside the key's IP allowlist. Distinguish them by `detail`.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, "examples": { - "Invalid Credentials": { - "summary": "Missing or invalid x-api-key", + "Missing or invalid credentials": { + "summary": "No x-api-key header, or the key failed validation", "value": { "detail": "Authentication credentials were not provided or are invalid." } }, - "Missing Privilege": { - "summary": "Credential lacks write:businesses", + "Other application": { + "summary": "A valid key that belongs to a different application than the session", "value": { "detail": "You do not have permission to perform this action." } + }, + "Scoped key without decisions write": { + "summary": "Scoped key lacks decisions: write", + "value": { + "detail": "This API key has no write access to decisions." + } + }, + "IP not allowed": { + "summary": "Request from outside the key's IP allowlist", + "value": { + "detail": "This API key can't be used from this IP address." + } } } } } }, "404": { - "description": "Unknown party, or a party that belongs to a different session. Each case has a distinct `detail` message (see examples). Credential and permission checks run before the party lookup, so an unauthorised call reports `403` rather than `404`. There is no separate session lookup: an unknown `sessionId` paired with a party of yours reports the mismatch message.", + "description": "The session does not exist, was deleted or is outside a scoped key's limits, or no current document with this UUID belongs to it. The session lookup runs before the credential check, so an unknown `sessionId` returns `404` even without a valid key.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, "examples": { - "Unknown party": { - "summary": "No party with this UUID in your application", + "Unknown session": { + "summary": "No session with this ID exists, or it was deleted", "value": { "detail": "Not found." } }, - "Party / session mismatch": { - "summary": "partyUuid exists but hangs off another session", + "Outside key limits": { + "summary": "The session is outside a scoped key's limits (also returned for a session deleted moments ago)", "value": { - "detail": "Party does not belong to the given session." + "detail": "Session not found." + } + }, + "Unknown document": { + "summary": "documentUuid does not belong to the session", + "value": { + "detail": "Document AI document not found." } } } @@ -36307,444 +55288,350 @@ } }, "429": { - "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.", + "description": "Rate limit exceeded: 300 write (POST, PATCH, DELETE) requests per minute per API key. Wait `Retry-After` seconds before retrying; the `X-RateLimit-*` headers show the budget.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." } } }, "examples": { "Rate limited": { - "summary": "Write budget exhausted", + "summary": "Budget for the current minute exhausted", "value": { "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." } } } } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } } } - } - } - }, - "/v3/travel-rule/settings/": { - "get": { - "summary": "Get Travel Rule settings", - "description": "Read the application's Travel Rule settings and VASP profile. Auto-creates a default (disabled) settings row and an empty profile on first read.", - "operationId": "getTravelRuleSettings", - "tags": [ - "Travel Rule" - ], + }, "security": [ { "ApiKeyAuth": [] } + ] + } + }, + "/v3/aml/dataset-freshness/": { + "get": { + "summary": "Get AML dataset freshness", + "description": "Report how current the datasets behind AML screening are, per source family (`sanctions`, `pep`, `warnings`, `adverse_media`), as audit evidence for [ongoing AML monitoring](https://docs.didit.me/core-technology/aml-screening/continuous-monitoring-aml-screening#proving-that-monitoring-ran). The answer is platform-wide: no session in the path, no customer data in the body, the same answer for every application.\n\nAlways `200` for an accepted key. Check `availability` first: when it is not `available`, `dataset_freshness` is `null`. Dates are never estimated: a family whose datasets reported no date is absent from `sources`, and a family where only some datasets reported one has `oldest_updated_at: null`. Readings are taken while ongoing monitoring runs, not on each call; `captured_at` says when.\n\nA scoped API key needs `sessions: read`; a key limited to some workflows or to approved sessions cannot call this endpoint. Nothing is billed.", + "operationId": "get_aml_dataset_freshness", + "x-didit-sdk": { + "resource": "aml", + "method": "retrieve_dataset_freshness", + "auth": "api_key", + "server": true + }, + "tags": [ + "Standalone APIs" ], + "parameters": [], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET https://verification.didit.me/v3/travel-rule/settings/ \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/aml/dataset-freshness/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/aml/dataset-freshness/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nbody = resp.json()\nif body['availability'] == 'available':\n for family, info in body['dataset_freshness']['sources'].items():\n print(family, info['last_updated_at'], info['oldest_updated_at'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/aml/dataset-freshness/', {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Freshness lookup failed: ${resp.status}`);\nconst body = await resp.json();\nif (body.availability === 'available') {\n for (const [family, info] of Object.entries(body.dataset_freshness.sources)) {\n console.log(family, info.last_updated_at, info.oldest_updated_at);\n }\n}" } ], "responses": { "200": { - "description": "Current Travel Rule settings and VASP profile.", + "description": "The latest dataset freshness reading, or `null` with the reason.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TravelRuleSettingsDetail" + "type": "object", + "required": [ + "dataset_freshness", + "availability" + ], + "properties": { + "dataset_freshness": { + "allOf": [ + { + "$ref": "#/components/schemas/AMLDatasetFreshness" + } + ], + "nullable": true, + "description": "The latest reading, or `null` when `availability` is not `available`." + }, + "availability": { + "type": "string", + "enum": [ + "available", + "not_configured", + "not_yet_collected" + ], + "description": "`available` when a reading exists; `not_configured` when dataset dates are not collected in this environment; `not_yet_collected` when collection is set up but no reading exists yet." + } + } }, "examples": { - "Disabled (default)": { - "value": { - "is_enabled": false, - "jurisdiction": "EU", - "name_matching_strictness": "DEFAULT", - "confirmation_timeout_hours": 48, - "timeout_outcome": "HOLD", - "threshold_amount": "0.00", - "legal_name": "", - "lei": "", - "compliance_email": "", - "is_discoverable": true, - "disabled_networks": [], - "networks": { - "gtr": { - "rail": "GTR", - "mode": "unavailable", - "platform_available": false, - "connected": false - }, - "trust": { - "rail": "TRUST", - "mode": "unavailable", - "platform_available": false, - "connected": false - }, - "verifyvasp": { - "rail": "VERIFYVASP", - "mode": "unavailable", - "platform_available": false, - "connected": false - }, - "sygna": { - "rail": "SYGNA", - "mode": "unavailable", - "platform_available": false, - "connected": false + "Available": { + "value": { + "dataset_freshness": { + "captured_at": "2026-10-17T06:00:12.345678+00:00", + "provider": "aml_data_source", + "catalog_version": "20261017054500-abc", + "sources": { + "sanctions": { + "last_updated_at": "2026-10-17T04:10:00+00:00", + "oldest_updated_at": "2026-10-15T22:30:00+00:00", + "dataset_count": 48, + "dated_dataset_count": 48 + }, + "pep": { + "last_updated_at": "2026-10-16T23:05:00+00:00", + "oldest_updated_at": null, + "dataset_count": 12, + "dated_dataset_count": 11 + } } - } + }, + "availability": "available" + } + }, + "Not collected yet": { + "value": { + "dataset_freshness": null, + "availability": "not_yet_collected" } } } } } - } - } - }, - "put": { - "summary": "Update Travel Rule settings", - "description": "Upsert the application's Travel Rule settings and VASP profile. Partial update — only send the fields you want to change. Setting `is_enabled: true` requires a non-blank `legal_name` (already stored or included in the same request), otherwise returns 400.", - "operationId": "updateTravelRuleSettings", - "tags": [ - "Travel Rule" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRuleSettingsUpdate" - }, - "example": { - "is_enabled": true, - "legal_name": "Origin CASP SL", - "jurisdiction": "EU", - "name_matching_strictness": "DEFAULT", - "confirmation_timeout_hours": 48, - "timeout_outcome": "HOLD", - "is_discoverable": true - } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PUT https://verification.didit.me/v3/travel-rule/settings/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"is_enabled\": true, \"legal_name\": \"Origin CASP SL\", \"jurisdiction\": \"EU\"}'" - } - ], - "responses": { - "200": { - "description": "Updated settings and profile, in the same shape as GET.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRuleSettingsDetail" - } - } - } }, - "400": { - "description": "Validation error, e.g. enabling without a legal_name.", + "401": { + "description": "The API key has expired. Only a key created with an expiry date can expire; after that date every request with it answers `401`.", "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } }, - "example": { - "legal_name": [ - "legal_name is required to enable travel rule." - ] + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } } } } - } - } - } - }, - "/v3/travel-rule/wallet-addresses/": { - "get": { - "summary": "List wallet address book entries", - "description": "Paginated list of your application's Travel Rule wallet address book, newest first.", - "operationId": "listTravelRuleWalletAddresses", - "tags": [ - "Travel Rule" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "parameters": [ - { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "default": 50, - "minimum": 1 - } }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "integer", - "default": 0, - "minimum": 0 - } - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET https://verification.didit.me/v3/travel-rule/wallet-addresses/ \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Paginated wallet address book entries.", + "403": { + "description": "The request is not allowed. A missing or invalid API key gets the generic message (never `401`).", "content": { "application/json": { "schema": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { - "type": "string", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WalletAddressEntry" - } + "detail": { + "type": "string" + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without sessions read": { + "value": { + "detail": "This API key has no read access to sessions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "Outside the key's IP list": { + "value": { + "detail": "This API key can't be used from this IP address." } } - } - } - } - } - } - }, - "post": { - "summary": "Register a wallet address", - "description": "Add an entry to your Travel Rule wallet address book so inbound INTERNAL-rail transfers can resolve against it.", - "operationId": "createTravelRuleWalletAddress", - "tags": [ - "Travel Rule" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WalletAddressEntryCreate" - }, - "example": { - "address": "0xBeneficiaryWallet01", - "chain": "ethereum", - "holder_name": "Ana Diaz", - "holder_vendor_data": "user-042", - "self_declared": true - } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/travel-rule/wallet-addresses/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"address\": \"0xBeneficiaryWallet01\", \"chain\": \"ethereum\", \"holder_name\": \"Ana Diaz\", \"self_declared\": true}'" - } - ], - "responses": { - "201": { - "description": "The created entry.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WalletAddressEntry" } } } }, - "400": { - "description": "Duplicate address for this application and chain.", + "429": { + "description": "More than 600 GET requests per minute with this API key. Wait the number of seconds in `Retry-After`, then retry.", "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } }, - "example": { - "address": [ - "A wallet address entry with this address and chain already exists." - ] + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } } } - } - } - } - } - }, - "/v3/travel-rule/wallet-addresses/{entry_uuid}/": { - "patch": { - "summary": "Update wallet address holder metadata", - "description": "Partial update of holder_name, holder_vendor_data, entity_type, and travel_address. Setting travel_address attaches a TRP routing address to the entry; invalid values are rejected with 400. The response echoes only these four fields.", - "operationId": "updateTravelRuleWalletAddress", - "tags": [ - "Travel Rule" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "parameters": [ - { - "name": "entry_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WalletAddressEntryUpdate" + }, + "headers": { + "Retry-After": { + "description": "Seconds until the current one-minute window resets.", + "schema": { + "type": "integer", + "example": 23 + } }, - "example": { - "holder_name": "Ana Updated", - "travel_address": "ta..." - } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH https://verification.didit.me/v3/travel-rule/wallet-addresses/{entry_uuid}/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"holder_name\": \"Ana Updated\", \"travel_address\": \"ta...\"}'" - } - ], - "responses": { - "200": { - "description": "The updated fields.", - "content": { - "application/json": { + "X-RateLimit-Limit": { + "description": "Requests allowed per minute in this budget.", "schema": { - "$ref": "#/components/schemas/WalletAddressEntryUpdate" + "type": "integer" + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Reset": { + "description": "Unix time when the window resets.", + "schema": { + "type": "integer" } } } } - } - }, - "delete": { - "summary": "Delete a wallet address", - "description": "Deletes the entry. It stops matching new transfers immediately and the address is freed up for re-registration.", - "operationId": "deleteTravelRuleWalletAddress", - "tags": [ - "Travel Rule" - ], + }, "security": [ { "ApiKeyAuth": [] } + ] + } + }, + "/v3/lists/": { + "get": { + "summary": "List lists", + "description": "Returns the application's lists, newest first: the system lists Didit provisions (one blocklist per entry type, the **High Risk Countries** custom list, and any system allowlist) and the allowlists and custom lists you created. Filter with `list_type` and `entry_type`; deleted lists never appear. Results are paginated with `limit` and `offset`. A scoped API key needs `workflows: read`. See [Lists](https://docs.didit.me/management-api/lists/overview) for what each list type does.", + "operationId": "list_lists", + "x-didit-sdk": { + "resource": "lists", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Lists" ], "parameters": [ { - "name": "entry_uuid", - "in": "path", - "required": true, + "name": "list_type", + "in": "query", + "required": false, + "description": "Only lists of this type.", "schema": { "type": "string", - "format": "uuid" + "enum": [ + "blocklist", + "allowlist", + "custom" + ] } - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X DELETE https://verification.didit.me/v3/travel-rule/wallet-addresses/{entry_uuid}/ \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "204": { - "description": "Deleted." - } - } - } - }, - "/v3/travel-rule/vasps/": { - "get": { - "summary": "Search the VASP directory", - "description": "Paginated search across discoverable Didit customers with Travel Rule enabled, plus VASPs catalogued in the internal counterparty registry. Excludes your own application's profile.", - "operationId": "listTravelRuleVasps", - "tags": [ - "Travel Rule" - ], - "security": [ + }, { - "ApiKeyAuth": [] - } - ], - "parameters": [ + "name": "entry_type", + "in": "query", + "required": false, + "description": "Only lists of this entry type.", + "schema": { + "type": "string", + "enum": [ + "face", + "document", + "phone", + "email", + "ip_address", + "device_fingerprint", + "wallet_address", + "bank_account", + "country", + "user", + "business", + "key" + ] + } + }, { - "name": "search", + "name": "ordering", "in": "query", + "required": false, + "description": "Sort order: `-created_at` (newest first, the default) or `created_at` (oldest first).", "schema": { - "type": "string" - }, - "description": "Case-insensitive substring match on VASP name." + "type": "string", + "enum": [ + "-created_at", + "created_at" + ], + "default": "-created_at" + } }, { "name": "limit", "in": "query", + "required": false, + "description": "Page size. Defaults to 50. A value above 1000000 returns `400`; zero, a negative or a non-numeric value falls back to 50.", "schema": { "type": "integer", "default": 50, - "minimum": 1 + "minimum": 1, + "maximum": 1000000 } }, { "name": "offset", "in": "query", + "required": false, + "description": "Zero-based index of the first item to return. Prefer following `next`. A value above 1000000 returns `400`; a negative or non-numeric value falls back to 0.", "schema": { "type": "integer", "default": 0, - "minimum": 0 + "minimum": 0, + "maximum": 1000000 } } ], @@ -36752,333 +55639,257 @@ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/travel-rule/vasps/?search=Bene' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/lists/?list_type=blocklist' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/lists/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'list_type': 'blocklist'},\n timeout=10,\n)\nresp.raise_for_status()\nfor item in resp.json()['results']:\n print(item['uuid'], item['entry_type'], item['name'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/lists/?list_type=blocklist', {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst page = await resp.json();\nconsole.log(page.results.map((list) => [list.uuid, list.entry_type, list.name]));" } ], "responses": { "200": { - "description": "Paginated, name-sorted VASP directory results.", + "description": "A page of lists.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], "properties": { "count": { - "type": "integer" + "type": "integer", + "minimum": 0, + "description": "Total number of lists matching the filters." }, "next": { "type": "string", - "nullable": true + "format": "uri", + "nullable": true, + "description": "Absolute URL of the next page, or `null` on the last page." }, "previous": { "type": "string", - "nullable": true + "format": "uri", + "nullable": true, + "description": "Absolute URL of the previous page, or `null` on the first page." }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/VaspDirectoryEntry" + "$ref": "#/components/schemas/ComplianceList" } } } - } - } - } - } - } - } - }, - "/v3/transactions/{transaction_id}/travel-rule/": { - "patch": { - "summary": "Finish, cancel, or resend a Travel Rule transfer", - "description": "Provide `payment_txn_id` to report the on-chain hash and move an outbound COMPLETED transfer to FINISHED (its INTERNAL-rail counterpart also moves to FINISHED), `{\"action\": \"cancel\"}` to cancel a non-terminal transfer, or `{\"action\": \"resend\"}` to re-run routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA (improve the counterparty data first, e.g. add a travel_address to the destination wallet entry).", - "operationId": "patchTravelRuleTransfer", - "tags": [ - "Travel Rule" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "parameters": [ - { - "name": "transaction_id", - "in": "path", - "required": true, - "description": "Didit-stable transaction UUID that owns the transfer.", - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRuleTransferPatchRequest" - }, - "examples": { - "Finish": { - "value": { - "payment_txn_id": "0xchainhash" - } - }, - "Cancel": { - "value": { - "action": "cancel" - } }, - "Resend": { - "value": { - "action": "resend" + "examples": { + "Lists": { + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "uuid": "22222222-3333-4444-5555-666666666666", + "name": "Trusted office IPs", + "description": "Office egress ranges", + "list_type": "allowlist", + "entry_type": "ip_address", + "is_system": false, + "entry_count": 3, + "last_entry_added_at": "2026-10-02T08:15:27.501334Z", + "created_at": "2026-09-30T13:20:45.889120Z", + "updated_at": "2026-09-30T13:20:45.889120Z" + }, + { + "uuid": "11111111-2222-3333-4444-555555555555", + "name": "Face Blocklist", + "description": "Blocked biometric faces. When a face matching an entry in this list is detected during liveness verification, the session will be automatically declined.", + "list_type": "blocklist", + "entry_type": "face", + "is_system": true, + "entry_count": 12, + "last_entry_added_at": "2026-10-09T16:42:03.118204Z", + "created_at": "2026-01-14T10:02:11.402917Z", + "updated_at": "2026-01-14T10:02:11.402917Z" + } + ] + } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH https://verification.didit.me/v3/transactions/{transaction_id}/travel-rule/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"payment_txn_id\": \"0xchainhash\"}'" - } - ], - "responses": { - "200": { - "description": "The updated transfer.", + }, + "400": { + "description": "A query parameter is invalid.", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRuleTransferDetail" + "examples": { + "Unknown list type": { + "value": { + "list_type": [ + "\"denylist\" is not a valid choice." + ] + } + }, + "Unsupported ordering": { + "value": { + "ordering": [ + "\"name\" is not a valid choice." + ] + } + }, + "Limit too large": { + "value": [ + "limit must not exceed 1000000." + ] + } } } } }, - "400": { - "description": "Neither payment_txn_id nor a supported action (cancel, resend) was provided." - }, - "409": { - "description": "The transfer is not in a state that allows this operation (e.g. not COMPLETED/outbound for finish, already terminal for cancel, or not an outbound transfer in a resendable status for resend)." - } - } - } - }, - "/v3/transactions/{transaction_id}/travel-rule/ownership/": { - "post": { - "summary": "Confirm or deny wallet ownership", - "description": "Called by the beneficiary's application when its INTERNAL-rail transfer is UNCONFIRMED_OWNERSHIP. Confirming marks the matching wallet address book entry as ownership-verified and runs the beneficiary name match on both sides of the transfer; denying declines both sides.", - "operationId": "confirmTravelRuleOwnership", - "tags": [ - "Travel Rule" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "parameters": [ - { - "name": "transaction_id", - "in": "path", - "required": true, - "description": "Didit-stable transaction UUID that owns the transfer (the beneficiary side's transaction).", - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRuleOwnershipConfirmRequest" - }, - "example": { - "confirmed": true - } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/transactions/{transaction_id}/travel-rule/ownership/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"confirmed\": true}'" - } - ], - "responses": { - "200": { - "description": "The updated transfer. originator_data/beneficiary_data are omitted until ownership_confirmed is true on an inbound transfer.", + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRuleTransferDetail" + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } } } } }, - "409": { - "description": "The transfer is not currently UNCONFIRMED_OWNERSHIP, or its matching wallet address book entry was deleted." - } - } - } - }, - "/v3/travel-rule/pickup/{token}/": { - "get": { - "summary": "Get email-rail pickup info", - "description": "Public, unauthenticated. Lets a non-Didit counterparty VASP preview the pending exchange from the emailed pickup link. The token itself is the authentication.", - "operationId": "getTravelRulePickup", - "tags": [ - "Travel Rule" - ], - "security": [], - "parameters": [ - { - "name": "token", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl https://verification.didit.me/v3/travel-rule/pickup/{token}/" - } - ], - "responses": { - "200": { - "description": "Pickup preview.", + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: read` or is used from an IP address outside its allowlist.", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRulePickupInfo" + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no read access to workflows." + } + } } } } }, - "404": { - "description": "Unknown token." - }, - "410": { - "description": "The pickup window has closed (expired, or the transfer already left AWAITING_COUNTERPARTY)." - } - } - } - }, - "/v3/travel-rule/pickup/{token}/respond/": { - "post": { - "summary": "Respond to an email-rail pickup", - "description": "Public, unauthenticated. Accepts or declines the exchange from the emailed pickup link. On accept, beneficiary_name is checked against the originator's name_matching_strictness policy.", - "operationId": "respondTravelRulePickup", - "tags": [ - "Travel Rule" - ], - "security": [], - "parameters": [ - { - "name": "token", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRulePickupRespondRequest" - }, - "example": { - "decision": "accept", - "beneficiary_name": "Carlos Ruiz" - } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/travel-rule/pickup/{token}/respond/ \\\n -H 'Content-Type: application/json' \\\n -d '{\"decision\": \"accept\", \"beneficiary_name\": \"Carlos Ruiz\"}'" - } - ], - "responses": { - "200": { - "description": "The resulting exchange status.", + "429": { + "description": "The shared read limit of 600 requests per minute per API key was exceeded. Honor `Retry-After` before retrying.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "status": { - "type": "string" + "examples": { + "Rate limited": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." } } - }, - "example": { - "status": "COMPLETED" } } } - }, - "404": { - "description": "Unknown token." - }, - "410": { - "description": "The pickup window has closed." } - } - } - }, - "/v3/travel-rule/inbound/": { - "post": { - "summary": "Register an after-deposit (sunrise) inbound transfer", - "description": "Register Travel Rule data for a crypto deposit that already settled on-chain before any exchange took place. Creates an inbound travelRule transaction (or reuses an unclaimed deposit) and resolves the transfer against your wallet address book. A transaction.created webhook fires for newly-created transactions.", - "operationId": "registerTravelRuleInbound", - "tags": [ - "Travel Rule" - ], + }, "security": [ { "ApiKeyAuth": [] } + ] + }, + "post": { + "summary": "Create a list", + "description": "Creates an allowlist or a custom list in the application the API key belongs to. Blocklists cannot be created: Didit provisions one system blocklist per entry type, and you add to it with [Create a list entry](https://docs.didit.me/management-api/lists/create-entry). `entry_type` cannot change after creation and decides how entry values are validated and matched. Allowlist entries mark matching items as trusted (for example, a face allowlist skips the duplicated-face action) and never override a blocklist match; reference any list from a workflow or transaction rule condition with `value_type: \"list\"`. A scoped API key needs `workflows: write`.", + "operationId": "create_list", + "x-didit-sdk": { + "resource": "lists", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Lists" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TravelRuleInboundRequest" + "type": "object", + "required": [ + "name", + "list_type", + "entry_type" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "description": "List name. Must differ from the name of every other list of the application, system lists included." + }, + "description": { + "type": "string", + "default": "", + "description": "Optional free-text description." + }, + "list_type": { + "type": "string", + "enum": [ + "allowlist", + "custom" + ], + "description": "`allowlist` or `custom`. `blocklist` is refused with `400`." + }, + "entry_type": { + "type": "string", + "enum": [ + "face", + "document", + "phone", + "email", + "ip_address", + "device_fingerprint", + "wallet_address", + "bank_account", + "country", + "user", + "business", + "key" + ], + "description": "What the list holds: `face`, `document` (document number), `phone`, `email`, `ip_address` (IP address or CIDR range), `device_fingerprint`, `wallet_address`, `bank_account`, `country` (ISO 3166-1 alpha-3), `user` or `business` (your `vendor_data`), or `key` (any string). It decides how entry values are validated and matched. Cannot be changed later." + } + } }, - "example": { - "chain": "ethereum", - "txid": "0xdeposit-hash", - "wallet_address": "0xYourDepositWallet", - "amount": "500.00", - "currency": "USDC", - "originator_data": { - "name": "Origin CASP SL" - }, - "beneficiary_data": { - "name": "Ana Diaz" - }, - "originating_vasp": { - "name": "Origin CASP SL", - "lei": "", - "travel_address": "ta..." + "examples": { + "IP allowlist": { + "value": { + "name": "Trusted office IPs", + "description": "Office egress ranges", + "list_type": "allowlist", + "entry_type": "ip_address" + } + }, + "Custom country list": { + "value": { + "name": "Enhanced due diligence countries", + "list_type": "custom", + "entry_type": "country" + } } } } @@ -37088,160 +55899,303 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/travel-rule/inbound/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"chain\":\"ethereum\",\"txid\":\"0xdeposit-hash\",\"wallet_address\":\"0xYourDepositWallet\",\"amount\":\"500.00\",\"currency\":\"USDC\"}'" + "source": "curl -X POST 'https://verification.didit.me/v3/lists/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"name\": \"Trusted office IPs\",\n \"description\": \"Office egress ranges\",\n \"list_type\": \"allowlist\",\n \"entry_type\": \"ip_address\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/lists/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'name': 'Trusted office IPs',\n 'description': 'Office egress ranges',\n 'list_type': 'allowlist',\n 'entry_type': 'ip_address',\n },\n timeout=10,\n)\nresp.raise_for_status()\nprint('Created list', resp.json()['uuid'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/lists/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n name: 'Trusted office IPs',\n description: 'Office egress ranges',\n list_type: 'allowlist',\n entry_type: 'ip_address',\n }),\n});\nif (!resp.ok) throw new Error(`Create failed: ${resp.status}`);\nconst list = await resp.json();\nconsole.log('Created list', list.uuid);" } ], "responses": { "201": { - "description": "A new inbound transaction and transfer were created.", + "description": "The list was created.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TravelRuleInboundResponse" + "$ref": "#/components/schemas/ComplianceList" + }, + "examples": { + "New allowlist": { + "value": { + "uuid": "22222222-3333-4444-5555-666666666666", + "name": "Trusted office IPs", + "description": "Office egress ranges", + "list_type": "allowlist", + "entry_type": "ip_address", + "is_system": false, + "entry_count": 0, + "last_entry_added_at": null, + "created_at": "2026-10-12T09:15:44.031876Z", + "updated_at": "2026-10-12T09:15:44.031876Z" + } + } } } } }, - "200": { - "description": "An existing unclaimed deposit was reused (created is false).", + "400": { + "description": "The body is invalid.", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRuleInboundResponse" + "examples": { + "Blocklist refused": { + "value": { + "list_type": [ + "Cannot create additional blocklist lists. System blocklists are auto-created for each application." + ] + } + }, + "Duplicate name": { + "value": { + "name": [ + "A list with this name already exists for this application." + ] + } + }, + "Missing fields": { + "value": { + "name": [ + "This field is required." + ], + "list_type": [ + "This field is required." + ], + "entry_type": [ + "This field is required." + ] + } + }, + "Unknown entry type": { + "value": { + "entry_type": [ + "\"iban\" is not a valid choice." + ] + } + }, + "Name too long": { + "value": { + "name": [ + "Ensure this field has no more than 255 characters." + ] + } + } } } } - } - } - } - }, - "/v3/travel-rule/widget-session/": { - "post": { - "summary": "Mint a wallet-ownership widget session", - "description": "Mint a hosted wallet-ownership widget URL/token. Send the customer to the returned url to prove control of a wallet (message signing, Satoshi test, screenshot, or self-declaration). The widget is web-only in this release - open the url in the system browser or a custom tab, never inside a mobile SDK webview.", - "operationId": "createTravelRuleWidgetSession", - "tags": [ - "Travel Rule" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRuleWidgetSessionCreate" - }, - "example": { - "wallet_address": "0xBeneficiaryWallet01", - "chain": "ethereum", - "holder_name": "Ana Diaz", - "vendor_data": "user-042", - "transaction_id": "22222222-3333-4444-5555-666666666666", - "expires_in_minutes": 60 + }, + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", + "content": { + "application/json": { + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } + } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/travel-rule/widget-session/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"wallet_address\":\"0xBeneficiaryWallet01\",\"chain\":\"ethereum\",\"holder_name\":\"Ana Diaz\"}'" - } - ], - "responses": { - "201": { - "description": "The minted widget session.", + }, + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: write` or is used from an IP address outside its allowlist.", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/TravelRuleWidgetSessionResponse" + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no write access to workflows." + } + } } } } + }, + "429": { + "$ref": "#/components/responses/RateLimitedWrite" } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/travel-rule/widget/{token}/": { + "/v3/lists/{list_uuid}/": { "get": { - "summary": "Get wallet-ownership widget context", - "description": "Public read endpoint keyed by the widget session token - no API key required. Returns the widget context plus the status of every proof, so you can poll it to detect completion; completed_at becomes non-null once a proof is verified. A SATOSHI_TEST proof can sit at status PENDING while its deposit is seen on-chain but unconfirmed - a background job automatically rechecks it and completes it once the deposit confirms, so no client action is needed while it waits. Each proof carries the fields its method needs: deposit_address, expected_amount, and its own expires_at for SATOSHI_TEST; challenge for MESSAGE_SIGNING. Prefer the travel_rule.status.updated webhook over polling when the session is linked to a transfer.", - "operationId": "getTravelRuleWidgetContext", + "summary": "Get a list", + "description": "Returns one list of the application with its entry count and when its newest entry was added. A list of another application, or one that was deleted, returns `404`. This returns the list only: read its values with [List list entries](https://docs.didit.me/management-api/lists/list-entries). A scoped API key needs `workflows: read`.", + "operationId": "get_list", + "x-didit-sdk": { + "resource": "lists", + "method": "retrieve", + "auth": "api_key", + "server": true + }, "tags": [ - "Travel Rule" + "Lists" ], - "security": [], "parameters": [ { - "name": "token", + "name": "list_uuid", "in": "path", "required": true, - "description": "Opaque session token returned when the widget session was minted.", + "description": "UUID of the list, as returned by List lists or Create a list.", "schema": { - "type": "string" - } + "type": "string", + "format": "uuid" + }, + "example": "11111111-2222-3333-4444-555555555555" } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl https://verification.didit.me/v3/travel-rule/widget/{token}/" + "source": "curl -X GET 'https://verification.didit.me/v3/lists/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nlist_uuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n f'https://verification.didit.me/v3/lists/{list_uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json())" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const listUuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/lists/${listUuid}/`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Lookup failed: ${resp.status}`);\nconst list = await resp.json();\nconsole.log(list.name, list.entry_count);" } ], "responses": { "200": { - "description": "The widget context and proof statuses.", + "description": "The list.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TravelRuleWidgetContext" + "$ref": "#/components/schemas/ComplianceList" + }, + "examples": { + "System face blocklist": { + "value": { + "uuid": "11111111-2222-3333-4444-555555555555", + "name": "Face Blocklist", + "description": "Blocked biometric faces. When a face matching an entry in this list is detected during liveness verification, the session will be automatically declined.", + "list_type": "blocklist", + "entry_type": "face", + "is_system": true, + "entry_count": 12, + "last_entry_added_at": "2026-10-09T16:42:03.118204Z", + "created_at": "2026-01-14T10:02:11.402917Z", + "updated_at": "2026-01-14T10:02:11.402917Z" + } + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", + "content": { + "application/json": { + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: read` or is used from an IP address outside its allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no read access to workflows." + } + } } } } }, "404": { - "description": "Unknown token." + "description": "No list with this UUID exists in the API key's application, or it was deleted.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "No List matches the given query." + } + } + } + } + } }, - "410": { - "description": "The session expired without completing." + "429": { + "description": "The shared read limit of 600 requests per minute per API key was exceeded. Honor `Retry-After` before retrying.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + } } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/vendor-users/toggle-ongoing-monitoring/": { + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, "patch": { - "summary": "Toggle AML ongoing monitoring for users", - "description": "Enables or disables AML ongoing monitoring at the user level. Exactly one monitored AML screening is kept per user; users without any AML screening (for example imported users) get one created and screened first, billed as a regular AML screening. Set estimate_only to preview counts and costs without writing. Batches of 20 or fewer resolve inline (200, COMPLETED); larger batches return 202 and run asynchronously.", - "operationId": "toggle_vendor_users_ongoing_monitoring", + "summary": "Update a list", + "description": "Renames a list or changes its description. Only `name` and `description` are read; any other field is ignored, so `list_type` and `entry_type` keep the values they were created with. System lists cannot be modified and return `400`. The response contains only `name` and `description`: call [Get a list](https://docs.didit.me/management-api/lists/get) for the full list. A scoped API key needs `workflows: write`.", + "operationId": "update_list", + "x-didit-sdk": { + "resource": "lists", + "method": "update", + "auth": "api_key", + "server": true + }, "tags": [ - "Users" + "Lists" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "list_uuid", "in": "path", "required": true, + "description": "UUID of the list, as returned by List lists or Create a list.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." + "example": "11111111-2222-3333-4444-555555555555" } ], "requestBody": { @@ -37250,330 +56204,241 @@ "application/json": { "schema": { "type": "object", - "required": [ - "is_enabled" - ], "properties": { - "is_enabled": { - "type": "boolean", - "description": "true to enable monitoring, false to disable it." - }, - "estimate_only": { - "type": "boolean", - "default": false, - "description": "When true, returns the cost estimate and performs no writes." - }, - "update_all": { - "type": "boolean", - "default": false, - "description": "Apply to every entity of the application (scoped by the filter_* params when present)." - }, - "filter_search": { - "type": "string" - }, - "filter_status": { - "type": "array", - "items": { - "type": "string" - } - }, - "filter_country": { - "type": "string" - }, - "filter_date_from": { + "name": { "type": "string", - "format": "date" + "minLength": 1, + "maxLength": 255, + "description": "New name. Must differ from the name of every other list of the application." }, - "filter_date_to": { + "description": { "type": "string", - "format": "date" - }, - "didit_internal_id_list": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "Entities to toggle. Required unless update_all is true." + "description": "New description. Send an empty string to clear it." + } + } + }, + "examples": { + "Rename": { + "value": { + "name": "Trusted office and VPN IPs" + } + }, + "Rename and describe": { + "value": { + "name": "Trusted office and VPN IPs", + "description": "Office egress ranges and the corporate VPN" } } } } } }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/lists/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"name\": \"Trusted office and VPN IPs\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nlist_uuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.patch(\n f'https://verification.didit.me/v3/lists/{list_uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'name': 'Trusted office and VPN IPs'},\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json())" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const listUuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/lists/${listUuid}/`, {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ name: 'Trusted office and VPN IPs' }),\n});\nif (!resp.ok) throw new Error(`Update failed: ${resp.status}`);\nconsole.log(await resp.json());" + } + ], "responses": { "200": { - "description": "Estimate (when estimate_only) or the completed job for small batches processed inline.", + "description": "The list was updated. Only the editable fields are returned.", "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } + "type": "object", + "required": [ + "name", + "description" + ], + "properties": { + "name": { + "type": "string", + "description": "The list's name after the update." }, - { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean" - }, - "total": { - "type": "integer" - }, - "processed": { - "type": "integer" - }, - "outcomes": { - "type": "object", - "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active (legacy duplicate rows are consolidated)." - }, - "disabled": { - "type": "integer", - "description": "Monitoring disabled on every AML row of the entity." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: no full name + date of birth + country available to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application." - }, - "failed_provider": { - "type": "integer", - "description": "The screening provider call failed." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out mid-job." - } - } - }, - "error": { - "type": "string", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } - } - } + "description": { + "type": "string", + "description": "The list's description after the update." + } + } + }, + "examples": { + "Renamed": { + "value": { + "name": "Trusted office and VPN IPs", + "description": "Office egress ranges" + } + } + } + } + } + }, + "400": { + "description": "The list is a system list, or the body is invalid.", + "content": { + "application/json": { + "examples": { + "System list": { + "value": { + "non_field_errors": [ + "System lists cannot be modified." + ] + } + }, + "Duplicate name": { + "value": { + "name": [ + "A list with this name already exists for this application." + ] + } + }, + "Blank name": { + "value": { + "name": [ + "This field may not be blank." + ] + } + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", + "content": { + "application/json": { + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: write` or is used from an IP address outside its allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no write access to workflows." + } + } + } + } + } + }, + "404": { + "description": "No list with this UUID exists in the API key's application, or it was deleted.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "No List matches the given query." + } + } + } + } + } + }, + "429": { + "$ref": "#/components/responses/RateLimitedWrite" + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "delete": { + "summary": "Delete a list", + "description": "Deletes an allowlist or custom list together with its entries. Deletion is irreversible: the list and its entries no longer appear in any read, and workflow and transaction rule conditions that reference the list evaluate it as an empty list from then on. Sessions and transactions already evaluated keep their results, and the name can be reused for a new list. System lists cannot be deleted and return `400`; to empty one, [delete its entries](https://docs.didit.me/management-api/lists/delete-entry). A scoped API key needs `workflows: write`.", + "operationId": "delete_list", + "x-didit-sdk": { + "resource": "lists", + "method": "delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Lists" + ], + "parameters": [ + { + "name": "list_uuid", + "in": "path", + "required": true, + "description": "UUID of the list, as returned by List lists or Create a list.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "11111111-2222-3333-4444-555555555555" + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/lists/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nlist_uuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.delete(\n f'https://verification.didit.me/v3/lists/{list_uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nprint('Deleted:', resp.status_code == 204)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const listUuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/lists/${listUuid}/`, {\n method: 'DELETE',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (resp.status !== 204) throw new Error(`Delete failed: ${resp.status}`);\nconsole.log('List deleted');" + } + ], + "responses": { + "204": { + "description": "The list and its entries were deleted. No body." + }, + "400": { + "description": "The list is a system list.", + "content": { + "application/json": { + "examples": { + "System list": { + "value": { + "detail": "System lists cannot be deleted." } - ] + } } } } }, - "202": { - "description": "Larger batches are processed asynchronously; poll the job endpoint with the returned job_id.", + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean" - }, - "total": { - "type": "integer" - }, - "processed": { - "type": "integer" - }, - "outcomes": { - "type": "object", - "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active (legacy duplicate rows are consolidated)." - }, - "disabled": { - "type": "integer", - "description": "Monitoring disabled on every AML row of the entity." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: no full name + date of birth + country available to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application." - }, - "failed_provider": { - "type": "integer", - "description": "The screening provider call failed." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out mid-job." - } - } - }, - "error": { - "type": "string", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." } } } @@ -37581,164 +56446,269 @@ } }, "403": { - "description": "Missing write:sessions privilege, or insufficient balance to enable (the estimate is included in the error body)." + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: write` or is used from an IP address outside its allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no write access to workflows." + } + } + } + } + } + }, + "404": { + "description": "No list with this UUID exists in the API key's application, or it was already deleted.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "No List matches the given query." + } + } + } + } + } + }, + "429": { + "$ref": "#/components/responses/RateLimitedWrite" } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/vendor-users/toggle-ongoing-monitoring/{job_id}/": { + "/v3/lists/{list_uuid}/entries/": { "get": { - "summary": "Get AML ongoing monitoring job", - "description": "Progress and per-outcome counters of a monitoring toggle job. Poll every couple of seconds while status is PENDING or PROCESSING.", - "operationId": "get_vendor_users_ongoing_monitoring_job", + "summary": "List list entries", + "description": "Returns the current entries of one list, newest first. `search` matches part of an entry's `value` or `display_label` without regard to case, and `reference_session_id` narrows the results to the entries taken from one person verification session. Image URLs in `metadata` are short-lived signed URLs issued again on every call; a scoped API key without `media: read` receives `null` in their place. A scoped API key needs `workflows: read`.", + "operationId": "list_list_entries", + "x-didit-sdk": { + "resource": "lists.entries", + "method": "list", + "auth": "api_key", + "server": true + }, "tags": [ - "Users" + "Lists" ], "parameters": [ { - "name": "organization_id", + "name": "list_uuid", "in": "path", "required": true, + "description": "UUID of the list, as returned by List lists or Create a list.", "schema": { "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "example": "11111111-2222-3333-4444-555555555555" }, { - "name": "application_id", - "in": "path", - "required": true, + "name": "search", + "in": "query", + "required": false, + "description": "Case-insensitive text that the entry's `value` or `display_label` must contain.", "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "type": "string" + } }, { - "name": "job_id", - "in": "path", - "required": true, + "name": "reference_session_id", + "in": "query", + "required": false, + "description": "Only the entries taken from this person verification session, for example to see which of its faces are already listed.", "schema": { "type": "string", "format": "uuid" - }, - "description": "Monitoring job UUID returned by the toggle endpoint." + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "Page size. Defaults to 50. A value above 1000000 returns `400`; zero, a negative or a non-numeric value falls back to 50.", + "schema": { + "type": "integer", + "default": 50, + "minimum": 1, + "maximum": 1000000 + } + }, + { + "name": "offset", + "in": "query", + "required": false, + "description": "Zero-based index of the first item to return. Prefer following `next`. A value above 1000000 returns `400`; a negative or non-numeric value falls back to 0.", + "schema": { + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 1000000 + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/lists/11111111-2222-3333-4444-555555555555/entries/?search=example.com' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nlist_uuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n f'https://verification.didit.me/v3/lists/{list_uuid}/entries/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'search': 'example.com', 'limit': 100},\n timeout=10,\n)\nresp.raise_for_status()\nfor entry in resp.json()['results']:\n print(entry['uuid'], entry['value'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const listUuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(\n `https://verification.didit.me/v3/lists/${listUuid}/entries/?search=example.com`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst page = await resp.json();\nconsole.log(page.count, page.results.map((entry) => entry.value));" } ], "responses": { "200": { - "description": "The job.", + "description": "A page of entries, newest first.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], "properties": { - "job_id": { - "type": "string", - "format": "uuid" + "count": { + "type": "integer", + "minimum": 0, + "description": "Total number of entries matching the filters." }, - "status": { + "next": { "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean" - }, - "total": { - "type": "integer" - }, - "processed": { - "type": "integer" - }, - "outcomes": { - "type": "object", - "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active (legacy duplicate rows are consolidated)." - }, - "disabled": { - "type": "integer", - "description": "Monitoring disabled on every AML row of the entity." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: no full name + date of birth + country available to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application." - }, - "failed_provider": { - "type": "integer", - "description": "The screening provider call failed." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out mid-job." - } - } + "format": "uri", + "nullable": true, + "description": "Absolute URL of the next page, or `null` on the last page." }, - "error": { + "previous": { "type": "string", - "nullable": true + "format": "uri", + "nullable": true, + "description": "Absolute URL of the previous page, or `null` on the first page." }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ComplianceListEntry" + } + } + } + }, + "examples": { + "Entries": { + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "uuid": "bbbbbbbb-cccc-dddd-eeee-ffffffffffff", + "value": "X1234567", + "display_label": "Jane Doe - X1234567", + "comment": "", + "added_by": "api", + "reference_session_id": "33333333-4444-5555-6666-777777777777", + "reference_session_number": 1042, + "reference_session_type": "kyc", + "reference_object_uuid": "66666666-7777-8888-9999-000000000000", + "metadata": { + "full_name": "Jane Doe", + "issuing_state": "ESP", + "document_type": "ID", + "region": "", + "document_number": "X1234567" + }, + "created_at": "2026-10-12T10:06:12.774915Z" }, - "balance_sufficient": { - "type": "boolean" + { + "uuid": "eeeeeeee-ffff-0000-1111-222222222222", + "value": "y7654321", + "display_label": "", + "comment": "Reported by a partner", + "added_by": "api", + "reference_session_id": null, + "reference_session_number": null, + "reference_session_type": null, + "reference_object_uuid": null, + "metadata": null, + "created_at": "2026-10-03T07:48:30.552810Z" } - } + ] + } + } + } + } + } + }, + "400": { + "description": "A query parameter is invalid.", + "content": { + "application/json": { + "examples": { + "Invalid session id": { + "value": { + "reference_session_id": [ + "Must be a valid UUID." + ] + } + }, + "Limit too large": { + "value": [ + "limit must not exceed 1000000." + ] + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", + "content": { + "application/json": { + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: read` or is used from an IP address outside its allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no read access to workflows." } } } @@ -37746,576 +56716,558 @@ } }, "404": { - "description": "Unknown job for this application." + "description": "No list with this UUID exists in the API key's application, or it was deleted.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "List not found." + } + } + } + } + } + }, + "429": { + "description": "The shared read limit of 600 requests per minute per API key was exceeded. Honor `Retry-After` before retrying.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + } } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/vendor-businesses/toggle-ongoing-monitoring/": { - "patch": { - "summary": "Toggle AML ongoing monitoring for businesses", - "description": "Business-level equivalent of the user toggle: one monitored AML screening per business, with businesses lacking any AML screening screened first (company entity). Same estimate_only, inline fast-path and async job contract.", - "operationId": "toggle_vendor_businesses_ongoing_monitoring", + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + }, + "post": { + "summary": "Create a list entry", + "description": "Adds one entry to a list, in one of three ways: by `value`; from a verification session with `reference_session_id`, where Didit reads the value from the session's most recent record of the list's entry type and links the entry to it; or by `value` with a `reference_object_uuid` and `metadata.reference_type` pointing back to a transaction, user or business. On the system face, document, phone and email blocklists, the record read from the session is also flagged as blocklisted, so new matching sessions are declined; on the system user and business blocklists, matching users or businesses are set to `BLOCKED`. Sessions and transactions that already finished are not changed. A scoped API key needs `workflows: write`. See [Create a list entry](https://docs.didit.me/management-api/lists/create-entry) for each mode.", + "operationId": "create_list_entry", + "x-didit-sdk": { + "resource": "lists.entries", + "method": "create", + "auth": "api_key", + "server": true + }, "tags": [ - "Businesses" + "Lists" ], "parameters": [ { - "name": "organization_id", + "name": "list_uuid", "in": "path", "required": true, + "description": "UUID of the list to add to. For a face you only have as an image, use Upload a face to a list instead.", "schema": { "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "example": "11111111-2222-3333-4444-555555555555" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Send `value`, `reference_session_id`, or both. Which fields apply depends on the list's `entry_type`.", + "properties": { + "value": { + "type": "string", + "description": "The value to list, validated against the list's `entry_type`: `email` a valid email address; `phone` a valid international number, stored in E.164; `ip_address` an IP address or CIDR range (for example `10.0.0.0/8`); `device_fingerprint` at least 8 characters among letters, digits, `-`, `_`, `:` and `.` (a blocklist refuses a fingerprint that several distinct devices have reported); `country` an ISO 3166-1 alpha-2 or alpha-3 code, stored as alpha-3; `user` and `business` the `vendor_data`; any other type a non-empty string. Stored in lower case except phone numbers and country codes, and a list holds each value once (case-insensitive). Required unless `reference_session_id` is sent; must be left empty with a document-capture `face_image_source`." + }, + "reference_session_id": { + "type": "string", + "format": "uuid", + "description": "A person or business verification session of your application to take the value from. Didit reads the session's most recent record of the list's entry type (`face`, `document`, `phone`, `email`, `ip_address` or `device_fingerprint`), links the entry to the session and to that record, and fills `display_label` and `metadata` for documents. For a `device_fingerprint` blocklist, a session that only has a device-class fingerprint, or was completed on more than one device, is refused: send the exact fingerprint as `value`. For other entry types, or when the session has no such record, send `value` too; your `value` is then stored and the entry is still linked to the session." + }, + "reference_object_uuid": { + "type": "string", + "format": "uuid", + "description": "UUID of the record the entry stands for when it does not come from a session: a transaction UUID, or the `didit_internal_id` of a user or business. Pair it with `metadata.reference_type`. Replaced by the session's record when `reference_session_id` provides one." + }, + "face_image_source": { + "type": "string", + "enum": [ + "liveness", + "front_image_camera_front", + "back_image_camera_front" + ], + "default": "liveness", + "description": "Face lists with `reference_session_id`: which of the session's images the face is taken from. `liveness` (default) is the liveness selfie. `front_image_camera_front` and `back_image_camera_front` are the selfies the front camera took while the user photographed the front or back of their document: accepted only on the system face blocklist, with `reference_session_id` and an empty `value`. Each selfie can be added once and must show one clear face." + }, + "reference_node_id": { + "type": "string", + "nullable": true, + "maxLength": 100, + "description": "With a document-capture `face_image_source`: the ID verification node whose selfie to use, for workflows with more than one. Defaults to the most recent ID verification that has that image. Refused with `liveness`." + }, + "display_label": { + "type": "string", + "maxLength": 512, + "description": "Human-readable label. When empty, Didit derives one from the session where it can (for example the holder's name and document number)." + }, + "comment": { + "type": "string", + "description": "Free-text note, such as the reason for listing." + }, + "metadata": { + "type": "object", + "additionalProperties": true, + "description": "Free-form JSON stored with the entry and returned as stored. Use `reference_type` and `reference_id` so the console links back to the source. `reference_image_url` and `front_image_url` are set by Didit only and are dropped if you send them. For an entry read from a session, Didit's keys are merged with yours.", + "properties": { + "reference_type": { + "type": "string", + "enum": [ + "transaction", + "vendor_user", + "vendor_business" + ], + "description": "What `reference_object_uuid` points to." + }, + "reference_id": { + "type": "string", + "description": "Identifier of the source record." + } + } + } + } + }, + "examples": { + "By value": { + "value": { + "value": "+14155552671", + "comment": "Reported fraud" + } + }, + "From a session": { + "value": { + "reference_session_id": "33333333-4444-5555-6666-777777777777" + } + }, + "Face from a document-capture selfie": { + "value": { + "reference_session_id": "33333333-4444-5555-6666-777777777777", + "face_image_source": "front_image_camera_front" + } + }, + "From a transaction": { + "value": { + "value": "0x52908400098527886E0F7030069857D2E4169EE7", + "reference_object_uuid": "55555555-6666-7777-8888-999999999999", + "comment": "Linked to a chargeback", + "metadata": { + "reference_type": "transaction", + "reference_id": "55555555-6666-7777-8888-999999999999" + } + } + }, + "Country": { + "value": { + "value": "PRK", + "comment": "Sanctioned jurisdiction" + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/lists/11111111-2222-3333-4444-555555555555/entries/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"value\": \"+14155552671\", \"comment\": \"Reported fraud\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nlist_uuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.post(\n f'https://verification.didit.me/v3/lists/{list_uuid}/entries/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'reference_session_id': '33333333-4444-5555-6666-777777777777'},\n timeout=10,\n)\nresp.raise_for_status()\nentry = resp.json()\nprint('Listed', entry['value'], 'as', entry['uuid'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const listUuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/lists/${listUuid}/entries/`, {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ value: '+14155552671', comment: 'Reported fraud' }),\n});\nif (!resp.ok) throw new Error(`Create failed: ${resp.status} ${await resp.text()}`);\nconst entry = await resp.json();\nconsole.log('Listed', entry.value, 'as', entry.uuid);" + } + ], + "responses": { + "201": { + "description": "The entry was created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ComplianceListEntry" + }, + "examples": { + "By value": { + "value": { + "uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "value": "+14155552671", + "display_label": "", + "comment": "Reported fraud", + "added_by": "api", + "reference_session_id": null, + "reference_session_number": null, + "reference_session_type": null, + "reference_object_uuid": null, + "metadata": null, + "created_at": "2026-10-12T10:04:51.220391Z" + } + }, + "Document from a session": { + "value": { + "uuid": "bbbbbbbb-cccc-dddd-eeee-ffffffffffff", + "value": "X1234567", + "display_label": "Jane Doe - X1234567", + "comment": "", + "added_by": "api", + "reference_session_id": "33333333-4444-5555-6666-777777777777", + "reference_session_number": 1042, + "reference_session_type": "kyc", + "reference_object_uuid": "66666666-7777-8888-9999-000000000000", + "metadata": { + "full_name": "Jane Doe", + "issuing_state": "ESP", + "document_type": "ID", + "region": "", + "document_number": "X1234567" + }, + "created_at": "2026-10-12T10:06:12.774915Z" + } + }, + "From a transaction": { + "value": { + "uuid": "dddddddd-eeee-ffff-0000-111111111111", + "value": "0x52908400098527886e0f7030069857d2e4169ee7", + "display_label": "", + "comment": "Linked to a chargeback", + "added_by": "api", + "reference_session_id": null, + "reference_session_number": null, + "reference_session_type": null, + "reference_object_uuid": "55555555-6666-7777-8888-999999999999", + "metadata": { + "reference_type": "transaction", + "reference_id": "55555555-6666-7777-8888-999999999999" + }, + "created_at": "2026-10-12T10:20:40.117062Z" + } + } + } + } + } }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "is_enabled" - ], - "properties": { - "is_enabled": { - "type": "boolean", - "description": "true to enable monitoring, false to disable it." + "400": { + "description": "The body is invalid, the value is already in the list, or the session cannot provide the value.", + "content": { + "application/json": { + "examples": { + "Nothing to list": { + "value": { + "value": [ + "Either 'value' or 'reference_session_id' must be provided." + ] + } }, - "estimate_only": { - "type": "boolean", - "default": false, - "description": "When true, returns the cost estimate and performs no writes." + "Duplicate value": { + "value": { + "value": [ + "An entry with this value already exists in this list." + ] + } }, - "update_all": { - "type": "boolean", - "default": false, - "description": "Apply to every entity of the application (scoped by the filter_* params when present)." + "Invalid phone": { + "value": { + "value": [ + "Invalid phone number. Provide a valid international number (e.g. +1234567890)." + ] + } }, - "filter_search": { - "type": "string" + "Invalid IP": { + "value": { + "value": [ + "Invalid IP address or CIDR range (e.g. 192.168.1.1 or 10.0.0.0/24)." + ] + } }, - "filter_status": { - "type": "array", - "items": { - "type": "string" + "Invalid country": { + "value": { + "value": [ + "Invalid country code: XX. Provide a valid ISO 3166-1 alpha-2 or alpha-3 country code." + ] } }, - "filter_country": { - "type": "string" + "Device-class fingerprint": { + "value": { + "value": [ + "This value is a device-class fingerprint: several distinct devices have reported it, so blocklisting it would decline unrelated people. Block the user by document, face, email or phone instead." + ] + } }, - "filter_date_from": { - "type": "string", - "format": "date" + "Unknown session": { + "value": { + "reference_session_id": [ + "Session not found or does not belong to this application." + ] + } }, - "filter_date_to": { - "type": "string", - "format": "date" + "No data in session": { + "value": { + "value": [ + "Either provide a 'value' or ensure the session contains data for this entry type." + ] + } }, - "didit_internal_id_list": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "description": "Entities to toggle. Required unless update_all is true." + "Selfie on another list": { + "value": { + "face_image_source": [ + "A document-capture selfie can only be added to the face blocklist." + ] + } + }, + "No clear face in selfie": { + "value": { + "face_image_source": [ + "No clear face was found in this selfie, so it cannot be blocklisted." + ] + } + }, + "Selfie already listed": { + "value": { + "face_image_source": [ + "This selfie is already in the blocklist." + ] + } } } } } - } - }, - "responses": { - "200": { - "description": "Estimate (when estimate_only) or the completed job for small batches processed inline.", + }, + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", "content": { "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } - }, - { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean" - }, - "total": { - "type": "integer" - }, - "processed": { - "type": "integer" - }, - "outcomes": { - "type": "object", - "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active (legacy duplicate rows are consolidated)." - }, - "disabled": { - "type": "integer", - "description": "Monitoring disabled on every AML row of the entity." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: no full name + date of birth + country available to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application." - }, - "failed_provider": { - "type": "integer", - "description": "The screening provider call failed." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out mid-job." - } - } - }, - "error": { - "type": "string", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } - } - } + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." } - ] + } } } } }, - "202": { - "description": "Larger batches are processed asynchronously; poll the job endpoint with the returned job_id.", + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: write` or is used from an IP address outside its allowlist.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean" - }, - "total": { - "type": "integer" - }, - "processed": { - "type": "integer" - }, - "outcomes": { - "type": "object", - "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active (legacy duplicate rows are consolidated)." - }, - "disabled": { - "type": "integer", - "description": "Monitoring disabled on every AML row of the entity." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: no full name + date of birth + country available to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application." - }, - "failed_provider": { - "type": "integer", - "description": "The screening provider call failed." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out mid-job." - } - } - }, - "error": { - "type": "string", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no write access to workflows." } } } } } }, - "403": { - "description": "Missing write:sessions privilege, or insufficient balance to enable (the estimate is included in the error body)." + "404": { + "description": "No list with this UUID exists in the API key's application, or it was deleted.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "List not found." + } + } + } + } + } + }, + "429": { + "$ref": "#/components/responses/RateLimitedWrite" + }, + "503": { + "description": "Face analysis of a document-capture selfie is temporarily unavailable. Nothing was added; retry later.", + "content": { + "application/json": { + "examples": { + "Unavailable": { + "value": { + "detail": "Service temporarily unavailable. Please try again later." + } + } + } + } + } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/vendor-businesses/toggle-ongoing-monitoring/{job_id}/": { - "get": { - "summary": "Get AML ongoing monitoring job", - "description": "Progress and per-outcome counters of a monitoring toggle job. Poll every couple of seconds while status is PENDING or PROCESSING.", - "operationId": "get_vendor_businesses_ongoing_monitoring_job", + "/v3/lists/{list_uuid}/entries/{entry_uuid}/": { + "delete": { + "summary": "Delete a list entry", + "description": "Deletes one entry from a list, system lists included. The entry stops matching at once, and deletion is irreversible: to list the value again, create a new entry. On a system face, document, phone or email blocklist, the record the entry stands for is no longer flagged as blocklisted; on the system user or business blocklist, users or businesses of the application whose `vendor_data` equals the entry's value (or whose `didit_internal_id` equals its `reference_object_uuid`) and that are `BLOCKED` return to `ACTIVE`. Sessions and transactions that already finished keep their status. A scoped API key needs `workflows: write`.", + "operationId": "delete_list_entry", + "x-didit-sdk": { + "resource": "lists.entries", + "method": "delete", + "auth": "api_key", + "server": true + }, "tags": [ - "Businesses" + "Lists" ], "parameters": [ { - "name": "organization_id", + "name": "list_uuid", "in": "path", "required": true, + "description": "UUID of the list that holds the entry.", "schema": { "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "example": "11111111-2222-3333-4444-555555555555" }, { - "name": "application_id", + "name": "entry_uuid", "in": "path", "required": true, + "description": "UUID of the entry, as returned by List list entries or Create a list entry.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." + "example": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE 'https://verification.didit.me/v3/lists/11111111-2222-3333-4444-555555555555/entries/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/' \\\n -H 'x-api-key: YOUR_API_KEY'" }, { - "name": "job_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Monitoring job UUID returned by the toggle endpoint." + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nlist_uuid = '11111111-2222-3333-4444-555555555555'\nentry_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'\nresp = requests.delete(\n f'https://verification.didit.me/v3/lists/{list_uuid}/entries/{entry_uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nprint('Deleted:', resp.status_code == 204)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const listUuid = '11111111-2222-3333-4444-555555555555';\nconst entryUuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';\nconst resp = await fetch(\n `https://verification.didit.me/v3/lists/${listUuid}/entries/${entryUuid}/`,\n { method: 'DELETE', headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (resp.status !== 204) throw new Error(`Delete failed: ${resp.status}`);\nconsole.log('Entry deleted');" } ], "responses": { - "200": { - "description": "The job.", + "204": { + "description": "The entry was deleted. No body." + }, + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean" - }, - "total": { - "type": "integer" - }, - "processed": { - "type": "integer" - }, - "outcomes": { - "type": "object", - "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active (legacy duplicate rows are consolidated)." - }, - "disabled": { - "type": "integer", - "description": "Monitoring disabled on every AML row of the entity." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: no full name + date of birth + country available to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application." - }, - "failed_provider": { - "type": "integer", - "description": "The screening provider call failed." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out mid-job." - } - } - }, - "error": { - "type": "string", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: write` or is used from an IP address outside its allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no write access to workflows." + } + } + } + } + } + }, + "404": { + "description": "No entry with this UUID exists in this list, the list is not in the API key's application or was deleted, or the entry was already deleted.", + "content": { + "application/json": { + "examples": { + "Not found": { + "value": { + "detail": "No ListEntry matches the given query." } } } } } }, - "404": { - "description": "Unknown job for this application." + "429": { + "$ref": "#/components/responses/RateLimitedWrite" } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/businesses/toggle-ongoing-monitoring/": { - "patch": { - "summary": "Toggle AML ongoing monitoring for business sessions", - "description": "Session-level toggle for KYB verifications, addressed by session number. Sessions without an AML screening get one created and attached to the session itself. Same estimate_only, inline fast-path and async job contract as the user-level endpoints. Replaces the legacy synchronous behavior that skipped sessions without an AML screening.", - "operationId": "toggle_business_sessions_ongoing_monitoring", + "/v3/lists/{list_uuid}/entries/face-upload/": { + "post": { + "summary": "Upload a face to a list", + "description": "Adds a face to a face blocklist or face allowlist from a base64-encoded JPEG or PNG image, for when you have no verification session to reference. Didit detects the faces in the image, uses the largest one, and creates an entry whose `value` and `reference_object_uuid` are the UUID of the new face. A blocklisted face declines new sessions whose face matches; an allowlisted face skips the duplicated-face action and never overrides a blocklist match. In a sandbox application the image is not analyzed and the entry matches nothing. A scoped API key needs `workflows: write`. To list a face from an existing session, use [Create a list entry](https://docs.didit.me/management-api/lists/create-entry) with `reference_session_id` instead.", + "operationId": "upload_list_entry_face", + "x-didit-sdk": { + "resource": "lists.entries", + "method": "upload_face", + "auth": "api_key", + "server": true + }, "tags": [ - "Businesses" + "Lists" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "list_uuid", "in": "path", "required": true, + "description": "UUID of a list with `entry_type: face` and `list_type: blocklist` or `allowlist`.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." + "example": "11111111-2222-3333-4444-555555555555" } ], "requestBody": { @@ -38325,754 +57277,463 @@ "schema": { "type": "object", "required": [ - "is_enabled" + "image" ], "properties": { - "is_enabled": { - "type": "boolean", - "description": "true to enable monitoring, false to disable it." - }, - "estimate_only": { - "type": "boolean", - "default": false, - "description": "When true, returns the cost estimate and performs no writes." - }, - "update_all": { - "type": "boolean", - "default": false, - "description": "Apply to every entity of the application (scoped by the filter_* params when present)." - }, - "filter_search": { - "type": "string" - }, - "filter_status": { - "type": "array", - "items": { - "type": "string" - } - }, - "filter_country": { - "type": "string" - }, - "filter_date_from": { + "image": { "type": "string", - "format": "date" + "format": "byte", + "description": "Base64-encoded JPEG or PNG, at most 2 MB once decoded, showing a clearly visible face. Send the bare base64 string, without a `data:image/...;base64,` prefix." }, - "filter_date_to": { + "comment": { "type": "string", - "format": "date" - }, - "session_numbers": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Business session numbers to toggle. Required unless update_all (or the legacy toggle_all alias) is true." - }, - "toggle_all": { - "type": "boolean", - "default": false, - "description": "Legacy alias of update_all." - }, - "filter_workflow_id": { - "type": "string" + "default": "", + "description": "Optional free-text note, such as the reason for listing." + } + } + }, + "examples": { + "Face image": { + "value": { + "image": "/9j/4AAQSkZJRgABAQAAAQABAAD...", + "comment": "Known fraud attempt" } } } } } }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/lists/11111111-2222-3333-4444-555555555555/entries/face-upload/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d \"{\\\"image\\\": \\\"$(base64 < face.jpg | tr -d '\\n')\\\", \\\"comment\\\": \\\"Known fraud attempt\\\"}\"" + }, + { + "lang": "python", + "label": "Python", + "source": "import base64\nimport os\n\nimport requests\n\nlist_uuid = '11111111-2222-3333-4444-555555555555'\nwith open('face.jpg', 'rb') as image_file:\n image_b64 = base64.b64encode(image_file.read()).decode('ascii')\n\nresp = requests.post(\n f'https://verification.didit.me/v3/lists/{list_uuid}/entries/face-upload/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'image': image_b64, 'comment': 'Known fraud attempt'},\n timeout=10,\n)\nresp.raise_for_status()\nprint('Face entry', resp.json()['uuid'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import { readFile } from 'node:fs/promises';\n\nconst listUuid = '11111111-2222-3333-4444-555555555555';\nconst image = (await readFile('face.jpg')).toString('base64');\nconst resp = await fetch(`https://verification.didit.me/v3/lists/${listUuid}/entries/face-upload/`, {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ image, comment: 'Known fraud attempt' }),\n});\nif (!resp.ok) throw new Error(`Upload failed: ${resp.status} ${await resp.text()}`);\nconst entry = await resp.json();\nconsole.log('Face entry', entry.uuid);" + } + ], "responses": { - "200": { - "description": "Estimate (when estimate_only) or the completed job for small batches processed inline.", + "201": { + "description": "The face entry was created.", "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } - }, - { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean" - }, - "total": { - "type": "integer" - }, - "processed": { - "type": "integer" - }, - "outcomes": { - "type": "object", - "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active (legacy duplicate rows are consolidated)." - }, - "disabled": { - "type": "integer", - "description": "Monitoring disabled on every AML row of the entity." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: no full name + date of birth + country available to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application." - }, - "failed_provider": { - "type": "integer", - "description": "The screening provider call failed." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out mid-job." - } - } - }, - "error": { - "type": "string", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } - } - } + "$ref": "#/components/schemas/ComplianceListEntry" + }, + "examples": { + "Uploaded face": { + "value": { + "uuid": "cccccccc-dddd-eeee-ffff-000000000000", + "value": "44444444-5555-6666-7777-888888888888", + "display_label": "Manually uploaded face", + "comment": "Known fraud attempt", + "added_by": "api", + "reference_session_id": null, + "reference_session_number": null, + "reference_session_type": null, + "reference_object_uuid": "44444444-5555-6666-7777-888888888888", + "metadata": { + "reference_image_url": "https://media.example.com/faces/reference.jpg?expires=1760263938&signature=3f9a1c" + }, + "created_at": "2026-10-12T10:12:18.904467Z" } - ] + }, + "Sandbox application": { + "value": { + "uuid": "cccccccc-dddd-eeee-ffff-000000000000", + "value": "44444444-5555-6666-7777-888888888888", + "display_label": "Sandbox face entry", + "comment": "Known fraud attempt", + "added_by": "api", + "reference_session_id": null, + "reference_session_number": null, + "reference_session_type": null, + "reference_object_uuid": "44444444-5555-6666-7777-888888888888", + "metadata": { + "sandbox": true + }, + "created_at": "2026-10-12T10:12:18.904467Z" + } + } } } } }, - "202": { - "description": "Larger batches are processed asynchronously; poll the job endpoint with the returned job_id.", + "400": { + "description": "The list does not take face uploads, or the image is missing, invalid, too large or shows no usable face. No entry was created.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" + "examples": { + "Not a face list": { + "value": { + "detail": "Face upload is only available for face-type lists." + } + }, + "Custom face list": { + "value": { + "detail": "Face upload is only available for face blocklists and allowlists." + } + }, + "Missing image": { + "value": { + "image": [ + "This field is required." ] - }, - "is_enabled": { - "type": "boolean" - }, - "total": { - "type": "integer" - }, - "processed": { - "type": "integer" - }, - "outcomes": { - "type": "object", - "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active (legacy duplicate rows are consolidated)." - }, - "disabled": { - "type": "integer", - "description": "Monitoring disabled on every AML row of the entity." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: no full name + date of birth + country available to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application." - }, - "failed_provider": { - "type": "integer", - "description": "The screening provider call failed." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out mid-job." - } - } - }, - "error": { - "type": "string", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } + } + }, + "Invalid base64": { + "value": { + "image": [ + "Invalid base64 encoding." + ] + } + }, + "Too large": { + "value": { + "image": [ + "Image exceeds maximum size of 2 MB." + ] + } + }, + "Unreadable image": { + "value": { + "image": "Invalid image. Provide a valid base64-encoded JPG or PNG." + } + }, + "No face": { + "value": { + "image": "No face detected in the image. Please upload a clear photo with a visible face." + } + }, + "No usable face": { + "value": { + "image": "Could not extract face embedding. Try a different image." } } } } } }, - "403": { - "description": "Missing write:sessions privilege, or insufficient balance to enable (the estimate is included in the error body)." - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/businesses/toggle-ongoing-monitoring/{job_id}/": { - "get": { - "summary": "Get AML ongoing monitoring job", - "description": "Progress and per-outcome counters of a monitoring toggle job. Poll every couple of seconds while status is PENDING or PROCESSING.", - "operationId": "get_business_sessions_ongoing_monitoring_job", - "tags": [ - "Businesses" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", + "content": { + "application/json": { + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: write` or is used from an IP address outside its allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no write access to workflows." + } + } + } + } + } }, - { - "name": "job_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Monitoring job UUID returned by the toggle endpoint." - } - ], - "responses": { - "200": { - "description": "The job.", + "404": { + "description": "No list with this UUID exists in the API key's application, or it was deleted.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED" - ] - }, - "is_enabled": { - "type": "boolean" - }, - "total": { - "type": "integer" - }, - "processed": { - "type": "integer" - }, - "outcomes": { - "type": "object", - "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", - "properties": { - "enabled": { - "type": "integer", - "description": "Monitoring enabled on an existing AML screening." - }, - "screened_and_enabled": { - "type": "integer", - "description": "A new AML screening was created (billed) and monitoring enabled." - }, - "already_enabled": { - "type": "integer", - "description": "Monitoring was already active (legacy duplicate rows are consolidated)." - }, - "disabled": { - "type": "integer", - "description": "Monitoring disabled on every AML row of the entity." - }, - "skipped_no_identity_data": { - "type": "integer", - "description": "Skipped: no full name + date of birth + country available to screen." - }, - "skipped_blocked": { - "type": "integer", - "description": "Skipped: the user is blocked." - }, - "skipped_sandbox": { - "type": "integer", - "description": "Skipped: sandbox application." - }, - "failed_provider": { - "type": "integer", - "description": "The screening provider call failed." - }, - "failed_insufficient_balance": { - "type": "integer", - "description": "Balance ran out mid-job." - } - } - }, - "error": { - "type": "string", - "nullable": true - }, - "estimate": { - "type": "object", - "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", - "properties": { - "users_total": { - "type": "integer", - "description": "Entities in the selection." - }, - "already_enabled": { - "type": "integer" - }, - "with_existing_aml": { - "type": "integer", - "description": "Entities whose existing AML screening will be switched to monitored." - }, - "needing_screening": { - "type": "integer", - "description": "Entities without any AML screening that will be screened first (billed)." - }, - "skipped_no_identity_data": { - "type": "integer" - }, - "skipped_blocked": { - "type": "integer" - }, - "estimated_screening_cost_usd": { - "type": "number", - "example": 0.2 - }, - "estimated_annual_monitoring_cost_usd": { - "type": "number", - "example": 0.07 - }, - "balance_sufficient": { - "type": "boolean" - } - } + "examples": { + "Not found": { + "value": { + "detail": "List not found." + } + } + } + } + } + }, + "429": { + "$ref": "#/components/responses/RateLimitedWrite" + }, + "503": { + "description": "Face analysis is temporarily unavailable. No entry was created; retry later.", + "content": { + "application/json": { + "examples": { + "Unavailable": { + "value": { + "detail": "Service temporarily unavailable. Please try again later." } } } } } - }, - "404": { - "description": "Unknown job for this application." } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/": { + "/v3/blocklist/": { "get": { - "summary": "List cases", - "description": "List cases for the application, newest first. Supports filtering, search, and sorting; see query parameters. Paginated with `limit`/`offset`.", - "operationId": "list_cases", + "summary": "List blocklisted items", + "description": "Returns one paginated feed of the faces, identity documents, phone numbers and email addresses of the application's verification sessions that are currently blocklisted, newest `blocklisted_at` first; filter with `item_type`. Items of deleted sessions are not included. The feed covers records blocklisted from a session (with [Add session items to the blocklist](https://docs.didit.me/management-api/blocklist/add), from the console, or by a list entry created from a session); values added to a list by hand, uploaded faces, faces from a document-capture selfie and the other entry types are read with [List list entries](https://docs.didit.me/management-api/lists/list-entries). `face_image` and `front_image` are short-lived signed URLs, and a scoped API key without `media: read` receives `null`. A scoped API key needs `workflows: read`.", + "operationId": "list_blocklist_items", + "x-didit-sdk": { + "resource": "blocklist", + "method": "list", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" + "Blocklist" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, + "name": "item_type", + "in": "query", + "required": false, + "description": "Only items of this type. When omitted, every type is returned.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "enum": [ + "face", + "document", + "phone", + "email" + ] + } }, { "name": "limit", "in": "query", + "required": false, + "description": "Page size. Defaults to 50. A value above 1000000 returns `400`; zero, a negative or a non-numeric value falls back to 50.", "schema": { "type": "integer", "default": 50, - "minimum": 1 - }, - "description": "Page size. Defaults to 50." + "minimum": 1, + "maximum": 1000000 + } }, { "name": "offset", "in": "query", + "required": false, + "description": "Zero-based index of the first item to return. Prefer following `next`. A value above 1000000 returns `400`; a negative or non-numeric value falls back to 0.", "schema": { "type": "integer", "default": 0, - "minimum": 0 - }, - "description": "Zero-based offset of the first record." - }, - { - "name": "status__in", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Comma-separated statuses to include, e.g. OPEN,AWAITING_USER." - }, - { - "name": "priority", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Comma-separated priorities to include." - }, - { - "name": "source__in", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Comma-separated sources to include." - }, - { - "name": "blueprint", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Filter to cases on this blueprint." - }, - { - "name": "assigned_to", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Filter to cases assigned to this user." - }, - { - "name": "unassigned", - "in": "query", - "schema": { - "type": "boolean" - }, - "description": "true for unassigned cases, false for assigned cases." - }, - { - "name": "tag", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Filter to cases carrying this exact tag." - }, - { - "name": "search", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Matches title, case_number, tag, subject name/UUID/external ID, linked session UUID, or linked transaction UUID." - }, - { - "name": "date_from", - "in": "query", - "schema": { - "type": "string", - "format": "date" - }, - "description": "Created-at lower bound (inclusive)." - }, - { - "name": "date_to", - "in": "query", - "schema": { - "type": "string", - "format": "date" - }, - "description": "Created-at upper bound (inclusive)." - }, - { - "name": "due_before", - "in": "query", - "schema": { - "type": "string", - "format": "date-time" - }, - "description": "due_at upper bound." - }, - { - "name": "overdue", - "in": "query", - "schema": { - "type": "boolean" - }, - "description": "true to return only overdue open/awaiting-user cases." - }, - { - "name": "ordering", - "in": "query", - "schema": { - "type": "string", - "enum": [ - "created_at", - "-created_at", - "due_at", - "-due_at" - ], - "default": "-created_at" - }, - "description": "Sort order. due_at sorts nulls last." + "minimum": 0, + "maximum": 1000000 + } } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/?status__in=OPEN,AWAITING_USER&ordering=-due_at' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/blocklist/?item_type=document' \\\n -H 'x-api-key: YOUR_API_KEY'" }, { "lang": "python", "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'status__in': 'OPEN,AWAITING_USER', 'ordering': '-due_at'},\n timeout=10,\n)\nresp.raise_for_status()\nfor case in resp.json()['results']:\n print(case['case_number'], case['title'], case['priority'], case['due_at'])" + "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/blocklist/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'item_type': 'document'},\n timeout=10,\n)\nresp.raise_for_status()\nfor item in resp.json()['results']:\n print(item['session_id'], item['document_type'], item['document_number'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/blocklist/?item_type=document', {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst page = await resp.json();\nconsole.log(page.count, page.results);" } ], "responses": { "200": { - "description": "Paginated case list.", + "description": "A page of blocklisted items, newest first.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], "properties": { "count": { "type": "integer", - "description": "Number of matching rows. Exact up to 100, capped at 100 beyond that for performance. Rely on `next` being null to detect the last page." + "minimum": 0, + "description": "Total number of blocklisted items matching the filters." }, "next": { "type": "string", + "format": "uri", "nullable": true, - "description": "Absolute URL of the next page, or null." + "description": "Absolute URL of the next page, or `null` on the last page." }, "previous": { "type": "string", + "format": "uri", "nullable": true, - "description": "Absolute URL of the previous page, or null." + "description": "Absolute URL of the previous page, or `null` on the first page." }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/CaseListItem" + "$ref": "#/components/schemas/BlocklistedItem" } } } + }, + "examples": { + "Mixed feed": { + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "type": "document", + "session_id": "33333333-4444-5555-6666-777777777777", + "session_number": 1042, + "blocklisted_at": "2026-10-12T10:06:12.701553Z", + "face_image": null, + "document_type": "ID", + "document_number": "X1234567", + "front_image": "https://media.example.com/documents/front.jpg?expires=1760263938&signature=8b2e47", + "phone_number": null, + "country_code": null, + "email": null + }, + { + "type": "phone", + "session_id": "77777777-8888-9999-0000-111111111111", + "session_number": 1017, + "blocklisted_at": "2026-10-08T15:31:09.400127Z", + "face_image": null, + "document_type": null, + "document_number": null, + "front_image": null, + "phone_number": "+14155552671", + "country_code": "US", + "email": null + } + ] + } + } + } + } + } + }, + "400": { + "description": "A query parameter is invalid.", + "content": { + "application/json": { + "examples": { + "Unknown item type": { + "value": { + "item_type": [ + "\"wallet\" is not a valid choice." + ] + } + }, + "Limit too large": { + "value": [ + "limit must not exceed 1000000." + ] + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", + "content": { + "application/json": { + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: read` or is used from an IP address outside its allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no read access to workflows." + } + } } } } - } - } - }, - "post": { - "summary": "Create case", - "description": "Create a case anchored to exactly one subject (a user or a business), directly or by deriving the subject from an attached session/transaction link. Source is always `MANUAL` for API-created cases.", - "operationId": "create_case", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, + "429": { + "description": "The shared read limit of 600 requests per minute per API key was exceeded. Honor `Retry-After` before retrying.", + "content": { + "application/json": { + "examples": { + "Rate limited": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + } + } + }, + "security": [ { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "ApiKeyAuth": [] } + ] + } + }, + "/v3/blocklist/add/": { + "post": { + "summary": "Add session items to the blocklist", + "description": "Blocklists the face, identity document, phone number or email address of one person verification session of the application. For each selected type, Didit takes the session's most recent record, flags it as blocklisted and adds an entry linked to the session to the matching system blocklist (`added_by: \"api\"`), so new sessions with a matching face, document number, phone number or email address are declined. Every selected item is checked first, so if one is missing nothing is blocklisted; repeating the call for an item already listed keeps its single entry and refreshes `blocklisted_at`. The session itself and sessions that already finished are not changed. A scoped API key needs `workflows: write`.", + "operationId": "add_to_blocklist", + "x-didit-sdk": { + "resource": "blocklist", + "method": "add", + "auth": "api_key", + "server": true + }, + "tags": [ + "Blocklist" ], "requestBody": { "required": true, @@ -39081,80 +57742,252 @@ "schema": { "type": "object", "required": [ - "title" + "session_id" ], + "description": "At least one `blocklist_*` flag must be `true`.", "properties": { - "title": { - "type": "string", - "maxLength": 255 - }, - "blueprint": { + "session_id": { "type": "string", "format": "uuid", - "nullable": true, - "description": "CaseBlueprint UUID. Omit for no blueprint." + "description": "A person verification session of your application. Deleted sessions are refused." }, - "priority": { - "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ], - "default": "MEDIUM" + "blocklist_face": { + "type": "boolean", + "default": false, + "description": "Blocklist the session's face." }, - "due_at": { - "type": "string", - "format": "date-time", - "nullable": true + "blocklist_document": { + "type": "boolean", + "default": false, + "description": "Blocklist the session's identity document. The document needs its issuing state, document type, document number and full name." }, - "tags": { - "type": "array", - "items": { - "type": "string" + "blocklist_phone": { + "type": "boolean", + "default": false, + "description": "Blocklist the session's phone number." + }, + "blocklist_email": { + "type": "boolean", + "default": false, + "description": "Blocklist the session's email address." + } + } + }, + "examples": { + "Face and document": { + "value": { + "session_id": "33333333-4444-5555-6666-777777777777", + "blocklist_face": true, + "blocklist_document": true + } + }, + "Phone only": { + "value": { + "session_id": "33333333-4444-5555-6666-777777777777", + "blocklist_phone": true + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/blocklist/add/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"session_id\": \"33333333-4444-5555-6666-777777777777\",\n \"blocklist_face\": true,\n \"blocklist_document\": true\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/blocklist/add/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'session_id': '33333333-4444-5555-6666-777777777777',\n 'blocklist_face': True,\n 'blocklist_document': True,\n },\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()['message'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/blocklist/add/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n session_id: '33333333-4444-5555-6666-777777777777',\n blocklist_face: true,\n blocklist_document: true,\n }),\n});\nif (!resp.ok) throw new Error(`Blocklist failed: ${resp.status} ${await resp.text()}`);\nconsole.log((await resp.json()).message);" + } + ], + "responses": { + "200": { + "description": "The selected items were blocklisted.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string", + "description": "Always `Successfully updated blocklist.`" + } + } + }, + "examples": { + "Blocklisted": { + "value": { + "message": "Successfully updated blocklist." + } + } + } + } + } + }, + "400": { + "description": "The body is invalid, the session is not a verification session of this application, or it has no record of a selected type. Nothing was blocklisted.", + "content": { + "application/json": { + "examples": { + "Unknown session": { + "value": { + "non_field_errors": [ + "Session not found" + ] } }, - "subject": { - "type": "object", - "description": "Required unless links is provided; the case's exactly-one subject.", - "properties": { - "type": { - "type": "string", - "enum": [ - "user", - "business" - ] - }, - "uuid": { - "type": "string", - "format": "uuid" - } + "No item selected": { + "value": { + "non_field_errors": [ + "Must specify at least one item to blocklist" + ] } }, - "links": { - "type": "array", - "description": "Sessions/business sessions/transactions to attach on creation, e.g. [{\"entity_type\": \"transaction\", \"entity_uuid\": \"...\"}]. When subject is omitted, the subject is derived from the first link's owner.", - "items": { - "type": "object" + "No face record": { + "value": { + "non_field_errors": [ + "Session does not have a face record" + ] } }, - "assigned_to": { + "Incomplete document": { + "value": { + "non_field_errors": [ + "To blocklist a document, the document must have an issuing state, document type, document number and full name" + ] + } + }, + "Invalid session id": { + "value": { + "session_id": [ + "Must be a valid UUID." + ] + } + }, + "Missing session id": { + "value": { + "session_id": [ + "This field is required." + ] + } + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", + "content": { + "application/json": { + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: write` or is used from an IP address outside its allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no write access to workflows." + } + } + } + } + } + }, + "429": { + "$ref": "#/components/responses/RateLimitedWrite" + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/blocklist/remove/": { + "post": { + "summary": "Remove session items from the blocklist", + "description": "Lifts the block on the face, identity document, phone number or email address of one person verification session of the application. For each selected type, Didit takes the session's most recent record, clears its blocklisted flag and deletes the system blocklist entries created from that record, so it no longer declines new sessions. Entries added to a list by value are not touched: find them with [List list entries](https://docs.didit.me/management-api/lists/list-entries) and delete them with [Delete a list entry](https://docs.didit.me/management-api/lists/delete-entry). Unblocking a record that is not blocklisted succeeds and changes nothing, and sessions that already finished are not changed. A scoped API key needs `workflows: write`.", + "operationId": "remove_from_blocklist", + "x-didit-sdk": { + "resource": "blocklist", + "method": "remove", + "auth": "api_key", + "server": true + }, + "tags": [ + "Blocklist" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "session_id" + ], + "description": "At least one `unblock_*` flag must be `true`.", + "properties": { + "session_id": { "type": "string", "format": "uuid", - "nullable": true, - "description": "User UUID to assign immediately." + "description": "A person verification session of your application. Deleted sessions are refused." }, - "review_duplicates": { + "unblock_face": { "type": "boolean", "default": false, - "description": "Recheck existing investigations before creating the case. When true, a subject with existing cases requires a nonblank separate_incident_reason. The console always enables this protection. Omitting it preserves existing API behavior." + "description": "Lift the block on the session's face." }, - "separate_incident_reason": { - "type": "string", - "maxLength": 1000 + "unblock_document": { + "type": "boolean", + "default": false, + "description": "Lift the block on the session's identity document." }, - "source_context": { - "$ref": "#/components/schemas/CaseDuplicateCaseFraudSourceContextRequest" + "unblock_phone": { + "type": "boolean", + "default": false, + "description": "Lift the block on the session's phone number." + }, + "unblock_email": { + "type": "boolean", + "default": false, + "description": "Lift the block on the session's email address." + } + } + }, + "examples": { + "Face and document": { + "value": { + "session_id": "33333333-4444-5555-6666-777777777777", + "unblock_face": true, + "unblock_document": true } } } @@ -39165,420 +57998,486 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"title\": \"Manual - Jane Doe\", \"blueprint\": \"b1de0000-0000-4000-8000-000000000002\", \"priority\": \"HIGH\", \"subject\": {\"type\": \"user\", \"uuid\": \"5b9a0000-0000-4000-8000-00000000f00d\"}, \"tags\": [\"review\"]}'" + "source": "curl -X POST 'https://verification.didit.me/v3/blocklist/remove/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"session_id\": \"33333333-4444-5555-6666-777777777777\",\n \"unblock_face\": true,\n \"unblock_document\": true\n }'" }, { "lang": "python", "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'title': 'Manual - Jane Doe',\n 'blueprint': 'b1de0000-0000-4000-8000-000000000002',\n 'priority': 'HIGH',\n 'subject': {'type': 'user', 'uuid': '5b9a0000-0000-4000-8000-00000000f00d'},\n },\n timeout=10,\n)\nresp.raise_for_status()\ncase = resp.json()\nprint(case['case_number'], case['uuid'])" + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/blocklist/remove/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'session_id': '33333333-4444-5555-6666-777777777777',\n 'unblock_face': True,\n 'unblock_document': True,\n },\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()['message'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/blocklist/remove/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n session_id: '33333333-4444-5555-6666-777777777777',\n unblock_face: true,\n unblock_document: true,\n }),\n});\nif (!resp.ok) throw new Error(`Unblock failed: ${resp.status} ${await resp.text()}`);\nconsole.log((await resp.json()).message);" } ], "responses": { - "201": { - "description": "Case created.", + "200": { + "description": "The block on the selected items was lifted.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseDetail" + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string", + "description": "Always `Successfully removed from blocklist.`" + } + } + }, + "examples": { + "Unblocked": { + "value": { + "message": "Successfully removed from blocklist." + } + } } } } }, "400": { - "description": "Neither subject nor links provided, or the subject/blueprint/assignee was not found.", + "description": "The body is invalid, the session is not a verification session of this application, or it has no record of a selected type. Nothing was changed.", "content": { "application/json": { - "schema": { - "type": "object" + "examples": { + "Unknown session": { + "value": { + "non_field_errors": [ + "Session not found" + ] + } + }, + "No item selected": { + "value": { + "non_field_errors": [ + "Must specify at least one item to unblock" + ] + } + }, + "No email record": { + "value": { + "non_field_errors": [ + "Session does not have an email record" + ] + } + }, + "Invalid session id": { + "value": { + "session_id": [ + "Must be a valid UUID." + ] + } + }, + "Missing session id": { + "value": { + "session_id": [ + "This field is required." + ] + } + } } } } }, - "409": { - "description": "Duplicate review is enabled and an existing case was found without a separate-incident reason. No case was created." + "401": { + "description": "The scoped API key has expired. Other authentication failures return `403`.", + "content": { + "application/json": { + "examples": { + "Expired API key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The `x-api-key` header is missing or invalid, or a scoped API key lacks `workflows: write` or is used from an IP address outside its allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without workflows access": { + "value": { + "detail": "This API key has no write access to workflows." + } + } + } + } + } + }, + "429": { + "$ref": "#/components/responses/RateLimitedWrite" } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/": { + "/v3/webhook/": { "get": { - "summary": "Get case", - "description": "Retrieve a case with its subject, blueprint, notes, events, links, and checklist.", - "operationId": "get_case", + "summary": "Get webhook configuration", + "description": "Return the application's webhook settings in one call: every [webhook destination](https://docs.didit.me/management-api/webhook/list-destinations) with its signing secret, the event types a destination can subscribe to, the application-level (legacy) webhook URL, version and secret, the data and biometric-template retention policy, and the Digital ID wallet return hosts. Didit delivers webhooks to destinations, each signed with its own `secret_shared_key`; the top-level `webhook_url`, `webhook_version` and `secret_shared_key` are used only while no destination is enabled, and then only for `status.updated` and `data.updated`. The response contains signing secrets, so keep it out of logs and client-side code. A scoped API key needs `webhooks: read`.", + "operationId": "get_webhook_config", + "x-didit-sdk": { + "resource": "webhooks", + "method": "retrieve_config", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" + "Webhook" ], - "parameters": [ + "parameters": [], + "x-codeSamples": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/webhook/' \\\n -H 'x-api-key: YOUR_API_KEY'" }, { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/webhook/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nconfig = resp.json()\nfor destination in config['endpoints']:\n print(destination['label'], destination['url'], destination['subscribed_events'])" }, { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/webhook/', {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst config = await resp.json();\nfor (const destination of config.endpoints) {\n console.log(destination.label, destination.url, destination.subscribed_events);\n}" } ], "responses": { "200": { - "description": "Case detail.", + "description": "The webhook configuration.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseDetail" + "$ref": "#/components/schemas/WebhookConfig" + }, + "examples": { + "One destination": { + "value": { + "webhook_url": "https://yourapp.com/webhooks/didit", + "webhook_version": "v3", + "secret_shared_key": "dGhpcy1pcy10aGUtYXBwLWxldmVsLXNlY3JldC0wMDA", + "capture_method": "MOBILE", + "data_retention_months": 24, + "face_retention_policy": "delete_with_session", + "face_retention_days": null, + "available_events": [ + { + "value": "status.updated", + "label": "Session status updated" + }, + { + "value": "data.updated", + "label": "Session data updated" + }, + { + "value": "user.status.updated", + "label": "User status updated" + }, + { + "value": "user.data.updated", + "label": "User data updated" + }, + { + "value": "business.status.updated", + "label": "Business status updated" + }, + { + "value": "business.data.updated", + "label": "Business data updated" + }, + { + "value": "activity.created", + "label": "Activity created" + }, + { + "value": "transaction.created", + "label": "Transaction created" + }, + { + "value": "transaction.status.updated", + "label": "Transaction status updated" + }, + { + "value": "travel_rule.status.updated", + "label": "Travel Rule Status Updated" + }, + { + "value": "workflow.compliance_changed", + "label": "Workflow Compliance Changed" + } + ], + "allowed_return_hosts": [ + "shop.example.com" + ], + "endpoints": [ + { + "uuid": "11111111-2222-3333-4444-555555555555", + "label": "Production session webhooks", + "url": "https://yourapp.com/webhooks/didit", + "enabled": true, + "webhook_version": "v3", + "secret_shared_key": "dGhpcy1pcy1hbi1leGFtcGxlLXNlY3JldC0wMDAwMDA", + "subscribed_events": [ + "status.updated", + "data.updated" + ], + "created_at": "2026-01-04T08:00:00.000000Z", + "updated_at": "2026-04-22T14:09:11.523671Z" + } + ] + } + } } } } }, - "404": { - "description": "Case (or nested resource) not found in this application.", + "401": { + "description": "The API key has expired: it was created with an expiry date that has passed. A missing, invalid or revoked key gets `403`, not `401`.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." } } } } } - } - } - }, - "patch": { - "summary": "Update case", - "description": "Update the case's title, priority, due date, or tags. Logs PRIORITY_CHANGED/DUE_DATE_CHANGED/TAG_ADDED/TAG_REMOVED events as applicable.", - "operationId": "update_case", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } - ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "title": { - "type": "string", - "maxLength": 255 - }, - "priority": { - "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ] + "403": { + "description": "The request is not allowed. A missing, invalid or revoked `x-api-key` gets `You do not have permission to perform this action.` A scoped key without the access this endpoint needs gets a message naming the missing access, and a key used from outside its IP allowlist gets `This API key can't be used from this IP address.`", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } }, - "due_at": { - "type": "string", - "format": "date-time", - "nullable": true + "Scoped key without webhooks read": { + "value": { + "detail": "This API key has no read access to webhooks." + } }, - "tags": { - "type": "array", - "items": { - "type": "string" + "Outside the IP allowlist": { + "value": { + "detail": "This API key can't be used from this IP address." } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"priority\": \"HIGH\", \"due_at\": \"2026-08-01T00:00:00Z\"}'" - } - ], - "responses": { - "200": { - "description": "Updated case.", - "content": { - "application/json": { + }, + "429": { + "description": "Rate limit exceeded: more than 600 GET requests in the current one-minute window for this API key. Wait `Retry-After` seconds before retrying.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", "schema": { - "$ref": "#/components/schemas/CaseDetail" + "type": "integer", + "minimum": 1, + "example": 30 } - } - } - } - } - }, - "delete": { - "summary": "Delete case", - "description": "Delete a case. There is no undelete endpoint.", - "operationId": "delete_case", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "204": { - "description": "Case deleted." - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/assign/": { - "post": { - "summary": "Assign or unassign case", - "description": "Assign the case to a user, or unassign it by sending assignee_user_id: null. assignee_email/assignee_name auto-provision a User row when the assignee does not exist yet.", - "operationId": "assign_case", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "assignee_user_id" - ], - "properties": { - "assignee_user_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "null to unassign." - }, - "assignee_email": { - "type": "string", - "format": "email", - "nullable": true - }, - "assignee_name": { - "type": "string", - "nullable": true - } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed in the one-minute window (600).", + "schema": { + "type": "integer", + "example": 600 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix time (seconds) when the current window resets.", + "schema": { + "type": "integer", + "example": 1792056120 + } + }, + "X-RateLimit-Scope": { + "description": "Name of the budget that was exceeded.", + "schema": { + "type": "string", + "example": "generic-get" } } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/assign/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"assignee_user_id\": \"9a000000-0000-4000-8000-0000000000aa\"}'" - } - ], - "responses": { - "200": { - "description": "Case assigned/unassigned.", + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseDetail" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } } } - } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/resolve/": { - "post": { - "summary": "Resolve case", - "description": "Resolve the case as false_positive or valid_threat. Blocked with 400 if the blueprint's checklist is not optional and items remain incomplete, or if the blueprint requires a resolution note and none is given. When the case's blueprint has a four_eyes_checker_blueprint configured, the resolution is staged (case.pending_resolution) and the case moves to the checker blueprint instead of resolving immediately; a different user must call approve or reject-approval.", - "operationId": "resolve_case", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." + } + } + }, + "security": [ + { + "ApiKeyAuth": [] } + ] + }, + "patch": { + "summary": "Update webhook configuration", + "description": "Partially update application-level settings: the legacy webhook URL, version and secret, the data and biometric-template retention policy, and the Digital ID wallet return hosts. Send only the fields to change; the response is the full configuration, the same shape as `GET /v3/webhook/`. Destinations are not managed here: create, update and delete them with the `/v3/webhook/destinations/` endpoints, and do not send an `endpoints` field to this endpoint. A `data_retention_months` change also applies to existing sessions, and sessions it deletes cannot be restored; read [Data retention](https://docs.didit.me/console/data-retention) first. A scoped API key needs `webhooks: write`.", + "operationId": "update_webhook_config", + "x-didit-sdk": { + "resource": "webhooks", + "method": "update_config", + "auth": "api_key", + "server": true + }, + "tags": [ + "Webhook" ], + "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", - "required": [ - "resolution" - ], + "description": "Send only the fields to change. Destinations are not managed here.", "properties": { - "resolution": { + "webhook_url": { + "type": "string", + "format": "uri", + "nullable": true, + "maxLength": 2000, + "description": "Legacy application-level webhook URL. Didit uses it only while the application has no enabled destination, and then only for `status.updated` and `data.updated`. It does not create a destination, and the next create, update or delete of a destination replaces it. To receive webhooks, create a destination with `POST /v3/webhook/destinations/` instead." + }, + "webhook_version": { "type": "string", "enum": [ - "FALSE_POSITIVE", - "VALID_THREAT" - ] + "v1", + "v2", + "v3" + ], + "description": "Payload version of the legacy `webhook_url`." }, - "note": { + "rotate_secret_key": { + "type": "boolean", + "default": false, + "description": "`true` replaces the application-level `secret_shared_key` with a new random value at once; the new value is in the response. Destination secrets are not changed. Every call with `true` generates a new secret, so it is not idempotent." + }, + "capture_method": { "type": "string", + "enum": [ + "MOBILE", + "MOBILE_AND_DESKTOP" + ], + "description": "Legacy application setting. The verification flow does not read it." + }, + "data_retention_months": { + "type": "integer", "nullable": true, - "description": "Required when the blueprint sets require_resolution_note." + "minimum": 1, + "maximum": 120, + "description": "Automatic deletion window in months (a month counts as 30 days), or `null` for no automatic deletion. A change applies to existing sessions too: every user verification session older than the new window is deleted automatically, and that deletion is irreversible." + }, + "face_retention_policy": { + "type": "string", + "enum": [ + "delete_with_session", + "retain_until_user_deleted" + ], + "description": "What happens to a session's face embedding when the session is deleted. `retain_until_user_deleted` needs `face_retention_days` in the same request or already set. A change applies to session deletions from then on; it creates or purges no existing biometric template." + }, + "face_retention_days": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 3650, + "description": "Retention period, in days, of biometric templates under `retain_until_user_deleted`." + }, + "allowed_return_hosts": { + "type": "array", + "maxItems": 20, + "description": "Replaces the whole list of Digital ID wallet return hosts. Bare hostnames only, such as `shop.example.com`: no scheme, port, path or wildcard. Values are lowercased and duplicates are dropped. Send `[]` to clear the list.", + "items": { + "type": "string", + "maxLength": 253 + } + } + } + }, + "examples": { + "Enable biometric template retention": { + "value": { + "face_retention_policy": "retain_until_user_deleted", + "face_retention_days": 365 + } + }, + "Rotate the legacy secret": { + "value": { + "rotate_secret_key": true + } + }, + "Set data retention": { + "value": { + "data_retention_months": 24 + } + }, + "Register return hosts": { + "value": { + "allowed_return_hosts": [ + "shop.example.com", + "app.example.com" + ] } } } @@ -39589,977 +58488,2420 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/resolve/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"resolution\": \"FALSE_POSITIVE\", \"note\": \"Confirmed legitimate activity after reviewing documents.\"}'" + "source": "curl -X PATCH 'https://verification.didit.me/v3/webhook/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"face_retention_policy\": \"retain_until_user_deleted\", \"face_retention_days\": 365}'" }, { "lang": "python", "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/resolve/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'resolution': 'FALSE_POSITIVE', 'note': 'Confirmed legitimate activity.'},\n timeout=10,\n)\nresp.raise_for_status()\ncase = resp.json()\nprint(case['status'], case['resolution'], case.get('pending_resolution'))" + "source": "import os\n\nimport requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/webhook/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'face_retention_policy': 'retain_until_user_deleted', 'face_retention_days': 365},\n timeout=10,\n)\nresp.raise_for_status()\nconfig = resp.json()\nprint(config['face_retention_policy'], config['face_retention_days'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/webhook/', {\n method: 'PATCH',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ face_retention_policy: 'retain_until_user_deleted', face_retention_days: 365 }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst config = await resp.json();\nconsole.log(config.face_retention_policy, config.face_retention_days);" } ], "responses": { "200": { - "description": "Resolved, or staged for 4-eyes approval.", + "description": "The full configuration after the update, the same shape as `GET /v3/webhook/`. When you rotated the secret, store the new `secret_shared_key` before you discard the response.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseDetail" + "$ref": "#/components/schemas/WebhookConfig" + }, + "examples": { + "Retention enabled": { + "value": { + "webhook_url": "https://yourapp.com/webhooks/didit", + "webhook_version": "v3", + "secret_shared_key": "dGhpcy1pcy10aGUtYXBwLWxldmVsLXNlY3JldC0wMDA", + "capture_method": "MOBILE", + "data_retention_months": 24, + "face_retention_policy": "retain_until_user_deleted", + "face_retention_days": 365, + "available_events": [ + { + "value": "status.updated", + "label": "Session status updated" + }, + { + "value": "data.updated", + "label": "Session data updated" + }, + { + "value": "user.status.updated", + "label": "User status updated" + }, + { + "value": "user.data.updated", + "label": "User data updated" + }, + { + "value": "business.status.updated", + "label": "Business status updated" + }, + { + "value": "business.data.updated", + "label": "Business data updated" + }, + { + "value": "activity.created", + "label": "Activity created" + }, + { + "value": "transaction.created", + "label": "Transaction created" + }, + { + "value": "transaction.status.updated", + "label": "Transaction status updated" + }, + { + "value": "travel_rule.status.updated", + "label": "Travel Rule Status Updated" + }, + { + "value": "workflow.compliance_changed", + "label": "Workflow Compliance Changed" + } + ], + "allowed_return_hosts": [ + "shop.example.com" + ], + "endpoints": [ + { + "uuid": "11111111-2222-3333-4444-555555555555", + "label": "Production session webhooks", + "url": "https://yourapp.com/webhooks/didit", + "enabled": true, + "webhook_version": "v3", + "secret_shared_key": "dGhpcy1pcy1hbi1leGFtcGxlLXNlY3JldC0wMDAwMDA", + "subscribed_events": [ + "status.updated", + "data.updated" + ], + "created_at": "2026-01-04T08:00:00.000000Z", + "updated_at": "2026-04-22T14:09:11.523671Z" + } + ] + } + } } } } }, "400": { - "description": "Incomplete checklist items, or a resolution note is required.", + "description": "Validation failed: each invalid field is mapped to a list of messages, and nothing is changed. A body that is not valid JSON returns `{\"detail\": \"JSON parse error - ...\"}`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Each invalid field mapped to a list of messages.", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "examples": { + "Retention days missing": { + "value": { + "face_retention_days": [ + "A finite retention duration is required when face retention is enabled." + ] + } + }, + "Retention window out of range": { + "value": { + "data_retention_months": [ + "Ensure this value is less than or equal to 120." + ] + } + }, + "Invalid return host": { + "value": { + "allowed_return_hosts": [ + "'https://shop.example.com' is not a hostname. Register the bare host (e.g. shop.example.com): no scheme, port, path or wildcard." + ] + } + }, + "Invalid choice": { + "value": { + "face_retention_policy": [ + "\"forever\" is not a valid choice." + ] + } + } + } + } + } + }, + "401": { + "description": "The API key has expired: it was created with an expiry date that has passed. A missing, invalid or revoked key gets `403`, not `401`.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The request is not allowed. A missing, invalid or revoked `x-api-key` gets `You do not have permission to perform this action.` A scoped key without the access this endpoint needs gets a message naming the missing access, and a key used from outside its IP allowlist gets `This API key can't be used from this IP address.`", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without webhooks write": { + "value": { + "detail": "This API key has no write access to webhooks." + } + }, + "Outside the IP allowlist": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded: more than 300 write requests (POST, PATCH, PUT, DELETE) in the current one-minute window for this API key. Wait `Retry-After` seconds before retrying.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed in the one-minute window (300).", + "schema": { + "type": "integer", + "example": 300 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix time (seconds) when the current window resets.", + "schema": { + "type": "integer", + "example": 1792056120 + } + }, + "X-RateLimit-Scope": { + "description": "Name of the budget that was exceeded.", + "schema": { + "type": "string", + "example": "generic-write" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Throttled": { + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." } } } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/reopen/": { - "post": { - "summary": "Reopen case", - "description": "Move an awaiting-user or resolved case back to open, clearing resolution, resolution_notes, resolved_at, and resolved_by_email.", - "operationId": "reopen_case", + "/v3/webhook/destinations/{destination_uuid}/analytics/": { + "get": { + "summary": "Get webhook destination analytics", + "description": "Delivery totals, failure rate and response times for one webhook destination, over the whole range (`summary`) and per UTC day (`points`). Every attempt counts as a delivery: the first send of an event, each automatic [retry](https://docs.didit.me/integration/webhooks#retry-policy) and each resend. A delivery is failed when its recorded HTTP status is 400 or above, so timeouts (recorded as `504`) and connection failures (`503`) count as failed; a delivery still queued counts in `total_deliveries` only. Delivery records are available for about 30 days. A scoped API key needs `webhooks: read`.", + "operationId": "get_webhook_destination_analytics", + "x-didit-sdk": { + "resource": "webhooks.destinations", + "method": "retrieve_analytics", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" + "Webhook" ], "parameters": [ { - "name": "organization_id", + "name": "destination_uuid", "in": "path", "required": true, + "description": "UUID of the destination (the `uuid` returned by `GET /v3/webhook/destinations/`).", "schema": { "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "example": "11111111-2222-3333-4444-555555555555" }, { - "name": "application_id", - "in": "path", - "required": true, + "name": "date_from", + "in": "query", + "required": false, + "description": "First UTC day to include, as `YYYY-MM-DD` (inclusive). Omit to start from the oldest delivery available. `dateFrom` is accepted as an alias.", "schema": { "type": "string", - "format": "uuid" + "format": "date" }, - "description": "Application UUID." + "example": "2026-10-01" }, { - "name": "case_uuid", - "in": "path", - "required": true, + "name": "date_to", + "in": "query", + "required": false, + "description": "Last UTC day to include, as `YYYY-MM-DD` (inclusive). Must not be earlier than `date_from`. `dateTo` is accepted as an alias.", "schema": { "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" + "format": "date" }, - "description": "Case UUID." + "example": "2026-10-16" } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/reopen/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/analytics/?date_from=2026-10-01&date_to=2026-10-16' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\ndestination_uuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n f'https://verification.didit.me/v3/webhook/destinations/{destination_uuid}/analytics/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'date_from': '2026-10-01', 'date_to': '2026-10-16'},\n timeout=10,\n)\nresp.raise_for_status()\nsummary = resp.json()['summary']\nprint(f\"{summary['failed_deliveries']} of {summary['total_deliveries']} failed ({summary['error_rate_percentage']}%)\")" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const destinationUuid = '11111111-2222-3333-4444-555555555555';\nconst params = new URLSearchParams({ date_from: '2026-10-01', date_to: '2026-10-16' });\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${destinationUuid}/analytics/?${params}`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst { summary, points } = await resp.json();\nconsole.log(summary.error_rate_percentage, points.length);" } ], "responses": { "200": { - "description": "Reopen case.", + "description": "Delivery statistics for the destination.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseDetail" + "type": "object", + "required": [ + "summary", + "points" + ], + "properties": { + "summary": { + "type": "object", + "description": "Totals over the requested range.", + "required": [ + "total_deliveries", + "failed_deliveries", + "min_response_time_ms", + "avg_response_time_ms", + "max_response_time_ms", + "error_rate_percentage" + ], + "properties": { + "total_deliveries": { + "type": "integer", + "minimum": 0, + "description": "Delivery attempts, including queued ones." + }, + "failed_deliveries": { + "type": "integer", + "minimum": 0, + "description": "Attempts whose recorded HTTP status is 400 or above." + }, + "min_response_time_ms": { + "type": "integer", + "nullable": true, + "description": "Fastest attempt in milliseconds, or `null` when no attempt has a recorded time." + }, + "avg_response_time_ms": { + "type": "integer", + "nullable": true, + "description": "Mean attempt time in milliseconds, rounded, or `null` when no attempt has a recorded time." + }, + "max_response_time_ms": { + "type": "integer", + "nullable": true, + "description": "Slowest attempt in milliseconds, or `null` when no attempt has a recorded time." + }, + "error_rate_percentage": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "description": "`failed_deliveries` / `total_deliveries` x 100, rounded to a whole number. `0` when there are no deliveries." + } + } + }, + "points": { + "type": "array", + "description": "One entry per UTC day that had deliveries, oldest first. Days without deliveries are omitted, so fill the gaps with zeros when you chart them.", + "items": { + "type": "object", + "required": [ + "date", + "total_deliveries", + "failed_deliveries", + "min_response_time_ms", + "avg_response_time_ms", + "max_response_time_ms" + ], + "properties": { + "date": { + "type": "string", + "format": "date", + "description": "UTC day, `YYYY-MM-DD`." + }, + "total_deliveries": { + "type": "integer", + "minimum": 0, + "description": "Delivery attempts, including queued ones." + }, + "failed_deliveries": { + "type": "integer", + "minimum": 0, + "description": "Attempts whose recorded HTTP status is 400 or above." + }, + "min_response_time_ms": { + "type": "integer", + "nullable": true, + "description": "Fastest attempt in milliseconds, or `null` when no attempt has a recorded time." + }, + "avg_response_time_ms": { + "type": "integer", + "nullable": true, + "description": "Mean attempt time in milliseconds, rounded, or `null` when no attempt has a recorded time." + }, + "max_response_time_ms": { + "type": "integer", + "nullable": true, + "description": "Slowest attempt in milliseconds, or `null` when no attempt has a recorded time." + } + } + } + } + } + }, + "examples": { + "Three days of deliveries": { + "value": { + "summary": { + "total_deliveries": 1250, + "failed_deliveries": 12, + "min_response_time_ms": 38, + "avg_response_time_ms": 164, + "max_response_time_ms": 5004, + "error_rate_percentage": 1 + }, + "points": [ + { + "date": "2026-10-14", + "total_deliveries": 410, + "failed_deliveries": 0, + "min_response_time_ms": 38, + "avg_response_time_ms": 152, + "max_response_time_ms": 911 + }, + { + "date": "2026-10-15", + "total_deliveries": 395, + "failed_deliveries": 12, + "min_response_time_ms": 41, + "avg_response_time_ms": 190, + "max_response_time_ms": 5004 + }, + { + "date": "2026-10-16", + "total_deliveries": 445, + "failed_deliveries": 0, + "min_response_time_ms": 40, + "avg_response_time_ms": 151, + "max_response_time_ms": 1220 + } + ] + } + }, + "No deliveries in range": { + "value": { + "summary": { + "total_deliveries": 0, + "failed_deliveries": 0, + "min_response_time_ms": null, + "avg_response_time_ms": null, + "max_response_time_ms": null, + "error_rate_percentage": 0 + }, + "points": [] + } + } + } + } + } + }, + "400": { + "description": "`date_from` or `date_to` is not a `YYYY-MM-DD` date, or `date_to` is earlier than `date_from`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Each invalid field mapped to a list of messages.", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "examples": { + "Malformed date": { + "value": { + "date_from": [ + "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." + ] + } + }, + "Reversed range": { + "value": { + "date_to": [ + "date_to must be greater than or equal to date_from." + ] + } + } + } + } + } + }, + "401": { + "description": "The API key has expired: it was created with an expiry date that has passed. A missing, invalid or revoked key gets `403`, not `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The request is not allowed. A missing, invalid or revoked `x-api-key` gets `You do not have permission to perform this action.` A scoped key without the access this endpoint needs gets a message naming the missing access, and a key used from outside its IP allowlist gets `This API key can't be used from this IP address.`", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without webhooks read": { + "value": { + "detail": "This API key has no read access to webhooks." + } + }, + "Outside the IP allowlist": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } + }, + "404": { + "description": "No destination with this UUID exists for the application, or it has been deleted.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Destination not found": { + "value": { + "detail": "Webhook destination not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded: more than 600 GET requests in the current one-minute window for this API key. Wait `Retry-After` seconds before retrying.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed in the one-minute window (600).", + "schema": { + "type": "integer", + "example": 600 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix time (seconds) when the current window resets.", + "schema": { + "type": "integer", + "example": 1792056120 + } + }, + "X-RateLimit-Scope": { + "description": "Name of the budget that was exceeded.", + "schema": { + "type": "string", + "example": "generic-get" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/request-info/": { - "post": { - "summary": "Request info (awaiting user)", - "description": "Move an open case to awaiting_user while more information is collected from the verified user. Fails with 400 if a resolution is currently pending 4-eyes approval.", - "operationId": "request_case_info", + "/v3/webhook/destinations/{destination_uuid}/deliveries/": { + "get": { + "summary": "List webhook destination deliveries", + "description": "Page through every delivery attempt to one webhook destination, newest first. The first send of each event, each automatic [retry](https://docs.didit.me/integration/webhooks#retry-policy) and each resend are separate entries. Entries carry the HTTP status and response time but not the payload: fetch one delivery to read its headers and body. `count` is exact up to 100 and reads 100 above that, so follow `next` until it is `null`. Delivery records are available for about 30 days. A scoped API key needs `webhooks: read` and `decisions: read`; a key limited to some workflows or to approved sessions cannot use this endpoint.", + "operationId": "list_webhook_destination_deliveries", + "x-didit-sdk": { + "resource": "webhooks.destinations", + "method": "list_deliveries", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" + "Webhook" ], "parameters": [ { - "name": "organization_id", + "name": "destination_uuid", "in": "path", "required": true, + "description": "UUID of the destination (the `uuid` returned by `GET /v3/webhook/destinations/`).", "schema": { "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "example": "11111111-2222-3333-4444-555555555555" }, { - "name": "application_id", - "in": "path", - "required": true, + "name": "limit", + "in": "query", + "required": false, + "description": "Maximum number of deliveries per page. Defaults to 50. A value that is not a positive integer falls back to 50; a value above 1000000 returns `400`.", "schema": { - "type": "string", - "format": "uuid" + "type": "integer", + "default": 50, + "minimum": 1, + "maximum": 1000000 }, - "description": "Application UUID." + "example": 50 }, { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/request-info/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Request info (awaiting user).", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseDetail" - } - } - } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/escalate/": { - "post": { - "summary": "Escalate case", - "description": "Move the case to its blueprint's configured escalation_blueprint. Requires the current blueprint to have escalation_enabled and an escalation_blueprint set, otherwise 400.", - "operationId": "escalate_case", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, + "name": "offset", + "in": "query", + "required": false, + "description": "Number of deliveries to skip. Defaults to 0. Prefer following the `next` and `previous` URLs from the response.", "schema": { - "type": "string", - "format": "uuid" + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 1000000 }, - "description": "Organization UUID." + "example": 0 }, { - "name": "application_id", - "in": "path", - "required": true, + "name": "date_from", + "in": "query", + "required": false, + "description": "First UTC day to include, as `YYYY-MM-DD` (inclusive). Omit to start from the oldest delivery available. `dateFrom` is accepted as an alias.", "schema": { "type": "string", - "format": "uuid" + "format": "date" }, - "description": "Application UUID." + "example": "2026-10-01" }, { - "name": "case_uuid", - "in": "path", - "required": true, + "name": "date_to", + "in": "query", + "required": false, + "description": "Last UTC day to include, as `YYYY-MM-DD` (inclusive). Must not be earlier than `date_from`. `dateTo` is accepted as an alias.", "schema": { "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" + "format": "date" }, - "description": "Case UUID." + "example": "2026-10-16" } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/escalate/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/deliveries/?limit=50&date_from=2026-10-01' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\ndestination_uuid = '11111111-2222-3333-4444-555555555555'\nurl = f'https://verification.didit.me/v3/webhook/destinations/{destination_uuid}/deliveries/'\nparams = {'limit': 50, 'date_from': '2026-10-01'}\n\nwhile url:\n resp = requests.get(\n url,\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params=params,\n timeout=10,\n )\n resp.raise_for_status()\n page = resp.json()\n for delivery in page['results']:\n if delivery['response_status'] is None or delivery['response_status'] >= 400:\n print(delivery['uuid'], delivery['webhook_type'], delivery['response_status'])\n url, params = page['next'], None # next already carries the query string" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const destinationUuid = '11111111-2222-3333-4444-555555555555';\nlet url = `https://verification.didit.me/v3/webhook/destinations/${destinationUuid}/deliveries/?limit=50&date_from=2026-10-01`;\nwhile (url) {\n const resp = await fetch(url, { headers: { 'x-api-key': process.env.DIDIT_API_KEY } });\n if (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\n const page = await resp.json();\n for (const delivery of page.results) {\n if (delivery.response_status === null || delivery.response_status >= 400) {\n console.log(delivery.uuid, delivery.webhook_type, delivery.response_status);\n }\n }\n url = page.next;\n}" } ], "responses": { "200": { - "description": "Escalate case.", + "description": "One page of deliveries, newest first.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseDetail" + "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], + "properties": { + "count": { + "type": "integer", + "minimum": 0, + "description": "Matching deliveries, exact up to 100 and capped at 100 above that. Use `next` to know whether more pages exist." + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Absolute URL of the next page, or `null` on the last page." + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Absolute URL of the previous page, or `null` on the first page." + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebhookDestinationDelivery" + } + } + } + }, + "examples": { + "Failed delivery, its retry and a queued event": { + "value": { + "count": 3, + "next": null, + "previous": null, + "results": [ + { + "uuid": "66666666-7777-8888-9999-000000000003", + "session_id": null, + "request_url": "https://yourapp.com/webhooks/didit", + "response_status": null, + "response_time_ms": null, + "status": "APPROVED", + "webhook_type": "transaction.created", + "created_at": "2026-10-15T09:23:02.871190Z" + }, + { + "uuid": "66666666-7777-8888-9999-000000000002", + "session_id": "22222222-3333-4444-5555-666666666666", + "request_url": "https://yourapp.com/webhooks/didit", + "response_status": 200, + "response_time_ms": 143, + "status": "In Progress", + "webhook_type": "status.updated", + "created_at": "2026-10-15T09:22:19.104233Z" + }, + { + "uuid": "66666666-7777-8888-9999-000000000001", + "session_id": "22222222-3333-4444-5555-666666666666", + "request_url": "https://yourapp.com/webhooks/didit", + "response_status": 503, + "response_time_ms": 412, + "status": "In Progress", + "webhook_type": "status.updated", + "created_at": "2026-10-15T09:21:18.412345Z" + } + ] + } + } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/transfer/": { - "post": { - "summary": "Transfer case", - "description": "Move the case to a different, active blueprint of the same application. Requires the current blueprint to have transfer_enabled, otherwise 400.", - "operationId": "transfer_case", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "400": { + "description": "`date_from` or `date_to` is not a `YYYY-MM-DD` date, `date_to` is earlier than `date_from`, or `limit` or `offset` is above 1000000 (that error is a JSON array of messages).", + "content": { + "application/json": { + "examples": { + "Malformed date": { + "value": { + "date_from": [ + "Date has wrong format. Use one of these formats instead: YYYY-MM-DD." + ] + } + }, + "Reversed range": { + "value": { + "date_to": [ + "date_to must be greater than or equal to date_from." + ] + } + }, + "Limit too large": { + "value": [ + "limit must not exceed 1000000." + ] + } + } + } + } }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "target_blueprint" - ], - "properties": { - "target_blueprint": { - "type": "string", - "format": "uuid" + "401": { + "description": "The API key has expired: it was created with an expiry date that has passed. A missing, invalid or revoked key gets `403`, not `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/transfer/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"target_blueprint\": \"b1de0000-0000-4000-8000-000000000002\"}'" - } - ], - "responses": { - "200": { - "description": "Case transferred.", + }, + "403": { + "description": "The request is not allowed. A missing, invalid or revoked `x-api-key` gets `You do not have permission to perform this action.` A scoped key without the access this endpoint needs gets a message naming the missing access, and a key used from outside its IP allowlist gets `This API key can't be used from this IP address.` A key limited to some workflows or to approved sessions gets `This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint.`", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseDetail" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without webhooks read": { + "value": { + "detail": "This API key has no read access to webhooks." + } + }, + "Scoped key without decisions read": { + "value": { + "detail": "This API key has no read access to decisions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "Outside the IP allowlist": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } + }, + "404": { + "description": "No destination with this UUID exists for the application, or it has been deleted.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Destination not found": { + "value": { + "detail": "Webhook destination not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded: more than 600 GET requests in the current one-minute window for this API key. Wait `Retry-After` seconds before retrying.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed in the one-minute window (600).", + "schema": { + "type": "integer", + "example": 600 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix time (seconds) when the current window resets.", + "schema": { + "type": "integer", + "example": 1792056120 + } + }, + "X-RateLimit-Scope": { + "description": "Name of the budget that was exceeded.", + "schema": { + "type": "string", + "example": "generic-get" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/approve/": { - "post": { - "summary": "Approve staged resolution (4-eyes)", - "description": "Apply a resolution staged by resolve on a maker blueprint. The caller must be a different user than the one who submitted it, otherwise 403.", - "operationId": "approve_case_resolution", + "/v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/": { + "get": { + "summary": "Get webhook destination delivery", + "description": "Retrieve one delivery attempt to a webhook destination with the exact request headers and JSON body Didit sent, the HTTP status your endpoint returned and the response time. `request_header` and `request_body` are JSON text: parse them to read the signature headers and the payload, for example to check your [signature verification](https://docs.didit.me/integration/webhooks#signature-verification) against the destination's `secret_shared_key`. A delivery never contains the signing secret, and Didit does not record your endpoint's response body. A scoped API key needs `webhooks: read` and `decisions: read`; a key limited to some workflows or to approved sessions cannot use this endpoint.", + "operationId": "get_webhook_destination_delivery", + "x-didit-sdk": { + "resource": "webhooks.destinations", + "method": "retrieve_delivery", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" + "Webhook" ], "parameters": [ { - "name": "organization_id", + "name": "destination_uuid", "in": "path", "required": true, + "description": "UUID of the destination the delivery was sent to.", "schema": { "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "example": "11111111-2222-3333-4444-555555555555" }, { - "name": "application_id", + "name": "delivery_uuid", "in": "path", "required": true, + "description": "UUID of the delivery: the `uuid` of an entry returned by `GET /v3/webhook/destinations/{destination_uuid}/deliveries/`.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." + "example": "66666666-7777-8888-9999-000000000001" } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/approve/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/deliveries/66666666-7777-8888-9999-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import json\nimport os\n\nimport requests\n\ndestination_uuid = '11111111-2222-3333-4444-555555555555'\ndelivery_uuid = '66666666-7777-8888-9999-000000000001'\nresp = requests.get(\n f'https://verification.didit.me/v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\ndelivery = resp.json()\nheaders = json.loads(delivery['request_header'])\npayload = json.loads(delivery['request_body'])\nprint(delivery['response_status'], headers['X-Signature-V2'], payload['event_id'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const destinationUuid = '11111111-2222-3333-4444-555555555555';\nconst deliveryUuid = '66666666-7777-8888-9999-000000000001';\nconst resp = await fetch(\n `https://verification.didit.me/v3/webhook/destinations/${destinationUuid}/deliveries/${deliveryUuid}/`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst delivery = await resp.json();\nconst headers = JSON.parse(delivery.request_header);\nconst payload = JSON.parse(delivery.request_body);\nconsole.log(delivery.response_status, headers['X-Signature-V2'], payload.event_id);" } ], "responses": { "200": { - "description": "Resolution applied.", + "description": "The delivery, with its request headers and body.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookDestinationDeliveryDetail" + }, + "examples": { + "Connection failure": { + "value": { + "uuid": "66666666-7777-8888-9999-000000000001", + "session_id": "22222222-3333-4444-5555-666666666666", + "request_url": "https://yourapp.com/webhooks/didit", + "response_status": 503, + "response_time_ms": 412, + "status": "In Progress", + "webhook_type": "status.updated", + "created_at": "2026-10-15T09:21:18.412345Z", + "request_header": "{\"Content-Type\": \"application/json\", \"X-Signature\": \"a5e02a03404e1337a0198e6be15d34abbd053c658a0e51e5d0e78554213ef29b\", \"X-Signature-V2\": \"927e7cb770c94ce745939bff73f04aa200015f3bc64795ba46c2aaf78a6cedc5\", \"X-Signature-Simple\": \"e37b0d4c716c72bb5ae7127b9cc455d28ab4686e9e22312b4ec2327409a73302\", \"X-Timestamp\": \"1792056078\", \"User-Agent\": \"DiditWebhook/2.0 +https://didit.me\"}", + "request_body": "{\"application_id\":\"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\",\"created_at\":1792056057,\"environment\":\"live\",\"event_id\":\"33333333-4444-5555-6666-777777777777\",\"sandbox_scenario\":null,\"session_id\":\"22222222-3333-4444-5555-666666666666\",\"status\":\"In Progress\",\"timestamp\":1792056078,\"vendor_data\":\"user-123\",\"webhook_type\":\"status.updated\",\"workflow_id\":\"44444444-5555-6666-7777-888888888888\",\"workflow_version\":3}" + } + } + } + } + } + }, + "401": { + "description": "The API key has expired: it was created with an expiry date that has passed. A missing, invalid or revoked key gets `403`, not `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The request is not allowed. A missing, invalid or revoked `x-api-key` gets `You do not have permission to perform this action.` A scoped key without the access this endpoint needs gets a message naming the missing access, and a key used from outside its IP allowlist gets `This API key can't be used from this IP address.` A key limited to some workflows or to approved sessions gets `This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint.`", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without webhooks read": { + "value": { + "detail": "This API key has no read access to webhooks." + } + }, + "Scoped key without decisions read": { + "value": { + "detail": "This API key has no read access to decisions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "Outside the IP allowlist": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } + }, + "404": { + "description": "The destination does not exist for the application or has been deleted, or the delivery does not exist, belongs to another destination, or is no longer available.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseDetail" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Destination not found": { + "value": { + "detail": "Webhook destination not found." + } + }, + "Delivery not found": { + "value": { + "detail": "Webhook delivery not found." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded: more than 600 GET requests in the current one-minute window for this API key. Wait `Retry-After` seconds before retrying.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed in the one-minute window (600).", + "schema": { + "type": "integer", + "example": 600 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix time (seconds) when the current window resets.", + "schema": { + "type": "integer", + "example": 1792056120 + } + }, + "X-RateLimit-Scope": { + "description": "Name of the budget that was exceeded.", + "schema": { + "type": "string", + "example": "generic-get" } } - } - }, - "409": { - "description": "Case has no resolution pending approval.", + }, "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." } } - } - } - } - }, - "403": { - "description": "The caller is the same user who submitted the resolution (or the case has no recorded maker); a different officer must approve it.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." } } } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/reject-approval/": { + "/v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/resend/": { "post": { - "summary": "Reject staged resolution (4-eyes)", - "description": "Reject a resolution staged by resolve and return the case to its maker blueprint. The caller must be a different user than the one who submitted it, otherwise 403.", - "operationId": "reject_case_resolution", + "summary": "Resend webhook destination delivery", + "description": "Send a past delivery again, for example after your endpoint was down. Didit takes the stored payload, sets a fresh `timestamp`, signs it again with the destination's current `secret_shared_key` (`X-Signature`, `X-Signature-V2`, `X-Signature-Simple` and `X-Timestamp`) and posts it to the delivery's `request_url`, even if the destination's URL has changed since or the destination is disabled. `event_id` and the rest of the payload are unchanged, so deduplicate on `event_id`. The call waits up to 5 seconds for your endpoint and returns the new delivery with `200` whatever your endpoint answered: read `response_status`. A resend is not retried automatically, every call sends again and creates a new delivery, and the request has no body. A scoped API key needs `webhooks: write` and `decisions: read`; a key limited to some workflows or to approved sessions cannot use this endpoint.", + "operationId": "resend_webhook_destination_delivery", + "x-didit-sdk": { + "resource": "webhooks.destinations", + "method": "resend_delivery", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" + "Webhook" ], "parameters": [ { - "name": "organization_id", + "name": "destination_uuid", "in": "path", "required": true, + "description": "UUID of the destination the delivery was sent to.", "schema": { "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "example": "11111111-2222-3333-4444-555555555555" }, { - "name": "application_id", + "name": "delivery_uuid", "in": "path", "required": true, + "description": "UUID of the delivery to resend: the `uuid` of an entry returned by `GET /v3/webhook/destinations/{destination_uuid}/deliveries/`.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." + "example": "66666666-7777-8888-9999-000000000001" } ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "notes": { - "type": "string", - "nullable": true - } - } - } - } - } - }, "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/reject-approval/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"notes\": \"Needs a clearer rationale before this can be approved.\"}'" + "source": "curl -X POST 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/deliveries/66666666-7777-8888-9999-000000000001/resend/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\ndestination_uuid = '11111111-2222-3333-4444-555555555555'\ndelivery_uuid = '66666666-7777-8888-9999-000000000001'\nresp = requests.post(\n f'https://verification.didit.me/v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/resend/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nresent = resp.json()\nif resent['response_status'] is None or resent['response_status'] >= 400:\n print('Your endpoint still rejects it:', resent['response_status'])\nelse:\n print('Delivered as', resent['uuid'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const destinationUuid = '11111111-2222-3333-4444-555555555555';\nconst deliveryUuid = '66666666-7777-8888-9999-000000000001';\nconst resp = await fetch(\n `https://verification.didit.me/v3/webhook/destinations/${destinationUuid}/deliveries/${deliveryUuid}/resend/`,\n { method: 'POST', headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!resp.ok) throw new Error(await resp.text());\nconst resent = await resp.json();\nconsole.log(resent.uuid, resent.response_status);" } ], "responses": { "200": { - "description": "Resolution rejected, case returned to the maker blueprint.", + "description": "The new delivery, already attempted. `response_status` is what your endpoint answered this time (`504` timeout, `503` connection failure). The original delivery is unchanged.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseDetail" + "$ref": "#/components/schemas/WebhookDestinationDeliveryDetail" + }, + "examples": { + "Accepted on resend": { + "value": { + "uuid": "66666666-7777-8888-9999-000000000004", + "session_id": "22222222-3333-4444-5555-666666666666", + "request_url": "https://yourapp.com/webhooks/didit", + "response_status": 200, + "response_time_ms": 151, + "status": "In Progress", + "webhook_type": "status.updated", + "created_at": "2026-10-16T14:05:42.330981Z", + "request_header": "{\"Content-Type\": \"application/json\", \"X-Signature\": \"b8eb1e376517ee0cad8ba98ff7f8fd35fc72eca231000963a96a335f82328835\", \"X-Signature-V2\": \"9efd2ce9ed1303e1d05273ef9608a59363ed9789a05253db4748b3e8e50cb109\", \"X-Signature-Simple\": \"bf3a9dc04b2c20ab539203b0cd5cca2a10f630bf62d8feb6d68d82af80f64774\", \"X-Timestamp\": \"1792159542\", \"User-Agent\": \"DiditWebhook/2.0 +https://didit.me\"}", + "request_body": "{\"application_id\":\"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\",\"created_at\":1792056057,\"environment\":\"live\",\"event_id\":\"33333333-4444-5555-6666-777777777777\",\"sandbox_scenario\":null,\"session_id\":\"22222222-3333-4444-5555-666666666666\",\"status\":\"In Progress\",\"timestamp\":1792159542,\"vendor_data\":\"user-123\",\"webhook_type\":\"status.updated\",\"workflow_id\":\"44444444-5555-6666-7777-888888888888\",\"workflow_version\":3}" + } + } } } } }, - "409": { - "description": "Case has no resolution pending approval.", + "400": { + "description": "The stored delivery could not be signed again. Nothing is sent.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + }, + "code": { + "type": "string", + "description": "Stable machine-readable error code. Branch on this, not on `detail`." + } + } + }, + "examples": { + "Unsignable delivery": { + "value": { + "detail": "Invalid webhook data format", + "code": "invalid_webhook_payload" } } } } } }, - "403": { - "description": "The caller is the same user who submitted the resolution (or the case has no recorded maker); a different officer must reject it.", + "401": { + "description": "The API key has expired: it was created with an expiry date that has passed. A missing, invalid or revoked key gets `403`, not `401`.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." } } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/entity-actions/": { - "post": { - "operationId": "apply_case_entity_action", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true }, - { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Case Management" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CaseActionCaseEntityActionRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseActionCaseEntityActionRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CaseActionCaseEntityActionRequest" + "403": { + "description": "The request is not allowed. A missing, invalid or revoked `x-api-key` gets `You do not have permission to perform this action.` A scoped key without the access this endpoint needs gets a message naming the missing access, and a key used from outside its IP allowlist gets `This API key can't be used from this IP address.` A key limited to some workflows or to approved sessions gets `This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint.`", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without webhooks write": { + "value": { + "detail": "This API key has no write access to webhooks." + } + }, + "Scoped key without decisions read": { + "value": { + "detail": "This API key has no read access to decisions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "Outside the IP allowlist": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } } } }, - "required": true - }, - "responses": { - "200": { + "404": { + "description": "The destination does not exist for the application or has been deleted, or the delivery does not exist, belongs to another destination, or is no longer available.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseActionCaseEntityActionResult" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Destination not found": { + "value": { + "detail": "Webhook destination not found." + } + }, + "Delivery not found": { + "value": { + "detail": "Webhook delivery not found." + } + } } } - }, - "description": "" + } }, - "409": { - "description": "The reference belongs to a different action payload." + "429": { + "description": "Rate limit exceeded: more than 300 write requests (POST, PATCH, PUT, DELETE) in the current one-minute window for this API key. Wait `Retry-After` seconds before retrying.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed in the one-minute window (300).", + "schema": { + "type": "integer", + "example": 300 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix time (seconds) when the current window resets.", + "schema": { + "type": "integer", + "example": 1792056120 + } + }, + "X-RateLimit-Scope": { + "description": "Name of the budget that was exceeded.", + "schema": { + "type": "string", + "example": "generic-write" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Throttled": { + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + } + } + } } }, "security": [ { "ApiKeyAuth": [] } - ], - "description": "Apply one explicit action to the case subject. Supply a unique client_reference to obtain an action receipt and make retries idempotent. Reusing that reference with a different payload returns 409. With a reference, inspect receipt.status even when HTTP status is 200: FAILED means no action was applied. Repeating a failed request does not execute it again unless retry is true. Omitting client_reference preserves the legacy response." + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/print/": { + "/v3/events/": { "get": { - "summary": "Print case (PDF)", - "description": "Render the full case (summary, subject, checklist state, notes, attached sessions/transactions, events, FIU report list) as a PDF and return a presigned download URL.", - "operationId": "print_case", + "summary": "List monitoring events", + "description": "List the monitoring events stored for your application, newest `occurred_at` first, with optional filters that combine with AND. Multi-value filters (`event_category`, `action_type`, `status`, `retention_class`) repeat the parameter and match any of the values; the other filters are exact matches, and unknown values simply return no events. Each result is a summary: call `GET /v3/events/{event_uuid}/` for the payload, device context, IP enrichment and rule evidence, and prefer `GET /v3/events/subjects/{vendor_data}/` for one subject's history. Reads work whether or not event ingestion is switched on. A scoped API key needs `transactions: read`. See [Event ingestion](https://docs.didit.me/transaction-monitoring/events).", + "operationId": "list_monitoring_events", + "x-didit-sdk": { + "resource": "monitoring_events", + "method": "list", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" + "Monitoring Events" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, + "name": "limit", + "in": "query", + "required": false, + "description": "Maximum number of events per page. Defaults to 50. Values above 1000000 return `400`; a non-numeric, zero or negative value falls back to 50.", "schema": { - "type": "string", - "format": "uuid" + "type": "integer", + "default": 50, + "minimum": 1, + "maximum": 1000000 }, - "description": "Organization UUID." + "example": 50 }, { - "name": "application_id", - "in": "path", - "required": true, + "name": "offset", + "in": "query", + "required": false, + "description": "Number of events to skip. Defaults to 0. Prefer following the `next` URL from the response.", "schema": { - "type": "string", - "format": "uuid" + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 1000000 }, - "description": "Application UUID." + "example": 0 }, { - "name": "case_uuid", - "in": "path", - "required": true, + "name": "event_category", + "in": "query", + "required": false, + "description": "Only events in these categories. Repeat the parameter for several values (`?event_category=payment&event_category=dispute`); an event matching any of them is returned.", + "style": "form", + "explode": true, "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/print/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Presigned PDF URL.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "url": { - "type": "string", - "format": "uri" - } - } - } + "type": "array", + "items": { + "type": "string", + "enum": [ + "auth", + "session", + "gameplay", + "bonus", + "affiliate", + "payment", + "dispute", + "security", + "responsible_gambling", + "custom" + ] } - } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/": { - "get": { - "operationId": "list_case_notes", - "parameters": [ + }, + "example": [ + "payment", + "dispute" + ] + }, { - "in": "path", - "name": "organization_id", + "name": "action_type", + "in": "query", + "required": false, + "description": "Only events with these action types (exact, case-sensitive). Repeat for several values.", + "style": "form", + "explode": true, "schema": { - "type": "string", - "format": "uuid" + "type": "array", + "items": { + "type": "string" + } }, - "required": true + "example": [ + "deposit_completed", + "chargeback_opened" + ] }, { - "in": "path", - "name": "application_id", + "name": "status", + "in": "query", + "required": false, + "description": "Only events with these statuses. Repeat for several values.", + "style": "form", + "explode": true, "schema": { - "type": "string", - "format": "uuid" + "type": "array", + "items": { + "type": "string", + "enum": [ + "ACCEPTED", + "APPROVED", + "IN_REVIEW", + "DECLINED" + ] + } }, - "required": true + "example": [ + "IN_REVIEW", + "DECLINED" + ] }, { - "in": "path", - "name": "case_uuid", + "name": "retention_class", + "in": "query", + "required": false, + "description": "Only events in these retention classes. Repeat for several values.", + "style": "form", + "explode": true, "schema": { - "type": "string", - "format": "uuid" + "type": "array", + "items": { + "type": "string", + "enum": [ + "low_value", + "standard", + "risk_relevant", + "monetary_or_compliance" + ] + } }, - "required": true + "example": [ + "risk_relevant" + ] }, { - "name": "ordering", - "required": false, + "name": "subject_vendor_data", "in": "query", - "description": "Which field to use when ordering the results.", + "required": false, + "description": "Exact match on the subject's `vendor_data`. The same id can name an individual and a company; add `subject_entity_type` to pick one, or use `GET /v3/events/subjects/{vendor_data}/`.", "schema": { "type": "string" - } + }, + "example": "user-42" }, { - "name": "limit", - "required": false, + "name": "subject_entity_type", "in": "query", - "description": "Number of results to return per page.", + "required": false, + "description": "`company` returns only events of company subjects; any other non-empty value (for example `individual`) returns only events of individuals. Omit it to return both.", "schema": { - "type": "integer" - } + "type": "string", + "enum": [ + "individual", + "company" + ] + }, + "example": "individual" }, { - "name": "offset", - "required": false, + "name": "business_vendor_data", "in": "query", - "description": "The initial index from which to return the results.", + "required": false, + "description": "Exact match on the `business.vendor_data` sent with the event.", "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseCollaborationPaginatedCaseNoteList" - } - } + "type": "string" }, - "description": "" - }, - "403": { - "description": "The caller lacks the required cases permission." + "example": "merchant-7" }, - "404": { - "description": "The case or note is unavailable for this caller." - } - }, - "tags": [ - "Case Management" - ], - "security": [ { - "ApiKeyAuth": [] - } - ] - }, - "post": { - "operationId": "create_case_note", - "parameters": [ - { - "in": "path", - "name": "organization_id", + "name": "session_id", + "in": "query", + "required": false, + "description": "Exact match on `session.session_id`.", "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, - "required": true + "example": "web-session-abc" }, { - "in": "path", - "name": "application_id", + "name": "device_fingerprint", + "in": "query", + "required": false, + "description": "Exact match on `device_context.fingerprint`.", "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, - "required": true + "example": "fp-a8c1" }, { - "in": "path", - "name": "case_uuid", + "name": "ip_address", + "in": "query", + "required": false, + "description": "Exact match on the event's IP address.", "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CaseCollaborationCaseNoteCreateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseCollaborationCaseNoteCreateRequest" - } + "type": "string" }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CaseCollaborationCaseNoteCreateRequest" - } - } + "example": "203.0.113.42" }, - "required": true - }, - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseCollaborationCaseNote" - } - } + { + "name": "game_id", + "in": "query", + "required": false, + "description": "Exact match on the promoted `game_id`.", + "schema": { + "type": "string" }, - "description": "" + "example": "poker-holdem-01" }, - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseCollaborationCaseNote" - } - } + { + "name": "round_id", + "in": "query", + "required": false, + "description": "Exact match on the promoted `round_id`.", + "schema": { + "type": "string" }, - "description": "" + "example": "round-88231" }, - "403": { - "description": "The caller lacks the required cases permission." + { + "name": "affiliate_code", + "in": "query", + "required": false, + "description": "Exact match on the promoted `affiliate_code`.", + "schema": { + "type": "string" + }, + "example": "aff-summer" }, - "404": { - "description": "The case or note is unavailable for this caller." + { + "name": "payment_reference_id", + "in": "query", + "required": false, + "description": "Exact match on the payment reference (from `transaction_context.payment_reference_id` or `payload.payment_reference_id`).", + "schema": { + "type": "string" + }, + "example": "dep_735" }, - "409": { - "description": "This client reference was already used for another note or for a note that has been deleted." - } - }, - "tags": [ - "Case Management" - ], - "security": [ { - "ApiKeyAuth": [] - } - ], - "description": "Add a case-scoped note. An optional client_reference makes an unchanged retry return the same note without duplicate activity or notifications. reply_to must identify a live note in this case. The response includes a bounded parent excerpt without copying that excerpt into the saved reply." - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/{note_uuid}/": { - "patch": { - "operationId": "root_partial_update", - "parameters": [ + "name": "counterparty_vendor_data", + "in": "query", + "required": false, + "description": "Exact match on the promoted `counterparty_vendor_data`.", + "schema": { + "type": "string" + }, + "example": "user-77" + }, { - "in": "path", - "name": "organization_id", + "name": "matched_only", + "in": "query", + "required": false, + "description": "`true` (also `1` or `yes`, case-insensitive) returns only events at least one rule matched (`rules_matched_count` above 0). Any other value is ignored.", "schema": { - "type": "string", - "format": "uuid" + "type": "boolean", + "default": false }, - "required": true + "example": true }, { - "in": "path", - "name": "application_id", + "name": "occurred_from", + "in": "query", + "required": false, + "description": "Only events with `occurred_at` at or after this instant (inclusive). Send an ISO 8601 value: `2026-10-01T00:00:00Z`, an explicit offset with the `+` percent-encoded (`2026-10-01T02:00:00%2B02:00`; an unencoded `+` is decoded as a space and the value no longer parses), or a date alone (`2026-10-01`), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted.", "schema": { "type": "string", - "format": "uuid" + "format": "date-time" }, - "required": true + "example": "2026-10-01T00:00:00Z" }, { - "in": "path", - "name": "case_uuid", + "name": "occurred_to", + "in": "query", + "required": false, + "description": "Only events with `occurred_at` at or before this instant (inclusive). A date alone means the start of that day, so `occurred_to=2026-10-15` excludes the rest of 15 October; send `2026-10-15T23:59:59Z` to include it. Send an ISO 8601 value: `2026-10-01T00:00:00Z`, an explicit offset with the `+` percent-encoded (`2026-10-01T02:00:00%2B02:00`; an unencoded `+` is decoded as a space and the value no longer parses), or a date alone (`2026-10-01`), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted.", "schema": { "type": "string", - "format": "uuid" + "format": "date-time" }, - "required": true + "example": "2026-10-17T23:59:59Z" }, { - "in": "path", - "name": "note_uuid", + "name": "search", + "in": "query", + "required": false, + "description": "Case-insensitive substring match on `event_id`, `action_type`, `subject_vendor_data` or `counterparty_vendor_data`.", "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, - "required": true + "example": "chargeback" } ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CaseCollaborationPatchedCaseNoteUpdateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseCollaborationPatchedCaseNoteUpdateRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CaseCollaborationPatchedCaseNoteUpdateRequest" - } - } + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/events/?event_category=payment&event_category=dispute&matched_only=true&occurred_from=2026-10-01T00:00:00Z&limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nurl = 'https://verification.didit.me/v3/events/'\nparams = {\n 'event_category': ['payment', 'dispute'],\n 'matched_only': 'true',\n 'occurred_from': '2026-10-01T00:00:00Z',\n 'limit': 50,\n}\nwhile url:\n resp = requests.get(\n url,\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params=params,\n timeout=10,\n )\n resp.raise_for_status()\n page = resp.json()\n for event in page['results']:\n print(event['event_uuid'], event['action_type'], event['status'])\n url, params = page['next'], None" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const params = new URLSearchParams([\n ['event_category', 'payment'],\n ['event_category', 'dispute'],\n ['matched_only', 'true'],\n ['occurred_from', '2026-10-01T00:00:00Z'],\n ['limit', '50'],\n]);\nlet url = `https://verification.didit.me/v3/events/?${params}`;\nwhile (url) {\n const resp = await fetch(url, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n });\n if (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\n const page = await resp.json();\n for (const event of page.results) console.log(event.event_uuid, event.action_type, event.status);\n url = page.next;\n}" } - }, + ], "responses": { "200": { + "description": "One page of event summaries.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseCollaborationCaseNote" + "$ref": "#/components/schemas/MonitoringEventPage" + }, + "examples": { + "Payment and dispute events": { + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "event_uuid": "11111111-2222-3333-4444-555555555555", + "event_id": "chargeback-2026-10-16-000318", + "event_category": "dispute", + "action_type": "chargeback_opened", + "occurred_at": "2026-10-16T14:02:11Z", + "received_at": "2026-10-16T14:02:12.418230Z", + "status": "IN_REVIEW", + "score": 60, + "subject_vendor_data": "user-42", + "subject_entity_type": "individual", + "subject_uuid": "66666666-7777-8888-9999-000000000000", + "counterparty_vendor_data": null, + "session_id": null, + "device_fingerprint": null, + "ip_address": null, + "ip_country": null, + "amount": "250.000000000000000000", + "currency": "EUR", + "game_id": null, + "round_id": null, + "bonus_id": null, + "affiliate_code": null, + "payment_reference_id": "dep_735", + "retention_class": "monetary_or_compliance", + "rules_matched_count": 1, + "correlation_pending": false, + "is_droppable": false + }, + { + "event_uuid": "22222222-3333-4444-5555-666666666666", + "event_id": "login-2026-10-15-000912", + "event_category": "auth", + "action_type": "login", + "occurred_at": "2026-10-15T08:30:00.120000Z", + "received_at": "2026-10-15T08:30:00.734512Z", + "status": "ACCEPTED", + "score": 0, + "subject_vendor_data": "user-42", + "subject_entity_type": "individual", + "subject_uuid": "66666666-7777-8888-9999-000000000000", + "counterparty_vendor_data": null, + "session_id": "web-session-abc", + "device_fingerprint": "fp-a8c1", + "ip_address": "203.0.113.42", + "ip_country": "ES", + "amount": null, + "currency": null, + "game_id": null, + "round_id": null, + "bonus_id": null, + "affiliate_code": null, + "payment_reference_id": null, + "retention_class": "standard", + "rules_matched_count": 0, + "correlation_pending": false, + "is_droppable": true + } + ] + } + } } } - }, - "description": "" + } + }, + "400": { + "description": "`limit` or `offset` is larger than 1000000. The body is a JSON array with one message. A non-numeric or non-positive `limit` falls back to the default of 50 instead of failing.", + "content": { + "application/json": { + "examples": { + "limit too large": { + "summary": "limit above 1000000", + "value": [ + "limit must not exceed 1000000." + ] + }, + "offset too large": { + "summary": "offset above 1000000", + "value": [ + "offset must not exceed 1000000." + ] + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. Only an expired key gets `401`; a missing or invalid key gets `403`.", + "content": { + "application/json": { + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } }, "403": { - "description": "The caller lacks the required cases permission." + "description": "Missing or invalid `x-api-key` (this endpoint answers `403`, never `401`, for a missing or invalid key), a scoped key without `transactions: read`, a key limited to some workflows or to approved sessions (such a key never reaches transaction monitoring data), or a request from outside the key's IP allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without access": { + "value": { + "detail": "This API key has no read access to transactions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "IP not allowed": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } }, - "404": { - "description": "The case or note is unavailable for this caller." + "429": { + "description": "Rate limit exceeded: `GET` requests share a budget of 600 requests per minute per API key. Wait `Retry-After` seconds, then retry.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } + }, + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + } } }, - "tags": [ - "Case Management" - ], "security": [ { "ApiKeyAuth": [] } - ], - "description": "Pin or unpin an existing note. Pinned notes appear first. Pin changes are recorded in case activity and do not change evidence or source decisions." + ] }, - "delete": { - "operationId": "delete_case_note", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, + "post": { + "summary": "Create monitoring event", + "description": "Send one non-monetary event (login, session, gameplay, bonus, affiliate, payment, dispute, security, responsible gambling or custom) from your backend. The event is validated against its category contract, stored, IP-enriched once per unique public address, and evaluated by your real-time event rules before the response returns; cross-subject and long-window rules run asynchronously (`correlation_pending: true`), and in a sandbox application no rules run. `event_id` is the idempotency key: the same `event_id` with the same `event_category`, `action_type`, `occurred_at`, `subject.vendor_data` and `payload` returns `200` with `idempotent_replay: true` and the stored outcome (other envelope fields are not compared), while different values return `409`. Event ingestion is opt-in per application: until it is switched on this endpoint returns `403` with `code: event_monitoring_disabled` and stores nothing. A scoped API key needs `transactions: write`. See [Event ingestion](https://docs.didit.me/transaction-monitoring/events) and [Rules on events](https://docs.didit.me/transaction-monitoring/event-rules); to send events from a browser or app, use [SDK event submission](https://docs.didit.me/transaction-monitoring/sdk-event-submission).", + "operationId": "create_monitoring_event", + "x-didit-sdk": { + "resource": "monitoring_events", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Monitoring Events" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitoringEventCreateRequest" + }, + "examples": { + "Login": { + "value": { + "event_id": "login-2026-10-17-000001", + "event_category": "auth", + "action_type": "login", + "occurred_at": "2026-10-17T09:15:02.481Z", + "time_zone": "Europe/Madrid", + "subject": { + "vendor_data": "user-42", + "entity_type": "individual" + }, + "session": { + "session_id": "web-session-abc", + "sequence": 142 + }, + "device_context": { + "fingerprint": "fp-a8c1", + "network_context": { + "ip_address": "203.0.113.42" + } + }, + "payload": { + "method": "password" + }, + "custom_values": { + "vip_tier": "gold" + } + } + }, + "Deposit completed": { + "value": { + "event_id": "deposit-2026-10-17-000002", + "event_category": "payment", + "action_type": "deposit_completed", + "occurred_at": "2026-10-17T09:16:40Z", + "subject": { + "vendor_data": "user-42" + }, + "transaction_context": { + "amount": "100.00", + "currency": "EUR", + "direction": "inbound", + "payment_reference_id": "dep_736" + }, + "payload": { + "payment_method": "card", + "status": "completed" + } + } + }, + "Chargeback opened": { + "value": { + "event_id": "chargeback-2026-10-16-000318", + "event_category": "dispute", + "action_type": "chargeback_opened", + "occurred_at": "2026-10-16T14:02:11Z", + "subject": { + "vendor_data": "user-42" + }, + "payload": { + "payment_reference_id": "dep_735", + "chargeback_id": "cb_5521", + "amount": "250.00", + "currency": "EUR", + "reason_code": "fraud" + } + } + } + } + } + } + }, + "x-codeSamples": [ { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/events/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"event_id\": \"login-2026-10-17-000001\",\n \"event_category\": \"auth\",\n \"action_type\": \"login\",\n \"occurred_at\": \"2026-10-17T09:15:02.481Z\",\n \"subject\": {\"vendor_data\": \"user-42\", \"entity_type\": \"individual\"},\n \"session\": {\"session_id\": \"web-session-abc\", \"sequence\": 142},\n \"device_context\": {\n \"fingerprint\": \"fp-a8c1\",\n \"network_context\": {\"ip_address\": \"203.0.113.42\"}\n },\n \"payload\": {\"method\": \"password\"}\n }'" }, { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/events/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'event_id': 'login-2026-10-17-000001',\n 'event_category': 'auth',\n 'action_type': 'login',\n 'occurred_at': '2026-10-17T09:15:02.481Z',\n 'subject': {'vendor_data': 'user-42', 'entity_type': 'individual'},\n 'session': {'session_id': 'web-session-abc', 'sequence': 142},\n 'device_context': {\n 'fingerprint': 'fp-a8c1',\n 'network_context': {'ip_address': '203.0.113.42'},\n },\n 'payload': {'method': 'password'},\n },\n timeout=10,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result['event_uuid'], result['status'], result['idempotent_replay'])" }, { - "in": "path", - "name": "note_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/events/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n event_id: 'login-2026-10-17-000001',\n event_category: 'auth',\n action_type: 'login',\n occurred_at: '2026-10-17T09:15:02.481Z',\n subject: { vendor_data: 'user-42', entity_type: 'individual' },\n session: { session_id: 'web-session-abc', sequence: 142 },\n device_context: {\n fingerprint: 'fp-a8c1',\n network_context: { ip_address: '203.0.113.42' },\n },\n payload: { method: 'password' },\n }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst result = await resp.json();\nconsole.log(result.event_uuid, result.status, result.idempotent_replay);" } ], "responses": { - "204": { - "description": "The note is permanently deleted. Replies remain, without the deleted parent excerpt. Attachments are deleted unless another note still uses them." + "201": { + "description": "The event was stored and evaluated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitoringEventIngestResult" + }, + "examples": { + "Stored, no rule matched": { + "value": { + "event_uuid": "33333333-4444-5555-6666-777777777777", + "event_id": "login-2026-10-17-000001", + "status": "ACCEPTED", + "score": 0, + "rules_evaluated_count": 6, + "rules_matched_count": 0, + "correlation_pending": false, + "retention_class": "standard", + "expires_at": "2026-11-16T09:15:02.481000Z", + "idempotent_replay": false, + "rule_matches": [] + } + }, + "Rule matched, async rule pending": { + "value": { + "event_uuid": "33333333-4444-5555-6666-777777777777", + "event_id": "login-2026-10-17-000001", + "status": "IN_REVIEW", + "score": 40, + "rules_evaluated_count": 6, + "rules_matched_count": 1, + "correlation_pending": true, + "retention_class": "risk_relevant", + "expires_at": "2027-01-15T09:15:02.481000Z", + "idempotent_replay": false, + "rule_matches": [ + { + "rule_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "library_key": null, + "mode": "ACTIVE", + "is_async": false + } + ] + } + } + } + } + } + }, + "200": { + "description": "Idempotent replay: an event with this `event_id` and the same content is already stored. Nothing was stored or evaluated again; the body describes the stored event.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitoringEventIngestResult" + }, + "examples": { + "Replay": { + "value": { + "event_uuid": "33333333-4444-5555-6666-777777777777", + "event_id": "login-2026-10-17-000001", + "status": "ACCEPTED", + "score": 0, + "rules_evaluated_count": 6, + "rules_matched_count": 0, + "correlation_pending": false, + "retention_class": "standard", + "expires_at": "2026-11-16T09:15:02.481000Z", + "idempotent_replay": true, + "rule_matches": [] + } + } + } + } + } + }, + "400": { + "description": "Validation failed. The body is keyed by field; nested fields are nested objects. Nothing is stored.", + "content": { + "application/json": { + "examples": { + "Missing fields": { + "value": { + "event_id": [ + "This field is required." + ], + "subject": [ + "This field is required." + ] + } + }, + "Unknown category": { + "value": { + "event_category": [ + "\"login\" is not a valid choice." + ] + } + }, + "occurred_at too old": { + "value": { + "occurred_at": [ + "occurred_at is more than 7 days in the past. Use the batch import lane for backfills." + ] + } + }, + "occurred_at in the future": { + "value": { + "occurred_at": [ + "occurred_at is more than 5 minutes in the future." + ] + } + }, + "Missing subject vendor_data": { + "value": { + "subject": { + "vendor_data": [ + "This field is required." + ] + } + } + }, + "Missing required payload keys": { + "value": { + "payload": [ + "payment.deposit_completed requires: payment_method, status." + ] + } + }, + "Missing one-of payload key": { + "value": { + "payload": [ + "dispute.chargeback_opened requires one of: dispute_id, chargeback_id." + ] + } + }, + "Security change without changed_field": { + "value": { + "payload": [ + "security.*_changed requires 'changed_field'." + ] + } + }, + "Payload too large": { + "value": { + "payload": [ + "payload exceeds 16384 bytes. Summarise telemetry client-side." + ] + } + }, + "Too many custom_values": { + "value": { + "custom_values": [ + "custom_values accepts at most 100 keys." + ] + } + }, + "Nested custom value": { + "value": { + "custom_values": [ + "custom_values['limits'] must be a scalar - rule conditions cannot address nested values." + ] + } + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. Only an expired key gets `401`; a missing or invalid key gets `403`.", + "content": { + "application/json": { + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } }, "403": { - "description": "The caller lacks the required cases permission." + "description": "Missing or invalid `x-api-key` (this endpoint answers `403`, never `401`, for a missing or invalid key), a scoped key without `transactions: write`, a key limited to some workflows or to approved sessions (such a key never reaches transaction monitoring data), or a request from outside the key's IP allowlist. Also returned with `code: event_monitoring_disabled` while event ingestion is switched off for the application; nothing is stored in that case, and the check runs before the body is validated.", + "content": { + "application/json": { + "examples": { + "Event monitoring disabled": { + "value": { + "detail": "Event monitoring is not enabled for this application. Turn on event ingestion in the transaction monitoring settings before submitting events.", + "code": "event_monitoring_disabled" + } + }, + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without access": { + "value": { + "detail": "This API key has no write access to transactions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "IP not allowed": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } }, - "404": { - "description": "The case or note is unavailable for this caller." + "409": { + "description": "`event_id` was already used for an event with different content (category, action type, `occurred_at`, subject `vendor_data` or payload). Send the event under a new `event_id`.", + "content": { + "application/json": { + "examples": { + "event_id reused": { + "value": { + "event_id": "event_id 'login-2026-10-17-000001' already exists with a different payload." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded: write requests (`POST`, `PATCH`, `DELETE`) share a budget of 300 requests per minute per API key. Wait `Retry-After` seconds, then retry.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } + }, + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + } + } + } } }, - "tags": [ - "Case Management" - ], "security": [ { "ApiKeyAuth": [] @@ -40567,62 +60909,71 @@ ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/presign/": { + "/v3/events/batch/": { "post": { - "summary": "Upload note attachment", - "description": "Upload a file (JPEG, PNG, WebP, PDF, plain text, or CSV; max 5 MB) to attach to a note. Returns the s3_key to pass in the note's attachments array.", - "operationId": "upload_case_note_attachment", + "summary": "Create monitoring events in batch", + "description": "Submit 1 to 500 events in one request, each in the same envelope as `POST /v3/events/`. Every item is validated, stored and evaluated on its own and gets its own outcome (`accepted`, `replayed` or `rejected`), so one malformed event never rolls back the others: the response is `201` when no item was rejected and `207` when at least one was, and both mean the request was processed. Send `{\"events\": [...]}`; a bare JSON array of events is also accepted. Event ingestion must be switched on for the application, otherwise the whole request returns `403` with `code: event_monitoring_disabled` and nothing is stored. A scoped API key needs `transactions: write`. See [Batches](https://docs.didit.me/transaction-monitoring/events#batches).", + "operationId": "create_monitoring_events_batch", + "x-didit-sdk": { + "resource": "monitoring_events", + "method": "create_batch", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } + "Monitoring Events" ], "requestBody": { "required": true, "content": { - "multipart/form-data": { + "application/json": { "schema": { "type": "object", "required": [ - "file" - ], - "properties": { - "file": { - "type": "string", - "format": "binary" + "events" + ], + "properties": { + "events": { + "type": "array", + "minItems": 1, + "maxItems": 500, + "description": "1 to 500 events. Each item follows `MonitoringEventCreateRequest`, including the 7-day past and 5-minute future window on `occurred_at`.", + "items": { + "$ref": "#/components/schemas/MonitoringEventCreateRequest" + } } } + }, + "example": { + "events": [ + { + "event_id": "login-2026-10-17-000001", + "event_category": "auth", + "action_type": "login", + "occurred_at": "2026-10-17T09:15:02.481Z", + "subject": { + "vendor_data": "user-42" + } + }, + { + "event_id": "deposit-2026-10-17-000002", + "event_category": "payment", + "action_type": "deposit_completed", + "occurred_at": "2026-10-17T09:16:40Z", + "subject": { + "vendor_data": "user-42" + }, + "transaction_context": { + "amount": "100.00", + "currency": "EUR", + "payment_reference_id": "dep_736" + }, + "payload": { + "payment_method": "card", + "status": "completed" + } + } + ] } } } @@ -40631,242 +60982,541 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/notes/presign/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'file=@evidence.pdf'" + "source": "curl -X POST 'https://verification.didit.me/v3/events/batch/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"events\": [\n {\"event_id\": \"login-2026-10-17-000001\", \"event_category\": \"auth\", \"action_type\": \"login\",\n \"occurred_at\": \"2026-10-17T09:15:02.481Z\", \"subject\": {\"vendor_data\": \"user-42\"}},\n {\"event_id\": \"deposit-2026-10-17-000002\", \"event_category\": \"payment\", \"action_type\": \"deposit_completed\",\n \"occurred_at\": \"2026-10-17T09:16:40.000Z\", \"subject\": {\"vendor_data\": \"user-42\"},\n \"transaction_context\": {\"amount\": \"100.00\", \"currency\": \"EUR\", \"payment_reference_id\": \"dep_736\"},\n \"payload\": {\"payment_method\": \"card\", \"status\": \"completed\"}}\n ]\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nevents = [\n {\n 'event_id': 'login-2026-10-17-000001',\n 'event_category': 'auth',\n 'action_type': 'login',\n 'occurred_at': '2026-10-17T09:15:02.481Z',\n 'subject': {'vendor_data': 'user-42'},\n },\n {\n 'event_id': 'deposit-2026-10-17-000002',\n 'event_category': 'payment',\n 'action_type': 'deposit_completed',\n 'occurred_at': '2026-10-17T09:16:40.000Z',\n 'subject': {'vendor_data': 'user-42'},\n 'transaction_context': {'amount': '100.00', 'currency': 'EUR', 'payment_reference_id': 'dep_736'},\n 'payload': {'payment_method': 'card', 'status': 'completed'},\n },\n]\nresp = requests.post(\n 'https://verification.didit.me/v3/events/batch/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'events': events},\n timeout=10,\n)\nresp.raise_for_status() # 201 and 207 are both success\nbatch = resp.json()\nfor item in batch['results']:\n if item['outcome'] == 'rejected':\n print('fix and resend', events[item['index']]['event_id'], item['errors'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const events = [\n {\n event_id: 'login-2026-10-17-000001',\n event_category: 'auth',\n action_type: 'login',\n occurred_at: '2026-10-17T09:15:02.481Z',\n subject: { vendor_data: 'user-42' },\n },\n {\n event_id: 'deposit-2026-10-17-000002',\n event_category: 'payment',\n action_type: 'deposit_completed',\n occurred_at: '2026-10-17T09:16:40.000Z',\n subject: { vendor_data: 'user-42' },\n transaction_context: { amount: '100.00', currency: 'EUR', payment_reference_id: 'dep_736' },\n payload: { payment_method: 'card', status: 'completed' },\n },\n];\nconst resp = await fetch('https://verification.didit.me/v3/events/batch/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ events }),\n});\nif (!resp.ok) throw new Error(await resp.text()); // 201 and 207 are both ok\nconst batch = await resp.json();\nfor (const item of batch.results) {\n if (item.outcome === 'rejected') console.log('fix and resend', events[item.index].event_id, item.errors);\n}" } ], "responses": { "201": { - "description": "File uploaded.", + "description": "No item was rejected: every item is `accepted` or `replayed`.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "accepted_count", + "replayed_count", + "rejected_count", + "results" + ], "properties": { - "s3_key": { - "type": "string" + "accepted_count": { + "type": "integer", + "minimum": 0, + "description": "Items stored and evaluated." }, - "url": { - "type": "string", - "format": "uri" + "replayed_count": { + "type": "integer", + "minimum": 0, + "description": "Items already stored with the same content." }, - "filename": { - "type": "string" + "rejected_count": { + "type": "integer", + "minimum": 0, + "description": "Items not stored." }, - "file_size": { - "type": "integer" + "results": { + "type": "array", + "description": "One entry per item, in request order.", + "items": { + "type": "object", + "description": "Outcome of one item. Accepted and replayed items also carry every field of `MonitoringEventIngestResult`; rejected items carry `event_id` and `errors` instead.", + "required": [ + "index", + "outcome" + ], + "properties": { + "index": { + "type": "integer", + "minimum": 0, + "description": "Position of the item in your `events` array." + }, + "outcome": { + "type": "string", + "enum": [ + "accepted", + "replayed", + "rejected" + ], + "description": "`accepted`: stored and evaluated. `replayed`: already stored with the same content. `rejected`: not stored; see `errors`." + }, + "event_id": { + "type": "string", + "nullable": true, + "description": "Your `event_id`. On items rejected by validation it is read from the item's snake_case `event_id` key, so it is null when the item omitted it or sent the `eventId` alias." + }, + "errors": { + "type": "object", + "additionalProperties": true, + "description": "Rejected items only. Validation errors keyed by field, in the same shape as the `400` of `POST /v3/events/`; a reused `event_id` with different content is reported as a string under `errors.event_id`." + }, + "event_uuid": { + "type": "string", + "format": "uuid", + "description": "Accepted and replayed items only." + }, + "status": { + "type": "string", + "enum": [ + "ACCEPTED", + "APPROVED", + "IN_REVIEW", + "DECLINED" + ], + "description": "Accepted and replayed items only." + }, + "score": { + "type": "integer", + "description": "Accepted and replayed items only." + }, + "rules_evaluated_count": { + "type": "integer", + "description": "Accepted and replayed items only." + }, + "rules_matched_count": { + "type": "integer", + "description": "Accepted and replayed items only." + }, + "correlation_pending": { + "type": "boolean", + "description": "Accepted and replayed items only." + }, + "retention_class": { + "type": "string", + "enum": [ + "low_value", + "standard", + "risk_relevant", + "monetary_or_compliance" + ], + "description": "Accepted and replayed items only." + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Accepted and replayed items only." + }, + "idempotent_replay": { + "type": "boolean", + "description": "Accepted and replayed items only." + }, + "rule_matches": { + "type": "array", + "description": "Accepted and replayed items only.", + "items": { + "type": "object", + "required": [ + "rule_id", + "library_key", + "mode", + "is_async" + ], + "properties": { + "rule_id": { + "type": "string", + "format": "uuid", + "description": "UUID of the rule." + }, + "library_key": { + "type": "string", + "nullable": true, + "description": "Library key when the rule was installed from the rules library, otherwise null." + }, + "mode": { + "type": "string", + "enum": [ + "ACTIVE", + "TEST" + ], + "description": "`TEST` rules record the match but apply no actions." + }, + "is_async": { + "type": "boolean", + "description": "`true` when the match came from the asynchronous pass." + } + } + } + } + } + } + } + } + }, + "examples": { + "All landed": { + "value": { + "accepted_count": 1, + "replayed_count": 1, + "rejected_count": 0, + "results": [ + { + "index": 0, + "outcome": "accepted", + "event_uuid": "33333333-4444-5555-6666-777777777777", + "event_id": "login-2026-10-17-000001", + "status": "ACCEPTED", + "score": 0, + "rules_evaluated_count": 6, + "rules_matched_count": 0, + "correlation_pending": false, + "retention_class": "standard", + "expires_at": "2026-11-16T09:15:02.481000Z", + "idempotent_replay": false, + "rule_matches": [] + }, + { + "index": 1, + "outcome": "replayed", + "event_uuid": "11111111-2222-3333-4444-555555555555", + "event_id": "deposit-2026-10-17-000002", + "status": "ACCEPTED", + "score": 0, + "rules_evaluated_count": 6, + "rules_matched_count": 0, + "correlation_pending": false, + "retention_class": "monetary_or_compliance", + "expires_at": null, + "idempotent_replay": true, + "rule_matches": [] + } + ] } } } } } }, - "400": { - "description": "No file, unsupported content type, or file exceeds 5 MB.", + "207": { + "description": "At least one item was rejected. The other items were stored; fix and resend only the rejected ones (resending an accepted item is safe, it comes back `replayed`).", "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "required": [ + "accepted_count", + "replayed_count", + "rejected_count", + "results" + ], + "properties": { + "accepted_count": { + "type": "integer", + "minimum": 0, + "description": "Items stored and evaluated." + }, + "replayed_count": { + "type": "integer", + "minimum": 0, + "description": "Items already stored with the same content." + }, + "rejected_count": { + "type": "integer", + "minimum": 0, + "description": "Items not stored." + }, + "results": { + "type": "array", + "description": "One entry per item, in request order.", + "items": { + "type": "object", + "description": "Outcome of one item. Accepted and replayed items also carry every field of `MonitoringEventIngestResult`; rejected items carry `event_id` and `errors` instead.", + "required": [ + "index", + "outcome" + ], + "properties": { + "index": { + "type": "integer", + "minimum": 0, + "description": "Position of the item in your `events` array." + }, + "outcome": { + "type": "string", + "enum": [ + "accepted", + "replayed", + "rejected" + ], + "description": "`accepted`: stored and evaluated. `replayed`: already stored with the same content. `rejected`: not stored; see `errors`." + }, + "event_id": { + "type": "string", + "nullable": true, + "description": "Your `event_id`. On items rejected by validation it is read from the item's snake_case `event_id` key, so it is null when the item omitted it or sent the `eventId` alias." + }, + "errors": { + "type": "object", + "additionalProperties": true, + "description": "Rejected items only. Validation errors keyed by field, in the same shape as the `400` of `POST /v3/events/`; a reused `event_id` with different content is reported as a string under `errors.event_id`." + }, + "event_uuid": { + "type": "string", + "format": "uuid", + "description": "Accepted and replayed items only." + }, + "status": { + "type": "string", + "enum": [ + "ACCEPTED", + "APPROVED", + "IN_REVIEW", + "DECLINED" + ], + "description": "Accepted and replayed items only." + }, + "score": { + "type": "integer", + "description": "Accepted and replayed items only." + }, + "rules_evaluated_count": { + "type": "integer", + "description": "Accepted and replayed items only." + }, + "rules_matched_count": { + "type": "integer", + "description": "Accepted and replayed items only." + }, + "correlation_pending": { + "type": "boolean", + "description": "Accepted and replayed items only." + }, + "retention_class": { + "type": "string", + "enum": [ + "low_value", + "standard", + "risk_relevant", + "monetary_or_compliance" + ], + "description": "Accepted and replayed items only." + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Accepted and replayed items only." + }, + "idempotent_replay": { + "type": "boolean", + "description": "Accepted and replayed items only." + }, + "rule_matches": { + "type": "array", + "description": "Accepted and replayed items only.", + "items": { + "type": "object", + "required": [ + "rule_id", + "library_key", + "mode", + "is_async" + ], + "properties": { + "rule_id": { + "type": "string", + "format": "uuid", + "description": "UUID of the rule." + }, + "library_key": { + "type": "string", + "nullable": true, + "description": "Library key when the rule was installed from the rules library, otherwise null." + }, + "mode": { + "type": "string", + "enum": [ + "ACTIVE", + "TEST" + ], + "description": "`TEST` rules record the match but apply no actions." + }, + "is_async": { + "type": "boolean", + "description": "`true` when the match came from the asynchronous pass." + } + } + } + } + } + } + } + } + }, + "examples": { + "Partial success": { + "value": { + "accepted_count": 1, + "replayed_count": 0, + "rejected_count": 2, + "results": [ + { + "index": 0, + "outcome": "accepted", + "event_uuid": "33333333-4444-5555-6666-777777777777", + "event_id": "login-2026-10-17-000001", + "status": "ACCEPTED", + "score": 0, + "rules_evaluated_count": 6, + "rules_matched_count": 0, + "correlation_pending": false, + "retention_class": "standard", + "expires_at": "2026-11-16T09:15:02.481000Z", + "idempotent_replay": false, + "rule_matches": [] + }, + { + "index": 1, + "event_id": "deposit-2026-10-17-000002", + "outcome": "rejected", + "errors": { + "payload": [ + "payment.deposit_completed requires: payment_method, status." + ] + } + }, + { + "index": 2, + "event_id": "login-2026-10-16-000777", + "outcome": "rejected", + "errors": { + "event_id": "event_id 'login-2026-10-16-000777' already exists with a different payload." + } + } + ] + } + } } } } - } - } - }, - "get": { - "summary": "Get note attachment URL", - "description": "Mint a fresh presigned download URL for an attachment already stored on one of the case's notes.", - "operationId": "get_case_note_attachment_url", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." }, - { - "name": "s3_key", - "in": "query", - "schema": { - "type": "string" - }, - "description": "The attachment's s3_key, as returned by upload or listed on the note.", - "required": true - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/notes/presign/?s3_key=case-notes/c1a5e000-0000-4000-8000-000000000001/evidence.pdf' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Presigned URL.", + "400": { + "description": "The batch itself is malformed: `events` is missing or not an array, is empty, or has more than 500 items. Nothing is stored; split large batches.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "s3_key": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "filename": { - "type": "string" + "examples": { + "Not a list": { + "value": { + "events": "Expected a list of events." + } + }, + "Empty": { + "value": { + "events": "At least one event is required." + } + }, + "Too many": { + "value": { + "events": "A batch accepts at most 500 events; received 612." } } } } } }, - "404": { - "description": "s3_key missing or does not belong to this case.", + "401": { + "description": "The scoped API key has expired. Only an expired key gets `401`; a missing or invalid key gets `403`.", "content": { "application/json": { - "schema": { - "type": "object" + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/links/": { - "get": { - "summary": "List case links", - "description": "List sessions, business sessions, and transactions attached to the case.", - "operationId": "list_case_links", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "403": { + "description": "Missing or invalid `x-api-key` (this endpoint answers `403`, never `401`, for a missing or invalid key), a scoped key without `transactions: write`, a key limited to some workflows or to approved sessions (such a key never reaches transaction monitoring data), or a request from outside the key's IP allowlist. Also returned with `code: event_monitoring_disabled` while event ingestion is switched off for the application; nothing is stored in that case, and the check runs before the body is validated.", + "content": { + "application/json": { + "examples": { + "Event monitoring disabled": { + "value": { + "detail": "Event monitoring is not enabled for this application. Turn on event ingestion in the transaction monitoring settings before submitting events.", + "code": "event_monitoring_disabled" + } + }, + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without access": { + "value": { + "detail": "This API key has no write access to transactions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "IP not allowed": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" + "429": { + "description": "Rate limit exceeded: write requests (`POST`, `PATCH`, `DELETE`) share a budget of 300 requests per minute per API key. Wait `Retry-After` seconds, then retry.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } }, - "description": "Case UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/links/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Case links.", "content": { "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseLink" + "examples": { + "Throttled": { + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } } } } } } - } - }, - "post": { - "summary": "Add case link", - "description": "Attach a session, business session, or transaction to the case. Rejected with 400 if the entity belongs to a different subject than the case.", - "operationId": "create_case_link", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, + }, + "security": [ { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." + "ApiKeyAuth": [] } + ] + } + }, + "/v3/events/sdk-token/": { + "post": { + "summary": "Mint monitoring SDK token", + "description": "Mint a short-lived device credential bound to one subject, so your web or mobile app can send that user's events straight to Didit without an API key. The app sends the returned `monitoring_token` in the `X-Monitoring-Token` header to the device lane (`POST /v1/events/` and `POST /v1/events/batch/`), where the subject is always overwritten with this `vendor_data`, events outside `allowed_event_categories` are rejected, each submitted event spends one use, and a request whose `Origin` does not equal `bind_to_origin` is refused. Call it from your backend only; minting creates the credential and nothing else (no user is created). Event ingestion must be switched on for the application, otherwise this endpoint returns `403` with `code: event_monitoring_disabled` instead of minting a credential that could never write. A scoped API key needs `transactions: write`. See [SDK event submission](https://docs.didit.me/transaction-monitoring/sdk-event-submission).", + "operationId": "create_monitoring_event_sdk_token", + "x-didit-sdk": { + "resource": "monitoring_events", + "method": "create_sdk_token", + "auth": "api_key", + "server": true + }, + "tags": [ + "Monitoring Events" ], "requestBody": { "required": true, @@ -40874,24 +61524,74 @@ "application/json": { "schema": { "type": "object", + "description": "camelCase aliases are accepted: `vendorData`, `entityType`, `allowedEventCategories`, `ttlSeconds`, `maxUses`, `bindToOrigin`.", "required": [ - "entity_type", - "entity_uuid" + "vendor_data" ], "properties": { + "vendor_data": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "description": "Your id for the subject every event submitted with this credential belongs to." + }, "entity_type": { "type": "string", - "enum": [ - "session", - "business_session", - "transaction" - ] + "maxLength": 32, + "default": "individual", + "description": "`company` binds a company subject; any other value (default `individual`) binds an individual." }, - "entity_uuid": { + "allowed_event_categories": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "auth", + "session", + "gameplay", + "bonus", + "affiliate", + "payment", + "dispute", + "security", + "responsible_gambling", + "custom" + ] + }, + "description": "Categories the credential may submit. Omit it or send `[]` to allow every category." + }, + "ttl_seconds": { + "type": "integer", + "minimum": 60, + "maximum": 86400, + "default": 900, + "description": "Lifetime of the credential in seconds." + }, + "max_uses": { + "type": "integer", + "minimum": 1, + "nullable": true, + "default": 1000, + "description": "Number of events the credential may submit (a batch spends one use per event). Send `null` for no cap within the lifetime." + }, + "bind_to_origin": { "type": "string", - "format": "uuid" + "maxLength": 255, + "nullable": true, + "description": "When set, the browser `Origin` header of every device request must equal this value exactly; a request without `Origin` is refused." } } + }, + "example": { + "vendor_data": "user-42", + "allowed_event_categories": [ + "auth", + "session", + "gameplay" + ], + "ttl_seconds": 900, + "max_uses": 1000, + "bind_to_origin": "https://app.example.com" } } } @@ -40900,2445 +61600,2026 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/links/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"entity_type\": \"transaction\", \"entity_uuid\": \"77000000-0000-4000-8000-000000000077\"}'" + "source": "curl -X POST 'https://verification.didit.me/v3/events/sdk-token/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"vendor_data\": \"user-42\",\n \"allowed_event_categories\": [\"auth\", \"session\", \"gameplay\"],\n \"ttl_seconds\": 900,\n \"max_uses\": 1000,\n \"bind_to_origin\": \"https://app.example.com\"\n }'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/events/sdk-token/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'vendor_data': 'user-42',\n 'allowed_event_categories': ['auth', 'session', 'gameplay'],\n 'ttl_seconds': 900,\n 'max_uses': 1000,\n 'bind_to_origin': 'https://app.example.com',\n },\n timeout=10,\n)\nresp.raise_for_status()\ncredential = resp.json()\n# Hand only monitoring_token to the app, never the API key.\nprint(credential['monitoring_token'], credential['expires_at'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/events/sdk-token/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n vendor_data: 'user-42',\n allowed_event_categories: ['auth', 'session', 'gameplay'],\n ttl_seconds: 900,\n max_uses: 1000,\n bind_to_origin: 'https://app.example.com',\n }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst { monitoring_token, expires_at } = await resp.json();\n// Hand only monitoring_token to the app, never the API key.\nconsole.log(monitoring_token, expires_at);" } ], "responses": { "201": { - "description": "Link created.", + "description": "The credential was minted. Hand `monitoring_token` to the app and treat it as a secret until `expires_at`.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CaseLink" + "type": "object", + "required": [ + "monitoring_token", + "expires_at", + "allowed_event_categories", + "max_uses", + "bind_to_origin" + ], + "properties": { + "monitoring_token": { + "type": "string", + "description": "The device credential (a 43-character URL-safe token). Send it in the `X-Monitoring-Token` header." + }, + "expires_at": { + "type": "string", + "format": "date-time", + "description": "When the credential stops being accepted." + }, + "allowed_event_categories": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "auth", + "session", + "gameplay", + "bonus", + "affiliate", + "payment", + "dispute", + "security", + "responsible_gambling", + "custom" + ] + }, + "description": "Categories the credential may submit; `[]` means every category." + }, + "max_uses": { + "type": "integer", + "nullable": true, + "description": "Event budget of the credential; null means no cap." + }, + "bind_to_origin": { + "type": "string", + "nullable": true, + "description": "Origin the credential is bound to, or null." + } + } + }, + "example": { + "monitoring_token": "q7ZtR2xKp9VbN4mWc8LdH1sYfJ3gT6uEaQ0oPiXzBkC", + "expires_at": "2026-10-17T09:30:02.481000Z", + "allowed_event_categories": [ + "auth", + "session", + "gameplay" + ], + "max_uses": 1000, + "bind_to_origin": "https://app.example.com" } } } }, "400": { - "description": "Entity belongs to a different profile than the case subject.", + "description": "Validation failed: missing `vendor_data`, `ttl_seconds` outside 60 to 86400, `max_uses` below 1, or an unknown category (keyed by its position in the array).", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "examples": { + "Missing vendor_data": { + "value": { + "vendor_data": [ + "This field is required." + ] + } + }, + "ttl_seconds too short": { + "value": { + "ttl_seconds": [ + "Ensure this value is greater than or equal to 60." + ] + } + }, + "Unknown category": { + "value": { + "allowed_event_categories": { + "1": [ + "\"login\" is not a valid choice." + ] + } } } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/links/{link_uuid}/": { - "delete": { - "summary": "Remove case link", - "description": "Detach a linked session, business session, or transaction from the case.", - "operationId": "delete_case_link", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "401": { + "description": "The scoped API key has expired. Only an expired key gets `401`; a missing or invalid key gets `403`.", + "content": { + "application/json": { + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." + "403": { + "description": "Missing or invalid `x-api-key` (this endpoint answers `403`, never `401`, for a missing or invalid key), a scoped key without `transactions: write`, a key limited to some workflows or to approved sessions (such a key never reaches transaction monitoring data), or a request from outside the key's IP allowlist. Also returned with `code: event_monitoring_disabled` while event ingestion is switched off for the application; nothing is stored in that case, and the check runs before the body is validated.", + "content": { + "application/json": { + "examples": { + "Event monitoring disabled": { + "value": { + "detail": "Event monitoring is not enabled for this application. Turn on event ingestion in the transaction monitoring settings before submitting events.", + "code": "event_monitoring_disabled" + } + }, + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without access": { + "value": { + "detail": "This API key has no write access to transactions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "IP not allowed": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } }, - { - "name": "link_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "11000000-0000-4000-8000-000000000004" + "429": { + "description": "Rate limit exceeded: write requests (`POST`, `PATCH`, `DELETE`) share a budget of 300 requests per minute per API key. Wait `Retry-After` seconds, then retry.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } }, - "description": "Link UUID." + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + } + } + } } - ], - "x-codeSamples": [ + }, + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/links/11000000-0000-4000-8000-000000000004/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "204": { - "description": "Link removed." + "ApiKeyAuth": [] } - } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/events/": { + "/v3/events/{event_uuid}/": { "get": { - "summary": "List case events", - "description": "Read-only audit timeline of the case (created, status changes, assignment, resolution, escalation, transfers, tag/priority/due-date changes, entity actions, and more).", - "operationId": "list_case_events", + "summary": "Get monitoring event", + "description": "Retrieve one monitoring event with everything the list returns plus the payload, custom values, device context, field provenance (`field_sources`), the promoted domain fields, `expires_at`, every matching rule run with its evidence, the IP enrichment and the tags rules attached. While `correlation_pending` is `true` an asynchronous rule still has to run; read the event again a few minutes later for the final `status`, `score` and `rule_runs`. Reads work whether or not event ingestion is switched on. A scoped API key needs `transactions: read`. See [Evidence](https://docs.didit.me/transaction-monitoring/event-rules#evidence) and [IP enrichment](https://docs.didit.me/transaction-monitoring/ip-enrichment).", + "operationId": "get_monitoring_event", + "x-didit-sdk": { + "resource": "monitoring_events", + "method": "retrieve", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" + "Monitoring Events" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "event_uuid", "in": "path", "required": true, + "description": "Didit identifier of the event (`event_uuid` from the create, batch, list or timeline response), not your own `event_id`.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "default": 50, - "minimum": 1 - }, - "description": "Page size." - }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "integer", - "default": 0, - "minimum": 0 - }, - "description": "Zero-based offset." + "example": "11111111-2222-3333-4444-555555555555" } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/events/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/events/11111111-2222-3333-4444-555555555555/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nevent_uuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n f'https://verification.didit.me/v3/events/{event_uuid}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nevent = resp.json()\nprint(event['status'], event['score'])\nfor run in event['rule_runs']:\n print(run['rule_title'], run['score_delta'], run['action_summary'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const eventUuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/events/${eventUuid}/`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst event = await resp.json();\nconsole.log(event.status, event.score, event.rule_runs.length, 'rule runs');" } ], "responses": { "200": { - "description": "Paginated events, newest first.", + "description": "The event with its evidence.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer", - "description": "Number of matching rows. Exact up to 100, capped at 100 beyond that for performance. Rely on `next` being null to detect the last page." - }, - "next": { - "type": "string", - "nullable": true, - "description": "Absolute URL of the next page, or null." - }, - "previous": { - "type": "string", - "nullable": true, - "description": "Absolute URL of the previous page, or null." - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseEvent" - } + "$ref": "#/components/schemas/MonitoringEventDetail" + }, + "examples": { + "Login from a VPN, in review": { + "value": { + "event_uuid": "33333333-4444-5555-6666-777777777777", + "event_id": "login-2026-10-17-000001", + "event_category": "auth", + "action_type": "login", + "occurred_at": "2026-10-17T09:15:02.481000Z", + "received_at": "2026-10-17T09:15:02.903114Z", + "status": "IN_REVIEW", + "score": 40, + "subject_vendor_data": "user-42", + "subject_entity_type": "individual", + "subject_uuid": "66666666-7777-8888-9999-000000000000", + "counterparty_vendor_data": null, + "session_id": "web-session-abc", + "device_fingerprint": "fp-a8c1", + "ip_address": "203.0.113.42", + "ip_country": "ES", + "amount": null, + "currency": null, + "game_id": null, + "round_id": null, + "bonus_id": null, + "affiliate_code": null, + "payment_reference_id": null, + "retention_class": "risk_relevant", + "rules_matched_count": 1, + "correlation_pending": false, + "is_droppable": true, + "payload": { + "method": "password" + }, + "custom_values": { + "vip_tier": "gold", + "vpn_or_proxy_detected": true + }, + "device_context": { + "fingerprint": "fp-a8c1", + "network_context": { + "ip_address": "203.0.113.42", + "ip_country": "Spain", + "ip_country_code": "ES", + "ip_city": "Madrid", + "asn_number": 64500, + "asn_organization": "Example Networks", + "is_vpn_or_tor": true, + "is_data_center": false + } + }, + "field_sources": { + "subject.vendor_data": "customer", + "event_category": "customer", + "action_type": "customer", + "occurred_at": "customer", + "received_at": "server", + "payload.method": "customer", + "session.session_id": "customer", + "session.sequence": "customer", + "device_context.fingerprint": "customer", + "device_context.network_context.ip_address": "customer", + "device_context.network_context.ip_country": "server", + "device_context.network_context.ip_country_code": "server", + "device_context.network_context.ip_city": "server", + "device_context.network_context.asn_number": "server", + "device_context.network_context.asn_organization": "server", + "device_context.network_context.is_vpn_or_tor": "server", + "device_context.network_context.is_data_center": "server" + }, + "session_sequence": 142, + "table_id": null, + "hand_id": null, + "match_id": null, + "market_id": null, + "side": null, + "role": null, + "net_result_amount": null, + "payment_fingerprint": null, + "instrument_reference_hash": null, + "withdrawal_address_hash": null, + "dispute_id": null, + "chargeback_id": null, + "changed_field": null, + "identity_match_ref": null, + "schema_version": 1, + "expires_at": "2027-01-15T09:15:02.481000Z", + "rules_evaluated_count": 6, + "rule_runs": [ + { + "rule_uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "rule_title": "Login from VPN or Tor", + "library_key": null, + "matched": true, + "mode": "ACTIVE", + "is_async": false, + "severity": "MEDIUM", + "score_delta": 40, + "action_summary": "score +40; add_tag:VPN_LOGIN", + "matched_payload": [ + { + "field": "event.ip.is_vpn_or_tor", + "operator": "eq", + "expected_value": true, + "current_value": true, + "matched": true, + "group_index": null, + "group_logic": "AND", + "field_source": "server" + } + ], + "created_at": "2026-10-17T09:15:02.951207Z" + } + ], + "ip_enrichment": { + "ip_country_code": "ES", + "ip_city": "Madrid", + "asn_number": 64500, + "asn_organization": "Example Networks", + "is_vpn_or_tor": true, + "is_data_center": false, + "proxy_type": null, + "last_enriched_at": "2026-10-17T09:15:02.910442Z" + }, + "subject_profile": null, + "tags": [ + "VPN_LOGIN" + ] } } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/checklist/": { - "get": { - "summary": "List case checklist", - "description": "List the case's checklist items, copied from the blueprint's content_config.checklist.items at creation, ordered.", - "operationId": "list_case_checklist", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/checklist/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Checklist items.", + "401": { + "description": "The scoped API key has expired. Only an expired key gets `401`; a missing or invalid key gets `403`.", "content": { "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseChecklistItem" + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/checklist/{item_uuid}/": { - "patch": { - "summary": "Toggle checklist item", - "description": "Mark a checklist item complete or incomplete. Resolution is blocked while any item is incomplete, unless the blueprint marks the checklist optional.", - "operationId": "toggle_case_checklist_item", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." }, - { - "name": "item_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c4ec0000-0000-4000-8000-000000000005" - }, - "description": "Checklist item UUID." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "is_completed" - ], - "properties": { - "is_completed": { - "type": "boolean" + "403": { + "description": "Missing or invalid `x-api-key` (this endpoint answers `403`, never `401`, for a missing or invalid key), a scoped key without `transactions: read`, a key limited to some workflows or to approved sessions (such a key never reaches transaction monitoring data), or a request from outside the key's IP allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without access": { + "value": { + "detail": "This API key has no read access to transactions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "IP not allowed": { + "value": { + "detail": "This API key can't be used from this IP address." + } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/checklist/c4ec0000-0000-4000-8000-000000000005/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"is_completed\": true}'" - } - ], - "responses": { - "200": { - "description": "Updated checklist item.", + }, + "404": { + "description": "No event with this `event_uuid` exists in the application: it was never stored, it was deleted, or it belongs to another application. A path segment that is not a UUID does not match the route and also returns `404`.", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseChecklistItem" + "examples": { + "Not found": { + "value": { + "detail": "No MonitoringEvent matches the given query." + } + } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/aml/": { - "get": { - "summary": "List case AML hits", - "description": "List AML screening hits for the case's subject. Same shape as the hits returned by POST /v3/aml/.", - "operationId": "list_case_aml_hits", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/aml/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "AML hits.", + "429": { + "description": "Rate limit exceeded: `GET` requests share a budget of 600 requests per minute per API key. Wait `Retry-After` seconds, then retry.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } + }, "content": { "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object" + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } } } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/statistics/": { + "/v3/events/subjects/{vendor_data}/": { "get": { - "summary": "Case analytics", - "description": "Aggregate case analytics for the date range (defaults to the last 30 days; max 731 days). Charts, tables, totals, assignment events, and current queue counts use the same application-scoped filters. Filters use current case values. Dates apply to each metric event, so older cases assigned or resolved during the period remain included. Queue depth is independent of the date range. Invalid filters return 400; an unknown blueprint or assignee returns an empty result.", - "operationId": "get_case_analytics", + "summary": "Get subject event timeline", + "description": "List every monitoring event of one subject, newest `occurred_at` first. A `vendor_data` is unique only within an entity type, so the same id can name an individual and a company: `entity_type` picks which timeline you get, and it defaults to `individual`, so a company's events need `entity_type=company`. The filters of `GET /v3/events/` apply on top, and a `vendor_data` with no events returns an empty page rather than `404`. A scoped API key needs `transactions: read`. See [Reading events back](https://docs.didit.me/transaction-monitoring/events#reading-events-back).", + "operationId": "get_monitoring_event_subject_timeline", + "x-didit-sdk": { + "resource": "monitoring_events", + "method": "retrieve_subject_timeline", + "auth": "api_key", + "server": true + }, "tags": [ - "Cases" + "Monitoring Events" ], "parameters": [ { - "name": "organization_id", + "name": "vendor_data", "in": "path", "required": true, + "description": "Your id for the subject (`subject.vendor_data` of its events). URL-encode it; a value containing `/` cannot be addressed by this endpoint.", "schema": { "type": "string", - "format": "uuid" + "minLength": 1, + "maxLength": 255 }, - "description": "Organization UUID." + "example": "user-42" }, { - "name": "application_id", - "in": "path", - "required": true, + "name": "entity_type", + "in": "query", + "required": false, + "description": "`company` returns the company with this `vendor_data`; any other value, or none, returns the individual. `subject_entity_type` is accepted as an alias when `entity_type` is absent.", "schema": { "type": "string", - "format": "uuid" + "enum": [ + "individual", + "company" + ], + "default": "individual" }, - "description": "Application UUID." + "example": "individual" }, { - "name": "date_from", + "name": "limit", "in": "query", + "required": false, + "description": "Maximum number of events per page. Defaults to 50. Values above 1000000 return `400`; a non-numeric, zero or negative value falls back to 50.", "schema": { - "type": "string", - "format": "date" + "type": "integer", + "default": 50, + "minimum": 1, + "maximum": 1000000 }, - "description": "Range start (inclusive). Defaults to 30 days ago." + "example": 50 }, { - "name": "date_to", + "name": "offset", "in": "query", + "required": false, + "description": "Number of events to skip. Defaults to 0. Prefer following the `next` URL from the response.", "schema": { - "type": "string", - "format": "date" + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 1000000 }, - "description": "Range end (inclusive). Defaults to today." + "example": 0 }, { - "name": "blueprint", + "name": "event_category", "in": "query", "required": false, + "description": "Only events in these categories. Repeat the parameter for several values (`?event_category=payment&event_category=dispute`); an event matching any of them is returned.", + "style": "form", + "explode": true, "schema": { - "type": "string", - "format": "uuid" + "type": "array", + "items": { + "type": "string", + "enum": [ + "auth", + "session", + "gameplay", + "bonus", + "affiliate", + "payment", + "dispute", + "security", + "responsible_gambling", + "custom" + ] + } }, - "description": "Filter by the current blueprint UUID." + "example": [ + "payment", + "dispute" + ] }, { - "name": "assigned_to", + "name": "action_type", "in": "query", "required": false, + "description": "Only events with these action types (exact, case-sensitive). Repeat for several values.", + "style": "form", + "explode": true, "schema": { - "type": "string", - "format": "uuid" + "type": "array", + "items": { + "type": "string" + } }, - "description": "Filter by the current assigned reviewer user UUID." + "example": [ + "deposit_completed", + "chargeback_opened" + ] }, { - "name": "priority", + "name": "status", "in": "query", "required": false, + "description": "Only events with these statuses. Repeat for several values.", + "style": "form", + "explode": true, "schema": { - "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ] + "type": "array", + "items": { + "type": "string", + "enum": [ + "ACCEPTED", + "APPROVED", + "IN_REVIEW", + "DECLINED" + ] + } }, - "description": "Filter by current case priority." + "example": [ + "IN_REVIEW", + "DECLINED" + ] }, { - "name": "source", + "name": "retention_class", "in": "query", "required": false, + "description": "Only events in these retention classes. Repeat for several values.", + "style": "form", + "explode": true, "schema": { - "type": "string", - "enum": [ - "MANUAL", - "AML_SCREENING", - "TRANSACTION_RULE", - "WORKFLOW_BUILDER" - ] + "type": "array", + "items": { + "type": "string", + "enum": [ + "low_value", + "standard", + "risk_relevant", + "monetary_or_compliance" + ] + } }, - "description": "Filter by case creation source." + "example": [ + "risk_relevant" + ] }, { - "name": "status", + "name": "business_vendor_data", "in": "query", "required": false, + "description": "Exact match on the `business.vendor_data` sent with the event.", "schema": { - "type": "string", - "enum": [ - "OPEN", - "AWAITING_USER", - "RESOLVED" - ] + "type": "string" }, - "description": "Filter by current case status." - } - ], - "x-codeSamples": [ + "example": "merchant-7" + }, { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/statistics/?date_from=2026-06-01&date_to=2026-06-30' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Analytics payload.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "series": { - "type": "array", - "items": { - "type": "object", - "properties": { - "date": { - "type": "string", - "format": "date" - }, - "created": { - "type": "integer" - }, - "reviewed": { - "type": "integer" - } - } - } - }, - "tables": { - "type": "object", - "properties": { - "officer": { - "type": "array", - "items": { - "type": "object", - "properties": { - "key": { - "type": "string", - "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions." - }, - "created": { - "type": "integer" - }, - "assigned": { - "type": "integer" - }, - "resolved": { - "type": "integer" - }, - "threat_rate": { - "type": "number", - "format": "float" - }, - "false_positive_rate": { - "type": "number", - "format": "float" - }, - "unresolved": { - "type": "integer" - }, - "reassignment_rate": { - "type": "number", - "format": "float" - }, - "escalation_rate": { - "type": "number", - "format": "float" - }, - "avg_assign_hours": { - "type": "number", - "format": "float" - }, - "avg_resolve_hours": { - "type": "number", - "format": "float" - }, - "avg_handling_hours": { - "type": "number", - "format": "float" - } - } - } - }, - "blueprint": { - "type": "array", - "items": { - "type": "object", - "properties": { - "key": { - "type": "string", - "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions." - }, - "created": { - "type": "integer" - }, - "assigned": { - "type": "integer" - }, - "resolved": { - "type": "integer" - }, - "threat_rate": { - "type": "number", - "format": "float" - }, - "false_positive_rate": { - "type": "number", - "format": "float" - }, - "unresolved": { - "type": "integer" - }, - "reassignment_rate": { - "type": "number", - "format": "float" - }, - "escalation_rate": { - "type": "number", - "format": "float" - }, - "avg_assign_hours": { - "type": "number", - "format": "float" - }, - "avg_resolve_hours": { - "type": "number", - "format": "float" - }, - "avg_handling_hours": { - "type": "number", - "format": "float" - } - } - } - }, - "source": { - "type": "array", - "items": { - "type": "object", - "properties": { - "key": { - "type": "string", - "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions." - }, - "created": { - "type": "integer" - }, - "assigned": { - "type": "integer" - }, - "resolved": { - "type": "integer" - }, - "threat_rate": { - "type": "number", - "format": "float" - }, - "false_positive_rate": { - "type": "number", - "format": "float" - }, - "unresolved": { - "type": "integer" - }, - "reassignment_rate": { - "type": "number", - "format": "float" - }, - "escalation_rate": { - "type": "number", - "format": "float" - }, - "avg_assign_hours": { - "type": "number", - "format": "float" - }, - "avg_resolve_hours": { - "type": "number", - "format": "float" - }, - "avg_handling_hours": { - "type": "number", - "format": "float" - } - } - } - }, - "status": { - "type": "array", - "items": { - "type": "object", - "properties": { - "key": { - "type": "string", - "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions." - }, - "created": { - "type": "integer" - }, - "assigned": { - "type": "integer" - }, - "resolved": { - "type": "integer" - }, - "threat_rate": { - "type": "number", - "format": "float" - }, - "false_positive_rate": { - "type": "number", - "format": "float" - }, - "unresolved": { - "type": "integer" - }, - "reassignment_rate": { - "type": "number", - "format": "float" - }, - "escalation_rate": { - "type": "number", - "format": "float" - }, - "avg_assign_hours": { - "type": "number", - "format": "float" - }, - "avg_resolve_hours": { - "type": "number", - "format": "float" - }, - "avg_handling_hours": { - "type": "number", - "format": "float" - } - } - } - } - } - }, - "totals": { - "type": "object", - "properties": { - "created": { - "type": "integer" - }, - "assigned": { - "type": "integer" - }, - "resolved": { - "type": "integer" - }, - "threat_rate": { - "type": "number", - "format": "float" - }, - "false_positive_rate": { - "type": "number", - "format": "float" - }, - "unresolved": { - "type": "integer" - }, - "reassignment_rate": { - "type": "number", - "format": "float" - }, - "escalation_rate": { - "type": "number", - "format": "float" - }, - "avg_assign_hours": { - "type": "number", - "format": "float" - }, - "avg_resolve_hours": { - "type": "number", - "format": "float" - }, - "avg_handling_hours": { - "type": "number", - "format": "float" - } - } - }, - "queue": { - "type": "object", - "description": "Live snapshot, not scoped to the date range.", - "properties": { - "open": { - "type": "integer" - }, - "overdue": { - "type": "integer" - } - } - } - } - } - } - } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/bulk/status/": { - "post": { - "summary": "Bulk update case status", - "description": "Apply the same status transition to several cases in one call: OPEN reopens, AWAITING_USER requests info, RESOLVED resolves (resolution required). Each case is evaluated independently; gate failures (incomplete checklist, missing required note) are reported per case without aborting the batch.", - "operationId": "bulk_update_case_status", - "tags": [ - "Cases" - ], - "parameters": [ + "name": "session_id", + "in": "query", + "required": false, + "description": "Exact match on `session.session_id`.", + "schema": { + "type": "string" + }, + "example": "web-session-abc" + }, + { + "name": "device_fingerprint", + "in": "query", + "required": false, + "description": "Exact match on `device_context.fingerprint`.", + "schema": { + "type": "string" + }, + "example": "fp-a8c1" + }, + { + "name": "ip_address", + "in": "query", + "required": false, + "description": "Exact match on the event's IP address.", + "schema": { + "type": "string" + }, + "example": "203.0.113.42" + }, + { + "name": "game_id", + "in": "query", + "required": false, + "description": "Exact match on the promoted `game_id`.", + "schema": { + "type": "string" + }, + "example": "poker-holdem-01" + }, + { + "name": "round_id", + "in": "query", + "required": false, + "description": "Exact match on the promoted `round_id`.", + "schema": { + "type": "string" + }, + "example": "round-88231" + }, + { + "name": "affiliate_code", + "in": "query", + "required": false, + "description": "Exact match on the promoted `affiliate_code`.", + "schema": { + "type": "string" + }, + "example": "aff-summer" + }, + { + "name": "payment_reference_id", + "in": "query", + "required": false, + "description": "Exact match on the payment reference (from `transaction_context.payment_reference_id` or `payload.payment_reference_id`).", + "schema": { + "type": "string" + }, + "example": "dep_735" + }, + { + "name": "counterparty_vendor_data", + "in": "query", + "required": false, + "description": "Exact match on the promoted `counterparty_vendor_data`.", + "schema": { + "type": "string" + }, + "example": "user-77" + }, + { + "name": "matched_only", + "in": "query", + "required": false, + "description": "`true` (also `1` or `yes`, case-insensitive) returns only events at least one rule matched (`rules_matched_count` above 0). Any other value is ignored.", + "schema": { + "type": "boolean", + "default": false + }, + "example": true + }, { - "name": "organization_id", - "in": "path", - "required": true, + "name": "occurred_from", + "in": "query", + "required": false, + "description": "Only events with `occurred_at` at or after this instant (inclusive). Send an ISO 8601 value: `2026-10-01T00:00:00Z`, an explicit offset with the `+` percent-encoded (`2026-10-01T02:00:00%2B02:00`; an unencoded `+` is decoded as a space and the value no longer parses), or a date alone (`2026-10-01`), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted.", "schema": { "type": "string", - "format": "uuid" + "format": "date-time" }, - "description": "Organization UUID." + "example": "2026-10-01T00:00:00Z" }, { - "name": "application_id", - "in": "path", - "required": true, + "name": "occurred_to", + "in": "query", + "required": false, + "description": "Only events with `occurred_at` at or before this instant (inclusive). A date alone means the start of that day, so `occurred_to=2026-10-15` excludes the rest of 15 October; send `2026-10-15T23:59:59Z` to include it. Send an ISO 8601 value: `2026-10-01T00:00:00Z`, an explicit offset with the `+` percent-encoded (`2026-10-01T02:00:00%2B02:00`; an unencoded `+` is decoded as a space and the value no longer parses), or a date alone (`2026-10-01`), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted.", "schema": { "type": "string", - "format": "uuid" + "format": "date-time" }, - "description": "Application UUID." + "example": "2026-10-17T23:59:59Z" + }, + { + "name": "search", + "in": "query", + "required": false, + "description": "Case-insensitive substring match on `event_id`, `action_type`, `subject_vendor_data` or `counterparty_vendor_data`.", + "schema": { + "type": "string" + }, + "example": "chargeback" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "case_uuids", - "status" - ], - "properties": { - "case_uuids": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "minItems": 1 - }, - "status": { - "type": "string", - "enum": [ - "OPEN", - "AWAITING_USER", - "RESOLVED" - ] - }, - "resolution": { - "type": "string", - "enum": [ - "FALSE_POSITIVE", - "VALID_THREAT" - ], - "description": "Required when status is RESOLVED." - }, - "note": { - "type": "string", - "nullable": true - } - } - } - } - } - }, "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/bulk/status/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"case_uuids\": [\"c1a5e000-0000-4000-8000-000000000001\"], \"status\": \"RESOLVED\", \"resolution\": \"FALSE_POSITIVE\"}'" + "source": "curl -X GET 'https://verification.didit.me/v3/events/subjects/user-42/?entity_type=individual&limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\nfrom urllib.parse import quote\n\nimport requests\n\nvendor_data = 'user-42'\nresp = requests.get(\n f'https://verification.didit.me/v3/events/subjects/{quote(vendor_data, safe=\"\")}/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'entity_type': 'individual', 'limit': 50},\n timeout=10,\n)\nresp.raise_for_status()\nfor event in resp.json()['results']:\n print(event['occurred_at'], event['event_category'], event['action_type'], event['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const vendorData = 'user-42';\nconst url = `https://verification.didit.me/v3/events/subjects/${encodeURIComponent(vendorData)}/?entity_type=individual&limit=50`;\nconst resp = await fetch(url, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst page = await resp.json();\nfor (const event of page.results) console.log(event.occurred_at, event.event_category, event.action_type, event.status);" } ], "responses": { "200": { - "description": "Per-case results.", + "description": "One page of the subject's events.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "updated": { - "type": "integer" - }, - "failures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "case_uuid": { - "type": "string", - "format": "uuid" - }, - "detail": { - "type": "string" - } + "$ref": "#/components/schemas/MonitoringEventPage" + }, + "examples": { + "Individual timeline": { + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "event_uuid": "11111111-2222-3333-4444-555555555555", + "event_id": "chargeback-2026-10-16-000318", + "event_category": "dispute", + "action_type": "chargeback_opened", + "occurred_at": "2026-10-16T14:02:11Z", + "received_at": "2026-10-16T14:02:12.418230Z", + "status": "IN_REVIEW", + "score": 60, + "subject_vendor_data": "user-42", + "subject_entity_type": "individual", + "subject_uuid": "66666666-7777-8888-9999-000000000000", + "counterparty_vendor_data": null, + "session_id": null, + "device_fingerprint": null, + "ip_address": null, + "ip_country": null, + "amount": "250.000000000000000000", + "currency": "EUR", + "game_id": null, + "round_id": null, + "bonus_id": null, + "affiliate_code": null, + "payment_reference_id": "dep_735", + "retention_class": "monetary_or_compliance", + "rules_matched_count": 1, + "correlation_pending": false, + "is_droppable": false + }, + { + "event_uuid": "22222222-3333-4444-5555-666666666666", + "event_id": "login-2026-10-15-000912", + "event_category": "auth", + "action_type": "login", + "occurred_at": "2026-10-15T08:30:00.120000Z", + "received_at": "2026-10-15T08:30:00.734512Z", + "status": "ACCEPTED", + "score": 0, + "subject_vendor_data": "user-42", + "subject_entity_type": "individual", + "subject_uuid": "66666666-7777-8888-9999-000000000000", + "counterparty_vendor_data": null, + "session_id": "web-session-abc", + "device_fingerprint": "fp-a8c1", + "ip_address": "203.0.113.42", + "ip_country": "ES", + "amount": null, + "currency": null, + "game_id": null, + "round_id": null, + "bonus_id": null, + "affiliate_code": null, + "payment_reference_id": null, + "retention_class": "standard", + "rules_matched_count": 0, + "correlation_pending": false, + "is_droppable": true } - } + ] + } + }, + "No events": { + "value": { + "count": 0, + "next": null, + "previous": null, + "results": [] } } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/bulk/assign/": { - "post": { - "summary": "Bulk assign cases", - "description": "Assign (or unassign, with assignee_user_id: null) several cases at once. All cases must share the same blueprint; mismatches are reported per case without aborting the batch.", - "operationId": "bulk_assign_cases", - "tags": [ - "Cases" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "case_uuids", - "assignee_user_id" - ], - "properties": { - "case_uuids": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "minItems": 1 + "400": { + "description": "`limit` or `offset` is larger than 1000000. The body is a JSON array with one message. A non-numeric or non-positive `limit` falls back to the default of 50 instead of failing.", + "content": { + "application/json": { + "examples": { + "limit too large": { + "summary": "limit above 1000000", + "value": [ + "limit must not exceed 1000000." + ] }, - "assignee_user_id": { - "type": "string", - "format": "uuid", - "nullable": true + "offset too large": { + "summary": "offset above 1000000", + "value": [ + "offset must not exceed 1000000." + ] + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. Only an expired key gets `401`; a missing or invalid key gets `403`.", + "content": { + "application/json": { + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "Missing or invalid `x-api-key` (this endpoint answers `403`, never `401`, for a missing or invalid key), a scoped key without `transactions: read`, a key limited to some workflows or to approved sessions (such a key never reaches transaction monitoring data), or a request from outside the key's IP allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } }, - "assignee_email": { - "type": "string", - "format": "email", - "nullable": true + "Scoped key without access": { + "value": { + "detail": "This API key has no read access to transactions." + } }, - "assignee_name": { - "type": "string", - "nullable": true + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "IP not allowed": { + "value": { + "detail": "This API key can't be used from this IP address." + } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/bulk/assign/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"case_uuids\": [\"c1a5e000-0000-4000-8000-000000000001\"], \"assignee_user_id\": \"9a000000-0000-4000-8000-0000000000aa\"}'" - } - ], - "responses": { - "200": { - "description": "Per-case results.", + }, + "429": { + "description": "Rate limit exceeded: `GET` requests share a budget of 600 requests per minute per API key. Wait `Retry-After` seconds, then retry.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } + }, "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "updated": { - "type": "integer" - }, - "failures": { - "type": "array", - "items": { - "type": "object", - "properties": { - "case_uuid": { - "type": "string", - "format": "uuid" - }, - "detail": { - "type": "string" - } - } - } + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." } } } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/": { + "/v3/events/enrichment-metrics/": { "get": { - "summary": "List blueprints", - "description": "List case blueprints for the application.", - "operationId": "list_case_blueprints", + "summary": "Get event enrichment metrics", + "description": "Return the event ingestion and IP-enrichment counters of your application for a period, summed over every lane (server, batch and device), so you can watch event volume and confirm that enrichment is resolved once per unique address rather than once per event. The period covers today plus the previous `days` days, counted by UTC calendar date. The three ratios are null when their denominator is zero. A scoped API key needs `transactions: read`. See [IP enrichment](https://docs.didit.me/transaction-monitoring/events#ip-enrichment).", + "operationId": "get_monitoring_event_enrichment_metrics", + "x-didit-sdk": { + "resource": "monitoring_events", + "method": "retrieve_enrichment_metrics", + "auth": "api_key", + "server": true + }, "tags": [ - "Case Blueprints" + "Monitoring Events" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "default": 50, - "minimum": 1 - }, - "description": "Page size." - }, - { - "name": "offset", + "name": "days", "in": "query", + "required": false, + "description": "Length of the period in days. Defaults to 30; values are clamped to 1 to 365, and a non-integer value falls back to 30. The value applied is returned as `period_days`.", "schema": { "type": "integer", - "default": 0, - "minimum": 0 + "default": 30, + "minimum": 1, + "maximum": 365 }, - "description": "Zero-based offset." + "example": 30 } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/events/enrichment-metrics/?days=30' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nresp = requests.get(\n 'https://verification.didit.me/v3/events/enrichment-metrics/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'days': 30},\n timeout=10,\n)\nresp.raise_for_status()\nmetrics = resp.json()\nprint(metrics['events_ingested'], metrics['cache_hit_rate'], metrics['provider_calls_per_1k_events'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const resp = await fetch('https://verification.didit.me/v3/events/enrichment-metrics/?days=30', {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst metrics = await resp.json();\nconsole.log(metrics.events_ingested, metrics.cache_hit_rate, metrics.provider_calls_per_1k_events);" } ], "responses": { "200": { - "description": "Paginated blueprint list.", + "description": "Counters and ratios for the period.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "events_ingested", + "events_rejected", + "enrichment_lookups", + "enrichment_cache_hits", + "enrichment_provider_calls", + "enrichment_skipped_low_value", + "unique_ips_seen", + "period_days", + "cache_hit_rate", + "unique_ip_ratio", + "provider_calls_per_1k_events" + ], "properties": { - "count": { + "events_ingested": { "type": "integer", - "description": "Number of matching rows, capped at 100 for performance." + "description": "New events stored. Idempotent replays are not counted." }, - "next": { - "type": "string", - "nullable": true + "events_rejected": { + "type": "integer", + "description": "Items rejected inside batch requests (server and device lanes). A rejected single-event request is not counted." }, - "previous": { - "type": "string", - "nullable": true + "enrichment_lookups": { + "type": "integer", + "description": "Events whose public IP address was resolved, from the cache or freshly." }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseBlueprintListItem" - } + "enrichment_cache_hits": { + "type": "integer", + "description": "Lookups answered from the cache." + }, + "enrichment_provider_calls": { + "type": "integer", + "description": "Lookups that needed a fresh resolution of the address." + }, + "enrichment_skipped_low_value": { + "type": "integer", + "description": "Low-value telemetry events whose address was not already known. They never trigger a fresh resolution of their own." + }, + "unique_ips_seen": { + "type": "integer", + "description": "Addresses that needed a fresh resolution." + }, + "period_days": { + "type": "integer", + "description": "The `days` value applied after clamping." + }, + "cache_hit_rate": { + "type": "number", + "nullable": true, + "description": "`enrichment_cache_hits / enrichment_lookups`, rounded to 4 decimals. Null when there were no lookups." + }, + "unique_ip_ratio": { + "type": "number", + "nullable": true, + "description": "`unique_ips_seen / events_ingested`, rounded to 4 decimals. Null when no events were ingested." + }, + "provider_calls_per_1k_events": { + "type": "number", + "nullable": true, + "description": "Fresh resolutions per 1000 ingested events, rounded to 2 decimals. Null when no events were ingested." + } + } + }, + "examples": { + "Last 30 days": { + "value": { + "events_ingested": 128450, + "events_rejected": 214, + "enrichment_lookups": 41230, + "enrichment_cache_hits": 40118, + "enrichment_provider_calls": 1112, + "enrichment_skipped_low_value": 74320, + "unique_ips_seen": 1112, + "period_days": 30, + "cache_hit_rate": 0.973, + "unique_ip_ratio": 0.0087, + "provider_calls_per_1k_events": 8.66 + } + }, + "No events yet": { + "value": { + "events_ingested": 0, + "events_rejected": 0, + "enrichment_lookups": 0, + "enrichment_cache_hits": 0, + "enrichment_provider_calls": 0, + "enrichment_skipped_low_value": 0, + "unique_ips_seen": 0, + "period_days": 30, + "cache_hit_rate": null, + "unique_ip_ratio": null, + "provider_calls_per_1k_events": null } } } } } - } - } - }, - "post": { - "summary": "Create blueprint", - "description": "Create a case blueprint: general settings (assignment, escalation, transfer, 4-eyes pairing) plus content_config, the six sections that drive the case page. Set publish=false to create an unpublished draft; Publish creates its first live version. Discarding a never-published draft deletes it.", - "operationId": "create_case_blueprint", - "tags": [ - "Case Blueprints" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "name", - "assignee_pool" - ], - "properties": { - "name": { - "type": "string", - "maxLength": 24, - "description": "Max 24 characters, e.g. \"AML investigation\"." - }, - "description": { - "type": "string", - "nullable": true - }, - "assignee_pool": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "minItems": 1, - "description": "List of User UUIDs. Empty arrays are rejected with \"Pick at least one assignee\"." - }, - "assignment_method": { - "type": "string", - "enum": [ - "MANUAL", - "AUTOMATIC" - ], - "default": "MANUAL" - }, - "max_active_cases": { - "type": "integer", - "nullable": true, - "minimum": 1, - "description": "Automatic-mode threshold: \"fewer than N cases of this blueprint assigned\"." - }, - "escalation_enabled": { - "type": "boolean", - "default": false - }, - "escalation_blueprint": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Target blueprint UUID. Not enforced at save time, but the escalate action fails with 400 until both escalation_enabled and escalation_blueprint are set." - }, - "transfer_enabled": { - "type": "boolean", - "default": false - }, - "four_eyes_checker_blueprint": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Setting this enables 4-eyes review: resolutions on this blueprint stage on the checker blueprint's queue." - }, - "require_resolution_note": { - "type": "boolean", - "default": false - }, - "content_config": { - "$ref": "#/components/schemas/CaseContentConfig" - }, - "is_active": { - "type": "boolean", - "default": true - }, - "default_deadline_hours": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 8760, - "description": "Default deadline for newly opened cases. Existing case deadlines are unchanged." - }, - "publish": { - "type": "boolean", - "writeOnly": true, - "default": true, - "description": "Set false to save a never-published draft. The record remains inactive at version 0 until the draft is explicitly published. Omitting this field preserves immediate creation for existing integrations." + "401": { + "description": "The scoped API key has expired. Only an expired key gets `401`; a missing or invalid key gets `403`.", + "content": { + "application/json": { + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"name\": \"AML investigation\", \"assignee_pool\": [\"9a000000-0000-4000-8000-0000000000aa\"], \"assignment_method\": \"AUTOMATIC\", \"max_active_cases\": 15, \"require_resolution_note\": true, \"content_config\": {\"aml_control\": {\"enabled\": true, \"financial\": true, \"identity\": false}}}'" - } - ], - "responses": { - "201": { - "description": "Blueprint created.", + }, + "403": { + "description": "Missing or invalid `x-api-key` (this endpoint answers `403`, never `401`, for a missing or invalid key), a scoped key without `transactions: read`, a key limited to some workflows or to approved sessions (such a key never reaches transaction monitoring data), or a request from outside the key's IP allowlist.", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseBlueprintDetail" + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without access": { + "value": { + "detail": "This API key has no read access to transactions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "IP not allowed": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } } } } }, - "400": { - "description": "Name over 24 chars, empty assignee_pool, or a cross-referenced blueprint (escalation/checker) not found.", + "429": { + "description": "Rate limit exceeded: `GET` requests share a budget of 600 requests per minute per API key. Wait `Retry-After` seconds, then retry.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } + }, "content": { "application/json": { - "schema": { - "type": "object" + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/": { - "get": { - "summary": "Get blueprint", - "description": "Retrieve a blueprint, including its connected creation sources (AML config, transaction rules, workflow nodes) and which blueprints escalate to it.", - "operationId": "get_case_blueprint", + "/v3/events/{event_uuid}/delete/": { + "delete": { + "summary": "Delete monitoring event", + "description": "Permanently delete one monitoring event together with its rule runs, notes and tag links, for example an event sent by mistake. After the `204` the event is gone from every read (retrieve returns `404`, and it no longer appears in the list or the subject timeline), the deletion is irreversible, and its `event_id` is free to be used for a new event. Not affected: the user or company the event resolved to, cases a rule opened, list entries a rule added, and the scores and statuses already applied to other events. To erase everything about a person or a company, delete the user or the company instead, which also deletes their events. A scoped API key needs `transactions: write`.", + "operationId": "delete_monitoring_event", + "x-didit-sdk": { + "resource": "monitoring_events", + "method": "delete", + "auth": "api_key", + "server": true + }, "tags": [ - "Case Blueprints" + "Monitoring Events" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "event_uuid", "in": "path", "required": true, + "description": "Didit identifier of the event (`event_uuid` from the create, batch, list or timeline response), not your own `event_id`.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." - }, - { - "name": "blueprint_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "b1de0000-0000-4000-8000-000000000002" - }, - "description": "Blueprint UUID." + "example": "11111111-2222-3333-4444-555555555555" } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/b1de0000-0000-4000-8000-000000000002/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X DELETE 'https://verification.didit.me/v3/events/11111111-2222-3333-4444-555555555555/delete/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nevent_uuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.delete(\n f'https://verification.didit.me/v3/events/{event_uuid}/delete/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status() # 204 No Content\nprint('deleted', event_uuid)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const eventUuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/events/${eventUuid}/delete/`, {\n method: 'DELETE',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (resp.status !== 204) throw new Error(`Delete failed: ${resp.status}`);\nconsole.log('deleted', eventUuid);" } ], "responses": { - "200": { - "description": "Blueprint detail.", + "204": { + "description": "The event was deleted. No body." + }, + "401": { + "description": "The scoped API key has expired. Only an expired key gets `401`; a missing or invalid key gets `403`.", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseBlueprintDetail" + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } } } } - } - } - }, - "patch": { - "summary": "Update blueprint", - "description": "Partially update a blueprint's general settings or content_config.", - "operationId": "update_case_blueprint", - "tags": [ - "Case Blueprints" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." }, - { - "name": "blueprint_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "b1de0000-0000-4000-8000-000000000002" - }, - "description": "Blueprint UUID." - } - ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "maxLength": 24, - "description": "Max 24 characters, e.g. \"AML investigation\"." - }, - "description": { - "type": "string", - "nullable": true - }, - "assignee_pool": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "minItems": 1, - "description": "List of User UUIDs. Empty arrays are rejected with \"Pick at least one assignee\"." - }, - "assignment_method": { - "type": "string", - "enum": [ - "MANUAL", - "AUTOMATIC" - ] - }, - "max_active_cases": { - "type": "integer", - "nullable": true, - "minimum": 1, - "description": "Automatic-mode threshold: \"fewer than N cases of this blueprint assigned\"." - }, - "escalation_enabled": { - "type": "boolean" - }, - "escalation_blueprint": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Target blueprint UUID. Not enforced at save time, but the escalate action fails with 400 until both escalation_enabled and escalation_blueprint are set." - }, - "transfer_enabled": { - "type": "boolean" - }, - "four_eyes_checker_blueprint": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "Setting this enables 4-eyes review: resolutions on this blueprint stage on the checker blueprint's queue." - }, - "require_resolution_note": { - "type": "boolean" - }, - "content_config": { - "$ref": "#/components/schemas/CaseContentConfig" + "403": { + "description": "Missing or invalid `x-api-key` (this endpoint answers `403`, never `401`, for a missing or invalid key), a scoped key without `transactions: write`, a key limited to some workflows or to approved sessions (such a key never reaches transaction monitoring data), or a request from outside the key's IP allowlist.", + "content": { + "application/json": { + "examples": { + "Missing or invalid API key": { + "value": { + "detail": "You do not have permission to perform this action." + } }, - "is_active": { - "type": "boolean" + "Scoped key without access": { + "value": { + "detail": "This API key has no write access to transactions." + } }, - "default_deadline_hours": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 8760, - "description": "Default deadline for newly opened cases. Existing case deadlines are unchanged." + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } }, - "expected_version": { - "type": "integer", - "minimum": 1, - "description": "The version opened by the editor. A stale version returns 409 without changing the review policy." + "IP not allowed": { + "value": { + "detail": "This API key can't be used from this IP address." + } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/b1de0000-0000-4000-8000-000000000002/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"transfer_enabled\": true}'" - } - ], - "responses": { - "200": { - "description": "Updated blueprint.", + }, + "404": { + "description": "No event with this `event_uuid` exists in the application: it was never stored, it was deleted, or it belongs to another application. A path segment that is not a UUID does not match the route and also returns `404`.", "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseBlueprintDetail" + "examples": { + "Not found": { + "value": { + "detail": "No MonitoringEvent matches the given query." + } + } } } } }, - "409": { - "description": "The review policy changed since expected_version. Review the latest version before retrying." - } - } - }, - "delete": { - "summary": "Delete blueprint", - "description": "Delete a blueprint. Deletion always succeeds and does not touch the blueprint's cases: existing cases keep their blueprint reference and its settings continue to apply to them. A deleted blueprint no longer appears in blueprint lists and can no longer be chosen as a transfer target; if a maker blueprint is deleted while a resolution is pending 4-eyes approval, a rejected resolution leaves the case on the checker blueprint.", - "operationId": "delete_case_blueprint", - "tags": [ - "Case Blueprints" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "blueprint_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "b1de0000-0000-4000-8000-000000000002" + "429": { + "description": "Rate limit exceeded: write requests (`POST`, `PATCH`, `DELETE`) share a budget of 300 requests per minute per API key. Wait `Retry-After` seconds, then retry.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } }, - "description": "Blueprint UUID." + "content": { + "application/json": { + "examples": { + "Throttled": { + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + } + } + } } - ], - "x-codeSamples": [ + }, + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/b1de0000-0000-4000-8000-000000000002/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "204": { - "description": "Blueprint deleted." + "ApiKeyAuth": [] } - } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/presets/": { - "get": { - "summary": "List blueprint presets", - "description": "List the four built-in presets (Screening hit, AML investigation, Suspicious activity, KYC review) with their content_config and whether each is already installed on this application.", - "operationId": "list_case_blueprint_presets", + "/v3/session/{sessionId}/kyb/parties/{partyUuid}/regenerate/": { + "post": { + "summary": "Regenerate a Key People party's KYC session", + "description": "Give a Key People party of a Business Verification (KYB) session a new individual KYC session after its previous one was deleted, so the Key People check can finish again; send no request body. It works only when the party's KYC session was deleted (`kyc_session_deleted: true`): a party whose KYC session still exists (in any status), a party that never had one, or a company party returns `409`. Didit creates a new hosted KYC session with the workflow the deleted one ran (its latest published version) or, when that is no longer known, the workflow your Key People settings assign to the party's role, emails the link to the party when an email is on file, clears `is_skipped`, and recomputes the Key People check and the business session status. It then sends a `data.updated` [webhook](https://docs.didit.me/integration/webhooks) for the business session with trigger `console_key_people_regenerate`; to exempt the party instead, use `PATCH /v3/session/{sessionId}/kyb/parties/{partyUuid}/`. A scoped API key needs `decisions: write`, and without `sessions: write` it receives `kyc_session_url` as `null`.", + "operationId": "post_v3_session_kyb_party_regenerate", + "x-didit-sdk": { + "resource": "sessions", + "method": "regenerate_kyb_party", + "auth": "api_key", + "server": true + }, "tags": [ - "Case Blueprints" + "Sessions" ], "parameters": [ { - "name": "organization_id", + "name": "sessionId", "in": "path", "required": true, + "description": "UUID of the parent Business Verification (KYB) session.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "format": "uuid", + "example": "33333333-4444-5555-6666-777777777777" + } }, { - "name": "application_id", + "name": "partyUuid", "in": "path", "required": true, + "description": "UUID of the Key People party (`key_people_checks[].submitted.parties[].uuid` on the business session decision). Must belong to the session referenced by `sessionId`.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "format": "uuid", + "example": "0a3f1e2c-7c2f-4a1b-9d55-2f8d2a5d6b41" + } } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/presets/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X POST 'https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/parties/0a3f1e2c-7c2f-4a1b-9d55-2f8d2a5d6b41/regenerate/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nsession_id = '33333333-4444-5555-6666-777777777777'\nparty_uuid = '0a3f1e2c-7c2f-4a1b-9d55-2f8d2a5d6b41'\nresp = requests.post(\n f'https://verification.didit.me/v3/session/{session_id}/kyb/parties/{party_uuid}/regenerate/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nparty = resp.json()\nprint(party['kyc_session_id'], party['kyc_session_status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const sessionId = '33333333-4444-5555-6666-777777777777';\nconst partyUuid = '0a3f1e2c-7c2f-4a1b-9d55-2f8d2a5d6b41';\nconst resp = await fetch(`https://verification.didit.me/v3/session/${sessionId}/kyb/parties/${partyUuid}/regenerate/`, {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Regenerate failed: ${resp.status}`);\nconst party = await resp.json();\nconsole.log(party.kyc_session_id, party.kyc_session_status);" } ], "responses": { "200": { - "description": "Preset catalogue.", + "description": "New KYC session created. Returns the updated party with its new child session.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KYBKeyPeopleParty" + }, + "example": { + "uuid": "0a3f1e2c-7c2f-4a1b-9d55-2f8d2a5d6b41", + "entity_type": "person", + "source": "USER", + "name": "Jane Smith", + "first_name": "Jane", + "last_name": "Smith", + "company_name": null, + "registration_number": null, + "email": "user@example.com", + "nationality": "GB", + "phone_number": null, + "date_of_birth": "1980-05-04", + "position": "Director", + "custom_fields": {}, + "is_active": true, + "is_skipped": false, + "requires_verification": true, + "roles": [ + { + "role": "director", + "ownership_percent": null, + "voting_percent": null + } + ], + "kyc_session_id": "55555555-6666-7777-8888-999999999999", + "kyc_session_status": "Not Started", + "kyc_session_url": "https://verify.didit.me/session/abc123def456", + "kyc_session_deleted": false, + "kyb_sub_session_id": null, + "kyb_sub_session_status": null, + "kyb_sub_session_url": null, + "kyb_sub_session_deleted": false, + "verification_workflow_type": "kyc", + "verification_workflow_id": "12121212-3434-5656-7878-909090909090", + "verification_workflow_label": "Key People KYC", + "verification_workflow_features": [ + { + "feature": "ID_VERIFICATION", + "status": "Not Finished" + }, + { + "feature": "LIVENESS", + "status": "Not Finished" + }, + { + "feature": "AML", + "status": "Not Finished" + } + ], + "didit_internal_id": null + } + } + } + }, + "401": { + "description": "The scoped API key has expired. This is the only case that returns `401`: a missing, invalid or revoked key returns `403`.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "results": { - "type": "array", - "items": { - "type": "object", - "properties": { - "preset_key": { - "type": "string", - "enum": [ - "screening_hit", - "aml_investigation", - "suspicious_activity", - "kyc" - ] - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "content_config": { - "$ref": "#/components/schemas/CaseContentConfig" - }, - "installed": { - "type": "boolean" - } - } - } + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "summary": "The key's expiry date has passed", + "value": { + "detail": "This API key has expired." } } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/presets/install/": { - "post": { - "summary": "Install blueprint presets", - "description": "Copy one or more presets into the application as editable blueprints. Idempotent per preset_key; omit preset_keys to install every preset not yet installed.", - "operationId": "install_case_blueprint_presets", - "tags": [ - "Case Blueprints" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - } - ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "preset_keys": { - "type": "array", - "items": { - "type": "string" - }, - "nullable": true, - "description": "Subset of screening_hit, aml_investigation, suspicious_activity, kyc. Omit to install all." + "403": { + "description": "Authentication or authorization failure. Missing, invalid and revoked keys return `403` (not `401`); so do a key of another application, a scoped key without the permission this endpoint needs, and a request from outside the key's IP allowlist. Distinguish them by `detail`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "No x-api-key header, or the key failed validation", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Other application": { + "summary": "A valid key that belongs to a different application than the session", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without decisions write": { + "summary": "Scoped key lacks decisions: write", + "value": { + "detail": "This API key has no write access to decisions." + } + }, + "IP not allowed": { + "summary": "Request from outside the key's IP allowlist", + "value": { + "detail": "This API key can't be used from this IP address." + } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/presets/install/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"preset_keys\": [\"aml_investigation\", \"kyc\"]}'" - } - ], - "responses": { - "201": { - "description": "One or more presets installed.", + }, + "404": { + "description": "The session does not exist, was deleted or is outside a scoped key's limits; no party with this UUID exists in your application; or the party belongs to another session. Each case has its own `detail`. The session lookup runs before the credential check, so an unknown `sessionId` returns `404` even without a valid key.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Unknown session": { + "summary": "No session with this ID exists, or it was deleted", + "value": { + "detail": "Not found." + } + }, + "Outside key limits": { + "summary": "The session is outside a scoped key's workflow or approved-only limits", + "value": { + "detail": "Session not found." + } + }, + "Unknown party": { + "summary": "No party with this UUID in your application", + "value": { + "detail": "No BusinessParty matches the given query." + } + }, + "Party of another session": { + "summary": "partyUuid exists but belongs to a different session", + "value": { + "detail": "Party does not belong to the given session." + } + } + } + } + } + }, + "409": { + "description": "The party's KYC session was not deleted (it still exists, the party never had one, or the party is a company), so there is nothing to replace. Branch on `code`.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "detail", + "code" + ], "properties": { - "created": { - "type": "array", - "items": { - "type": "string" - } + "detail": { + "type": "string", + "description": "Human-readable error message." + }, + "code": { + "type": "string", + "enum": [ + "kyb_party_kyc_session_not_deleted" + ], + "description": "Stable machine-readable error code." } } + }, + "example": { + "detail": "This party's KYC session has not been deleted, so it cannot be regenerated.", + "code": "kyb_party_kyc_session_not_deleted" } } } }, - "200": { - "description": "No new presets to install (all requested presets already exist).", + "429": { + "description": "Rate limit exceeded: 300 write (POST, PATCH, DELETE) requests per minute per API key. Wait `Retry-After` seconds before retrying; the `X-RateLimit-*` headers show the budget.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "created": { - "type": "array", - "items": { - "type": "string" - } + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Rate limited": { + "summary": "Budget for the current minute exhausted", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." } } } } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/report-templates/": { + "/v3/transactions/{transaction_id}/notes/": { "get": { - "summary": "List report templates", - "description": "List FIU report templates for the application.", - "operationId": "list_report_templates", + "summary": "List transaction notes", + "description": "List the notes on one monitored transaction, newest first (`created_at` descending): notes you add through the API, notes and status changes reviewers make in the Console, and notes Didit adds automatically (a rule's `add_note` action, or a problem creating the user-action verification session), which carry `actor_name` `Rule Engine`. `metadata` tells the note types apart.\n\nPaginated with `limit` and `offset`. `count` is exact up to 100 and capped at 100 above that, so follow `next` until it is `null` instead of computing pages from `count`. This is a read with no side effects.\n\nA scoped API key needs `transactions: read`; a key limited to some workflows or to approved sessions cannot call this endpoint. Reviewers see the same list under **Notes** on the [transaction detail](https://docs.didit.me/transaction-monitoring/console#transaction-detail).", + "operationId": "list_transaction_notes", + "x-didit-sdk": { + "resource": "transactions.notes", + "method": "list", + "auth": "api_key", + "server": true + }, "tags": [ - "Report Templates" + "Transactions" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "transaction_id", "in": "path", "required": true, + "description": "Didit transaction UUID: the `uuid` returned by `POST /v3/transactions/` and `GET /v3/transactions/`. Your own `txn_id` is not accepted.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." + "example": "abcdef12-3456-7890-abcd-ef1234567890" }, { "name": "limit", "in": "query", + "required": false, "schema": { "type": "integer", "default": 50, - "minimum": 1 + "minimum": 1, + "maximum": 1000000 }, - "description": "Page size." + "description": "Number of notes per page. Defaults to 50. A value that is not a positive integer falls back to 50; a value above 1000000 returns `400`." }, { "name": "offset", "in": "query", + "required": false, "schema": { "type": "integer", "default": 0, - "minimum": 0 + "minimum": 0, + "maximum": 1000000 }, - "description": "Zero-based offset." + "description": "Zero-based position of the first note to return. Prefer following `next`. A value that is not a whole number falls back to 0; a value above 1000000 returns `400`." } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/transactions/abcdef12-3456-7890-abcd-ef1234567890/notes/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\ntransaction_id = 'abcdef12-3456-7890-abcd-ef1234567890'\nurl = f'https://verification.didit.me/v3/transactions/{transaction_id}/notes/'\nparams = {'limit': 50}\nwhile url:\n resp = requests.get(\n url,\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params=params,\n timeout=10,\n )\n resp.raise_for_status()\n page = resp.json()\n for note in page['results']:\n print(note['created_at'], note['actor_name'], note['note'])\n url, params = page['next'], None" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const transactionId = 'abcdef12-3456-7890-abcd-ef1234567890';\nlet url = `https://verification.didit.me/v3/transactions/${transactionId}/notes/?limit=50`;\nwhile (url) {\n const resp = await fetch(url, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n });\n if (!resp.ok) throw new Error(`List notes failed: ${resp.status}`);\n const page = await resp.json();\n for (const note of page.results) console.log(note.created_at, note.actor_name, note.note);\n url = page.next;\n}" } ], "responses": { "200": { - "description": "Paginated templates.", + "description": "A page of notes, newest first.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], "properties": { "count": { "type": "integer", - "description": "Number of matching rows, capped at 100 for performance." + "description": "Number of notes on the transaction: exact up to 100, capped at 100 above that. Follow `next` instead of computing pages from it." }, "next": { "type": "string", - "nullable": true + "format": "uri", + "nullable": true, + "description": "Absolute URL of the next page, or `null` on the last page." }, "previous": { "type": "string", - "nullable": true + "format": "uri", + "nullable": true, + "description": "Absolute URL of the previous page, or `null` on the first page." }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/ReportTemplateDetail" + "$ref": "#/components/schemas/TransactionNote" } } } - } - } - } - } - } - }, - "post": { - "summary": "Create report template", - "description": "Create a reusable per-jurisdiction, per-report-type goAML header template. Rejected with 400 if any of the template-level obligatory fields (rentity_id, submission_code, currency_code_local) is missing from field_config; an invalid template cannot be saved or used to create a report. Per-filing header fields (action, submission_date, transactions) are enforced at report validate/finalize time instead.", - "operationId": "create_report_template", - "tags": [ - "Report Templates" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "name", - "jurisdiction", - "report_type" - ], - "properties": { - "name": { - "type": "string" - }, - "jurisdiction": { - "type": "string", - "minLength": 2, - "maxLength": 2, - "description": "ISO 3166-1 alpha-2 country code, upper-cased on save." - }, - "report_type": { - "type": "string", - "enum": [ - "SAR", - "STR", - "CTR", - "TTR", - "UTR", - "IFT", - "CBR", - "TFR" - ] + }, + "examples": { + "API, Console and rule notes": { + "value": { + "count": 3, + "next": null, + "previous": null, + "results": [ + { + "uuid": "c1111111-2222-4333-8444-555555555555", + "note": "Customer confirmed the withdrawal by phone.", + "actor_email": null, + "actor_name": null, + "metadata": { + "mentioned_emails": [ + "analyst@example.com" + ] + }, + "created_at": "2026-10-16T10:05:12.481203Z" + }, + { + "uuid": "c2222222-3333-4444-8555-666666666666", + "note": "Status changed from IN_REVIEW to APPROVED. Reason: Source of funds verified", + "actor_email": "reviewer@example.com", + "actor_name": "Alex Reviewer", + "metadata": { + "type": "status_change", + "previous_status": "IN_REVIEW", + "new_status": "APPROVED", + "reason_code": "SOF_VERIFIED", + "reason_label": "Source of funds verified" + }, + "created_at": "2026-10-15T17:22:40.103911Z" + }, + { + "uuid": "c3333333-4444-4555-8666-777777777777", + "note": "Large withdrawal to a new wallet: check the source of funds.", + "actor_email": null, + "actor_name": "Rule Engine", + "metadata": { + "rule_uuid": "d1111111-2222-4333-8444-555555555555", + "source": "rule_action" + }, + "created_at": "2026-10-15T08:42:12.004518Z" + } + ] + } }, - "field_config": { - "type": "object", - "description": "Reusable goAML header defaults. Template-level obligatory fields: rentity_id, submission_code, and currency_code_local (3-letter). Optional: enabled_fields (which optional report blocks to include) plus reporting_person, location, reason, and action defaults. Per-filing header fields (action, submission_date, transactions, jurisdiction) are enforced when the report is validated or finalized, not on the template." + "No notes yet": { + "value": { + "count": 0, + "next": null, + "previous": null, + "results": [] + } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"name\": \"US SAR default\", \"jurisdiction\": \"US\", \"report_type\": \"SAR\", \"field_config\": {\"rentity_id\": \"FIU-US-00123\", \"submission_code\": \"E\", \"currency_code_local\": \"USD\"}}'" }, - { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'name': 'US SAR default',\n 'jurisdiction': 'US',\n 'report_type': 'SAR',\n 'field_config': {'rentity_id': 'FIU-US-00123', 'submission_code': 'E', 'currency_code_local': 'USD'},\n },\n timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()['uuid'])" - } - ], - "responses": { - "201": { - "description": "Template created.", + "400": { + "description": "`limit` or `offset` is above 1000000. The body is a JSON array of messages, not an object.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportTemplateDetail" + "type": "array", + "items": { + "type": "string" + } + }, + "examples": { + "Limit too large": { + "value": [ + "limit must not exceed 1000000." + ] + }, + "Offset too large": { + "value": [ + "offset must not exceed 1000000." + ] + } } } } }, - "400": { - "description": "Unrecognized jurisdiction, or field_config fails obligatory-field validation.", + "401": { + "description": "The API key has expired. Only a key created with an expiry date can expire; after that date every request with it answers `401`.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "errors": { - "type": "array", - "items": { - "type": "string" - } + "detail": { + "type": "string" } } - } - } - } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/report-templates/{template_uuid}/": { - "get": { - "summary": "Get report template", - "description": "Retrieve a report template.", - "operationId": "get_report_template", - "tags": [ - "Report Templates" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "template_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "7ec00000-0000-4000-8000-000000000006" - }, - "description": "Report template UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/7ec00000-0000-4000-8000-000000000006/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Template detail.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportTemplateDetail" - } - } - } - } - } - }, - "patch": { - "summary": "Update report template", - "description": "Partially update a report template. Re-validates obligatory goAML fields on save.", - "operationId": "update_report_template", - "tags": [ - "Report Templates" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "template_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "7ec00000-0000-4000-8000-000000000006" - }, - "description": "Report template UUID." - } - ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "jurisdiction": { - "type": "string", - "minLength": 2, - "maxLength": 2, - "description": "ISO 3166-1 alpha-2 country code, upper-cased on save." - }, - "report_type": { - "type": "string", - "enum": [ - "SAR", - "STR", - "CTR", - "TTR", - "UTR", - "IFT", - "CBR", - "TFR" - ] - }, - "field_config": { - "type": "object", - "description": "Reusable goAML header defaults. Template-level obligatory fields: rentity_id, submission_code, and currency_code_local (3-letter). Optional: enabled_fields (which optional report blocks to include) plus reporting_person, location, reason, and action defaults. Per-filing header fields (action, submission_date, transactions, jurisdiction) are enforced when the report is validated or finalized, not on the template." + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/7ec00000-0000-4000-8000-000000000006/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"field_config\": {\"rentity_id\": \"FIU-US-00124\"}}'" - } - ], - "responses": { - "200": { - "description": "Updated template.", + }, + "403": { + "description": "The request is not allowed. A missing or invalid API key gets the generic message (never `401`).", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportTemplateDetail" - } - } - } - } - } - }, - "delete": { - "summary": "Delete report template", - "description": "Delete a report template. Reports already created from it are unaffected.", - "operationId": "delete_report_template", - "tags": [ - "Report Templates" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "template_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "7ec00000-0000-4000-8000-000000000006" - }, - "description": "Report template UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/7ec00000-0000-4000-8000-000000000006/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "204": { - "description": "Template deleted." - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/regulatory-reports/": { - "get": { - "summary": "List reports for case", - "description": "List FIU reports generated for the case.", - "operationId": "list_case_regulatory_reports", - "tags": [ - "Regulatory Reports" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without transactions read": { + "value": { + "detail": "This API key has no read access to transactions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "Outside the key's IP list": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "default": 50, - "minimum": 1 - }, - "description": "Page size." + "404": { + "description": "No transaction with this UUID exists in the application (never created, deleted, or owned by another application). A `transaction_id` that is not a UUID does not reach the API: it returns a `404` HTML page instead of this JSON body.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Not found": { + "value": { + "detail": "No Transaction matches the given query." + } + } + } + } + } }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "integer", - "default": 0, - "minimum": 0 - }, - "description": "Zero-based offset." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/regulatory-reports/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Paginated reports for the case.", + "429": { + "description": "More than 600 GET requests per minute with this API key. Wait the number of seconds in `Retry-After`, then retry.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "count": { - "type": "integer", - "description": "Number of matching rows, capped at 100 for performance." - }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { - "type": "string", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RegulatoryReportDetail" - } + "detail": { + "type": "string" + } + } + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." } } } } + }, + "headers": { + "Retry-After": { + "description": "Seconds until the current one-minute window resets.", + "schema": { + "type": "integer", + "example": 23 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed per minute in this budget.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Reset": { + "description": "Unix time when the window resets.", + "schema": { + "type": "integer" + } + } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] }, "post": { - "summary": "Create report from case", - "description": "Create a draft FIU report for the case: DRAFT status, auto-populated from the optional template plus the case's subject, linked transactions, and narrative. Validate then finalize to produce the goAML XML and PDF.", - "operationId": "create_case_regulatory_report", + "summary": "Create transaction note", + "description": "Add a plain-text note to a monitored transaction's review timeline, where Console reviewers see it. A note is context, not a decision: the transaction's `status`, `score` and alerts do not change, no webhook is sent, and nothing is billed. The transaction's `activities` gain a `TRANSACTION_NOTE_ADDED` entry.\n\nNotes created with an API key have no author: `actor_email` and `actor_name` are `null`, so put the author in the text if you need it. Every address in `mentioned_emails` must belong to a member of your organization, otherwise the whole request fails with `400`; mentions are stored lower-cased and de-duplicated, and this endpoint does not notify the mentioned members.\n\nEach call creates a new note, so retrying a timed-out request can add it twice: list the notes before retrying. There is no API endpoint to edit or remove a note. A scoped API key needs `transactions: write`; a key limited to some workflows or to approved sessions cannot call this endpoint.", + "operationId": "create_transaction_note", + "x-didit-sdk": { + "resource": "transactions.notes", + "method": "create", + "auth": "api_key", + "server": true + }, "tags": [ - "Regulatory Reports" + "Transactions" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "transaction_id", "in": "path", "required": true, + "description": "Didit transaction UUID: the `uuid` returned by `POST /v3/transactions/` and `GET /v3/transactions/`. Your own `txn_id` is not accepted.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." - }, - { - "name": "case_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "c1a5e000-0000-4000-8000-000000000001" - }, - "description": "Case UUID." + "example": "abcdef12-3456-7890-abcd-ef1234567890" } ], "requestBody": { - "required": false, + "required": true, "content": { "application/json": { "schema": { "type": "object", + "required": [ + "note" + ], "properties": { - "report_type": { - "type": "string", - "enum": [ - "SAR", - "STR", - "CTR", - "TTR", - "UTR", - "IFT", - "CBR", - "TFR" - ], - "description": "Required unless a template is given; the template's report_type is used otherwise." - }, - "jurisdiction": { - "type": "string", - "minLength": 2, - "maxLength": 2, - "description": "Required unless a template is given." - }, - "template": { + "note": { "type": "string", - "format": "uuid", - "nullable": true, - "description": "Report template UUID; its field_config seeds the draft before case-derived data is merged in." + "minLength": 1, + "description": "Plain-text note. Leading and trailing whitespace is trimmed and a blank note is rejected. No length limit." }, - "transactions": { + "mentioned_emails": { "type": "array", "items": { "type": "string", - "format": "uuid" + "format": "email" }, - "nullable": true, - "description": "Transactions to attach; defaults to the case's linked transactions." - }, - "rentity_id": { - "type": "string", - "nullable": true - }, - "rentity_branch": { - "type": "string", - "nullable": true - }, - "submission_code": { - "type": "string", - "enum": [ - "E", - "M" - ], - "default": "E" - }, - "entity_reference": { - "type": "string", - "nullable": true - }, - "fiu_ref_number": { - "type": "string", - "nullable": true - }, - "currency_code_local": { - "type": "string", - "minLength": 3, - "maxLength": 3, - "nullable": true - }, - "submission_date": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "reporting_person": { - "type": "object" - }, - "location": { - "type": "object" - }, - "reason": { - "type": "string", - "nullable": true - }, - "action": { - "type": "string", - "nullable": true - }, - "activity": { - "type": "string", - "nullable": true - }, - "narrative": { - "type": "object", - "properties": { - "who": { - "type": "string" - }, - "what": { - "type": "string" - }, - "when": { - "type": "string" - }, - "where": { - "type": "string" - }, - "why": { - "type": "string" - }, - "how": { - "type": "string" - } - } - }, - "report_indicators": { - "type": "array", - "items": { - "type": "string" - } - }, - "included_fields": { - "type": "array", - "items": { - "type": "string" - } + "default": [], + "description": "Emails of members of your organization to mention. Each must be a valid email of an organization member." + } + } + }, + "examples": { + "Note with a mention": { + "value": { + "note": "Customer confirmed the withdrawal by phone.", + "mentioned_emails": [ + "analyst@example.com" + ] + } + }, + "Plain note": { + "value": { + "note": "Source of funds document requested by email." } } } @@ -43349,22 +63630,173 @@ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/regulatory-reports/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"template\": \"7ec00000-0000-4000-8000-000000000006\"}'" + "source": "curl -X POST 'https://verification.didit.me/v3/transactions/abcdef12-3456-7890-abcd-ef1234567890/notes/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"note\": \"Customer confirmed the withdrawal by phone.\", \"mentioned_emails\": [\"analyst@example.com\"]}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\ntransaction_id = 'abcdef12-3456-7890-abcd-ef1234567890'\nresp = requests.post(\n f'https://verification.didit.me/v3/transactions/{transaction_id}/notes/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n 'note': 'Customer confirmed the withdrawal by phone.',\n 'mentioned_emails': ['analyst@example.com'],\n },\n timeout=10,\n)\nresp.raise_for_status()\nnote = resp.json()\nprint(note['uuid'], note['created_at'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const transactionId = 'abcdef12-3456-7890-abcd-ef1234567890';\nconst resp = await fetch(`https://verification.didit.me/v3/transactions/${transactionId}/notes/`, {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n note: 'Customer confirmed the withdrawal by phone.',\n mentioned_emails: ['analyst@example.com'],\n }),\n});\nif (!resp.ok) throw new Error(`Create note failed: ${resp.status}`);\nconst note = await resp.json();\nconsole.log(note.uuid, note.created_at);" } ], "responses": { "201": { - "description": "Draft report created.", + "description": "The note was created.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RegulatoryReportDetail" + "$ref": "#/components/schemas/TransactionNote" + }, + "examples": { + "Created": { + "value": { + "uuid": "c1111111-2222-4333-8444-555555555555", + "note": "Customer confirmed the withdrawal by phone.", + "actor_email": null, + "actor_name": null, + "metadata": { + "mentioned_emails": [ + "analyst@example.com" + ] + }, + "created_at": "2026-10-16T10:05:12.481203Z" + } + } } } } }, "400": { - "description": "The referenced template was not found.", + "description": "The body is invalid: `note` missing, null or blank, an invalid address in `mentioned_emails` (errors keyed by list position), or a mentioned address that is not a member of your organization. Nothing is created.", + "content": { + "application/json": { + "schema": { + "type": "object", + "description": "Field errors keyed by field name.", + "additionalProperties": {} + }, + "examples": { + "Missing note": { + "value": { + "note": [ + "This field is required." + ] + } + }, + "Blank note": { + "value": { + "note": [ + "This field may not be blank." + ] + } + }, + "Invalid email": { + "value": { + "mentioned_emails": { + "0": [ + "Enter a valid email address." + ] + } + } + }, + "Not an organization member": { + "value": { + "mentioned_emails": [ + "Mentioned users must belong to this organization and have access to this application." + ] + } + } + } + } + } + }, + "401": { + "description": "The API key has expired. Only a key created with an expiry date can expire; after that date every request with it answers `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The request is not allowed. A missing or invalid API key gets the generic message (never `401`).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without transactions write": { + "value": { + "detail": "This API key has no write access to transactions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "Outside the key's IP list": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } + }, + "404": { + "description": "No transaction with this UUID exists in the application (never created, deleted, or owned by another application). A `transaction_id` that is not a UUID does not reach the API: it returns a `404` HTML page instead of this JSON body.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Not found": { + "value": { + "detail": "No Transaction matches the given query." + } + } + } + } + } + }, + "429": { + "description": "More than 300 write requests per minute with this API key. Wait the number of seconds in `Retry-After`, then retry.", "content": { "application/json": { "schema": { @@ -43374,333 +63806,325 @@ "type": "string" } } + }, + "examples": { + "Throttled": { + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds until the current one-minute window resets.", + "schema": { + "type": "integer", + "example": 23 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed per minute in this budget.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Reset": { + "description": "Unix time when the window resets.", + "schema": { + "type": "integer" } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/": { + "/v3/transactions/{transaction_id}/related/": { "get": { - "summary": "List reports", - "description": "Application-wide FIU report list, optionally filtered by status, report_type, or case.", - "operationId": "list_regulatory_reports", + "summary": "List related transactions", + "description": "List the other transactions in your application that involve the same people or companies as this one. A transaction is related when any of its parties, in any role, has the same `vendor_data` as one of this transaction's parties. The transaction itself is never returned, and parties without `vendor_data` are ignored: when none of this transaction's parties has one, the result is always empty.\n\nResults use the summary shape of `GET /v3/transactions/`, newest first (`txn_date`, then `created_at`). Paginated with `limit` and `offset`; `count` is exact up to 100 and capped at 100 above that, so follow `next` until it is `null`.\n\nA scoped API key needs `transactions: read`; a key limited to some workflows or to approved sessions cannot call this endpoint. Send the same `vendor_data` for the same person or company on every [transaction](https://docs.didit.me/transaction-monitoring/transactions#subject-and-counterparty) to make this list complete.", + "operationId": "list_related_transactions", + "x-didit-sdk": { + "resource": "transactions", + "method": "list_related", + "auth": "api_key", + "server": true + }, "tags": [ - "Regulatory Reports" + "Transactions" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "transaction_id", "in": "path", "required": true, + "description": "Didit transaction UUID: the `uuid` returned by `POST /v3/transactions/` and `GET /v3/transactions/`. Your own `txn_id` is not accepted.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." + "example": "abcdef12-3456-7890-abcd-ef1234567890" }, { "name": "limit", "in": "query", + "required": false, "schema": { "type": "integer", "default": 50, - "minimum": 1 + "minimum": 1, + "maximum": 1000000 }, - "description": "Page size." + "description": "Number of transactions per page. Defaults to 50. A value that is not a positive integer falls back to 50; a value above 1000000 returns `400`." }, { "name": "offset", "in": "query", + "required": false, "schema": { "type": "integer", - "default": 0, - "minimum": 0 - }, - "description": "Zero-based offset." - }, - { - "name": "status", - "in": "query", - "schema": { - "type": "string", - "enum": [ - "DRAFT", - "VALIDATED", - "FINALIZED" - ] - }, - "description": "Filter by status." - }, - { - "name": "report_type", - "in": "query", - "schema": { - "type": "string", - "enum": [ - "SAR", - "STR", - "CTR", - "TTR", - "UTR", - "IFT", - "CBR", - "TFR" - ] - }, - "description": "Filter by report type." - }, - { - "name": "case", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" + "default": 0, + "minimum": 0, + "maximum": 1000000 }, - "description": "Filter to reports on this case." + "description": "Zero-based position of the first transaction to return. Prefer following `next`. A value that is not a whole number falls back to 0; a value above 1000000 returns `400`." } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/transactions/abcdef12-3456-7890-abcd-ef1234567890/related/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\ntransaction_id = 'abcdef12-3456-7890-abcd-ef1234567890'\nresp = requests.get(\n f'https://verification.didit.me/v3/transactions/{transaction_id}/related/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'limit': 50},\n timeout=10,\n)\nresp.raise_for_status()\nfor tx in resp.json()['results']:\n print(tx['uuid'], tx['txn_date'], tx['status'], tx['amount'], tx['currency'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const transactionId = 'abcdef12-3456-7890-abcd-ef1234567890';\nconst resp = await fetch(\n `https://verification.didit.me/v3/transactions/${transactionId}/related/?limit=50`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!resp.ok) throw new Error(`Related lookup failed: ${resp.status}`);\nconst page = await resp.json();\nfor (const tx of page.results) console.log(tx.uuid, tx.status, tx.amount, tx.currency);" } ], "responses": { "200": { - "description": "Paginated reports across the application.", + "description": "A page of related transactions, newest first.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], "properties": { "count": { "type": "integer", - "description": "Number of matching rows, capped at 100 for performance." + "description": "Number of related transactions: exact up to 100, capped at 100 above that. Follow `next` instead of computing pages from it." }, "next": { "type": "string", - "nullable": true + "format": "uri", + "nullable": true, + "description": "Absolute URL of the next page, or `null` on the last page." }, "previous": { "type": "string", - "nullable": true + "format": "uri", + "nullable": true, + "description": "Absolute URL of the previous page, or `null` on the first page." }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/RegulatoryReportDetail" + "$ref": "#/components/schemas/TransactionSummary" } } } + }, + "examples": { + "One related transaction": { + "value": { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "uuid": "abcdef99-3456-4890-abcd-ef1234567890", + "transaction_number": 4087, + "txn_id": "your-txn-2026-10-02-017", + "transaction_type": "finance", + "action_type": "deposit", + "status": "APPROVED", + "source": "LIVE", + "direction": "INBOUND", + "amount": "250", + "currency": "EUR", + "currency_type": "fiat", + "preferred_currency_amount": "270.08", + "preferred_currency_code": "USD", + "payment_details": "Card top-up", + "txn_date": "2026-10-02T14:10:00Z", + "created_at": "2026-10-02T14:10:03.512244Z", + "score": 12, + "severity": "LOW", + "decision_reason_code": null, + "decision_reason_label": null, + "tags": [], + "subject_name": "Maria Garcia", + "subject_vendor_data": "user-12345", + "subject_type": "user", + "subject_uuid": "e1111111-2222-4333-8444-555555555555", + "counterparty_name": null, + "counterparty_vendor_data": null, + "counterparty_country": null, + "subject_country": "ESP", + "assigned_to_name": null, + "travel_rule_status": null, + "application_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "application_name": "Production app", + "organization_id": "11111111-2222-3333-4444-555555555555", + "organization_name": "Example Org" + } + ] + } + }, + "No party has vendor_data": { + "value": { + "count": 0, + "next": null, + "previous": null, + "results": [] + } + } } } } - } - } - }, - "post": { - "summary": "Create standalone report", - "description": "Create a draft FIU report from the standalone Reports page: both case and template are required (unlike the case-scoped create, which can omit template).", - "operationId": "create_standalone_regulatory_report", - "tags": [ - "Regulatory Reports" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "case", - "template" - ], - "properties": { - "case": { - "type": "string", - "format": "uuid" - }, - "template": { - "type": "string", - "format": "uuid" - }, - "report_type": { - "type": "string", - "enum": [ - "SAR", - "STR", - "CTR", - "TTR", - "UTR", - "IFT", - "CBR", - "TFR" - ], - "description": "Required unless a template is given; the template's report_type is used otherwise." - }, - "jurisdiction": { - "type": "string", - "minLength": 2, - "maxLength": 2, - "description": "Required unless a template is given." - }, - "transactions": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "nullable": true, - "description": "Transactions to attach; defaults to the case's linked transactions." - }, - "rentity_id": { - "type": "string", - "nullable": true - }, - "rentity_branch": { - "type": "string", - "nullable": true - }, - "submission_code": { - "type": "string", - "enum": [ - "E", - "M" - ], - "default": "E" - }, - "entity_reference": { - "type": "string", - "nullable": true - }, - "fiu_ref_number": { - "type": "string", - "nullable": true - }, - "currency_code_local": { - "type": "string", - "minLength": 3, - "maxLength": 3, - "nullable": true - }, - "submission_date": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "reporting_person": { - "type": "object" - }, - "location": { - "type": "object" - }, - "reason": { - "type": "string", - "nullable": true - }, - "action": { - "type": "string", - "nullable": true + "400": { + "description": "`limit` or `offset` is above 1000000. The body is a JSON array of messages, not an object.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "examples": { + "Limit too large": { + "value": [ + "limit must not exceed 1000000." + ] }, - "activity": { - "type": "string", - "nullable": true + "Offset too large": { + "value": [ + "offset must not exceed 1000000." + ] + } + } + } + } + }, + "401": { + "description": "The API key has expired. Only a key created with an expiry date can expire; after that date every request with it answers `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The request is not allowed. A missing or invalid API key gets the generic message (never `401`).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "You do not have permission to perform this action." + } }, - "narrative": { - "type": "object", - "properties": { - "who": { - "type": "string" - }, - "what": { - "type": "string" - }, - "when": { - "type": "string" - }, - "where": { - "type": "string" - }, - "why": { - "type": "string" - }, - "how": { - "type": "string" - } + "Scoped key without transactions read": { + "value": { + "detail": "This API key has no read access to transactions." } }, - "report_indicators": { - "type": "array", - "items": { - "type": "string" + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." } }, - "included_fields": { - "type": "array", - "items": { - "type": "string" + "Outside the key's IP list": { + "value": { + "detail": "This API key can't be used from this IP address." } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"case\": \"c1a5e000-0000-4000-8000-000000000001\", \"template\": \"7ec00000-0000-4000-8000-000000000006\"}'" - } - ], - "responses": { - "201": { - "description": "Draft report created.", + }, + "404": { + "description": "No transaction with this UUID exists in the application (never created, deleted, or owned by another application). A `transaction_id` that is not a UUID does not reach the API: it returns a `404` HTML page instead of this JSON body.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RegulatoryReportDetail" + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Not found": { + "value": { + "detail": "No Transaction matches the given query." + } + } } } } }, - "400": { - "description": "The referenced template was not found.", + "429": { + "description": "More than 600 GET requests per minute with this API key. Wait the number of seconds in `Retry-After`, then retry.", "content": { "application/json": { "schema": { @@ -43710,20 +64134,66 @@ "type": "string" } } + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds until the current one-minute window resets.", + "schema": { + "type": "integer", + "example": 23 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed per minute in this budget.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Reset": { + "description": "Unix time when the window resets.", + "schema": { + "type": "integer" } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/": { + "/v3/organization/{organization_id}/application/{application_id}/businesses/{business_session_id}/pdf/": { "get": { - "summary": "Get report", - "description": "Retrieve a regulatory report.", - "operationId": "get_regulatory_report", + "summary": "Download a business session PDF report", + "description": "Render the PDF report of one Business Verification (KYB) session: company registry data, key people and beneficial owners, documents, AML results and the other workflow features, plus the session's tags and review activity. With white-label customization on, the report carries your logo and privacy policy URL. The body is the PDF itself (`application/pdf`), not JSON.\n\nThe session must be `Approved`, `Declined` or `In Review`; any other status returns `403`, so wait for the `status.updated` [webhook](https://docs.didit.me/integration/webhooks) first. The report is rendered on every call and nothing is billed, so store the file instead of fetching it repeatedly. `GET /v3/session/{sessionId}/generate-pdf/` renders the same kind of report by session ID alone.\n\nA PDF report counts as media: a scoped API key needs `businesses: read` and `media: read`, and a key limited to some workflows or to approved sessions cannot call it.", + "operationId": "get_business_session_pdf", + "x-didit-sdk": { + "resource": "businesses", + "method": "generate_session_pdf", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Regulatory Reports" + "Businesses" ], "parameters": [ { @@ -43734,7 +64204,8 @@ "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "description": "Organization UUID.", + "example": "11111111-2222-3333-4444-555555555555" }, { "name": "application_id", @@ -43744,205 +64215,157 @@ "type": "string", "format": "uuid" }, - "description": "Application UUID." + "description": "Application UUID. Must be the application the API key belongs to: a key used on any other application gets `403`.", + "example": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" }, { - "name": "report_uuid", + "name": "business_session_id", "in": "path", "required": true, + "description": "`session_id` of the Business Verification (KYB) session, as returned when the session was created. A session of another application returns `404`.", "schema": { "type": "string", - "format": "uuid", - "example": "5ec00000-0000-4000-8000-000000000007" + "format": "uuid" }, - "description": "Report UUID." + "example": "22222222-3333-4444-5555-666666666666" } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl --fail -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/businesses/22222222-3333-4444-5555-666666666666/pdf/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n --output business_report.pdf" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\norg_id = '11111111-2222-3333-4444-555555555555'\napp_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'\nbusiness_session_id = '22222222-3333-4444-5555-666666666666'\nresp = requests.get(\n f'https://verification.didit.me/v3/organization/{org_id}/application/{app_id}'\n f'/businesses/{business_session_id}/pdf/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=60,\n)\nresp.raise_for_status()\nwith open(f'business_{business_session_id}.pdf', 'wb') as fh:\n fh.write(resp.content)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import { writeFile } from 'node:fs/promises';\n\nconst orgId = '11111111-2222-3333-4444-555555555555';\nconst appId = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';\nconst businessSessionId = '22222222-3333-4444-5555-666666666666';\nconst resp = await fetch(\n `https://verification.didit.me/v3/organization/${orgId}/application/${appId}/businesses/${businessSessionId}/pdf/`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!resp.ok) throw new Error(`PDF generation failed: ${resp.status}`);\nawait writeFile(`business_${businessSessionId}.pdf`, Buffer.from(await resp.arrayBuffer()));" } ], "responses": { "200": { - "description": "Report detail.", + "description": "The rendered PDF, returned directly as binary `application/pdf`.", + "headers": { + "Content-Disposition": { + "description": "Always `attachment; filename=business_{business_session_id}.pdf`.", + "schema": { + "type": "string", + "example": "attachment; filename=business_22222222-3333-4444-5555-666666666666.pdf" + } + } + }, "content": { - "application/json": { + "application/pdf": { "schema": { - "$ref": "#/components/schemas/RegulatoryReportDetail" + "type": "string", + "format": "binary" } } } - } - } - }, - "patch": { - "summary": "Update report", - "description": "Edit a draft or validated report's fields. Finalized reports cannot be edited (409).", - "operationId": "update_regulatory_report", - "tags": [ - "Regulatory Reports" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "401": { + "description": "The API key has expired. Only a key created with an expiry date can expire; after that date every request with it answers `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } }, - { - "name": "report_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "5ec00000-0000-4000-8000-000000000007" - }, - "description": "Report UUID." - } - ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "jurisdiction": { - "type": "string", - "minLength": 2, - "maxLength": 2 - }, - "rentity_id": { - "type": "string", - "nullable": true - }, - "rentity_branch": { - "type": "string", - "nullable": true - }, - "submission_code": { - "type": "string", - "enum": [ - "E", - "M" - ], - "default": "E" - }, - "entity_reference": { - "type": "string", - "nullable": true - }, - "fiu_ref_number": { - "type": "string", - "nullable": true - }, - "currency_code_local": { - "type": "string", - "minLength": 3, - "maxLength": 3, - "nullable": true - }, - "submission_date": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "reporting_person": { - "type": "object" - }, - "location": { - "type": "object" - }, - "reason": { - "type": "string", - "nullable": true + "403": { + "description": "The request is not allowed or the session is not finished. Read `detail`: a session that is not `Approved`, `Declined` or `In Review`, a missing or invalid API key (never `401`), an `application_id` that is not the key's application, a scoped key without `media: read` or `businesses: read`, a key limited to workflows or approved sessions, or a request from outside the key's IP list.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Session not finished": { + "value": { + "detail": "You can only generate a PDF for sessions in review, declined or approved." + } }, - "action": { - "type": "string", - "nullable": true + "Missing or invalid key": { + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } }, - "activity": { - "type": "string", - "nullable": true + "Another application": { + "value": { + "detail": "You do not have permission to perform this action." + } }, - "narrative": { - "type": "object", - "properties": { - "who": { - "type": "string" - }, - "what": { - "type": "string" - }, - "when": { - "type": "string" - }, - "where": { - "type": "string" - }, - "why": { - "type": "string" - }, - "how": { - "type": "string" - } + "Scoped key without media read": { + "value": { + "detail": "This API key has no read access to media." } }, - "report_indicators": { - "type": "array", - "items": { - "type": "string" + "Scoped key without businesses read": { + "value": { + "detail": "This API key has no read access to companies." } }, - "included_fields": { - "type": "array", - "items": { - "type": "string" + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "Outside the key's IP list": { + "value": { + "detail": "This API key can't be used from this IP address." } } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"rentity_id\": \"FIU-US-00123\", \"currency_code_local\": \"USD\"}'" - } - ], - "responses": { - "200": { - "description": "Updated report.", + }, + "404": { + "description": "No business session with this ID exists in the application (never created, deleted, or owned by another application). A value that is not a UUID returns a `404` HTML page instead of this JSON body.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RegulatoryReportDetail" + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Not found": { + "value": { + "detail": "Business session not found." + } + } } } } }, - "409": { - "description": "Report is already finalized.", + "429": { + "description": "More than 600 GET requests per minute with this API key. Wait the number of seconds in `Retry-After`, then retry.", "content": { "application/json": { "schema": { @@ -43952,18 +64375,66 @@ "type": "string" } } + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds until the current one-minute window resets.", + "schema": { + "type": "integer", + "example": 23 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed per minute in this budget.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Reset": { + "description": "Unix time when the window resets.", + "schema": { + "type": "integer" } } } } - } - }, - "delete": { - "summary": "Delete report", - "description": "Delete a draft or validated report. Finalized reports cannot be deleted (409).", - "operationId": "delete_regulatory_report", + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/pdf/": { + "get": { + "summary": "Download a transaction PDF report", + "description": "Render one monitored transaction as a PDF report: overview and verdict, parties and payment methods, custom properties, alerts and rule runs, screening and Travel Rule results, the network summary, the cost breakdown, up to 50 related transactions and the 20 most recent activity entries. The body is the PDF itself (`application/pdf`), not JSON.\n\nThe report is rendered from the transaction's current data on every call, so request it again after a status change. Nothing is stored and nothing is billed. Rendering can take a few seconds, so use a generous client timeout.\n\nThis endpoint lives on an application-scoped path: put your organization ID and the ID of the application your API key belongs to in the URL ([find your organization ID](https://docs.didit.me/getting-started/api-authentication#find-your-organization-id)). A PDF report counts as media: a scoped API key needs `media: read` and `transactions: read`, and a key limited to some workflows or to approved sessions cannot call it.", + "operationId": "get_transaction_pdf", + "x-didit-sdk": { + "resource": "transactions", + "method": "generate_pdf", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Regulatory Reports" + "Transactions" ], "parameters": [ { @@ -43974,7 +64445,8 @@ "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "description": "Organization UUID.", + "example": "11111111-2222-3333-4444-555555555555" }, { "name": "application_id", @@ -43984,33 +64456,152 @@ "type": "string", "format": "uuid" }, - "description": "Application UUID." + "description": "Application UUID. Must be the application the API key belongs to: a key used on any other application gets `403`.", + "example": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" }, { - "name": "report_uuid", + "name": "transaction_uuid", "in": "path", "required": true, + "description": "Didit transaction UUID: the `uuid` returned by `POST /v3/transactions/` and `GET /v3/transactions/`. Your own `txn_id` is not accepted.", "schema": { "type": "string", - "format": "uuid", - "example": "5ec00000-0000-4000-8000-000000000007" + "format": "uuid" }, - "description": "Report UUID." + "example": "abcdef12-3456-7890-abcd-ef1234567890" } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl --fail -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/transactions/abcdef12-3456-7890-abcd-ef1234567890/pdf/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n --output transaction_report.pdf" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\norg_id = '11111111-2222-3333-4444-555555555555'\napp_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'\ntransaction_uuid = 'abcdef12-3456-7890-abcd-ef1234567890'\nresp = requests.get(\n f'https://verification.didit.me/v3/organization/{org_id}/application/{app_id}'\n f'/transactions/{transaction_uuid}/pdf/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=60,\n)\nresp.raise_for_status()\nwith open(f'transaction_{transaction_uuid}.pdf', 'wb') as fh:\n fh.write(resp.content)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import { writeFile } from 'node:fs/promises';\n\nconst orgId = '11111111-2222-3333-4444-555555555555';\nconst appId = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';\nconst transactionUuid = 'abcdef12-3456-7890-abcd-ef1234567890';\nconst resp = await fetch(\n `https://verification.didit.me/v3/organization/${orgId}/application/${appId}/transactions/${transactionUuid}/pdf/`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!resp.ok) throw new Error(`PDF generation failed: ${resp.status}`);\nawait writeFile(`transaction_${transactionUuid}.pdf`, Buffer.from(await resp.arrayBuffer()));" } ], "responses": { - "204": { - "description": "Report deleted." + "200": { + "description": "The rendered PDF, returned directly as binary `application/pdf`.", + "headers": { + "Content-Disposition": { + "description": "Always `attachment; filename=transaction_{transaction_uuid}.pdf`.", + "schema": { + "type": "string", + "example": "attachment; filename=transaction_abcdef12-3456-7890-abcd-ef1234567890.pdf" + } + } + }, + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } }, - "409": { - "description": "Report is already finalized.", + "401": { + "description": "The API key has expired. Only a key created with an expiry date can expire; after that date every request with it answers `401`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "The request is not allowed. Read `detail`: a missing or invalid API key (never `401`), an `application_id` that is not the key's application, a scoped key without `media: read` or `transactions: read`, a key limited to workflows or approved sessions, or a request from outside the key's IP list.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Another application": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without media read": { + "value": { + "detail": "This API key has no read access to media." + } + }, + "Scoped key without transactions read": { + "value": { + "detail": "This API key has no read access to transactions." + } + }, + "Key limited to workflows or approved sessions": { + "value": { + "detail": "This API key only reaches sessions inside its workflow or status limits, so it can't use this endpoint." + } + }, + "Outside the key's IP list": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } + }, + "404": { + "description": "No transaction with this UUID exists in the application (never created, deleted, or owned by another application). A `transaction_uuid` that is not a UUID does not reach the API: it returns a `404` HTML page instead of this JSON body.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Not found": { + "value": { + "detail": "No Transaction matches the given query." + } + } + } + } + } + }, + "429": { + "description": "More than 600 GET requests per minute with this API key. Wait the number of seconds in `Retry-After`, then retry.", "content": { "application/json": { "schema": { @@ -44020,20 +64611,66 @@ "type": "string" } } + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds until the current one-minute window resets.", + "schema": { + "type": "integer", + "example": 23 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed per minute in this budget.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Reset": { + "description": "Unix time when the window resets.", + "schema": { + "type": "integer" } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/validate/": { - "post": { - "summary": "Validate report", - "description": "Check the report against the obligatory goAML header fields (reporting entity ID, submission code, local currency, action taken, submission date), plus at least one attached transaction and a 2-letter jurisdiction, without generating any files. Also stores the result on the report: validation_errors and validated_at are updated, and a non-finalized report's status moves to VALIDATED (no errors) or back to DRAFT (errors).", - "operationId": "validate_regulatory_report", + "/v3/organization/{organization_id}/application/{application_id}/analytics/": { + "get": { + "summary": "Get application analytics", + "description": "Read the numbers behind the Business Console [analytics dashboard](https://docs.didit.me/console/analytics) for a date range. Without `scope` and `widget_keys` the response is the user verification (KYC) summary (`ApplicationAnalyticsSummary`): daily session counts, status totals, conversion rate, the same figures for the preceding period of equal length, locations, demographics, devices, workflow funnels, active verification time, resubmissions, warnings and time series. With `scope` or `widget_keys` it is the widgets of one dashboard tab (`ApplicationAnalyticsWidgetResponse`): `kyc`, `kyb`, `transactions` or `home`.\n\nSession counts are by session creation date (UTC) and reflect each session's current status. They come from daily rollups refreshed in the background, so the newest activity can take a short while to appear. Invalid workflow IDs and versions are ignored rather than rejected. Nothing is billed.\n\nOnly a full-access API key can call this endpoint: every scoped key gets `403`, whatever access it holds, and so does a key of another application. See [API keys](https://docs.didit.me/console/api-keys).", + "operationId": "get_application_analytics", + "x-didit-sdk": { + "resource": "analytics", + "method": "retrieve", + "auth": "api_key", + "server": true, + "scope": "application" + }, "tags": [ - "Regulatory Reports" + "Analytics" ], "parameters": [ { @@ -44044,7 +64681,8 @@ "type": "string", "format": "uuid" }, - "description": "Organization UUID." + "description": "Organization UUID.", + "example": "11111111-2222-3333-4444-555555555555" }, { "name": "application_id", @@ -44054,135 +64692,639 @@ "type": "string", "format": "uuid" }, - "description": "Application UUID." + "description": "Application UUID. Must be the application the API key belongs to: a key used on any other application gets `403`.", + "example": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" }, { - "name": "report_uuid", - "in": "path", - "required": true, + "name": "start_date", + "in": "query", + "required": false, "schema": { "type": "string", - "format": "uuid", - "example": "5ec00000-0000-4000-8000-000000000007" + "format": "date", + "example": "2026-10-01" }, - "description": "Report UUID." + "description": "First day of the range, `YYYY-MM-DD`, inclusive (UTC). Send it together with `end_date`: when either is missing, both are ignored and the range runs from 30 days before today through today." + }, + { + "name": "end_date", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date", + "example": "2026-10-15" + }, + "description": "Last day of the range, `YYYY-MM-DD`, inclusive (UTC). A future date counts as today. The range may not exceed 730 days." + }, + { + "name": "scope", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "home", + "kyc", + "kyb", + "transactions" + ] + }, + "description": "Dashboard tab to return widgets for (case-insensitive). Omit it, together with `widget_keys`, for the KYC summary. Sending only `widget_keys` implies `kyc`. `home` accepts any KYC, KYB or transaction widget and defaults to the application's saved home layout." + }, + { + "name": "widget_keys", + "in": "query", + "required": false, + "schema": { + "type": "string", + "example": "tx_count_total,tx_count_daily" + }, + "description": "Comma-separated widget keys, answered in this order. Keys the scope does not accept are dropped; when none is left you get the scope's defaults. KYC: `kyc_sessions_volume`, `kyc_warning_insights`, `kyc_id_locations`, `kyc_ip_locations`, `kyc_workflow_funnel`, `kyc_average_verification_time`, `kyc_conversion_rate`, `kyc_conversion_by_country`, `kyc_resubmission_insights`, `kyc_original_status_distribution`, `kyc_database_validation_coverage`, `kyc_database_validation_latency`, `kyc_demographics`, `kyc_device_details`. KYB: `kyb_sessions_volume`, `kyb_workflow_funnel`, `kyb_average_verification_time`, `kyb_ip_locations`, `kyb_business_country_distribution`, `kyb_warning_insights`, `kyb_conversion_rate`, `kyb_conversion_by_country`, `kyb_industry_breakdown`, `kyb_resubmission_insights`, `kyb_original_status_distribution`, `kyb_registry_coverage`, `kyb_registry_data_completeness`, `kyb_device_details`. Transactions: `tx_count_total`, `tx_count_daily`, `tx_volume_total_preferred_currency`, `tx_volume_daily_preferred_currency`, `tx_average_amount`, `tx_average_amount_daily`, `tx_transaction_category_breakdown`, `tx_action_type_breakdown`, `tx_status_volume_trend`, `tx_user_ip_or_country_distribution`, `tx_tag_breakdown`, `tx_top_rules_triggered`, `tx_team_member_performance`, `tx_score_histogram`." + }, + { + "name": "workflow_ids", + "in": "query", + "required": false, + "schema": { + "type": "string", + "example": "33333333-4444-5555-6666-777777777777" + }, + "description": "Comma-separated workflow IDs. Limits the KYC summary and the KYC and KYB widgets to those workflows. Values that are not UUIDs are ignored; when none is valid the filter is dropped. The summary also accepts `workflow_id`." + }, + { + "name": "versions", + "in": "query", + "required": false, + "schema": { + "type": "string", + "example": "3,4" + }, + "description": "Comma-separated workflow version numbers. Applies only together with `workflow_ids`; values that are not whole numbers are ignored. `version` is accepted as an alias." + }, + { + "name": "branch_path_ids", + "in": "query", + "required": false, + "schema": { + "type": "string", + "example": "default" + }, + "description": "Comma-separated branch path IDs. Applies only together with `workflow_ids`, and only to KYC figures. Figures are currently recorded only for `default` (all paths combined), so any other value returns empty figures. `branch_path_id` is accepted as an alias." + }, + { + "name": "tag", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Transaction widgets only: comma-separated tag UUIDs or tag names." + }, + { + "name": "transaction_type", + "in": "query", + "required": false, + "schema": { + "type": "string", + "example": "finance" + }, + "description": "Transaction widgets only: comma-separated transaction categories." + }, + { + "name": "action_type", + "in": "query", + "required": false, + "schema": { + "type": "string", + "example": "withdrawal" + }, + "description": "Transaction widgets only: comma-separated action types (case-insensitive)." } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/validate/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/analytics/?start_date=2026-10-01&end_date=2026-10-15' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\norg_id = '11111111-2222-3333-4444-555555555555'\napp_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'\nresp = requests.get(\n f'https://verification.didit.me/v3/organization/{org_id}/application/{app_id}/analytics/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'start_date': '2026-10-01', 'end_date': '2026-10-15'},\n timeout=10,\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(data['request_breakdown']['total_approved'], data['conversion_rate'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const orgId = '11111111-2222-3333-4444-555555555555';\nconst appId = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';\nconst params = new URLSearchParams({ start_date: '2026-10-01', end_date: '2026-10-15' });\nconst resp = await fetch(\n `https://verification.didit.me/v3/organization/${orgId}/application/${appId}/analytics/?${params}`,\n { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!resp.ok) throw new Error(`Analytics failed: ${resp.status}`);\nconst data = await resp.json();\nconsole.log(data.request_breakdown.total_approved, data.conversion_rate);" } ], "responses": { "200": { - "description": "Validation result.", + "description": "The KYC summary, or the requested widgets when `scope` or `widget_keys` is sent.", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ApplicationAnalyticsSummary" + }, + { + "$ref": "#/components/schemas/ApplicationAnalyticsWidgetResponse" + } + ] + }, + "examples": { + "KYC summary": { + "value": { + "grouping_mode": "day", + "data_points": [ + { + "rounded_date": "2026-10-14", + "total": 12, + "approved": 8, + "declined": 1, + "canceled": 1, + "pending": 2, + "in_review": 1, + "expired": 1, + "abandoned": 0, + "in_progress": 1, + "not_started": 0, + "manually_approved": 1, + "manually_declined": 0 + }, + { + "rounded_date": "2026-10-15", + "total": 10, + "approved": 6, + "declined": 2, + "canceled": 0, + "pending": 2, + "in_review": 2, + "expired": 0, + "abandoned": 0, + "in_progress": 0, + "not_started": 0, + "manually_approved": 0, + "manually_declined": 1 + } + ], + "request_breakdown": { + "total_approved": 14, + "total_in_review": 3, + "total_declined": 3, + "total_canceled": 1, + "total_pending": 4, + "total_expired": 1, + "total_abandoned": 0, + "total_in_progress": 1, + "total_not_started": 0, + "total_manually_approved": 1, + "total_manually_declined": 1 + }, + "conversion_rate": 70.0, + "location_stats": { + "countries": [ + { + "session_count": 15, + "country": "Spain", + "country_code": "ES" + }, + { + "session_count": 7, + "country": "Portugal", + "country_code": "PT" + } + ], + "ip_countries": [ + { + "session_count": 15, + "country": "Spain", + "country_code": "ES" + }, + { + "session_count": 7, + "country": "Portugal", + "country_code": "PT" + } + ], + "id_countries": [ + { + "country": "Spain", + "country_code": "ES", + "session_count": 14 + }, + { + "country": "Portugal", + "country_code": "PT", + "session_count": 6 + } + ], + "conversion_by_country": [ + { + "country": "Spain", + "country_code": "ES", + "finished": 13, + "approved": 10 + }, + { + "country": "Portugal", + "country_code": "PT", + "finished": 6, + "approved": 4 + } + ], + "cities": [ + { + "session_count": 9, + "location": "Madrid, Spain", + "latitude": 40.4168, + "longitude": -3.7038 + } + ] + }, + "demographic_stats": { + "age_distribution": { + "under_18": 0, + "18_24": 5, + "25_34": 8, + "35_44": 4, + "45_64": 2, + "65_plus": 1 + }, + "gender_distribution": { + "M": 11, + "F": 9 + } + }, + "device_details": { + "device_models": [ + { + "device": "Apple", + "count": 12 + }, + { + "device": "Android", + "count": 10 + } + ], + "browsers": [ + { + "browser": "Safari", + "count": 12 + }, + { + "browser": "Chrome", + "count": 10 + } + ], + "operating_systems": [ + { + "os": "iOS", + "count": 12 + }, + { + "os": "Android", + "count": 10 + } + ] + }, + "workflow_funnel_stats": { + "33333333-4444-5555-6666-777777777777": { + "total": 22, + "steps": { + "OCR": { + "reached": 21 + }, + "LIVENESS": { + "reached": 19 + }, + "FACE_MATCH": { + "reached": 19 + }, + "AML": { + "reached": 18 + } + } + } + }, + "verification_time_stats": { + "overall_avg_seconds": 64.0, + "overall_count": 18, + "per_feature": { + "OCR": { + "avg_seconds": 41.2, + "total_seconds": 741.6, + "count": 18 + }, + "LIVENESS": { + "avg_seconds": 22.8, + "total_seconds": 410.4, + "count": 18 + } + }, + "per_sub_step": { + "OCR": { + "front_id_photo": { + "avg_seconds": 18.3, + "total_seconds": 329.4, + "count": 18 + } + } + } + }, + "resubmission_stats": { + "first_pass": 17, + "second": 2, + "third_plus": 1, + "abandoned": 0 + }, + "warning_stats": { + "ID_VERIFICATION": { + "total": 21, + "risks": { + "DOCUMENT_EXPIRED": { + "error": 1, + "warning": 0, + "information": 0 + } + } + }, + "LIVENESS": { + "total": 19, + "risks": { + "LOW_LIVENESS_SCORE": { + "error": 0, + "warning": 2, + "information": 0 + } + } + } + }, + "original_status_stats": { + "approved": 13, + "in_review": 5, + "declined": 2 + }, + "demographic_time_series": [ + { + "date": "2026-10-14", + "age_distribution": { + "under_18": 0, + "18_24": 3, + "25_34": 4, + "35_44": 2, + "45_64": 1, + "65_plus": 0 + }, + "gender_distribution": { + "M": 6, + "F": 4 + } + }, + { + "date": "2026-10-15", + "age_distribution": { + "under_18": 0, + "18_24": 2, + "25_34": 4, + "35_44": 2, + "45_64": 1, + "65_plus": 1 + }, + "gender_distribution": { + "M": 5, + "F": 5 + } + } + ], + "device_time_series": [ + { + "date": "2026-10-14", + "devices": { + "Apple": 7, + "Android": 5 + }, + "browsers": { + "Safari": 7, + "Chrome": 5 + }, + "operating_systems": { + "iOS": 7, + "Android": 5 + } + }, + { + "date": "2026-10-15", + "devices": { + "Apple": 5, + "Android": 5 + }, + "browsers": { + "Safari": 5, + "Chrome": 5 + }, + "operating_systems": { + "iOS": 5, + "Android": 5 + } + } + ], + "verification_time_time_series": [ + { + "date": "2026-10-14", + "overall_avg": 66.1, + "per_feature": { + "OCR": 42.0, + "LIVENESS": 24.1 + } + }, + { + "date": "2026-10-15", + "overall_avg": 61.5, + "per_feature": { + "OCR": 40.2, + "LIVENESS": 21.3 + } + } + ], + "resubmission_time_series": [ + { + "date": "2026-10-14", + "first_pass": 9, + "second": 1, + "third_plus": 0, + "abandoned": 0 + }, + { + "date": "2026-10-15", + "first_pass": 8, + "second": 1, + "third_plus": 1, + "abandoned": 0 + } + ], + "original_status_time_series": [ + { + "date": "2026-10-14", + "approved": 7, + "in_review": 3, + "declined": 1 + }, + { + "date": "2026-10-15", + "approved": 6, + "in_review": 2, + "declined": 1 + } + ], + "previous_data_points": [ + { + "rounded_date": "2026-10-12", + "total": 9, + "approved": 6, + "declined": 1, + "canceled": 1, + "pending": 1 + }, + { + "rounded_date": "2026-10-13", + "total": 11, + "approved": 7, + "declined": 2, + "canceled": 0, + "pending": 2 + } + ], + "previous_request_breakdown": { + "total_approved": 13, + "total_in_review": 2, + "total_declined": 3, + "total_canceled": 1, + "total_pending": 3 + } + } + }, + "Transaction widgets": { + "value": { + "scope": "transactions", + "available_widget_keys": [ + "tx_count_total", + "tx_count_daily", + "tx_volume_total_preferred_currency", + "tx_volume_daily_preferred_currency", + "tx_average_amount", + "tx_average_amount_daily", + "tx_transaction_category_breakdown", + "tx_action_type_breakdown", + "tx_status_volume_trend", + "tx_user_ip_or_country_distribution", + "tx_tag_breakdown", + "tx_top_rules_triggered", + "tx_team_member_performance", + "tx_score_histogram" + ], + "requested_widget_keys": [ + "tx_count_total", + "tx_count_daily" + ], + "widgets": { + "tx_count_total": { + "kind": "metric", + "title": "Transaction count", + "value": 42 + }, + "tx_count_daily": { + "kind": "time_series", + "title": "Transaction count", + "items": [ + { + "date": "2026-10-14", + "value": 20 + }, + { + "date": "2026-10-15", + "value": 22 + } + ], + "previous_items": [ + { + "date": "2026-10-12", + "value": 18 + }, + { + "date": "2026-10-13", + "value": 15 + } + ] + } + } + } + } + } + } + } + }, + "400": { + "description": "Invalid date range or scope. The body is an object keyed by the offending parameter, with a string message.", "content": { "application/json": { "schema": { "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "DRAFT", - "VALIDATED", - "FINALIZED" - ] - }, - "is_valid": { - "type": "boolean" - }, - "errors": { - "type": "array", - "description": "Empty when valid.", - "items": { - "type": "object", - "properties": { - "field": { - "type": "string" - }, - "message": { - "type": "string" - } - } - } + "additionalProperties": { + "type": "string" + } + }, + "examples": { + "Bad date format": { + "value": { + "start_date": "Invalid date format. Use YYYY-MM-DD (e.g., 2024-01-15)." + } + }, + "Start after end": { + "value": { + "start_date": "Start date must be before or equal to end date." + } + }, + "Range too long": { + "value": { + "date_range": "Date range cannot exceed 730 days (2 years)." + } + }, + "Unknown scope": { + "value": { + "scope": "Unsupported analytics scope." } } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/finalize/": { - "post": { - "summary": "Finalize report", - "description": "Re-validate, then render the goAML XML and PDF and upload both to S3. On SAR/STR/TFR reports, this also moves the case's transaction alerts from Pending SAR to SAR filed.", - "operationId": "finalize_regulatory_report", - "tags": [ - "Regulatory Reports" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "report_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "5ec00000-0000-4000-8000-000000000007" - }, - "description": "Report UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/finalize/' \\\n -H 'x-api-key: YOUR_API_KEY'" }, - { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/finalize/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=30,\n)\nif resp.status_code == 400:\n print('Validation failed:', resp.json()['errors'])\nelse:\n resp.raise_for_status()\n report = resp.json()\n print(report['status'], report['report_number'])" - } - ], - "responses": { - "200": { - "description": "Report finalized; xml_s3_key and pdf_s3_key are set.", + "401": { + "description": "The API key has expired. Only a key created with an expiry date can expire; after that date every request with it answers `401`.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RegulatoryReportDetail" + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } } } } }, - "400": { - "description": "Report failed validation.", + "403": { + "description": "The request is not allowed. Read `detail`: a missing or invalid API key (never `401`), a key of another application, any scoped key, or a request from outside the key's IP list.", "content": { "application/json": { "schema": { @@ -44190,28 +65332,36 @@ "properties": { "detail": { "type": "string" - }, - "errors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "field": { - "type": "string" - }, - "message": { - "type": "string" - } - } - } + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Another application": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key": { + "value": { + "detail": "This API key has no access to this endpoint." + } + }, + "Outside the key's IP list": { + "value": { + "detail": "This API key can't be used from this IP address." } } } } } }, - "409": { - "description": "Report is not in a finalizable state.", + "429": { + "description": "More than 600 GET requests per minute with this API key. Wait the number of seconds in `Retry-After`, then retry.", "content": { "application/json": { "schema": { @@ -44221,1493 +65371,2341 @@ "type": "string" } } + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds until the current one-minute window resets.", + "schema": { + "type": "integer", + "example": 23 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed per minute in this budget.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer" + } + }, + "X-RateLimit-Reset": { + "description": "Unix time when the window resets.", + "schema": { + "type": "integer" } } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/download-url/": { + "/v3/session/{sessionId}/webhooks/": { "get": { - "summary": "Get report download URL", - "description": "Mint a presigned URL for a finalized report's goAML XML or PDF.", - "operationId": "get_regulatory_report_download_url", + "summary": "List session webhook deliveries", + "description": "List every webhook delivery Didit recorded for one verification session, user (KYC) or business (KYB), newest first, as a plain JSON array (not paginated). Each entry is one attempt to one destination: the first send, each automatic [retry](https://docs.didit.me/integration/webhooks#retry-policy) and each resend are separate entries with their own `uuid`, and retries and resends keep the payload's `event_id`. `request_header` and `request_body` are JSON text; parse them to read the signatures and the payload. Delivery records are available for about 30 days, so an empty array means no webhook has been sent for the session yet or every delivery is older than that. A scoped API key needs `sessions: read` and `decisions: read`.", + "operationId": "get_v3_session_webhooks", + "x-didit-sdk": { + "resource": "sessions.webhooks", + "method": "list", + "auth": "api_key", + "server": true + }, "tags": [ - "Regulatory Reports" + "Sessions" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "sessionId", "in": "path", "required": true, + "description": "UUID of the verification session, user (KYC) or business (KYB): the `session_id` returned by `POST /v3/session/`. The server resolves it against both session types.\n\n**Must be a valid UUID.** The route only matches well-formed UUIDs, so a malformed value is rejected by the URL router before any application code runs and the response is a `404` **HTML** page rather than the JSON `{\"detail\": ...}` envelope shown below.", "schema": { "type": "string", "format": "uuid" }, - "description": "Application UUID." - }, - { - "name": "report_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "5ec00000-0000-4000-8000-000000000007" - }, - "description": "Report UUID." - }, - { - "name": "file", - "in": "query", - "schema": { - "type": "string", - "enum": [ - "pdf", - "xml" - ], - "default": "pdf" - }, - "description": "Which file to download." + "example": "22222222-3333-4444-5555-666666666666" } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/download-url/?file=xml' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/session/22222222-3333-4444-5555-666666666666/webhooks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import json\nimport os\n\nimport requests\n\nsession_id = '22222222-3333-4444-5555-666666666666'\nresp = requests.get(\n f'https://verification.didit.me/v3/session/{session_id}/webhooks/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nfor delivery in resp.json():\n payload = json.loads(delivery['request_body'])\n print(delivery['created_at'], delivery['webhook_type'], delivery['response_status'], payload['event_id'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const sessionId = '22222222-3333-4444-5555-666666666666';\nconst resp = await fetch(`https://verification.didit.me/v3/session/${sessionId}/webhooks/`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst deliveries = await resp.json();\nfor (const delivery of deliveries) {\n const payload = JSON.parse(delivery.request_body);\n console.log(delivery.created_at, delivery.webhook_type, delivery.response_status, payload.event_id);\n}" } ], "responses": { "200": { - "description": "Presigned download URL.", + "description": "Every delivery recorded for the session, newest first.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SessionWebhookDelivery" + } + }, + "examples": { + "Failed delivery and its successful retry": { + "value": [ + { + "uuid": "66666666-7777-8888-9999-000000000002", + "session_id": "22222222-3333-4444-5555-666666666666", + "request_url": "https://yourapp.com/webhooks/didit", + "response_status": 200, + "status": "In Progress", + "webhook_type": "status.updated", + "request_header": "{\"Content-Type\": \"application/json\", \"X-Signature\": \"9188851d160b5b3aacfc946e4dde1f061b7e568662518942c927ddaf507f533d\", \"X-Signature-V2\": \"381236af514d3fb538579365ac5a24f2464ce1b8224c6e770c60d8201c43ecc5\", \"X-Signature-Simple\": \"4740131dc38f29e78a8508371979be74967d5307464494bd3148a03b4d6c8716\", \"X-Timestamp\": \"1792056139\", \"User-Agent\": \"DiditWebhook/2.0 +https://didit.me\"}", + "request_body": "{\"application_id\":\"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\",\"created_at\":1792056057,\"environment\":\"live\",\"event_id\":\"33333333-4444-5555-6666-777777777777\",\"sandbox_scenario\":null,\"session_id\":\"22222222-3333-4444-5555-666666666666\",\"status\":\"In Progress\",\"timestamp\":1792056139,\"vendor_data\":\"user-123\",\"webhook_type\":\"status.updated\",\"workflow_id\":\"44444444-5555-6666-7777-888888888888\",\"workflow_version\":3}", + "created_at": "2026-10-15T09:22:19.104233Z" + }, + { + "uuid": "66666666-7777-8888-9999-000000000001", + "session_id": "22222222-3333-4444-5555-666666666666", + "request_url": "https://yourapp.com/webhooks/didit", + "response_status": 503, + "status": "In Progress", + "webhook_type": "status.updated", + "request_header": "{\"Content-Type\": \"application/json\", \"X-Signature\": \"a5e02a03404e1337a0198e6be15d34abbd053c658a0e51e5d0e78554213ef29b\", \"X-Signature-V2\": \"927e7cb770c94ce745939bff73f04aa200015f3bc64795ba46c2aaf78a6cedc5\", \"X-Signature-Simple\": \"e37b0d4c716c72bb5ae7127b9cc455d28ab4686e9e22312b4ec2327409a73302\", \"X-Timestamp\": \"1792056078\", \"User-Agent\": \"DiditWebhook/2.0 +https://didit.me\"}", + "request_body": "{\"application_id\":\"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\",\"created_at\":1792056057,\"environment\":\"live\",\"event_id\":\"33333333-4444-5555-6666-777777777777\",\"sandbox_scenario\":null,\"session_id\":\"22222222-3333-4444-5555-666666666666\",\"status\":\"In Progress\",\"timestamp\":1792056078,\"vendor_data\":\"user-123\",\"webhook_type\":\"status.updated\",\"workflow_id\":\"44444444-5555-6666-7777-888888888888\",\"workflow_version\":3}", + "created_at": "2026-10-15T09:21:18.412345Z" + } + ] + }, + "No webhook sent yet": { + "value": [] + } + } + } + } + }, + "401": { + "description": "The API key has expired: it was created with an expiry date that has passed. A missing, invalid or revoked key gets `403`, not `401`.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "download_url": { - "type": "string", - "format": "uri" - }, - "file": { + "detail": { "type": "string", - "enum": [ - "pdf", - "xml" - ] + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." } } } } } }, - "409": { - "description": "Report is not finalized yet.", + "403": { + "description": "The request is not allowed. No `x-api-key`, or an invalid or revoked key, gets `Authentication credentials were not provided or are invalid.` A key of another application gets `You do not have permission to perform this action.` A scoped key without the access this endpoint needs gets a message naming the missing access, and a key used from outside its IP allowlist gets `This API key can't be used from this IP address.` An unknown `sessionId` returns `404` before the credential is checked.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" - }, - "status": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Key of another application": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without sessions read": { + "value": { + "detail": "This API key has no read access to sessions." + } + }, + "Scoped key without decisions read": { + "value": { + "detail": "This API key has no read access to decisions." + } + }, + "Outside the IP allowlist": { + "value": { + "detail": "This API key can't be used from this IP address." } } } } } - } - } - } - }, - "/v3/transactions/rules/": { - "get": { - "summary": "List rules", - "description": "List the application's transaction-monitoring rules: your custom rules plus any library presets you have installed. Filter by source, category, mode, bundle or free-text search; order with `ordering` (run_count, approved_pct, reviewed_pct, declined_pct, latest_triggered_at, title, created_at, source, each with an optional `-` prefix).", - "operationId": "listTransactionRules", - "tags": [ - "Transactions" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "parameters": [ - { - "name": "source", - "in": "query", - "required": false, - "schema": { - "type": "string", - "enum": [ - "PRESET", - "CUSTOM" - ] - }, - "description": "PRESET or CUSTOM" - }, - { - "name": "category", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter by rule category" - }, - { - "name": "mode", - "in": "query", - "required": false, - "schema": { - "type": "string", - "enum": [ - "ACTIVE", - "DISABLED", - "TEST" - ] - }, - "description": "ACTIVE, DISABLED or TEST" - }, - { - "name": "bundle", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter presets by bundle" - }, - { - "name": "search", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Case-insensitive match against title and description" - }, - { - "name": "ordering", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Comma-separated ordering fields, default source,-run_count" - }, - { - "name": "limit", - "in": "query", - "required": false, - "schema": { - "type": "integer" - }, - "description": "Page size" }, - { - "name": "offset", - "in": "query", - "required": false, - "schema": { - "type": "integer" - }, - "description": "Page offset" - } - ], - "responses": { - "200": { - "description": "Paginated rules.", + "404": { + "description": "The session does not exist or has been deleted (`Not found.`, returned before the credential is checked), or it is outside the workflow or status limits of a scoped key (`Session not found.`).", "content": { "application/json": { "schema": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { + "detail": { "type": "string", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleDetail" - } + "description": "Human-readable error message." } } - } - } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/transactions/rules/?source=CUSTOM&ordering=-run_count' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ] - }, - "post": { - "summary": "Create rule", - "description": "Create a CUSTOM transaction-monitoring rule. Titles are unique per application (duplicate -> 400). A `change_status` action targeting `AWAITING_USER` must carry `workflow_id`. Rules created in ACTIVE mode apply to new transactions immediately - the recommended workflow is create in TEST mode, backtest, then switch mode to ACTIVE.", - "operationId": "createTransactionRule", - "tags": [ - "Transactions" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TransactionRuleCreateRequest" - }, - "example": { - "title": "Structuring - inbound (custom)", - "description": "20+ inbound EUR transfers, all under 10,000 EUR, 30 days", - "category": "finance", - "mode": "TEST", - "evaluation_mode": "ALL", - "scope": { - "transaction_types": [ - "finance" - ], - "directions": [ - "INBOUND" - ] }, - "conditions": [ - { - "field": "currency", - "operator": "eq", - "value": "EUR" - }, - { - "field": "amount", - "operator": "lt", - "value": 10000 - } - ], - "aggregation": [ - { - "metric": "count", - "operator": "gte", - "value": 20, - "window": "30d", - "filters": { - "direction": "INBOUND", - "transaction_type": "finance", - "subject_vendor_data": "__current__", - "currency": "EUR" + "examples": { + "Unknown session": { + "value": { + "detail": "Not found." } }, - { - "metric": "max", - "field": "amount", - "operator": "lt", - "value": 10000, - "window": "30d", - "filters": { - "direction": "INBOUND", - "transaction_type": "finance", - "subject_vendor_data": "__current__", - "currency": "EUR" + "Outside the key's limits": { + "value": { + "detail": "Session not found." } } - ], - "actions": [ - { - "type": "add_score", - "value": 35 - } - ] + } } } - } - }, - "responses": { - "201": { - "description": "The created rule.", - "content": { - "application/json": { + }, + "429": { + "description": "Rate limit exceeded: more than 600 GET requests in the current one-minute window for this API key. Wait `Retry-After` seconds before retrying.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", "schema": { - "$ref": "#/components/schemas/TransactionRuleDetail" + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed in the one-minute window (600).", + "schema": { + "type": "integer", + "example": 600 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix time (seconds) when the current window resets.", + "schema": { + "type": "integer", + "example": 1792056120 + } + }, + "X-RateLimit-Scope": { + "description": "Name of the budget that was exceeded.", + "schema": { + "type": "string", + "example": "generic-get" } } - } - }, - "400": { - "description": "Validation error - duplicate title, unknown operator/metric, malformed window, invalid scope direction, or AWAITING_USER without workflow_id.", + }, "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } }, - "example": { - "title": [ - "A rule with this name already exists." - ] + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } } } } } }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/session/{sessionId}/webhook/{webhook_uuid}": { + "get": { + "summary": "Get session webhook delivery", + "description": "Retrieve one webhook delivery of a verification session: the URL it went to, the HTTP status your endpoint returned, and the exact headers and JSON body Didit sent. `request_header` and `request_body` are JSON text; to check your [signature verification](https://docs.didit.me/integration/webhooks#signature-verification), recompute `X-Signature-V2` from the parsed body with the secret of the destination it was sent to (or the application-level secret for a delivery to the legacy webhook URL). The path has no trailing slash: with one it returns `404`. A scoped API key needs `sessions: read` and `decisions: read`.", + "operationId": "get_v3_session_webhook", + "x-didit-sdk": { + "resource": "sessions.webhooks", + "method": "retrieve", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "description": "UUID of the verification session, user (KYC) or business (KYB): the `session_id` returned by `POST /v3/session/`. The server resolves it against both session types.\n\n**Must be a valid UUID.** The route only matches well-formed UUIDs, so a malformed value is rejected by the URL router before any application code runs and the response is a `404` **HTML** page rather than the JSON `{\"detail\": ...}` envelope shown below.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "22222222-3333-4444-5555-666666666666" + }, + { + "name": "webhook_uuid", + "in": "path", + "required": true, + "description": "UUID of the delivery: the `uuid` of an entry returned by `GET /v3/session/{sessionId}/webhooks/`. It must belong to the session in the path.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "66666666-7777-8888-9999-000000000001" + } + ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/transactions/rules/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"title\": \"Structuring - inbound (custom)\", \"description\": \"20+ inbound EUR transfers, all under 10,000 EUR, 30 days\", \"category\": \"finance\", \"mode\": \"TEST\", \"evaluation_mode\": \"ALL\", \"scope\": {\"transaction_types\": [\"finance\"], \"directions\": [\"INBOUND\"]}, \"conditions\": [{\"field\": \"currency\", \"operator\": \"eq\", \"value\": \"EUR\"}, {\"field\": \"amount\", \"operator\": \"lt\", \"value\": 10000}], \"aggregation\": [{\"metric\": \"count\", \"operator\": \"gte\", \"value\": 20, \"window\": \"30d\", \"filters\": {\"direction\": \"INBOUND\", \"transaction_type\": \"finance\", \"subject_vendor_data\": \"__current__\", \"currency\": \"EUR\"}}, {\"metric\": \"max\", \"field\": \"amount\", \"operator\": \"lt\", \"value\": 10000, \"window\": \"30d\", \"filters\": {\"direction\": \"INBOUND\", \"transaction_type\": \"finance\", \"subject_vendor_data\": \"__current__\", \"currency\": \"EUR\"}}], \"actions\": [{\"type\": \"add_score\", \"value\": 35}]}'" + "source": "curl -X GET 'https://verification.didit.me/v3/session/22222222-3333-4444-5555-666666666666/webhook/66666666-7777-8888-9999-000000000001' \\\n -H 'x-api-key: YOUR_API_KEY'" }, { "lang": "python", "label": "Python", - "source": "import os\n\nimport requests\n\nrule = {\n \"title\": \"Structuring - inbound (custom)\",\n \"description\": \"20+ inbound EUR transfers, all under 10,000 EUR, 30 days\",\n \"category\": \"finance\",\n \"mode\": \"TEST\",\n \"evaluation_mode\": \"ALL\",\n \"scope\": {\n \"transaction_types\": [\n \"finance\"\n ],\n \"directions\": [\n \"INBOUND\"\n ]\n },\n \"conditions\": [\n {\n \"field\": \"currency\",\n \"operator\": \"eq\",\n \"value\": \"EUR\"\n },\n {\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000\n }\n ],\n \"aggregation\": [\n {\n \"metric\": \"count\",\n \"operator\": \"gte\",\n \"value\": 20,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n },\n {\n \"metric\": \"max\",\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n }\n ],\n \"actions\": [\n {\n \"type\": \"add_score\",\n \"value\": 35\n }\n ]\n}\n\nresp = requests.post(\n 'https://verification.didit.me/v3/transactions/rules/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json=rule,\n timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json()['uuid'], resp.json()['mode'])" + "source": "import json\nimport os\n\nimport requests\n\nsession_id = '22222222-3333-4444-5555-666666666666'\nwebhook_uuid = '66666666-7777-8888-9999-000000000001'\nresp = requests.get(\n f'https://verification.didit.me/v3/session/{session_id}/webhook/{webhook_uuid}', # no trailing slash\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\ndelivery = resp.json()\nheaders = json.loads(delivery['request_header'])\npayload = json.loads(delivery['request_body'])\nprint(delivery['response_status'], headers['X-Signature-V2'], payload['status'])" }, { "lang": "javascript", "label": "JavaScript", - "source": "const rule = {\n \"title\": \"Structuring - inbound (custom)\",\n \"description\": \"20+ inbound EUR transfers, all under 10,000 EUR, 30 days\",\n \"category\": \"finance\",\n \"mode\": \"TEST\",\n \"evaluation_mode\": \"ALL\",\n \"scope\": {\n \"transaction_types\": [\n \"finance\"\n ],\n \"directions\": [\n \"INBOUND\"\n ]\n },\n \"conditions\": [\n {\n \"field\": \"currency\",\n \"operator\": \"eq\",\n \"value\": \"EUR\"\n },\n {\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000\n }\n ],\n \"aggregation\": [\n {\n \"metric\": \"count\",\n \"operator\": \"gte\",\n \"value\": 20,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n },\n {\n \"metric\": \"max\",\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n }\n ],\n \"actions\": [\n {\n \"type\": \"add_score\",\n \"value\": 35\n }\n ]\n};\n\nconst resp = await fetch('https://verification.didit.me/v3/transactions/rules/', {\n method: 'POST',\n headers: {\n 'x-api-key': process.env.DIDIT_API_KEY,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(rule),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst created = await resp.json();\nconsole.log(created.uuid, created.mode);" - } - ] - } - }, - "/v3/transactions/rules/{rule_uuid}/": { - "parameters": [ - { - "name": "rule_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "The rule's uuid from list or create." - } - ], - "get": { - "summary": "Get rule", - "description": "Retrieve one rule with its full configuration and execution counters.", - "operationId": "getTransactionRule", - "tags": [ - "Transactions" - ], - "security": [ - { - "ApiKeyAuth": [] + "source": "const sessionId = '22222222-3333-4444-5555-666666666666';\nconst webhookUuid = '66666666-7777-8888-9999-000000000001';\n// No trailing slash on this path.\nconst resp = await fetch(`https://verification.didit.me/v3/session/${sessionId}/webhook/${webhookUuid}`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst delivery = await resp.json();\nconst payload = JSON.parse(delivery.request_body);\nconsole.log(delivery.response_status, payload.status);" } ], "responses": { "200": { - "description": "The rule.", + "description": "The delivery, with its request headers and body.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TransactionRuleDetail" + "$ref": "#/components/schemas/SessionWebhookDelivery" + }, + "examples": { + "Connection failure": { + "value": { + "uuid": "66666666-7777-8888-9999-000000000001", + "session_id": "22222222-3333-4444-5555-666666666666", + "request_url": "https://yourapp.com/webhooks/didit", + "response_status": 503, + "status": "In Progress", + "webhook_type": "status.updated", + "request_header": "{\"Content-Type\": \"application/json\", \"X-Signature\": \"a5e02a03404e1337a0198e6be15d34abbd053c658a0e51e5d0e78554213ef29b\", \"X-Signature-V2\": \"927e7cb770c94ce745939bff73f04aa200015f3bc64795ba46c2aaf78a6cedc5\", \"X-Signature-Simple\": \"e37b0d4c716c72bb5ae7127b9cc455d28ab4686e9e22312b4ec2327409a73302\", \"X-Timestamp\": \"1792056078\", \"User-Agent\": \"DiditWebhook/2.0 +https://didit.me\"}", + "request_body": "{\"application_id\":\"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\",\"created_at\":1792056057,\"environment\":\"live\",\"event_id\":\"33333333-4444-5555-6666-777777777777\",\"sandbox_scenario\":null,\"session_id\":\"22222222-3333-4444-5555-666666666666\",\"status\":\"In Progress\",\"timestamp\":1792056078,\"vendor_data\":\"user-123\",\"webhook_type\":\"status.updated\",\"workflow_id\":\"44444444-5555-6666-7777-888888888888\",\"workflow_version\":3}", + "created_at": "2026-10-15T09:21:18.412345Z" + } + } } } } }, - "404": { - "description": "No such rule on this application.", + "401": { + "description": "The API key has expired: it was created with an expiry date that has passed. A missing, invalid or revoked key gets `403`, not `401`.", "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/transactions/rules/RULE_UUID/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ] - }, - "patch": { - "summary": "Update rule", - "description": "Partially update a rule. Custom rules accept every configuration field. Library (PRESET) rules accept only `mode` - use it to activate, disable, or switch a preset to TEST; any other field returns 400.", - "operationId": "updateTransactionRule", - "tags": [ - "Transactions" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TransactionRuleUpdateRequest" - }, - "example": { - "mode": "ACTIVE" + }, + "403": { + "description": "The request is not allowed. No `x-api-key`, or an invalid or revoked key, gets `Authentication credentials were not provided or are invalid.` A key of another application gets `You do not have permission to perform this action.` A scoped key without the access this endpoint needs gets a message naming the missing access, and a key used from outside its IP allowlist gets `This API key can't be used from this IP address.` An unknown `sessionId` returns `404` before the credential is checked.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Key of another application": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without sessions read": { + "value": { + "detail": "This API key has no read access to sessions." + } + }, + "Scoped key without decisions read": { + "value": { + "detail": "This API key has no read access to decisions." + } + }, + "Outside the IP allowlist": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } } } - } - }, - "responses": { - "200": { - "description": "The updated rule.", + }, + "404": { + "description": "The session does not exist or has been deleted (`Not found.`, returned before the credential is checked), or it is outside the workflow or status limits of a scoped key (`Session not found.`). A delivery that does not exist, belongs to another session or is no longer available returns `No WebhookLog matches the given query.`", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TransactionRuleDetail" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Unknown session": { + "value": { + "detail": "Not found." + } + }, + "Outside the key's limits": { + "value": { + "detail": "Session not found." + } + }, + "Delivery not in this session": { + "value": { + "detail": "No WebhookLog matches the given query." + } + } } } } }, - "400": { - "description": "Validation error, or a non-mode field on a library rule.", + "429": { + "description": "Rate limit exceeded: more than 600 GET requests in the current one-minute window for this API key. Wait `Retry-After` seconds before retrying.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed in the one-minute window (600).", + "schema": { + "type": "integer", + "example": 600 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix time (seconds) when the current window resets.", + "schema": { + "type": "integer", + "example": 1792056120 + } + }, + "X-RateLimit-Scope": { + "description": "Name of the budget that was exceeded.", + "schema": { + "type": "string", + "example": "generic-get" + } + } + }, "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Throttled": { + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } } } } } }, + "security": [ + { + "ApiKeyAuth": [] + } + ] + } + }, + "/v3/session/{sessionId}/webhook/{webhook_uuid}/resend": { + "post": { + "summary": "Resend session webhook delivery", + "description": "Send a recorded session webhook again to the URL it first went to, with the same payload and `event_id` and a fresh `timestamp`, `X-Timestamp` header and signatures. Didit signs it with the current secret of the destination the delivery belongs to (or the application-level secret for a delivery to the legacy webhook URL), waits up to 5 seconds for your endpoint, records the attempt as a new delivery and returns it with `200` whatever your endpoint answered: read `response_status`. A resend is not retried automatically, and every call sends again, so make your endpoint idempotent on `event_id`. The path has no trailing slash and the request has no body. A scoped API key needs `webhooks: write` and `decisions: read`.", + "operationId": "post_v3_session_webhook_resend", + "x-didit-sdk": { + "resource": "sessions.webhooks", + "method": "resend", + "auth": "api_key", + "server": true + }, + "tags": [ + "Sessions" + ], + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "description": "UUID of the verification session, user (KYC) or business (KYB): the `session_id` returned by `POST /v3/session/`. The server resolves it against both session types.\n\n**Must be a valid UUID.** The route only matches well-formed UUIDs, so a malformed value is rejected by the URL router before any application code runs and the response is a `404` **HTML** page rather than the JSON `{\"detail\": ...}` envelope shown below.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "22222222-3333-4444-5555-666666666666" + }, + { + "name": "webhook_uuid", + "in": "path", + "required": true, + "description": "UUID of the delivery to resend: the `uuid` of an entry returned by `GET /v3/session/{sessionId}/webhooks/`. It must belong to the session in the path.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "66666666-7777-8888-9999-000000000001" + } + ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X PATCH 'https://verification.didit.me/v3/transactions/rules/RULE_UUID/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"mode\": \"ACTIVE\"}'" + "source": "curl -X POST 'https://verification.didit.me/v3/session/22222222-3333-4444-5555-666666666666/webhook/66666666-7777-8888-9999-000000000001/resend' \\\n -H 'x-api-key: YOUR_API_KEY'" }, { "lang": "python", "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.patch(\n 'https://verification.didit.me/v3/transactions/rules/RULE_UUID/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'mode': 'ACTIVE'},\n timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json()['mode'])" - } - ] - }, - "delete": { - "summary": "Delete rule", - "description": "Delete a CUSTOM rule. Deletion is immediate and permanent: the rule stops evaluating for new transactions and disappears from listings, while past rule runs on already-evaluated transactions keep their recorded results. Library rules cannot be deleted - uninstall them via the install endpoint instead (they can always be reinstalled from the library).", - "operationId": "deleteTransactionRule", - "tags": [ - "Transactions" - ], - "security": [ + "source": "import os\n\nimport requests\n\nsession_id = '22222222-3333-4444-5555-666666666666'\nwebhook_uuid = '66666666-7777-8888-9999-000000000001'\nresp = requests.post(\n f'https://verification.didit.me/v3/session/{session_id}/webhook/{webhook_uuid}/resend', # no trailing slash\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nresent = resp.json()\nprint(resent['uuid'], resent['response_status'])" + }, { - "ApiKeyAuth": [] + "lang": "javascript", + "label": "JavaScript", + "source": "const sessionId = '22222222-3333-4444-5555-666666666666';\nconst webhookUuid = '66666666-7777-8888-9999-000000000001';\n// No trailing slash on this path.\nconst resp = await fetch(`https://verification.didit.me/v3/session/${sessionId}/webhook/${webhookUuid}/resend`, {\n method: 'POST',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst resent = await resp.json();\nconsole.log(resent.uuid, resent.response_status);" } ], "responses": { - "204": { - "description": "Deleted." + "200": { + "description": "The new delivery, already attempted. `response_status` is what your endpoint answered this time. The original delivery is unchanged.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionWebhookDelivery" + }, + "examples": { + "Accepted on resend": { + "value": { + "uuid": "66666666-7777-8888-9999-000000000004", + "session_id": "22222222-3333-4444-5555-666666666666", + "request_url": "https://yourapp.com/webhooks/didit", + "response_status": 200, + "status": "In Progress", + "webhook_type": "status.updated", + "request_header": "{\"Content-Type\": \"application/json\", \"X-Signature\": \"b8eb1e376517ee0cad8ba98ff7f8fd35fc72eca231000963a96a335f82328835\", \"X-Signature-V2\": \"9efd2ce9ed1303e1d05273ef9608a59363ed9789a05253db4748b3e8e50cb109\", \"X-Signature-Simple\": \"bf3a9dc04b2c20ab539203b0cd5cca2a10f630bf62d8feb6d68d82af80f64774\", \"X-Timestamp\": \"1792159542\", \"User-Agent\": \"DiditWebhook/2.0 +https://didit.me\"}", + "request_body": "{\"application_id\":\"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\",\"created_at\":1792056057,\"environment\":\"live\",\"event_id\":\"33333333-4444-5555-6666-777777777777\",\"sandbox_scenario\":null,\"session_id\":\"22222222-3333-4444-5555-666666666666\",\"status\":\"In Progress\",\"timestamp\":1792159542,\"vendor_data\":\"user-123\",\"webhook_type\":\"status.updated\",\"workflow_id\":\"44444444-5555-6666-7777-888888888888\",\"workflow_version\":3}", + "created_at": "2026-10-16T14:05:42.330981Z" + } + } + } + } + } }, "400": { - "description": "The rule is a library preset - uninstall instead.", + "description": "The delivery could not be resent. Branch on `code`: `webhook_url_missing` means the delivery has no recorded URL and nothing is sent; `webhook_resend_error` means the stored delivery could not be signed again or the attempt could not be completed. After `webhook_resend_error`, `detail` starts with `Error processing webhook resend:` followed by a diagnostic text that can change.", "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + }, + "code": { + "type": "string", + "description": "Stable machine-readable error code. Branch on this, not on `detail`." + } + } }, - "example": { - "detail": "Library rules cannot be deleted. Uninstall them via the rules install endpoint instead." + "examples": { + "No recorded URL": { + "value": { + "detail": "The webhook delivery does not have a request URL configured", + "code": "webhook_url_missing" + } + }, + "Resend failed": { + "value": { + "detail": "Error processing webhook resend: Invalid webhook data format", + "code": "webhook_resend_error" + } + } } } } }, - "404": { - "description": "No such rule on this application.", + "401": { + "description": "The API key has expired: it was created with an expiry date that has passed. A missing, invalid or revoked key gets `403`, not `401`.", "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "value": { + "detail": "This API key has expired." + } + } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X DELETE 'https://verification.didit.me/v3/transactions/rules/RULE_UUID/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ] - } - }, - "/v3/transactions/rules/backtest/": { - "post": { - "summary": "Backtest rule configuration", - "description": "Evaluate a hypothetical rule configuration against your recent transactions (most recent first, up to the backtest cap) without creating a rule or touching any transaction. Use it before activating a rule to see how often it would have matched and how many subjects it would have affected.", - "operationId": "backtestTransactionRules", - "tags": [ - "Transactions" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TransactionRuleBacktestRequest" - }, - "example": { - "conditions": [ - { - "field": "currency", - "operator": "eq", - "value": "EUR" - }, - { - "field": "amount", - "operator": "lt", - "value": 10000 + }, + "403": { + "description": "The request is not allowed. No `x-api-key`, or an invalid or revoked key, gets `Authentication credentials were not provided or are invalid.` A key of another application gets `You do not have permission to perform this action.` A scoped key without the access this endpoint needs gets a message naming the missing access, and a key used from outside its IP allowlist gets `This API key can't be used from this IP address.` An unknown `sessionId` returns `404` before the credential is checked.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } } - ], - "aggregation": [ - { - "metric": "count", - "operator": "gte", - "value": 20, - "window": "30d", - "filters": { - "direction": "INBOUND", - "transaction_type": "finance", - "subject_vendor_data": "__current__", - "currency": "EUR" + }, + "examples": { + "Missing or invalid key": { + "value": { + "detail": "Authentication credentials were not provided or are invalid." } }, - { - "metric": "max", - "field": "amount", - "operator": "lt", - "value": 10000, - "window": "30d", - "filters": { - "direction": "INBOUND", - "transaction_type": "finance", - "subject_vendor_data": "__current__", - "currency": "EUR" + "Key of another application": { + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without webhooks write": { + "value": { + "detail": "This API key has no write access to webhooks." + } + }, + "Scoped key without decisions read": { + "value": { + "detail": "This API key has no read access to decisions." + } + }, + "Outside the IP allowlist": { + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } + }, + "404": { + "description": "The session does not exist or has been deleted (`Not found.`, returned before the credential is checked), or it is outside the workflow or status limits of a scoped key (`Session not found.`). A delivery that does not exist, belongs to another session or is no longer available returns `Webhook log not found`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." } } - ], - "evaluation_mode": "ALL", - "scope": { - "transaction_types": [ - "finance" - ], - "directions": [ - "INBOUND" - ] }, - "period_days": 90 + "examples": { + "Unknown session": { + "value": { + "detail": "Not found." + } + }, + "Outside the key's limits": { + "value": { + "detail": "Session not found." + } + }, + "Delivery not in this session": { + "value": { + "detail": "Webhook log not found" + } + } + } } } - } - }, - "responses": { - "200": { - "description": "Backtest statistics.", + }, + "429": { + "description": "Rate limit exceeded: more than 300 write requests (POST, PATCH, PUT, DELETE) in the current one-minute window for this API key. Wait `Retry-After` seconds before retrying.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + }, + "X-RateLimit-Limit": { + "description": "Requests allowed in the one-minute window (300).", + "schema": { + "type": "integer", + "example": 300 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the current window.", + "schema": { + "type": "integer", + "example": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix time (seconds) when the current window resets.", + "schema": { + "type": "integer", + "example": 1792056120 + } + }, + "X-RateLimit-Scope": { + "description": "Name of the budget that was exceeded.", + "schema": { + "type": "string", + "example": "generic-write" + } + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TransactionRuleBacktestResult" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } }, - "example": { - "evaluated": 4820, - "matched": 12, - "affected_entities": 4, - "period_days": 90 + "examples": { + "Throttled": { + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." + } + } } } } } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/transactions/rules/backtest/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"conditions\": [{\"field\": \"currency\", \"operator\": \"eq\", \"value\": \"EUR\"}, {\"field\": \"amount\", \"operator\": \"lt\", \"value\": 10000}], \"aggregation\": [{\"metric\": \"count\", \"operator\": \"gte\", \"value\": 20, \"window\": \"30d\", \"filters\": {\"direction\": \"INBOUND\", \"transaction_type\": \"finance\", \"subject_vendor_data\": \"__current__\", \"currency\": \"EUR\"}}, {\"metric\": \"max\", \"field\": \"amount\", \"operator\": \"lt\", \"value\": 10000, \"window\": \"30d\", \"filters\": {\"direction\": \"INBOUND\", \"transaction_type\": \"finance\", \"subject_vendor_data\": \"__current__\", \"currency\": \"EUR\"}}], \"evaluation_mode\": \"ALL\", \"scope\": {\"transaction_types\": [\"finance\"], \"directions\": [\"INBOUND\"]}, \"period_days\": 90}'" - }, + "security": [ { - "lang": "python", - "label": "Python", - "source": "import os\n\nimport requests\n\nresp = requests.post(\n 'https://verification.didit.me/v3/transactions/rules/backtest/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={\n \"conditions\": [\n {\n \"field\": \"currency\",\n \"operator\": \"eq\",\n \"value\": \"EUR\"\n },\n {\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000\n }\n ],\n \"aggregation\": [\n {\n \"metric\": \"count\",\n \"operator\": \"gte\",\n \"value\": 20,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n },\n {\n \"metric\": \"max\",\n \"field\": \"amount\",\n \"operator\": \"lt\",\n \"value\": 10000,\n \"window\": \"30d\",\n \"filters\": {\n \"direction\": \"INBOUND\",\n \"transaction_type\": \"finance\",\n \"subject_vendor_data\": \"__current__\",\n \"currency\": \"EUR\"\n }\n }\n ],\n \"evaluation_mode\": \"ALL\",\n \"scope\": {\n \"transaction_types\": [\n \"finance\"\n ],\n \"directions\": [\n \"INBOUND\"\n ]\n },\n \"period_days\": 90\n},\n timeout=30,\n)\nresp.raise_for_status()\nstats = resp.json()\nprint(stats['matched'], 'matches across', stats['affected_entities'], 'subjects')" + "ApiKeyAuth": [] } ] } }, - "/v3/transactions/rules/library/": { + "/v3/session/{sessionId}/events/": { "get": { - "summary": "List rule library", - "description": "Browse the full preset rule library (150+ rules) with each entry's complete configuration and an `is_installed` flag for this application. Filter by bundle, category, or free-text search.", - "operationId": "listTransactionRuleLibrary", + "summary": "List session events", + "description": "Read the event timeline of one verification session, newest first by `sent_at`: the screens the user saw, buttons and uploads, failed attempts, status and feature-step changes, and the workflow routing trace. Works for User Verification (KYC) and Business Verification (KYB) session IDs. Events are stored asynchronously, so a new event can take a few seconds to appear. Results are paginated with `limit` and `offset`; follow `next` until it is `null`. A scoped API key needs `sessions: read` and `decisions: read`, and without `media: read` it receives `file_url` as `null`. The routing events are described in [Retrieve session](https://docs.didit.me/sessions-api/retrieve-session).", + "operationId": "get_v3_session_events", + "x-didit-sdk": { + "resource": "sessions", + "method": "list_events", + "auth": "api_key", + "server": true + }, "tags": [ - "Transactions" - ], - "security": [ - { - "ApiKeyAuth": [] - } + "Sessions" ], "parameters": [ { - "name": "bundle", - "in": "query", - "required": false, + "name": "sessionId", + "in": "path", + "required": true, + "description": "UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`.", "schema": { - "type": "string" - }, - "description": "Filter by bundle, e.g. finance, aml_ctf, crypto_monitoring, travel_rule" + "type": "string", + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } }, { - "name": "category", + "name": "limit", "in": "query", "required": false, + "description": "Maximum number of events per page. Defaults to 50. A value below 1 or a non-numeric value falls back to 50; a value above 1,000,000 returns `400`.", "schema": { - "type": "string" - }, - "description": "Filter by category" + "type": "integer", + "default": 50, + "minimum": 1, + "maximum": 1000000, + "example": 50 + } }, { - "name": "search", + "name": "offset", "in": "query", "required": false, + "description": "Number of results to skip before the first returned one. Defaults to 0; a negative or non-numeric value falls back to 0, a value above 1,000,000 returns `400`. Prefer following the `next` and `previous` URLs.", "schema": { - "type": "string" - }, - "description": "Case-insensitive match against title and description" + "type": "integer", + "default": 0, + "minimum": 0, + "maximum": 1000000, + "example": 0 + } }, { - "name": "limit", + "name": "ordering", "in": "query", "required": false, + "description": "Optional sort override. The default order is `-sent_at`. Pass a result field name (for example `sent_at`, `created_at`, `event_type`, `feature_step` or `step`) to sort ascending, prefix it with `-` to sort descending, and separate several fields with commas. Unknown fields are ignored.", "schema": { - "type": "integer" - }, - "description": "Page size" + "type": "string", + "example": "sent_at" + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/events/?limit=50' \\\n -H 'x-api-key: YOUR_API_KEY'" }, { - "name": "offset", - "in": "query", - "required": false, - "schema": { - "type": "integer" - }, - "description": "Page offset" + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nsession_id = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n f'https://verification.didit.me/v3/session/{session_id}/events/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n params={'limit': 50},\n timeout=10,\n)\nresp.raise_for_status()\nfor event in resp.json()['results']:\n print(event['sent_at'], event['event_type'], event['step'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const sessionId = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/session/${sessionId}/events/?limit=50`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst page = await resp.json();\nfor (const event of page.results) console.log(event.sent_at, event.event_type, event.step);" } ], "responses": { "200": { - "description": "Paginated library entries.", + "description": "One page of the session's events, newest first.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], "properties": { "count": { - "type": "integer" + "type": "integer", + "description": "Total number of events of the session." }, "next": { "type": "string", - "nullable": true + "format": "uri", + "nullable": true, + "description": "URL of the next page, or `null` on the last page." }, "previous": { "type": "string", - "nullable": true + "format": "uri", + "nullable": true, + "description": "URL of the previous page, or `null` on the first page." }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/TransactionRuleLibraryEntry" + "$ref": "#/components/schemas/SessionEvent" } } } + }, + "examples": { + "Completed session": { + "value": { + "count": 3, + "next": null, + "previous": null, + "results": [ + { + "uuid": "aaaaaaaa-1111-2222-3333-444444444444", + "event_type": "SESSION_STATUS_CHANGED", + "created_at": "2026-09-14T10:02:13.904512Z", + "component": null, + "feature_step": null, + "step": null, + "details": { + "old_status": "In Progress", + "current_status": "Approved" + }, + "ip_address": null, + "user_agent": null, + "device_info": null, + "file_url": null, + "sent_at": 1789380131482 + }, + { + "uuid": "bbbbbbbb-1111-2222-3333-444444444444", + "event_type": "FEATURE_STEP_CHANGED", + "created_at": "2026-09-14T10:01:49.210377Z", + "component": null, + "feature_step": null, + "step": null, + "details": { + "old_feature_step": "OCR", + "current_feature_step": "LIVENESS" + }, + "ip_address": null, + "user_agent": null, + "device_info": null, + "file_url": null, + "sent_at": 1789380107103 + }, + { + "uuid": "cccccccc-1111-2222-3333-444444444444", + "event_type": "STEP_RENDERED", + "created_at": "2026-09-14T10:01:00.581930Z", + "component": "DocumentCapture", + "feature_step": "OCR", + "step": "DOCUMENT_CAPTURE_FRONT", + "details": {}, + "ip_address": "203.0.113.24", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1", + "device_info": { + "device_model": "iPhone" + }, + "file_url": null, + "sent_at": 1789380058776 + } + ] + } + } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/transactions/rules/library/?bundle=finance' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ] - } - }, - "/v3/transactions/rules/install/": { - "post": { - "summary": "Install library rules", - "description": "Install one or more presets onto the application, by `library_keys` or a whole `bundle`. Installed presets evaluate immediately in their default mode; already-installed keys are skipped.", - "operationId": "installTransactionRuleLibrary", - "tags": [ - "Transactions" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TransactionRuleInstallRequest" - }, - "example": { - "library_keys": [ - "structuring-inbound", - "structuring-outbound" - ] + }, + "400": { + "description": "`limit` or `offset` is above 1,000,000. The body is a JSON array with one message.", + "content": { + "application/json": { + "examples": { + "Limit too large": { + "summary": "limit=2000000", + "value": [ + "limit must not exceed 1000000." + ] + }, + "Offset too large": { + "summary": "offset=2000000", + "value": [ + "offset must not exceed 1000000." + ] + } + }, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } } } - } - }, - "responses": { - "200": { - "description": "What was installed.", + }, + "401": { + "description": "The scoped API key has expired. This is the only case that returns `401`: a missing, invalid or revoked key returns `403`.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TransactionRuleInstallResult" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } }, - "example": { - "installed_library_keys": [ - "structuring-inbound", - "structuring-outbound" - ], - "installed_count": 2 + "examples": { + "Expired key": { + "summary": "The key's expiry date has passed", + "value": { + "detail": "This API key has expired." + } + } } } } }, - "400": { - "description": "Neither library_keys nor bundle provided.", + "403": { + "description": "Authentication or authorization failure. Missing, invalid and revoked keys return `403` (not `401`); so do a key of another application, a scoped key without the permission this endpoint needs, and a request from outside the key's IP allowlist. Distinguish them by `detail`.", "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "No x-api-key header, or the key failed validation", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Other application": { + "summary": "A valid key that belongs to a different application than the session", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without sessions read": { + "summary": "Scoped key lacks sessions: read", + "value": { + "detail": "This API key has no read access to sessions." + } + }, + "Scoped key without decisions read": { + "summary": "Scoped key lacks decisions: read", + "value": { + "detail": "This API key has no read access to decisions." + } + }, + "IP not allowed": { + "summary": "Request from outside the key's IP allowlist", + "value": { + "detail": "This API key can't be used from this IP address." + } + } } } } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST 'https://verification.didit.me/v3/transactions/rules/install/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"library_keys\": [\"structuring-inbound\", \"structuring-outbound\"]}'" - } - ] - }, - "delete": { - "summary": "Uninstall library rules", - "description": "Remove installed presets from the application, by `library_keys` or a whole `bundle`. Uninstalled presets stop evaluating immediately and can be reinstalled from the library at any time.", - "operationId": "uninstallTransactionRuleLibrary", - "tags": [ - "Transactions" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TransactionRuleInstallRequest" - }, - "example": { - "bundle": "responsible_gaming" - } - } - } - }, - "responses": { - "200": { - "description": "How many presets were removed.", + }, + "404": { + "description": "The session does not exist, was deleted, or is outside a scoped key's workflow or approved-only limits. The session lookup runs before the credential check, so an unknown `sessionId` returns `404` even without a valid key.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TransactionRuleInstallResult" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } }, - "example": { - "uninstalled_count": 5 + "examples": { + "Unknown session": { + "summary": "No session with this ID exists, or it was deleted", + "value": { + "detail": "Not found." + } + }, + "Outside key limits": { + "summary": "The session is outside a scoped key's workflow or approved-only limits", + "value": { + "detail": "Session not found." + } + } } } } }, - "400": { - "description": "Neither library_keys nor bundle provided.", + "429": { + "description": "Rate limit exceeded: 600 GET requests per minute per API key. Wait `Retry-After` seconds before retrying; the `X-RateLimit-*` headers show the budget.", "content": { "application/json": { "schema": { - "type": "object" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Rate limited": { + "summary": "Budget for the current minute exhausted", + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." + } + } + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 } } } } }, - "x-codeSamples": [ + "security": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X DELETE 'https://verification.didit.me/v3/transactions/rules/install/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"bundle\": \"responsible_gaming\"}'" + "ApiKeyAuth": [] } ] } }, - "/v3/organization/{organization_id}/application/{application_id}/networks/": { + "/v3/session/{sessionId}/aml-screening-history/": { "get": { - "summary": "List networks", - "description": "List fraud networks detected within this application's organization, newest activity first by default. By default dismissed networks are excluded; pass status to include them. Sandbox applications never have networks - clustering excludes sandbox data entirely.", - "operationId": "list_networks", + "summary": "List AML screening history", + "description": "List every completed ongoing-monitoring re-screen of a session's AML check, newest first by `completed_at`, including the runs where nothing changed: alerts, activity entries and webhooks fire only when a result moves, so this history is the evidence that a clean subject was still being screened. Works for KYC and KYB session IDs; pass `node_id` when the session ran more than one AML step. Only ongoing-monitoring re-screens are listed (the first screening is part of the session decision) and runs from before this history existed are not reconstructed, so a session without ongoing monitoring returns an empty list. `count` is exact up to 100 and capped at 100 above that, so follow `next` until it is `null`. A scoped API key needs `decisions: read`; see [Continuous AML monitoring](https://docs.didit.me/core-technology/aml-screening/continuous-monitoring-aml-screening).", + "operationId": "get_v3_session_aml_screening_history", + "x-didit-sdk": { + "resource": "sessions", + "method": "list_aml_screening_history", + "auth": "api_key", + "server": true + }, "tags": [ - "Networks" + "Sessions" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "sessionId", "in": "path", "required": true, + "description": "UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "status", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Comma-separated statuses to include: active,in_review,resolved,dismissed. Omit to exclude dismissed." - }, - { - "name": "signal_type", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Comma-separated signal types: ip_address,device,face,document_number,phone,email,address." - }, - { - "name": "pattern_type", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Comma-separated pattern types, e.g. exact_same_device,same_email." - }, - { - "name": "risk_band", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Comma-separated bands: low,medium,high." - }, - { - "name": "min_size", - "in": "query", - "schema": { - "type": "integer" - }, - "description": "Minimum member count." - }, - { - "name": "max_size", - "in": "query", - "schema": { - "type": "integer" - }, - "description": "Maximum member count." - }, - { - "name": "date_from", - "in": "query", - "schema": { - "type": "string", - "format": "date" - }, - "description": "Lower bound on last_activity_at (ISO 8601 date or datetime)." + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } }, { - "name": "date_to", + "name": "node_id", "in": "query", + "required": false, + "description": "Workflow graph node ID of the AML check. Needed only when the session ran more than one AML step; without it the most recent AML check is used. An unknown node returns `404`.", "schema": { "type": "string", - "format": "date" - }, - "description": "Upper bound on first_activity_at (ISO 8601 date or datetime)." - }, - { - "name": "q", - "in": "query", - "schema": { - "type": "string" - }, - "description": "Free-text search across network name, member names, the network_id (N-0001), and exact pattern_type tokens." - }, - { - "name": "ordering", - "in": "query", - "schema": { - "type": "string" - }, - "description": "One of risk_score, -risk_score, last_activity_at, -last_activity_at, first_activity_at, -first_activity_at, network_number, -network_number. Defaults to -risk_score,-last_activity_at." + "example": "feature_aml_1" + } }, { "name": "limit", "in": "query", + "required": false, + "description": "Maximum number of screening runs per page. Defaults to 50. A value below 1 or a non-numeric value falls back to 50; a value above 1,000,000 returns `400`.", "schema": { "type": "integer", - "default": 100, + "default": 50, "minimum": 1, - "maximum": 500 - }, - "description": "Page size, 1-500. Defaults to 100." + "maximum": 1000000, + "example": 50 + } }, { "name": "offset", "in": "query", + "required": false, + "description": "Number of results to skip before the first returned one. Defaults to 0; a negative or non-numeric value falls back to 0, a value above 1,000,000 returns `400`. Prefer following the `next` and `previous` URLs.", "schema": { "type": "integer", "default": 0, - "minimum": 0 - }, - "description": "Zero-based offset of the first record." + "minimum": 0, + "maximum": 1000000, + "example": 0 + } + }, + { + "name": "ordering", + "in": "query", + "required": false, + "description": "Optional sort override. The default order is `-completed_at`. Pass a result field name (for example `completed_at`, `started_at`, `outcome` or `score`) to sort ascending, prefix it with `-` to sort descending, and separate several fields with commas. Unknown fields are ignored.", + "schema": { + "type": "string", + "example": "completed_at" + } } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/?status=active,in_review&ordering=-risk_score' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/aml-screening-history/' \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nsession_id = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n f'https://verification.didit.me/v3/session/{session_id}/aml-screening-history/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nfor run in resp.json()['results']:\n print(run['completed_at'], run['outcome'], run['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const sessionId = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/session/${sessionId}/aml-screening-history/`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Request failed: ${resp.status}`);\nconst page = await resp.json();\nfor (const run of page.results) console.log(run.completed_at, run.outcome, run.status);" } ], "responses": { "200": { - "description": "Paginated network list with console KPIs.", + "description": "One page of screening runs, newest first.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "count", + "next", + "previous", + "results" + ], "properties": { "count": { - "type": "integer" + "type": "integer", + "maximum": 100, + "description": "Number of runs, exact up to 100 and capped at 100 above that." }, "next": { - "type": "integer", + "type": "string", + "format": "uri", "nullable": true, - "description": "Offset of the next page, or null on the last page." + "description": "URL of the next page, or `null` on the last page." }, "previous": { - "type": "integer", - "nullable": true + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the previous page, or `null` on the first page." }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/NetworkListItem" + "$ref": "#/components/schemas/AMLScreeningRun" } - }, - "kpis": { - "type": "object", - "description": "Each key is {value, delta} over the results matching the current filters.", - "properties": { - "active": { - "type": "object" - }, - "in_review": { - "type": "object" - }, - "applicants_involved": { - "type": "object" - }, - "detected_this_period": { - "type": "object" + } + } + }, + "examples": { + "Monitored subject": { + "value": { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "uuid": "dddddddd-1111-2222-3333-444444444444", + "completed_at": "2026-09-14T04:12:33.481207Z", + "started_at": "2026-09-14T04:00:01.002311Z", + "outcome": "NO_CHANGE", + "trigger": "ONGOING_MONITORING", + "provider": "aml_data_source", + "status": "Approved", + "score": 0.0, + "total_hits": 0, + "dataset_freshness": { + "captured_at": "2026-09-14T03:58:02.114820+00:00", + "provider": "aml_data_source", + "catalog_version": "2026-09-13", + "sources": { + "sanctions": { + "last_updated_at": "2026-09-13T21:10:00+00:00", + "oldest_updated_at": "2026-09-11T06:00:00+00:00", + "dataset_count": 120, + "dated_dataset_count": 120 + }, + "pep": { + "last_updated_at": "2026-09-13T08:00:00+00:00", + "oldest_updated_at": null, + "dataset_count": 45, + "dated_dataset_count": 44 + }, + "warnings": { + "last_updated_at": "2026-09-12T18:45:00+00:00", + "oldest_updated_at": "2026-09-01T00:00:00+00:00", + "dataset_count": 30, + "dated_dataset_count": 30 + } + } + } }, - "false_positive_rate": { - "type": "object" + { + "uuid": "eeeeeeee-1111-2222-3333-444444444444", + "completed_at": "2026-09-13T04:11:58.020914Z", + "started_at": "2026-09-13T04:00:00.871022Z", + "outcome": "FAILED", + "trigger": "ONGOING_MONITORING", + "provider": "aml_data_source", + "status": "Approved", + "score": 0.0, + "total_hits": 0, + "dataset_freshness": null } - } + ] + } + } + } + } + } + }, + "400": { + "description": "`limit` or `offset` is above 1,000,000. The body is a JSON array with one message.", + "content": { + "application/json": { + "examples": { + "Limit too large": { + "summary": "limit=2000000", + "value": [ + "limit must not exceed 1000000." + ] + }, + "Offset too large": { + "summary": "offset=2000000", + "value": [ + "offset must not exceed 1000000." + ] + } + }, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. This is the only case that returns `401`: a missing, invalid or revoked key returns `403`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "summary": "The key's expiry date has passed", + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "Authentication or authorization failure. Missing, invalid and revoked keys return `403` (not `401`); so do a key of another application, a scoped key without the permission this endpoint needs, and a request from outside the key's IP allowlist. Distinguish them by `detail`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "No x-api-key header, or the key failed validation", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Other application": { + "summary": "A valid key that belongs to a different application than the session", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without decisions read": { + "summary": "Scoped key lacks decisions: read", + "value": { + "detail": "This API key has no read access to decisions." + } + }, + "IP not allowed": { + "summary": "Request from outside the key's IP allowlist", + "value": { + "detail": "This API key can't be used from this IP address." + } + } + } + } + } + }, + "404": { + "description": "The session does not exist, was deleted or is outside a scoped key's limits, or it has no AML check (for the given `node_id`). The session lookup runs before the credential check, so an unknown `sessionId` returns `404` even without a valid key.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Unknown session": { + "summary": "No session with this ID exists, or it was deleted", + "value": { + "detail": "Not found." + } + }, + "Outside key limits": { + "summary": "The session is outside a scoped key's limits (also returned for a session deleted moments ago)", + "value": { + "detail": "Session not found." + } + }, + "No AML check": { + "summary": "The session never ran an AML step", + "value": { + "detail": "The session does not have an AML check." + } + }, + "No AML check for node": { + "summary": "node_id does not match an AML check of the session", + "value": { + "detail": "The session does not have an AML check for node 'feature_aml_2'." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded: 600 GET requests per minute per API key. Wait `Retry-After` seconds before retrying; the `X-RateLimit-*` headers show the budget.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Rate limited": { + "summary": "Budget for the current minute exhausted", + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." } } } } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/": { + "/v3/session/{sessionId}/aml-screening-history/export/": { "get": { - "summary": "Get network", - "description": "Retrieve a single network's full detail, including stats, fraud tags, and risk signals.", - "operationId": "get_network", + "summary": "Export AML screening history as CSV", + "description": "Download the same ongoing-monitoring history as `GET /v3/session/{sessionId}/aml-screening-history/` as a CSV file, one line per completed re-screen, newest first, ready to hand to an auditor. The file is streamed whole (no pagination) and starts with a header line; an empty cell means unknown, dataset dates are never estimated, and a value that would start a spreadsheet formula is prefixed with an apostrophe. Works for KYC and KYB session IDs; pass `node_id` when the session ran more than one AML step. Errors are returned as JSON, not CSV. A scoped API key needs `decisions: read` and `media: read`: the response is a file, so a key without `media: read` gets `403`.", + "operationId": "get_v3_session_aml_screening_history_export", + "x-didit-sdk": { + "resource": "sessions", + "method": "export_aml_screening_history", + "auth": "api_key", + "server": true + }, "tags": [ - "Networks" + "Sessions" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "sessionId", "in": "path", "required": true, + "description": "UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } }, { - "name": "network_uuid", - "in": "path", - "required": true, + "name": "node_id", + "in": "query", + "required": false, + "description": "Workflow graph node ID of the AML check. Needed only when the session ran more than one AML step; without it the most recent AML check is used. An unknown node returns `404`.", "schema": { "type": "string", - "format": "uuid", - "example": "n3a5e000-0000-4000-8000-000000000001" - }, - "description": "Network UUID." + "example": "feature_aml_1" + } } ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X GET 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/aml-screening-history/export/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -o aml-screening-history.csv" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nsession_id = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n f'https://verification.didit.me/v3/session/{session_id}/aml-screening-history/export/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n timeout=10,\n)\nresp.raise_for_status()\nwith open('aml-screening-history.csv', 'wb') as fh:\n fh.write(resp.content)" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "import { writeFile } from 'node:fs/promises';\n\nconst sessionId = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/session/${sessionId}/aml-screening-history/export/`, {\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Export failed: ${resp.status}`);\nawait writeFile('aml-screening-history.csv', await resp.text());" } ], "responses": { "200": { - "description": "Network detail.", + "description": "The CSV file, streamed as `text/csv` with CRLF line endings. Columns, in order: `screened_at` (when the re-screen finished), `outcome` (`NO_CHANGE`, `CHANGED` or `FAILED`), `provider` (opaque identifier of the AML data source; do not branch on it), `aml_status`, `aml_score`, `total_hits`, `datasets_captured_at`, `datasets_sanctions_last_updated_at`, `datasets_pep_last_updated_at`, `datasets_warnings_last_updated_at`, `datasets_adverse_media_last_updated_at` and `datasets_catalog_version`. Dates are ISO 8601 with a UTC offset. A session with no re-screens yet returns the header line only.", + "headers": { + "Content-Disposition": { + "description": "`attachment; filename=\"aml-screening-history-{sessionId}.csv\"`.", + "schema": { + "type": "string", + "example": "attachment; filename=\"aml-screening-history-11111111-2222-3333-4444-555555555555.csv\"" + } + } + }, + "content": { + "text/csv": { + "schema": { + "type": "string", + "format": "binary" + }, + "example": "screened_at,outcome,provider,aml_status,aml_score,total_hits,datasets_captured_at,datasets_sanctions_last_updated_at,datasets_pep_last_updated_at,datasets_warnings_last_updated_at,datasets_adverse_media_last_updated_at,datasets_catalog_version\r\n2026-09-14T04:12:33.481207+00:00,NO_CHANGE,aml_data_source,Approved,0.0,0,2026-09-14T03:58:02.114820+00:00,2026-09-13T21:10:00+00:00,2026-09-13T08:00:00+00:00,2026-09-12T18:45:00+00:00,,2026-09-13\r\n2026-09-13T04:11:58.020914+00:00,FAILED,aml_data_source,Approved,0.0,0,,,,,,\r\n" + } + } + }, + "401": { + "description": "The scoped API key has expired. This is the only case that returns `401`: a missing, invalid or revoked key returns `403`.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "summary": "The key's expiry date has passed", + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "Authentication or authorization failure. Missing, invalid and revoked keys return `403` (not `401`); so do a key of another application, a scoped key without the permission this endpoint needs, and a request from outside the key's IP allowlist. Distinguish them by `detail`.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NetworkDetail" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "No x-api-key header, or the key failed validation", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Other application": { + "summary": "A valid key that belongs to a different application than the session", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without decisions read": { + "summary": "Scoped key lacks decisions: read", + "value": { + "detail": "This API key has no read access to decisions." + } + }, + "Scoped key without media read": { + "summary": "Scoped key lacks media: read (the response is a file)", + "value": { + "detail": "This API key has no read access to media." + } + }, + "IP not allowed": { + "summary": "Request from outside the key's IP allowlist", + "value": { + "detail": "This API key can't be used from this IP address." + } + } } } } }, "404": { - "description": "Network not found (or not visible to this application).", + "description": "The session does not exist, was deleted or is outside a scoped key's limits, or it has no AML check (for the given `node_id`). The session lookup runs before the credential check, so an unknown `sessionId` returns `404` even without a valid key.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Unknown session": { + "summary": "No session with this ID exists, or it was deleted", + "value": { + "detail": "Not found." + } + }, + "Outside key limits": { + "summary": "The session is outside a scoped key's limits (also returned for a session deleted moments ago)", + "value": { + "detail": "Session not found." + } + }, + "No AML check": { + "summary": "The session never ran an AML step", + "value": { + "detail": "The session does not have an AML check." + } + }, + "No AML check for node": { + "summary": "node_id does not match an AML check of the session", + "value": { + "detail": "The session does not have an AML check for node 'feature_aml_2'." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded: 600 GET requests per minute per API key. Wait `Retry-After` seconds before retrying; the `X-RateLimit-*` headers show the budget.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Rate limited": { + "summary": "Budget for the current minute exhausted", + "value": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." } } } } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/graph/": { - "get": { - "summary": "Get network graph", - "description": "Retrieve the network as a graph of subject nodes (vendor users, vendor businesses, transaction parties) and signal nodes (the shared device/IP/face/document/phone/email/address values linking them), with edges representing memberships.", - "operationId": "get_network_graph", + "/v3/session/{sessionId}/add-images/": { + "patch": { + "summary": "Add an image to a session", + "description": "Attach one extra image or PDF to the ID verification (`model_type=kyc`) or Proof of Address (`model_type=poa`) result of a finished User Verification (KYC) session, for example evidence collected outside the flow. The session must be `Approved`, `Declined`, `In Review` or `Kyc Expired` and carry the result named by `model_type`; Business Verification (KYB) sessions are not supported. Each call appends one file (to the most recent result when the step ran more than once), keeps the existing files and records a `FILE_ADDED` entry in the session activity log; the upload sends no webhook and does not change the session status. The file is compressed on upload and is returned in `extra_files` of the session decision. This endpoint is limited to 10 requests per minute per API key. A scoped API key needs `sessions: write`, and without `media: read` it receives the `extra_files` links as `null`.", + "operationId": "patch_v3_session_add_images", + "x-didit-sdk": { + "resource": "sessions", + "method": "add_images", + "auth": "api_key", + "server": true + }, "tags": [ - "Networks" + "Sessions" ], "parameters": [ { - "name": "organization_id", + "name": "sessionId", "in": "path", "required": true, + "description": "UUID of the User Verification (KYC) session to add the file to (the `session_id` returned by `POST /v3/session/`). A Business Verification (KYB) session ID returns `404`.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file", + "model_type" + ], + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "The image or PDF. Allowed extensions: `jpg`, `jpeg`, `png`, `webp`, `tiff`, `pdf`. At most 15 MB as uploaded; the file is then compressed and rejected with `400` if it is still larger than 2 MB (2,097,152 bytes). Empty files are rejected." + }, + "model_type": { + "type": "string", + "enum": [ + "kyc", + "poa" + ], + "description": "`kyc` adds the file to the ID verification result, `poa` to the Proof of Address result.", + "example": "kyc" + }, + "file_password": { + "type": "string", + "description": "Password of an encrypted PDF. Without it, or with a wrong one, an encrypted PDF returns `400`." + } + } + }, + "example": { + "file": "(binary)", + "model_type": "kyc" + } + } + } + }, + "x-codeSamples": [ { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "lang": "curl", + "label": "curl", + "source": "curl -X PATCH 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/add-images/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'file=@./back-of-id.jpg' \\\n -F 'model_type=kyc'" }, { - "name": "network_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "n3a5e000-0000-4000-8000-000000000001" - }, - "description": "Network UUID." + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nsession_id = '11111111-2222-3333-4444-555555555555'\nwith open('back-of-id.jpg', 'rb') as fh:\n resp = requests.patch(\n f'https://verification.didit.me/v3/session/{session_id}/add-images/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n files={'file': ('back-of-id.jpg', fh, 'image/jpeg')},\n data={'model_type': 'kyc'},\n timeout=10,\n )\nresp.raise_for_status()\nprint(resp.json()['extra_files'])" }, { - "name": "depth", - "in": "query", - "schema": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "all" - ] - }, - "description": "Hops from the focus subject to include. Defaults to all (unbounded)." + "lang": "javascript", + "label": "JavaScript", + "source": "import { readFile } from 'node:fs/promises';\n\nconst sessionId = '11111111-2222-3333-4444-555555555555';\nconst form = new FormData();\nform.append('file', new Blob([await readFile('./back-of-id.jpg')], { type: 'image/jpeg' }), 'back-of-id.jpg');\nform.append('model_type', 'kyc');\n\nconst resp = await fetch(`https://verification.didit.me/v3/session/${sessionId}/add-images/`, {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n body: form,\n});\nif (!resp.ok) throw new Error(`Upload failed: ${resp.status}`);\nconst { extra_files: extraFiles } = await resp.json();\nconsole.log(extraFiles);" + } + ], + "responses": { + "200": { + "description": "File added. Returns every extra file of that result, including the new one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "extra_files" + ], + "properties": { + "extra_files": { + "type": "array", + "description": "Short-lived presigned links to all extra files of the result named by `model_type`, oldest first. Each link is `null` for a scoped API key without `media: read`. Read fresh links from the session decision later.", + "items": { + "type": "string", + "format": "uri", + "nullable": true + } + } + } + }, + "example": { + "extra_files": [ + "https://media.example.com/session/11111111-2222-3333-4444-555555555555/kyc-extra-files/66666666-7777-8888-9999-000000000000.jpg?expires=1789383731&signature=abc123" + ] + } + } + } }, - { - "name": "focus_kind", - "in": "query", - "schema": { - "type": "string", - "enum": [ - "vendor_user", - "vendor_business", - "transaction_party" - ] - }, - "description": "Subject type to center the graph on. Must be supplied together with focus_id; defaults to an arbitrary member when depth is bounded and neither is supplied." + "400": { + "description": "Validation error. Field errors arrive as `{\"\": [\"\"]}`; encrypted-PDF errors as `{\"detail\": \"...\"}`.", + "content": { + "application/json": { + "examples": { + "Missing file": { + "summary": "No file part in the form", + "value": { + "file": [ + "No file was submitted." + ] + } + }, + "Missing model_type": { + "summary": "model_type omitted", + "value": { + "model_type": [ + "This field is required." + ] + } + }, + "Invalid model_type": { + "summary": "model_type is not kyc or poa", + "value": { + "model_type": [ + "\"passport\" is not a valid choice." + ] + } + }, + "File too large": { + "summary": "Upload over 15 MB", + "value": { + "file": [ + "File size should not exceed 15 MB" + ] + } + }, + "Too large after compression": { + "summary": "Still over 2 MB after compression", + "value": { + "file": [ + "File size is too big. Max allowed size is 2097152 bytes." + ] + } + }, + "Encrypted PDF": { + "summary": "Encrypted PDF without file_password", + "value": { + "detail": "The PDF is encrypted. Please upload a decrypted PDF or a photo instead." + } + }, + "Wrong PDF password": { + "summary": "file_password does not open the PDF", + "value": { + "detail": "The PDF password is incorrect. Please provide the correct password." + } + } + } + } + } }, - { - "name": "focus_id", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Subject UUID to center the graph on." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/graph/?depth=2' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Network graph.", + "401": { + "description": "The scoped API key has expired. This is the only case that returns `401`: a missing, invalid or revoked key returns `403`.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NetworkGraph" + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "summary": "The key's expiry date has passed", + "value": { + "detail": "This API key has expired." + } + } } } } }, - "404": { - "description": "Network not found (or not visible to this application).", + "403": { + "description": "Authentication or authorization failure. Missing, invalid and revoked keys return `403` (not `401`); so do a key of another application, a scoped key without the permission this endpoint needs, and a request from outside the key's IP allowlist. Distinguish them by `detail`.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "No x-api-key header, or the key failed validation", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Other application": { + "summary": "A valid key that belongs to a different application than the session", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without sessions write": { + "summary": "Scoped key lacks sessions: write", + "value": { + "detail": "This API key has no write access to sessions." + } + }, + "IP not allowed": { + "summary": "Request from outside the key's IP allowlist", + "value": { + "detail": "This API key can't be used from this IP address." } } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/members/": { - "get": { - "summary": "List network members", - "description": "List the subjects (vendor users, vendor businesses, or transaction parties) that belong to this network.", - "operationId": "list_network_members", - "tags": [ - "Networks" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." }, - { - "name": "network_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "n3a5e000-0000-4000-8000-000000000001" - }, - "description": "Network UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/members/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Network members. Not paginated.", + "404": { + "description": "The session does not exist, was deleted or is outside a scoped key's limits; it is a KYB session or not in `Approved`, `Declined`, `In Review` or `Kyc Expired`; or it has no result of the requested `model_type` (checked before the file is validated). The session lookup runs before the credential check, so an unknown `sessionId` returns `404` even without a valid key.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NetworkMember" - } + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Unknown session": { + "summary": "No session with this ID exists, or it was deleted", + "value": { + "detail": "Not found." + } + }, + "Outside key limits": { + "summary": "The session is outside a scoped key's workflow or approved-only limits", + "value": { + "detail": "Session not found." + } + }, + "Not eligible": { + "summary": "Session not finished yet, or a KYB session ID", + "value": { + "detail": "No Session matches the given query." + } + }, + "No ID verification": { + "summary": "model_type=kyc on a session without ID verification", + "value": { + "detail": "The session does not have KYC data." + } + }, + "No Proof of Address": { + "summary": "model_type=poa on a session without Proof of Address", + "value": { + "detail": "The session does not have POA data." } } } } } }, - "404": { - "description": "Network not found (or not visible to this application).", + "429": { + "description": "Rate limit exceeded: 10 add-images requests per minute per API key (rejected requests count too). The shared budget of 300 write requests per minute also applies. Wait `Retry-After` seconds before retrying; the `X-RateLimit-*` headers show the budget.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Rate limited": { + "summary": "Budget for the current minute exhausted", + "value": { + "detail": "Session add-images rate limit exceeded. You can make up to 10 requests per minute." } } } } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/signals/": { - "get": { - "summary": "List network signals", - "description": "List the signals (shared device, IP, face, document number, phone, email, or address values) that link this network's members.", - "operationId": "list_network_signals", + "/v3/session/{sessionId}/update-aml-status/": { + "patch": { + "summary": "Update an AML check status", + "description": "Set the status of a session's AML screening check as a whole (the `status` of an `aml_screenings[]` entry in the session decision), for example `Approved` after your team cleared every hit. Works for KYC and KYB session IDs; pass `node_id` (query parameter or body field) when the session ran more than one AML step, otherwise the most recent AML check is updated. When the status actually changes, an `AML_STATUS_UPDATED` activity entry is recorded and a `data.updated` [webhook](https://docs.didit.me/integration/webhooks) is sent; sending the current status returns `200` and changes nothing. The session status is not recomputed: use `PATCH /v3/session/{sessionId}/update-status/` for the session decision and `PATCH /v3/session/{sessionId}/update-aml-hit-status/` for single hits. A scoped API key needs `decisions: write`.", + "operationId": "patch_v3_session_update_aml_status", + "x-didit-sdk": { + "resource": "sessions", + "method": "update_aml_status", + "auth": "api_key", + "server": true + }, "tags": [ - "Networks" + "Sessions" ], "parameters": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", + "name": "sessionId", "in": "path", "required": true, + "description": "UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "format": "uuid", + "example": "11111111-2222-3333-4444-555555555555" + } }, { - "name": "network_uuid", - "in": "path", - "required": true, + "name": "node_id", + "in": "query", + "required": false, + "description": "Workflow graph node ID of the AML check to update. Needed only when the session ran more than one AML step. Takes precedence over `node_id` in the body.", "schema": { "type": "string", - "format": "uuid", - "example": "n3a5e000-0000-4000-8000-000000000001" - }, - "description": "Network UUID." + "example": "feature_aml_1" + } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "New status of the AML check.", + "example": "Approved" + }, + "node_id": { + "type": "string", + "description": "Workflow graph node ID of the AML check, as an alternative to the query parameter.", + "example": "feature_aml_1" + } + } + }, + "example": { + "status": "Approved" + } + } + } + }, "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/signals/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X PATCH 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-aml-status/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"status\": \"Approved\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import os\n\nimport requests\n\nsession_id = '11111111-2222-3333-4444-555555555555'\nresp = requests.patch(\n f'https://verification.didit.me/v3/session/{session_id}/update-aml-status/',\n headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n json={'status': 'Approved'},\n timeout=10,\n)\nresp.raise_for_status()\nbody = resp.json()\nprint(body['previous_status'], '->', body['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const sessionId = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/session/${sessionId}/update-aml-status/`, {\n method: 'PATCH',\n headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'Approved' }),\n});\nif (!resp.ok) throw new Error(`Update failed: ${resp.status}`);\nconst body = await resp.json();\nconsole.log(body.previous_status, '->', body.status);" } ], "responses": { "200": { - "description": "Network signals. Not paginated.", + "description": "AML check status set (or unchanged when it already had this value).", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "message", + "status", + "previous_status" + ], "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NetworkSignal" - } + "message": { + "type": "string", + "description": "Always `AML status updated successfully`." + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Status of the AML check now." + }, + "previous_status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Status before the call. Equal to `status` when nothing changed." } } + }, + "example": { + "message": "AML status updated successfully", + "status": "Approved", + "previous_status": "In Review" } } } }, - "404": { - "description": "Network not found (or not visible to this application).", + "400": { + "description": "Validation error: `status` missing or not one of the allowed values. The AML check lookup runs first, so a session without an AML check returns `404` even with an invalid body.", + "content": { + "application/json": { + "examples": { + "Missing status": { + "summary": "Empty body or no status", + "value": { + "status": [ + "This field is required." + ] + } + }, + "Invalid status": { + "summary": "status not in the enum", + "value": { + "status": [ + "\"Cleared\" is not a valid choice." + ] + } + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired. This is the only case that returns `401`: a missing, invalid or revoked key returns `403`.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Expired key": { + "summary": "The key's expiry date has passed", + "value": { + "detail": "This API key has expired." } } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/timeline/": { - "get": { - "summary": "Get network timeline", - "description": "Chronological events for this network: detection, each signal's observation, and lifecycle status changes.", - "operationId": "get_network_timeline", - "tags": [ - "Networks" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "network_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "n3a5e000-0000-4000-8000-000000000001" - }, - "description": "Network UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/timeline/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Timeline events, oldest first. Not paginated.", + "403": { + "description": "Authentication or authorization failure. Missing, invalid and revoked keys return `403` (not `401`); so do a key of another application, a scoped key without the permission this endpoint needs, and a request from outside the key's IP allowlist. Distinguish them by `detail`.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NetworkTimelineEvent" - } + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Missing or invalid credentials": { + "summary": "No x-api-key header, or the key failed validation", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Other application": { + "summary": "A valid key that belongs to a different application than the session", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped key without decisions write": { + "summary": "Scoped key lacks decisions: write", + "value": { + "detail": "This API key has no write access to decisions." + } + }, + "IP not allowed": { + "summary": "Request from outside the key's IP allowlist", + "value": { + "detail": "This API key can't be used from this IP address." } } } @@ -45715,244 +67713,448 @@ } }, "404": { - "description": "Network not found (or not visible to this application).", + "description": "The session does not exist, was deleted or is outside a scoped key's limits, or it has no AML check (for the given `node_id`). The session lookup runs before the credential check, so an unknown `sessionId` returns `404` even without a valid key.", "content": { "application/json": { "schema": { "type": "object", "properties": { "detail": { - "type": "string" + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Unknown session": { + "summary": "No session with this ID exists, or it was deleted", + "value": { + "detail": "Not found." + } + }, + "Outside key limits": { + "summary": "The session is outside a scoped key's limits (also returned for a session deleted moments ago)", + "value": { + "detail": "Session not found." + } + }, + "No AML check": { + "summary": "The session never ran an AML step", + "value": { + "detail": "The session does not have an AML check." + } + }, + "No AML check for node": { + "summary": "node_id does not match an AML check of the session", + "value": { + "detail": "The session does not have an AML check for node 'feature_aml_2'." + } + } + } + } + } + }, + "429": { + "description": "Rate limit exceeded: 300 write (POST, PATCH, DELETE) requests per minute per API key. Wait `Retry-After` seconds before retrying; the `X-RateLimit-*` headers show the budget.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Human-readable error message." + } + } + }, + "examples": { + "Rate limited": { + "summary": "Budget for the current minute exhausted", + "value": { + "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." } } } } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 30 + } + } } } - } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ] } }, - "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/map/": { + "/v3/status/": { "get": { - "summary": "Get network map", - "description": "Geographic points where this network's members' addresses/locations converge, sourced from the sessions, business sessions, and transactions backing its memberships.", - "operationId": "get_network_map", + "summary": "Get service status (public, unauthenticated)", + "description": "Returns the state of each Didit service (`operational`, `degraded` or `down`), the overall state (the worst of them), and the incidents that are ongoing or were resolved in the last 7 days. Use it to power a status widget or to check Didit's health from your integration.\n\nPublic: send no auth header. The response contains no organization data and is the same for every caller. Responses are cached for 30 seconds, so poll no more than once every 30 seconds.", + "operationId": "get_v3_status", "tags": [ - "Networks" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, - { - "name": "network_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid", - "example": "n3a5e000-0000-4000-8000-000000000001" - }, - "description": "Network UUID." - } + "Status" ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/n3a5e000-0000-4000-8000-000000000001/map/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } + "security": [ + {} ], + "parameters": [], "responses": { "200": { - "description": "Network map.", + "description": "Current service status.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NetworkMap" + "$ref": "#/components/schemas/ServiceStatusFeed" + }, + "examples": { + "One service degraded": { + "summary": "An ongoing incident and one resolved this week", + "value": { + "status": "degraded", + "updated_at": "2026-10-13T09:41:07.512345Z", + "services": [ + { + "key": "service_id_verification", + "name": "ID verification", + "status": "operational" + }, + { + "key": "service_liveness_face_match", + "name": "Liveness and face match", + "status": "operational" + }, + { + "key": "service_aml_screening", + "name": "AML screening", + "status": "operational" + }, + { + "key": "service_kyb_registries", + "name": "KYB registries", + "status": "operational" + }, + { + "key": "service_database_validation", + "name": "Database validation", + "status": "degraded" + }, + { + "key": "service_phone_delivery", + "name": "Phone verification delivery", + "status": "operational" + }, + { + "key": "service_email_delivery", + "name": "Email verification delivery", + "status": "operational" + }, + { + "key": "service_transaction_monitoring", + "name": "Transaction monitoring", + "status": "operational" + } + ], + "incidents": [ + { + "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "service": "service_database_validation", + "service_name": "Database validation", + "state": "degraded", + "status": "ongoing", + "title": "Database validation is degraded in Brazil.", + "body": "Checks against Brazil (CPF) may be slow or fail since 08:55 UTC.", + "affected": "Brazil (CPF)", + "country_name": "Brazil", + "started_at": "2026-10-13T08:55:00Z", + "resolved_at": null, + "duration_minutes": null + }, + { + "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", + "service": "service_aml_screening", + "service_name": "AML screening", + "state": "down", + "status": "resolved", + "title": "AML screening is back to normal.", + "body": "Recovered at 14:32 UTC after 17 minutes.", + "affected": "", + "country_name": "", + "started_at": "2026-10-11T14:15:00Z", + "resolved_at": "2026-10-11T14:32:00Z", + "duration_minutes": 17 + } + ] + } + }, + "All operational": { + "summary": "No incident in the last 7 days", + "value": { + "status": "operational", + "updated_at": "2026-10-13T09:41:07.512345Z", + "services": [ + { + "key": "service_id_verification", + "name": "ID verification", + "status": "operational" + }, + { + "key": "service_liveness_face_match", + "name": "Liveness and face match", + "status": "operational" + }, + { + "key": "service_aml_screening", + "name": "AML screening", + "status": "operational" + }, + { + "key": "service_kyb_registries", + "name": "KYB registries", + "status": "operational" + }, + { + "key": "service_database_validation", + "name": "Database validation", + "status": "operational" + }, + { + "key": "service_phone_delivery", + "name": "Phone verification delivery", + "status": "operational" + }, + { + "key": "service_email_delivery", + "name": "Email verification delivery", + "status": "operational" + }, + { + "key": "service_transaction_monitoring", + "name": "Transaction monitoring", + "status": "operational" + } + ], + "incidents": [] + } + } } } } }, - "404": { - "description": "Network not found (or not visible to this application).", + "429": { + "description": "More than 600 GET requests in a minute from the same IP address (or the same API key, if you send one). Honor `Retry-After` before retrying.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "detail" + ], "properties": { "detail": { "type": "string" } } + }, + "example": { + "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute." } } } } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -fsS https://verification.didit.me/v3/status/" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresp = requests.get('https://verification.didit.me/v3/status/', timeout=5)\nresp.raise_for_status()\nfeed = resp.json()\nprint(feed['status'])\nfor service in feed['services']:\n if service['status'] != 'operational':\n print(service['key'], service['status'])" + }, + { + "lang": "javascript", + "label": "JavaScript", + "source": "const feed = await fetch('https://verification.didit.me/v3/status/').then((r) => r.json());\nconsole.log(feed.status);\nconst affected = feed.services.filter((s) => s.status !== 'operational');\nconsole.log(affected.map((s) => `${s.key}: ${s.status}`));" + } + ], + "x-didit-sdk": { + "resource": "system", + "method": "retrieve_status", + "auth": "public", + "server": false } } }, - "/v3/organization/{organization_id}/application/{application_id}/sessions/{session_id}/networks/": { - "get": { - "summary": "List networks for a session", - "description": "List the networks the session's vendor user belongs to. Resolves through the session's vendor_data-linked user.", - "operationId": "list_session_network_memberships", + "/v3/session/{sessionId}/simulate/": { + "post": { + "summary": "Simulate a sandbox session's status", + "description": "Force a terminal status on a sandbox session after the fact, from your backend, without running the flow. Sandbox applications only: a live session returns `404`. Works for User Verification (KYC) and Business Verification (KYB) sessions.\n\nSet `new_status`, or let `scenario` or `risk` supply it. `risk` (or the first risk the scenario's magic values carry) adds that warning to the session. The change is recorded in the session's activity.\n\n`send_webhooks` (default `true`) sends exactly the webhooks a real status change sends. With `false` the status still changes and the console still updates live, but no webhook is logged or sent, the missed-webhook recovery never re-sends that decision, and the next status change sends webhooks as usual. The response echoes the value applied.\n\nRequires the `write:sessions` privilege; a scoped API key needs write access to sessions. An unknown `session_id` returns `404` and another application's session returns `403`, both before the body is read; a live session returns `404` only after the body is validated. To simulate from the hosted flow with the session's own token, use `POST /v3/session/{sessionId}/sandbox/simulate/`. See [Sandbox & Test Data](/integration/sandbox-testing) for the scenario catalog.", + "operationId": "post_v3_session_simulate", "tags": [ - "Networks" + "Sessions" ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, + "security": [ { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, + "ApiKeyAuth": [] + } + ], + "parameters": [ { - "name": "session_id", "in": "path", + "name": "sessionId", "required": true, + "description": "UUID of the sandbox verification session. Accepts both user (KYC) and business (KYB) session IDs.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Session UUID." + "format": "uuid", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + } } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/sessions/aaaaaaaa-0000-4000-8000-000000000001/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SandboxSimulateRequest" + }, + "examples": { + "Scenario Without Webhooks": { + "summary": "Decline through a scenario without sending webhooks", + "value": { + "scenario": "decline_aml_hit", + "send_webhooks": false + } + }, + "Status Only": { + "summary": "Approve and send the usual webhooks", + "value": { + "new_status": "Approved" + } + } + } + } } - ], + }, "responses": { "200": { - "description": "Networks this entity belongs to. Not paginated.", + "description": "The status changed.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityNetworkMembership" - } + "$ref": "#/components/schemas/SandboxSimulateResponse" + }, + "examples": { + "Simulated": { + "summary": "Declined through a scenario, webhooks not sent", + "value": { + "session_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "previous_status": "In Progress", + "status": "Declined", + "scenario": "decline_aml_hit", + "risk": "POSSIBLE_MATCH_FOUND", + "send_webhooks": false } } } } } }, - "404": { - "description": "Entity not found in this application, or has no linked subject to look up.", + "400": { + "description": "Validation error: an unknown scenario or risk, no `new_status` when neither `scenario` nor `risk` supplies one, a status this endpoint does not set, or the session already has the requested status (a bare JSON array).", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "examples": { + "Unknown Scenario": { + "summary": "scenario is not in the catalog", + "value": { + "scenario": [ + "Unknown sandbox scenario: 'not-a-scenario'." + ] + } + }, + "Unknown Risk": { + "summary": "risk is not in the catalog", + "value": { + "risk": [ + "Unknown sandbox risk case: 'NOT_A_RISK'." + ] + } + }, + "Missing new_status": { + "summary": "No new_status, scenario or risk", + "value": { + "new_status": [ + "This field is required." + ] + } + }, + "Invalid new_status": { + "summary": "A status the simulation does not set", + "value": { + "new_status": [ + "\"Resubmitted\" is not a valid choice." + ] } + }, + "Same Status": { + "summary": "The session already has this status", + "value": [ + "The new status is the same as the current status." + ] } } } } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/business-sessions/{business_session_id}/networks/": { - "get": { - "summary": "List networks for a business session", - "description": "List the networks the business session's vendor business belongs to.", - "operationId": "list_business_session_network_memberships", - "tags": [ - "Networks" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "401": { + "description": "The scoped API key has expired.", + "content": { + "application/json": { + "examples": { + "Expired API Key": { + "summary": "Scoped API key past its expiry date", + "value": { + "detail": "This API key has expired." + } + } + } + } + } }, - { - "name": "business_session_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Business session UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/business-sessions/aaaaaaaa-0000-4000-8000-000000000002/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ], - "responses": { - "200": { - "description": "Networks this entity belongs to. Not paginated.", + "403": { + "description": "Missing or invalid credentials, a caller without the `write:sessions` privilege, a scoped API key without the access this endpoint needs, or a `session_id` that belongs to another application.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityNetworkMembership" - } + "examples": { + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Missing Privilege": { + "summary": "Caller lacks write:sessions", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped Key Without Access": { + "summary": "Scoped API key without this access", + "value": { + "detail": "This API key has no write access to sessions." } } } @@ -45960,83 +68162,172 @@ } }, "404": { - "description": "Entity not found in this application, or has no linked subject to look up.", + "description": "No session with this `session_id` exists, or it is a live (non-sandbox) session.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "examples": { + "Not Found": { + "summary": "Unknown or live session", + "value": { + "detail": "Not found." } } } } } } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST \\\n https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/simulate/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"scenario\": \"decline_aml_hit\", \"send_webhooks\": false}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/simulate/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json={\"scenario\": \"decline_aml_hit\", \"send_webhooks\": False},\n)\nresponse.raise_for_status()\nprint(response.json())" + } + ], + "x-didit-sdk": { + "resource": "sessions", + "method": "simulate", + "auth": "api_key", + "server": true } } }, - "/v3/organization/{organization_id}/application/{application_id}/users/{vendor_user_uuid}/networks/": { - "get": { - "summary": "List networks for a user", - "description": "List the networks a vendor user belongs to.", - "operationId": "list_user_network_memberships", + "/v3/session/{sessionId}/sandbox/simulate/": { + "post": { + "summary": "Simulate a sandbox session's status from the client", + "description": "The same simulation as `POST /v3/session/{sessionId}/simulate/`, authenticated with the session's own `Session-Token` (returned in `session_token` by [Create Session](/sessions-api/create-session)) instead of your API key, so a test harness or the hosted flow can call it from the client. The body, the response and `send_webhooks` behave exactly as on that endpoint.\n\nA `Session-Token` of a live (non-sandbox) application, or one that does not match the `sessionId` in the path, returns `404`. See [Sandbox & Test Data](/integration/sandbox-testing).", + "operationId": "post_v3_session_sandbox_simulate", "tags": [ - "Networks" + "Sessions" ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, + "security": [ { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, + "SessionTokenAuth": [] + } + ], + "parameters": [ { - "name": "vendor_user_uuid", "in": "path", + "name": "sessionId", "required": true, + "description": "UUID of the sandbox verification session. Must match the session encoded in the Session-Token used to authenticate.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Vendor user UUID." + "format": "uuid", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + } } ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/users/aaaaaaaa-0000-4000-8000-000000000003/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SandboxSimulateRequest" + }, + "examples": { + "Scenario Without Webhooks": { + "summary": "Decline through a scenario without sending webhooks", + "value": { + "scenario": "decline_aml_hit", + "send_webhooks": false + } + }, + "Status Only": { + "summary": "Approve and send the usual webhooks", + "value": { + "new_status": "Approved" + } + } + } + } } - ], + }, "responses": { "200": { - "description": "Networks this entity belongs to. Not paginated.", + "description": "The status changed.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityNetworkMembership" - } + "$ref": "#/components/schemas/SandboxSimulateResponse" + }, + "examples": { + "Simulated": { + "summary": "Declined through a scenario, webhooks not sent", + "value": { + "session_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "previous_status": "In Progress", + "status": "Declined", + "scenario": "decline_aml_hit", + "risk": "POSSIBLE_MATCH_FOUND", + "send_webhooks": false + } + } + } + } + } + }, + "400": { + "description": "Validation error: an unknown scenario or risk, no `new_status` when neither `scenario` nor `risk` supplies one, a status this endpoint does not set, or the session already has the requested status (a bare JSON array).", + "content": { + "application/json": { + "examples": { + "Unknown Scenario": { + "summary": "scenario is not in the catalog", + "value": { + "scenario": [ + "Unknown sandbox scenario: 'not-a-scenario'." + ] + } + }, + "Unknown Risk": { + "summary": "risk is not in the catalog", + "value": { + "risk": [ + "Unknown sandbox risk case: 'NOT_A_RISK'." + ] + } + }, + "Missing new_status": { + "summary": "No new_status, scenario or risk", + "value": { + "new_status": [ + "This field is required." + ] + } + }, + "Invalid new_status": { + "summary": "A status the simulation does not set", + "value": { + "new_status": [ + "\"Resubmitted\" is not a valid choice." + ] + } + }, + "Same Status": { + "summary": "The session already has this status", + "value": [ + "The new status is the same as the current status." + ] + } + } + } + } + }, + "403": { + "description": "The `Session-Token` header is missing or invalid.", + "content": { + "application/json": { + "examples": { + "Invalid Session Token": { + "summary": "Missing or invalid Session-Token", + "value": { + "detail": "You do not have permission to perform this action." } } } @@ -46044,167 +68335,349 @@ } }, "404": { - "description": "Entity not found in this application, or has no linked subject to look up.", + "description": "The `Session-Token` belongs to a live (non-sandbox) application, or does not match the `sessionId` in the path.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "examples": { + "Not Found": { + "summary": "Live session, or session_id / token mismatch", + "value": { + "detail": "Not found." } } } } } } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST \\\n https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/sandbox/simulate/ \\\n -H 'Session-Token: YOUR_SESSION_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\"new_status\": \"Approved\", \"send_webhooks\": false}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/sandbox/simulate/\",\n headers={\"Session-Token\": \"YOUR_SESSION_TOKEN\"},\n json={\"new_status\": \"Approved\", \"send_webhooks\": False},\n)\nresponse.raise_for_status()\nprint(response.json())" + } + ], + "x-didit-sdk": { + "resource": "sessions", + "method": "simulate_sandbox", + "auth": "device_token", + "server": false } } }, - "/v3/organization/{organization_id}/application/{application_id}/businesses/{vendor_business_uuid}/networks/": { + "/v3/session/{sessionId}/features/overrides/": { "get": { - "summary": "List networks for a business", - "description": "List the networks a vendor business belongs to.", - "operationId": "list_business_network_memberships", + "summary": "List a session's check overrides", + "description": "Every override of an individual feature (step) status on a User Verification (KYC) or Business Verification (KYB) session, newest first and not paginated: applied ones, ones waiting for the person to finish the flow (`pending_until_flow_finishes`) and cancelled ones, each with who asked, their reason, and whether it applied while the person was still in the flow (`forced`).\n\n`person_in_flow` tells you whether a new override with `defer_until_flow_finishes: true` would wait. `flow_finished_at` is when the person last finished the flow; it is `null` while they are still in it (a resubmission included) and when the session closed before they finished.\n\nRequires the `read:sessions` privilege; a scoped API key needs read access to decisions.", + "operationId": "get_v3_session_feature_overrides", "tags": [ - "Networks" + "Sessions" ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, + "security": [ { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - }, + "ApiKeyAuth": [] + } + ], + "parameters": [ { - "name": "vendor_business_uuid", "in": "path", + "name": "sessionId", "required": true, + "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Vendor business UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/businesses/aaaaaaaa-0000-4000-8000-000000000004/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "format": "uuid", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + } } ], "responses": { "200": { - "description": "Networks this entity belongs to. Not paginated.", + "description": "The session's check overrides, newest first.", "content": { "application/json": { "schema": { "type": "object", + "required": [ + "session_id", + "person_in_flow", + "flow_finished_at", + "overrides" + ], "properties": { - "results": { + "session_id": { + "type": "string", + "format": "uuid", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + }, + "person_in_flow": { + "type": "boolean", + "description": "Whether the person is still in the flow, so a new override with `defer_until_flow_finishes: true` would wait.", + "example": true + }, + "flow_finished_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the person last finished the flow. `null` while they are still in it (a resubmission included) and when the session closed before they finished (abandoned, expired or decided mid-flow).", + "example": null + }, + "overrides": { "type": "array", + "description": "Every override of the session, newest first.", "items": { - "$ref": "#/components/schemas/EntityNetworkMembership" + "$ref": "#/components/schemas/FeatureStatusOverride" } } } + }, + "examples": { + "Overrides": { + "summary": "One override waiting, one applied", + "value": { + "session_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "person_in_flow": true, + "flow_finished_at": null, + "overrides": [ + { + "id": "9b2f4c1e-8d3a-4e5f-a6b7-c8d9e0f1a2b3", + "node_id": "feature_liveness_1", + "feature_type": "", + "feature_model": "Face", + "previous_status": "Declined", + "new_status": "Approved", + "state": "pending_until_flow_finishes", + "forced": false, + "reason": "Liveness retried with a reviewer present.", + "requested_by": { + "email": "reviewer@example.com", + "name": "Alex Reviewer", + "type": "CONSOLE_USER" + }, + "requested_at": "2026-10-13T09:15:00Z", + "applied_at": null, + "cancelled_at": null, + "cancel_reason": null + }, + { + "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", + "node_id": "feature_ocr_1", + "feature_type": "", + "feature_model": "KYC", + "previous_status": "In Review", + "new_status": "Approved", + "state": "applied", + "forced": true, + "reason": "Document manually verified by compliance.", + "requested_by": { + "email": "reviewer@example.com", + "name": "Alex Reviewer", + "type": "CONSOLE_USER" + }, + "requested_at": "2026-10-13T09:15:00Z", + "applied_at": "2026-10-13T09:15:00Z", + "cancelled_at": null, + "cancel_reason": null + } + ] + } + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired.", + "content": { + "application/json": { + "examples": { + "Expired API Key": { + "summary": "Scoped API key past its expiry date", + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "Missing or invalid credentials, a caller without the `read:sessions` privilege, a scoped API key without the access this endpoint needs, or a `session_id` that belongs to another application.", + "content": { + "application/json": { + "examples": { + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Missing Privilege": { + "summary": "Caller lacks read:sessions", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped Key Without Access": { + "summary": "Scoped API key without this access", + "value": { + "detail": "This API key has no read access to decisions." + } + } } } } }, "404": { - "description": "Entity not found in this application, or has no linked subject to look up.", + "description": "No KYC or KYB session with this `session_id` exists.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "examples": { + "Not Found": { + "summary": "Unknown session_id", + "value": { + "detail": "Not found." } } } } } } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET \\\n https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/features/overrides/ \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.get(\n \"https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/features/overrides/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n)\nresponse.raise_for_status()\nprint(response.json())" + } + ], + "x-didit-sdk": { + "resource": "sessions", + "method": "list_feature_overrides", + "auth": "api_key", + "server": true } } }, - "/v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/networks/": { - "get": { - "summary": "List networks for a transaction", - "description": "List the networks the transaction's backing KYC/KYB subject belongs to (resolved through the transaction's session or business session).", - "operationId": "list_transaction_network_memberships", + "/v3/session/{sessionId}/features/overrides/{overrideId}/cancel/": { + "post": { + "summary": "Cancel a check override that waits for the flow to finish", + "description": "Cancel an override of a feature (step) status that is still `pending_until_flow_finishes`, so it never applies. The check keeps its status, the cancellation is recorded in the session's activity as `FEATURE_OVERRIDE_CANCELLED`, and no webhook is sent. The response is the override with `state: cancelled` and `cancel_reason: cancelled_by_reviewer`.\n\nAn override that already applied or was cancelled returns `409` with the code `feature_override_not_pending`. Find override IDs with `GET /v3/session/{sessionId}/features/overrides/` or in the `override` returned by the feature status update.\n\nRequires the `write:sessions` privilege; a scoped API key needs write access to decisions.", + "operationId": "post_v3_session_feature_override_cancel", "tags": [ - "Networks" + "Sessions" ], - "parameters": [ + "security": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, + "ApiKeyAuth": [] + } + ], + "parameters": [ { - "name": "application_id", "in": "path", + "name": "sessionId", "required": true, + "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "format": "uuid", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + } }, { - "name": "transaction_uuid", "in": "path", + "name": "overrideId", "required": true, + "description": "The `id` of the override to cancel.", "schema": { "type": "string", - "format": "uuid" - }, - "description": "Transaction UUID." - } - ], - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/transactions/aaaaaaaa-0000-4000-8000-000000000005/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "format": "uuid", + "example": "9b2f4c1e-8d3a-4e5f-a6b7-c8d9e0f1a2b3" + } } ], "responses": { "200": { - "description": "Networks this entity belongs to. Not paginated.", + "description": "The override is cancelled and will never apply.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityNetworkMembership" - } + "$ref": "#/components/schemas/FeatureStatusOverride" + }, + "examples": { + "Cancelled": { + "summary": "The cancelled override", + "value": { + "id": "9b2f4c1e-8d3a-4e5f-a6b7-c8d9e0f1a2b3", + "node_id": "feature_liveness_1", + "feature_type": "", + "feature_model": "Face", + "previous_status": "Declined", + "new_status": "Approved", + "state": "cancelled", + "forced": false, + "reason": "Liveness retried with a reviewer present.", + "requested_by": { + "email": "reviewer@example.com", + "name": "Alex Reviewer", + "type": "CONSOLE_USER" + }, + "requested_at": "2026-10-13T09:15:00Z", + "applied_at": null, + "cancelled_at": "2026-10-13T09:20:00Z", + "cancel_reason": "cancelled_by_reviewer" + } + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired.", + "content": { + "application/json": { + "examples": { + "Expired API Key": { + "summary": "Scoped API key past its expiry date", + "value": { + "detail": "This API key has expired." + } + } + } + } + } + }, + "403": { + "description": "Missing or invalid credentials, a caller without the `write:sessions` privilege, a scoped API key without the access this endpoint needs, or a `session_id` that belongs to another application.", + "content": { + "application/json": { + "examples": { + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Missing Privilege": { + "summary": "Caller lacks write:sessions", + "value": { + "detail": "You do not have permission to perform this action." + } + }, + "Scoped Key Without Access": { + "summary": "Scoped API key without this access", + "value": { + "detail": "This API key has no write access to decisions." } } } @@ -46212,31 +68685,76 @@ } }, "404": { - "description": "Entity not found in this application, or has no linked subject to look up.", + "description": "No KYC or KYB session with this `session_id` exists, or the session has no override with this `overrideId`.", "content": { "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "examples": { + "Not Found": { + "summary": "Unknown session_id", + "value": { + "detail": "Not found." + } + }, + "Override Not Found": { + "summary": "No override with this id on the session", + "value": { + "detail": "Override not found." + } + } + } + } + } + }, + "409": { + "description": "The override already applied or was cancelled.", + "content": { + "application/json": { + "examples": { + "Not Pending": { + "summary": "Only a waiting override can be cancelled", + "value": { + "detail": "This override is no longer pending: it already applied or was cancelled.", + "code": "feature_override_not_pending" } } } } } } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST \\\n https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/features/overrides/9b2f4c1e-8d3a-4e5f-a6b7-c8d9e0f1a2b3/cancel/ \\\n -H 'x-api-key: YOUR_API_KEY'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/features/overrides/9b2f4c1e-8d3a-4e5f-a6b7-c8d9e0f1a2b3/cancel/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n)\nresponse.raise_for_status()\nprint(response.json())" + } + ], + "x-didit-sdk": { + "resource": "sessions", + "method": "cancel_feature_override", + "auth": "api_key", + "server": true } } }, - "/v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/parties/{transaction_party_uuid}/networks/": { - "get": { - "summary": "List networks for a transaction party", - "description": "List the networks a transaction party (remitter, beneficiary, or counterparty) belongs to, resolved through the party itself rather than through the transaction's backing session.", - "operationId": "list_transaction_party_network_memberships", + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/tags/add/": { + "post": { + "summary": "Add a tag to a network", + "description": "Put one of your application's tags on the network. Returns the network detail, whose `tags` lists your application's tags on the network, with `201` when the tag was added and `200` when it was already on the network. A tag that belongs to another application, or does not exist, returns 404 `Tag not found.`. Requires a full-access API key, or `write:networks` for a console member.", + "operationId": "add_network_tag", "tags": [ "Networks" ], + "security": [ + { + "ApiKeyAuth": [] + } + ], "parameters": [ { "name": "organization_id", @@ -46259,357 +68777,333 @@ "description": "Application UUID." }, { - "name": "transaction_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Transaction UUID." - }, - { - "name": "transaction_party_uuid", + "name": "network_uuid", "in": "path", "required": true, "schema": { "type": "string", - "format": "uuid" + "format": "uuid", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }, - "description": "Transaction party UUID." + "description": "Network UUID." } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetworkTagRequest" + }, + "example": { + "tag_uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7" + } + } + } + }, "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/transactions/aaaaaaaa-0000-4000-8000-000000000005/parties/bbbbbbbb-0000-4000-8000-000000000006/networks/' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/3fa85f64-5717-4562-b3fc-2c963f66afa6/tags/add/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"tag_uuid\": \"7c9e6679-7425-40de-944b-e07fc1f90ae7\"}'" } ], "responses": { - "200": { - "description": "Networks this entity belongs to. Not paginated.", + "201": { + "description": "The tag was added. Returns the network detail.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityNetworkMembership" + "$ref": "#/components/schemas/NetworkDetailResponse" + }, + "example": { + "uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "network_id": "N-0042", + "name": null, + "status": "in_review", + "risk": { + "score": 82, + "band": "high", + "bar": 0.82 + }, + "pattern_types": [ + "exact_same_device", + "same_email" + ], + "signal_types": [ + "device", + "email" + ], + "member_counts": { + "total": 3, + "users": 3, + "businesses": 0, + "transaction_parties": 0 + }, + "member_status": { + "approved": 1, + "declined": 1, + "in_review": 0, + "not_completed": 1 + }, + "signal_coverage": { + "device": { + "signals": 1, + "subjects": 3, + "coverage": 1.0 + }, + "email": { + "signals": 1, + "subjects": 2, + "coverage": 0.6667 + } + }, + "first_activity_at": "2026-10-01T09:12:44Z", + "last_activity_at": "2026-10-12T16:03:10Z", + "stats": { + "members": { + "total": 3, + "users": 3, + "businesses": 0, + "transaction_parties": 0 + }, + "member_status": { + "approved": 1, + "declined": 1, + "in_review": 0, + "not_completed": 1 + }, + "signals": { + "total": 2, + "by_type": { + "device": { + "signals": 1, + "subjects": 3, + "coverage": 1.0 + }, + "email": { + "signals": 1, + "subjects": 2, + "coverage": 0.6667 + } } } - } - } - } - } - }, - "404": { - "description": "Entity not found in this application, or has no linked subject to look up.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" + }, + "fraud_tags": [], + "risk_signals": { + "residential_proxy_reuse": { + "detected": false, + "signal_count": 0, + "max_subject_count": 0 + }, + "email_intelligence": { + "enriched_signal_count": 1, + "high_risk_count": 0, + "social_profile_count": 0 + }, + "phone_intelligence": { + "enriched_signal_count": 0, + "high_risk_count": 0 } - } + }, + "reviewed_at": "2026-10-12T16:30:00Z", + "reviewed_by": "reviewer@example.com", + "review_reason": "Shared device across three new accounts.", + "notes_count": 0, + "sharing_mode": "global", + "cross_org_available": true, + "cross_org_unavailable_reason": null, + "tags": [ + { + "uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7", + "name": "SUSPICIOUS", + "color": "#6366F1", + "source": "custom", + "added_by": null, + "added_at": "2026-10-13T08:15:00Z" + } + ], + "outcomes": { + "approved": 1, + "declined": 1, + "in_review": 0, + "total": 3 + }, + "patterns": [ + { + "type": "background", + "score": 0.8125, + "members": 3, + "reason": null, + "computed_at": "2026-10-13T08:00:00Z" + } + ] } } } - } - } - } - }, - "/v3/biometric-templates/": { - "get": { - "summary": "List retained biometric templates", - "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nReturns a paginated list of the templates the application currently holds. Purged templates are not listed.", - "operationId": "list_biometric_templates", - "tags": [ - "Biometric Templates" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "parameters": [ - { - "in": "query", - "name": "vendor_data", - "required": false, - "description": "Case-insensitive substring match on the owning User's current `vendor_data`.", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "vendor_user_uuid", - "required": false, - "description": "Exact match on the owning User.", - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "in": "query", - "name": "status", - "required": false, - "description": "Lifecycle status. Purged templates are never listed.", - "schema": { - "type": "string", - "enum": [ - "pending", - "active", - "expired", - "held_out_of_use", - "purge_pending", - "purge_failed" - ] - } - }, - { - "in": "query", - "name": "source_type", - "required": false, - "description": "How the template was created.", - "schema": { - "type": "string", - "enum": [ - "session_delete", - "automatic_retention", - "backfill" - ] - } - }, - { - "in": "query", - "name": "retained_from", - "required": false, - "description": "Templates retained at or after this time.", - "schema": { - "type": "string", - "format": "date-time" - } }, - { - "in": "query", - "name": "retained_to", - "required": false, - "description": "Templates retained at or before this time. Must not be earlier than `retained_from` (`400`).", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "in": "query", - "name": "ordering", - "required": false, - "description": "Sort order.", - "schema": { - "type": "string", - "enum": [ - "retained_at", - "-retained_at", - "expires_at", - "-expires_at" - ], - "default": "-retained_at" - } - }, - { - "in": "query", - "name": "limit", - "required": false, - "description": "Page size.", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "offset", - "required": false, - "description": "Pagination offset.", - "schema": { - "type": "integer" - } - } - ], - "responses": { "200": { - "description": "Paginated templates.", + "description": "The tag was already on the network and is left as it is. Returns the network detail.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "nullable": true + "$ref": "#/components/schemas/NetworkDetailResponse" + }, + "example": { + "uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "network_id": "N-0042", + "name": null, + "status": "in_review", + "risk": { + "score": 82, + "band": "high", + "bar": 0.82 + }, + "pattern_types": [ + "exact_same_device", + "same_email" + ], + "signal_types": [ + "device", + "email" + ], + "member_counts": { + "total": 3, + "users": 3, + "businesses": 0, + "transaction_parties": 0 + }, + "member_status": { + "approved": 1, + "declined": 1, + "in_review": 0, + "not_completed": 1 + }, + "signal_coverage": { + "device": { + "signals": 1, + "subjects": 3, + "coverage": 1.0 }, - "previous": { - "type": "string", - "nullable": true + "email": { + "signals": 1, + "subjects": 2, + "coverage": 0.6667 + } + }, + "first_activity_at": "2026-10-01T09:12:44Z", + "last_activity_at": "2026-10-12T16:03:10Z", + "stats": { + "members": { + "total": 3, + "users": 3, + "businesses": 0, + "transaction_parties": 0 + }, + "member_status": { + "approved": 1, + "declined": 1, + "in_review": 0, + "not_completed": 1 }, - "results": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Template id. Randomly generated; not derived from the deleted session or its face." - }, - "vendor_user_uuid": { - "type": "string", - "format": "uuid", - "description": "The owning User." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "The User's current `vendor_data`, resolved when you read the template (never copied)." - }, - "source_type": { - "type": "string", - "enum": [ - "session_delete", - "automatic_retention", - "backfill" - ], - "description": "How the template was created: a session deletion (API or Console), the application's automatic data-retention window, or a Didit-operated backfill you instructed." - }, - "provenance_reference": { - "type": "string", - "format": "uuid", - "description": "Opaque reference tying the template to its audit trail. It is not the deleted session's id and cannot be resolved to it." - }, - "retention_policy": { - "type": "string", - "enum": [ - "delete_with_session", - "retain_until_user_deleted" - ], - "description": "The application policy at retention time." - }, - "retention_override": { - "type": "boolean", - "nullable": true, - "description": "The `retain_face_embeddings` value sent on the deletion, or `null` when the policy applied." - }, - "retained_at": { - "type": "string", - "format": "date-time" - }, - "expires_at": { - "type": "string", - "format": "date-time", - "description": "When the template expires and is purged automatically. Always set." - }, - "retained_by": { - "type": "string", - "nullable": true, - "description": "Principal that gave the instruction: the application id for API keys, the Console member id for Console actions, or a `service:` identifier for automatic retention. Never an email address." - }, - "retained_by_type": { - "type": "string", - "enum": [ - "API_KEY", - "CONSOLE_USER", - "SYSTEM" - ] - }, - "instruction_class": { - "type": "string", - "enum": [ - "operational_session_delete", - "privacy_erasure" - ] - }, - "instruction_source": { - "type": "string", - "enum": [ - "api", - "console", - "automatic_retention", - "backfill", - "privacy_erasure", - "explicit_template_purge", - "vendor_user_delete", - "expiry", - "reconciliation", - "migration", - "system" - ] - }, - "instruction_id": { - "type": "string", - "description": "The `instruction_id` sent on the deletion, or a Didit-generated id." - }, - "held_out_of_use": { - "type": "boolean", - "description": "`true` only while a legal hold applies. A held template is excluded from all matching." - }, - "status": { - "type": "string", - "enum": [ - "pending", - "active", - "expired", - "held_out_of_use", - "purge_pending", - "purge_failed", - "purged" - ], - "description": "Only `active` templates that are not held, not past `expires_at`, and whose User still exists take part in matching." - }, - "purged_at": { - "type": "string", - "format": "date-time", - "nullable": true - } + "signals": { + "total": 2, + "by_type": { + "device": { + "signals": 1, + "subjects": 3, + "coverage": 1.0 + }, + "email": { + "signals": 1, + "subjects": 2, + "coverage": 0.6667 } } } + }, + "fraud_tags": [], + "risk_signals": { + "residential_proxy_reuse": { + "detected": false, + "signal_count": 0, + "max_subject_count": 0 + }, + "email_intelligence": { + "enriched_signal_count": 1, + "high_risk_count": 0, + "social_profile_count": 0 + }, + "phone_intelligence": { + "enriched_signal_count": 0, + "high_risk_count": 0 + } + }, + "reviewed_at": "2026-10-12T16:30:00Z", + "reviewed_by": "reviewer@example.com", + "review_reason": "Shared device across three new accounts.", + "notes_count": 0, + "sharing_mode": "global", + "cross_org_available": true, + "cross_org_unavailable_reason": null, + "tags": [ + { + "uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7", + "name": "SUSPICIOUS", + "color": "#6366F1", + "source": "custom", + "added_by": null, + "added_at": "2026-10-13T08:15:00Z" + } + ], + "outcomes": { + "approved": 1, + "declined": 1, + "in_review": 0, + "total": 3 + }, + "patterns": [ + { + "type": "background", + "score": 0.8125, + "members": 3, + "reason": null, + "computed_at": "2026-10-13T08:00:00Z" + } + ] + } + } + } + }, + "400": { + "description": "Invalid body: `tag_uuid` is missing or is not a UUID.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tag_uuid": { + "type": "array", + "items": { + "type": "string" + } + } } }, "examples": { - "One active template": { + "Missing tag_uuid": { + "summary": "Missing tag_uuid", "value": { - "count": 1, - "next": null, - "previous": null, - "results": [ - { - "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", - "vendor_user_uuid": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f", - "vendor_data": "user-42", - "source_type": "session_delete", - "provenance_reference": "6b1e8d2f-4a3c-4f9e-8c7d-1a2b3c4d5e6f", - "retention_policy": "retain_until_user_deleted", - "retention_override": null, - "retained_at": "2026-08-28T10:15:42.318402Z", - "expires_at": "2027-08-28T10:15:42.318402Z", - "retained_by": "3c9e2b1f-7d4a-4e8b-9c1d-5f6a7b8c9d0e", - "retained_by_type": "API_KEY", - "instruction_class": "operational_session_delete", - "instruction_source": "api", - "instruction_id": "post-approval-cleanup-2026-08", - "held_out_of_use": false, - "status": "active", - "purged_at": null - } + "tag_uuid": [ + "This field is required." + ] + } + }, + "Invalid tag_uuid": { + "summary": "Invalid tag_uuid", + "value": { + "tag_uuid": [ + "Must be a valid UUID." ] } } @@ -46617,16 +69111,27 @@ } } }, - "400": { - "description": "Invalid filter value, or `retained_to` earlier than `retained_from`. Field-keyed validation errors.", + "401": { + "description": "Missing or invalid credentials.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, "examples": { - "Bad range": { + "Missing or invalid credentials": { + "summary": "Missing or invalid credentials", "value": { - "retained_to": [ - "Must be on or after retained_from." - ] + "detail": "Authentication credentials were not provided or are invalid." } } } @@ -46634,11 +69139,37 @@ } }, "403": { - "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", + "description": "The API key belongs to another application, or it is a key with restricted access: Networks endpoints need a full-access key. A console member token needs `write:networks`. Also returned with `code: networks_not_entitled` when Networks is not enabled for the organization.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, "examples": { - "Forbidden": { + "Networks not enabled": { + "summary": "Networks not enabled", + "value": { + "detail": "Networks is disabled for this organization. Contact Didit support if you believe this is an error.", + "code": "networks_not_entitled" + } + }, + "Restricted API key": { + "summary": "Restricted API key", + "value": { + "detail": "This API key has no access to this endpoint." + } + }, + "No permission": { + "summary": "No permission", "value": { "detail": "You do not have permission to perform this action." } @@ -46646,24 +69177,57 @@ } } } + }, + "404": { + "description": "Not found.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, + "examples": { + "Network not found": { + "summary": "Network not found", + "value": { + "detail": "Network not found." + } + }, + "Tag not found": { + "summary": "Tag not found", + "value": { + "detail": "Tag not found." + } + } + } + } + } } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/biometric-templates/?vendor_data=user-42&status=active' \\\n -H 'x-api-key: YOUR_API_KEY'" - } - ] + "x-didit-sdk": { + "resource": "networks", + "method": "add_tag", + "auth": "api_key", + "server": true, + "scope": "application" + } } }, - "/v3/biometric-templates/count/": { - "get": { - "summary": "Count retained biometric templates", - "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nReturns the number of templates matching the same filters as the list endpoint. Use it to check what a filtered bulk purge with `delete_all: true` would touch.", - "operationId": "count_biometric_templates", + "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/tags/remove/": { + "post": { + "summary": "Remove a tag from a network", + "description": "Remove one of your application's tags from the network. Returns the network detail. 404 when the tag is not on the network, or does not belong to your application. Requires a full-access API key, or `write:networks` for a console member.", + "operationId": "remove_network_tag", "tags": [ - "Biometric Templates" + "Networks" ], "security": [ { @@ -46672,110 +69236,239 @@ ], "parameters": [ { - "in": "query", - "name": "vendor_data", - "required": false, - "description": "Case-insensitive substring match on the owning User's current `vendor_data`.", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "vendor_user_uuid", - "required": false, - "description": "Exact match on the owning User.", + "name": "organization_id", + "in": "path", + "required": true, "schema": { "type": "string", "format": "uuid" - } - }, - { - "in": "query", - "name": "status", - "required": false, - "description": "Lifecycle status. Purged templates are never listed.", - "schema": { - "type": "string", - "enum": [ - "pending", - "active", - "expired", - "held_out_of_use", - "purge_pending", - "purge_failed" - ] - } + }, + "description": "Organization UUID." }, { - "in": "query", - "name": "source_type", - "required": false, - "description": "How the template was created.", + "name": "application_id", + "in": "path", + "required": true, "schema": { "type": "string", - "enum": [ - "session_delete", - "automatic_retention", - "backfill" - ] - } + "format": "uuid" + }, + "description": "Application UUID." }, { - "in": "query", - "name": "retained_from", - "required": false, - "description": "Templates retained at or after this time.", + "name": "network_uuid", + "in": "path", + "required": true, "schema": { "type": "string", - "format": "date-time" + "format": "uuid", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + }, + "description": "Network UUID." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetworkTagRequest" + }, + "example": { + "tag_uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7" + } } - }, + } + }, + "x-codeSamples": [ { - "in": "query", - "name": "retained_to", - "required": false, - "description": "Templates retained at or before this time. Must not be earlier than `retained_from` (`400`).", - "schema": { - "type": "string", - "format": "date-time" - } + "lang": "curl", + "label": "curl", + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/networks/3fa85f64-5717-4562-b3fc-2c963f66afa6/tags/remove/' \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"tag_uuid\": \"7c9e6679-7425-40de-944b-e07fc1f90ae7\"}'" } ], "responses": { "200": { - "description": "Count.", + "description": "The tag was removed from the network. Returns the network detail.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetworkDetailResponse" + }, + "example": { + "uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "network_id": "N-0042", + "name": null, + "status": "in_review", + "risk": { + "score": 82, + "band": "high", + "bar": 0.82 + }, + "pattern_types": [ + "exact_same_device", + "same_email" + ], + "signal_types": [ + "device", + "email" + ], + "member_counts": { + "total": 3, + "users": 3, + "businesses": 0, + "transaction_parties": 0 + }, + "member_status": { + "approved": 1, + "declined": 1, + "in_review": 0, + "not_completed": 1 + }, + "signal_coverage": { + "device": { + "signals": 1, + "subjects": 3, + "coverage": 1.0 + }, + "email": { + "signals": 1, + "subjects": 2, + "coverage": 0.6667 + } + }, + "first_activity_at": "2026-10-01T09:12:44Z", + "last_activity_at": "2026-10-12T16:03:10Z", + "stats": { + "members": { + "total": 3, + "users": 3, + "businesses": 0, + "transaction_parties": 0 + }, + "member_status": { + "approved": 1, + "declined": 1, + "in_review": 0, + "not_completed": 1 + }, + "signals": { + "total": 2, + "by_type": { + "device": { + "signals": 1, + "subjects": 3, + "coverage": 1.0 + }, + "email": { + "signals": 1, + "subjects": 2, + "coverage": 0.6667 + } + } + } + }, + "fraud_tags": [], + "risk_signals": { + "residential_proxy_reuse": { + "detected": false, + "signal_count": 0, + "max_subject_count": 0 + }, + "email_intelligence": { + "enriched_signal_count": 1, + "high_risk_count": 0, + "social_profile_count": 0 + }, + "phone_intelligence": { + "enriched_signal_count": 0, + "high_risk_count": 0 + } + }, + "reviewed_at": "2026-10-12T16:30:00Z", + "reviewed_by": "reviewer@example.com", + "review_reason": "Shared device across three new accounts.", + "notes_count": 0, + "sharing_mode": "global", + "cross_org_available": true, + "cross_org_unavailable_reason": null, + "tags": [], + "outcomes": { + "approved": 1, + "declined": 1, + "in_review": 0, + "total": 3 + }, + "patterns": [ + { + "type": "background", + "score": 0.8125, + "members": 3, + "reason": null, + "computed_at": "2026-10-13T08:00:00Z" + } + ] + } + } + } + }, + "400": { + "description": "Invalid body: `tag_uuid` is missing or is not a UUID.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "count": { - "type": "integer", - "minimum": 0 + "tag_uuid": { + "type": "array", + "items": { + "type": "string" + } } } }, "examples": { - "Count": { + "Missing tag_uuid": { + "summary": "Missing tag_uuid", "value": { - "count": 128 + "tag_uuid": [ + "This field is required." + ] + } + }, + "Invalid tag_uuid": { + "summary": "Invalid tag_uuid", + "value": { + "tag_uuid": [ + "Must be a valid UUID." + ] } } } } } }, - "400": { - "description": "Invalid filter value, or `retained_to` earlier than `retained_from`. Field-keyed validation errors.", + "401": { + "description": "Missing or invalid credentials.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, "examples": { - "Bad range": { + "Missing or invalid credentials": { + "summary": "Missing or invalid credentials", "value": { - "retained_to": [ - "Must be on or after retained_from." - ] + "detail": "Authentication credentials were not provided or are invalid." } } } @@ -46783,11 +69476,37 @@ } }, "403": { - "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", + "description": "The API key belongs to another application, or it is a key with restricted access: Networks endpoints need a full-access key. A console member token needs `write:networks`. Also returned with `code: networks_not_entitled` when Networks is not enabled for the organization.", "content": { "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, "examples": { - "Forbidden": { + "Networks not enabled": { + "summary": "Networks not enabled", + "value": { + "detail": "Networks is disabled for this organization. Contact Didit support if you believe this is an error.", + "code": "networks_not_entitled" + } + }, + "Restricted API key": { + "summary": "Restricted API key", + "value": { + "detail": "This API key has no access to this endpoint." + } + }, + "No permission": { + "summary": "No permission", "value": { "detail": "You do not have permission to perform this action." } @@ -46795,24 +69514,296 @@ } } } + }, + "404": { + "description": "Not found.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "description": "Present on coded errors, for example `networks_not_entitled`." + } + } + }, + "examples": { + "Network not found": { + "summary": "Network not found", + "value": { + "detail": "Network not found." + } + }, + "Tag not found": { + "summary": "Tag not found", + "value": { + "detail": "Tag not found." + } + }, + "Tag not on the network": { + "summary": "Tag not on the network", + "value": { + "detail": "This tag is not on the network." + } + } + } + } + } } }, + "x-didit-sdk": { + "resource": "networks", + "method": "remove_tag", + "auth": "api_key", + "server": true, + "scope": "application" + } + } + }, + "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/remind/": { + "post": { + "summary": "Remind the customer of a request", + "description": "Send a waiting customer request's email again, to the same verified recipient, with the same content. You can remind a request at most once every 24 hours. A reminder changes nothing else: the request keeps its deadline and the case keeps its status. Each reminder is logged on the case as a CUSTOMER_REQUEST event with action `reminded`. The response's `delivery_status` is how this reminder's delivery ended: SENT, SIMULATED (the email was not sent), or UNKNOWN (the email might have been accepted; it is not sent again automatically). `next_reminder_at` is when you can remind this request again. Returns 409 with a `reason` when the customer no longer has the request to answer, and 429 with `retry_at` and a `Retry-After` header when the request was reminded in the last 24 hours. Requires `write:cases`.", + "operationId": "organization_application_cases_requests_remind_create", + "parameters": [ + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true, + "description": "Organization UUID." + }, + { + "in": "path", + "name": "application_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true, + "description": "Application UUID." + }, + { + "in": "path", + "name": "case_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true, + "description": "Case UUID." + }, + { + "in": "path", + "name": "request_uuid", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true, + "description": "Customer request UUID, from the case's request list." + } + ], + "tags": [ + "Case Management" + ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X GET 'https://verification.didit.me/v3/biometric-templates/count/?status=active' \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/requests/3fa85f64-5717-4562-b3fc-2c963f66afa6/remind/' \\\n -H 'x-api-key: YOUR_API_KEY'" } - ] + ], + "responses": { + "200": { + "description": "The reminder was recorded and its email sent.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "request", + "reminded_at", + "delivery_status", + "next_reminder_at" + ], + "properties": { + "request": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequest" + }, + "reminded_at": { + "type": "string", + "format": "date-time", + "description": "When this reminder was recorded." + }, + "delivery_status": { + "type": "string", + "enum": [ + "SENT", + "SIMULATED", + "UNKNOWN" + ], + "description": "How this reminder's delivery ended. SIMULATED: the email was not sent. UNKNOWN: the email might have been accepted; it is not sent again automatically." + }, + "next_reminder_at": { + "type": "string", + "format": "date-time", + "description": "When you can remind this request again: `reminded_at` plus 24 hours." + } + } + }, + "example": { + "request": { + "uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "purpose": "confirm_details", + "language": "en", + "recipient": "customer@example.com", + "expires_at": "2026-10-20T09:00:00Z", + "actor_email": "reviewer@example.com", + "delivery_status": "SENT", + "delivered_at": "2026-10-13T09:00:04Z", + "cancelled_at": null, + "responded_at": null, + "response": null, + "has_attachment": false, + "created_at": "2026-10-13T09:00:00Z" + }, + "reminded_at": "2026-10-14T10:30:00Z", + "delivery_status": "SENT", + "next_reminder_at": "2026-10-15T10:30:00Z" + } + } + } + }, + "400": { + "description": "The case has a resolution pending approval. The body is a list of strings.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "example": [ + "Customer messages require an active case without a pending resolution." + ] + } + } + }, + "403": { + "description": "The caller lacks the required cases permission." + }, + "404": { + "description": "The request or case is unavailable for this caller." + }, + "409": { + "description": "The request no longer waits on the customer, so there is nothing to remind them of. `reason` says why: `answered` (the customer responded), `cancelled`, `expired`, `closed` (the case was resolved after the request went out, even if it was reopened since), `not_actionable` (a review-in-progress update asks nothing), `not_delivered` (the first email has not gone out), or `contact_unavailable` (the verified email is no longer available).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "enum": [ + "case_request_not_waiting" + ] + }, + "reason": { + "type": "string", + "enum": [ + "answered", + "cancelled", + "expired", + "closed", + "not_actionable", + "not_delivered", + "contact_unavailable" + ] + } + } + }, + "example": { + "detail": "This request no longer waits on the customer, so there is nothing to remind them of.", + "code": "case_request_not_waiting", + "reason": "answered" + } + } + } + }, + "429": { + "description": "The request was already reminded in the last 24 hours. `retry_at` and the `Retry-After` header say when you can remind it again.", + "headers": { + "Retry-After": { + "description": "Seconds to wait before you can remind this request again.", + "schema": { + "type": "integer", + "minimum": 1, + "example": 3600 + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string", + "enum": [ + "case_request_reminder_too_soon" + ] + }, + "retry_at": { + "type": "string", + "format": "date-time" + } + } + }, + "example": { + "detail": "This request was already reminded in the last 24 hours.", + "code": "case_request_reminder_too_soon", + "retry_at": "2026-10-15T10:30:00Z" + } + } + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-didit-sdk": { + "resource": "cases.requests", + "method": "remind", + "auth": "api_key", + "server": true, + "scope": "application" + } } }, - "/v3/biometric-templates/{template_uuid}/": { - "get": { - "summary": "Get a retained biometric template", - "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nReturns one template. The response never includes an image, a vector, the deleted session's id, or identity data.", - "operationId": "get_biometric_template", + "/v3/session/{sessionId}/decision-email-preview/": { + "post": { + "summary": "Preview a session's decision email", + "description": "Render the email the person receives when you set a session to `Approved` or `Declined` with `send_email: true`, without changing the session or sending anything. The response shows the subject and HTML exactly as they are sent, the recipient, the sender, the language, and whether confirming the change sends the email at all (`will_be_sent`, `not_sent_reason`).\n\nThe recipient is the person's own address: the one they verified in an email verification step of the flow, else the one given in `contact_details` when the session was created. The sender is your application's verified sender domain when white label applies (`sender_kind: customer`), else Didit's sender (`sender_kind: didit`).\n\nTo send exactly what you previewed, call [Update Session Status](/sessions-api/update-status) with the same `new_status`, `send_email: true`, `email_address` set to `recipient`, `email_language` set to `language` and `email_preview_token` set to `preview_token`. The receipt expires after 15 minutes. When the recipient, language, session status, sender or email changed since the preview, the update returns `409` before anything changes; preview again and confirm with the new receipt.\n\nWorks for User Verification (KYC) and Business Verification (KYB) sessions in `Approved`, `Declined`, `In Review`, `Kyc Expired`, `Abandoned` or `Resubmitted` status. The status check runs before body validation. Requires the `write:sessions` privilege; a scoped API key needs write access to decisions. Responses use `Cache-Control: no-store`: treat them as sensitive verification content.", + "operationId": "post_v3_session_decision_email_preview", "tags": [ - "Biometric Templates" + "Sessions" ], "security": [ { @@ -46822,159 +69813,251 @@ "parameters": [ { "in": "path", - "name": "template_uuid", + "name": "sessionId", "required": true, - "description": "Template id, as returned in `biometric_template_uuid` by the session deletion endpoints, `biometric_template_id` on a Face Search match, or the list endpoint.", + "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs.", "schema": { "type": "string", "format": "uuid", - "example": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b" + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "new_status" + ], + "properties": { + "new_status": { + "type": "string", + "enum": [ + "Approved", + "Declined" + ], + "description": "The decision whose email you want to preview.", + "example": "Approved" + }, + "email_language": { + "type": "string", + "default": "en", + "description": "Language of the email, the same default as Update Session Status. A language that is not offered falls back to English; `language` in the response says which one is used.", + "example": "en" + } + } + }, + "examples": { + "Approved": { + "summary": "Preview the Approved email in English", + "value": { + "new_status": "Approved" + } + }, + "Declined in Spanish": { + "summary": "Preview the Declined email in Spanish", + "value": { + "new_status": "Declined", + "email_language": "es" + } + } + } } } - ], + }, "responses": { "200": { - "description": "The template.", + "description": "Preview only. The session is unchanged and no email is sent.", + "headers": { + "Cache-Control": { + "description": "Always `no-store`: the preview carries verification content and must not be cached.", + "schema": { + "type": "string", + "example": "no-store" + } + } + }, "content": { "application/json": { "schema": { "type": "object", + "required": [ + "recipient", + "recipient_source", + "sender", + "sender_name", + "sender_email", + "sender_kind", + "subject", + "html", + "language", + "will_be_sent", + "not_sent_reason", + "preview_token" + ], "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Template id. Randomly generated; not derived from the deleted session or its face." - }, - "vendor_user_uuid": { - "type": "string", - "format": "uuid", - "description": "The owning User." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "The User's current `vendor_data`, resolved when you read the template (never copied)." - }, - "source_type": { - "type": "string", - "enum": [ - "session_delete", - "automatic_retention", - "backfill" - ], - "description": "How the template was created: a session deletion (API or Console), the application's automatic data-retention window, or a Didit-operated backfill you instructed." - }, - "provenance_reference": { + "recipient": { "type": "string", - "format": "uuid", - "description": "Opaque reference tying the template to its audit trail. It is not the deleted session's id and cannot be resolved to it." + "description": "The address the email goes to. An empty string when no address is known for the person: then no email can be sent and `preview_token` is empty too. Pass it as `email_address` when you confirm.", + "example": "person@example.com" }, - "retention_policy": { + "recipient_source": { "type": "string", "enum": [ - "delete_with_session", - "retain_until_user_deleted" + "email_verification", + "contact_details", + "" ], - "description": "The application policy at retention time." - }, - "retention_override": { - "type": "boolean", - "nullable": true, - "description": "The `retain_face_embeddings` value sent on the deletion, or `null` when the policy applied." + "description": "Where `recipient` comes from. `email_verification`: the address the person verified in an email verification step of the flow. `contact_details`: the address given in `contact_details` when the session was created. An empty string when there is no address.", + "example": "email_verification" }, - "retained_at": { + "sender": { "type": "string", - "format": "date-time" + "description": "The `From` header the email goes out with.", + "example": "Example App " }, - "expires_at": { + "sender_name": { "type": "string", - "format": "date-time", - "description": "When the template expires and is purged automatically. Always set." + "description": "The display name part of `sender`.", + "example": "Example App" }, - "retained_by": { + "sender_email": { "type": "string", - "nullable": true, - "description": "Principal that gave the instruction: the application id for API keys, the Console member id for Console actions, or a `service:` identifier for automatic retention. Never an email address." + "format": "email", + "description": "The address part of `sender`.", + "example": "no-reply@mail.example.com" }, - "retained_by_type": { + "sender_kind": { "type": "string", "enum": [ - "API_KEY", - "CONSOLE_USER", - "SYSTEM" - ] + "customer", + "didit" + ], + "description": "`customer`: the email goes out from your application's verified sender domain (white label). `didit`: it goes out from Didit's own sender.", + "example": "customer" }, - "instruction_class": { + "subject": { "type": "string", - "enum": [ - "operational_session_delete", - "privacy_erasure" - ] + "description": "The subject line exactly as it is sent.", + "example": "Your verification has been approved in Example App" }, - "instruction_source": { + "html": { "type": "string", - "enum": [ - "api", - "console", - "automatic_retention", - "backfill", - "privacy_erasure", - "explicit_template_purge", - "vendor_user_delete", - "expiry", - "reconciliation", - "migration", - "system" - ] + "description": "The email's HTML exactly as it is sent, with its logo embedded as a data URL. Render it in a sandboxed frame, never directly in your application DOM.", + "example": "..." }, - "instruction_id": { + "language": { "type": "string", - "description": "The `instruction_id` sent on the deletion, or a Didit-generated id." + "description": "The language the email is written in. English (`en`) when the `email_language` you asked for is not offered.", + "example": "en" }, - "held_out_of_use": { + "will_be_sent": { "type": "boolean", - "description": "`true` only while a legal hold applies. A held template is excluded from all matching." + "description": "Whether confirming the change with `send_email: true` sends this email. `false` when `not_sent_reason` is set.", + "example": true }, - "status": { + "not_sent_reason": { "type": "string", + "nullable": true, "enum": [ - "pending", - "active", - "expired", - "held_out_of_use", - "purge_pending", - "purge_failed", - "purged" + "status_unchanged", + "notification_emails_disabled", + "no_recipient", + "sandbox", + null ], - "description": "Only `active` templates that are not held, not past `expires_at`, and whose User still exists take part in matching." + "description": "Why confirming would send no email, or `null` when it will be sent. `status_unchanged`: the session already has this status, so the update changes nothing and sends nothing. `notification_emails_disabled`: the session was created without `contact_details.send_notification_emails: true`, so no decision email goes to the address given in `contact_details`. `no_recipient`: no address is known for the person. `sandbox`: a sandbox session whose recipient has not opted into sandbox email delivery, so the send is only simulated.", + "example": null }, - "purged_at": { + "preview_token": { "type": "string", - "format": "date-time", - "nullable": true + "description": "Signed receipt for this exact email, valid for 15 minutes. Pass it as `email_preview_token` to [Update Session Status](/sessions-api/update-status). An empty string when there is no recipient.", + "example": "eyJkaWdlc3QiOiI0ZjJhIn0:1uExAm:Pl3x4mpleS1gnatureValue000000000000000" } } }, "examples": { - "Active template": { + "Will Be Sent": { + "summary": "The Approved email goes to the address verified in the flow", "value": { - "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", - "vendor_user_uuid": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f", - "vendor_data": "user-42", - "source_type": "session_delete", - "provenance_reference": "6b1e8d2f-4a3c-4f9e-8c7d-1a2b3c4d5e6f", - "retention_policy": "retain_until_user_deleted", - "retention_override": null, - "retained_at": "2026-08-28T10:15:42.318402Z", - "expires_at": "2027-08-28T10:15:42.318402Z", - "retained_by": "3c9e2b1f-7d4a-4e8b-9c1d-5f6a7b8c9d0e", - "retained_by_type": "API_KEY", - "instruction_class": "operational_session_delete", - "instruction_source": "api", - "instruction_id": "post-approval-cleanup-2026-08", - "held_out_of_use": false, - "status": "active", - "purged_at": null + "recipient": "person@example.com", + "recipient_source": "email_verification", + "sender": "Example App ", + "sender_name": "Example App", + "sender_email": "no-reply@mail.example.com", + "sender_kind": "customer", + "subject": "Your verification has been approved in Example App", + "html": "...", + "language": "en", + "will_be_sent": true, + "not_sent_reason": null, + "preview_token": "eyJkaWdlc3QiOiI0ZjJhIn0:1uExAm:Pl3x4mpleS1gnatureValue000000000000000" + } + }, + "No Recipient": { + "summary": "No address is known for the person", + "value": { + "recipient": "", + "recipient_source": "", + "sender": "Example App ", + "sender_name": "Example App", + "sender_email": "no-reply@mail.example.com", + "sender_kind": "customer", + "subject": "Your verification has been declined in Example App", + "html": "...", + "language": "en", + "will_be_sent": false, + "not_sent_reason": "no_recipient", + "preview_token": "" + } + } + } + } + } + }, + "400": { + "description": "The session is not in a status this endpoint accepts (`{\"detail\": ...}`), or the body is invalid (`{\"\": [\"\"]}`).", + "content": { + "application/json": { + "examples": { + "Wrong Current Status": { + "summary": "Session is not in a status this endpoint accepts", + "value": { + "detail": "Only sessions with status Approved, Declined, In Review, Kyc Expired, Abandoned, Resubmitted can be updated. Current status: In Progress" + } + }, + "Invalid new_status": { + "summary": "new_status is not Approved or Declined", + "value": { + "new_status": [ + "\"Resubmitted\" is not a valid choice." + ] + } + }, + "Missing new_status": { + "summary": "Required field omitted", + "value": { + "new_status": [ + "This field is required." + ] + } + } + } + } + } + }, + "401": { + "description": "The scoped API key has expired.", + "content": { + "application/json": { + "examples": { + "Expired API Key": { + "summary": "Scoped API key past its expiry date", + "value": { + "detail": "This API key has expired." } } } @@ -46982,27 +70065,41 @@ } }, "403": { - "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", + "description": "Missing or invalid credentials, a caller without the `write:sessions` privilege, a scoped API key without the access this endpoint needs, or a `session_id` that belongs to another application.", "content": { "application/json": { "examples": { - "Forbidden": { + "Invalid Credentials": { + "summary": "Missing or invalid x-api-key", + "value": { + "detail": "Authentication credentials were not provided or are invalid." + } + }, + "Missing Privilege": { + "summary": "Caller lacks write:sessions", "value": { "detail": "You do not have permission to perform this action." } + }, + "Scoped Key Without Access": { + "summary": "Scoped API key without this access", + "value": { + "detail": "This API key has no write access to decisions." + } } } } } }, "404": { - "description": "Unknown template, a template of another application, or (on `GET`) a purged template.", + "description": "No KYC or KYB session with this `session_id` exists.", "content": { "application/json": { "examples": { - "Not found": { + "Not Found": { + "summary": "Unknown session_id", "value": { - "detail": "Biometric template not found." + "detail": "Not found." } } } @@ -47014,140 +70111,499 @@ { "lang": "curl", "label": "curl", - "source": "curl -X GET https://verification.didit.me/v3/biometric-templates/9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b/ \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X POST \\\n https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/decision-email-preview/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"new_status\": \"Approved\", \"email_language\": \"en\"}'" + }, + { + "lang": "python", + "label": "Python", + "source": "import requests\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/session/3fa85f64-5717-4562-b3fc-2c963f66afa6/decision-email-preview/\",\n headers={\"x-api-key\": \"YOUR_API_KEY\"},\n json={\"new_status\": \"Approved\", \"email_language\": \"en\"},\n)\nresponse.raise_for_status()\nprint(response.json())" } - ] - }, - "delete": { - "summary": "Purge a retained biometric template", - "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nPurges one template: it is removed from every kind of matching and the stored template is deleted. Irreversible and recorded on the audit trail with the acting principal.\n\n**Retry semantics:** Didit deletes the stored template first and only then marks the template purged. If a biometric store is unavailable the call returns `503`, the template moves to `status: purge_failed` and is already excluded from matching; repeat the same call to finish. Repeating a purge of an already purged template returns `already_purged`.\n\nTemplates are also purged by `POST /v3/users/delete/` for the owning User, by any deletion of that User's sessions with `deletion_instruction: privacy_erasure`, and automatically at `expires_at`.", - "operationId": "purge_biometric_template", + ], + "x-didit-sdk": { + "resource": "sessions", + "method": "preview_decision_email", + "auth": "api_key", + "server": true + } + } + }, + "/v3/travel-rule/join/{token}/": { + "get": { + "summary": "Get a free-join invitation", + "description": "Public, unauthenticated. Shows the invitation a join link is for while it is open: who asked, the address it went to, and the directory values that will be prefilled. The token is the authentication.", + "operationId": "getTravelRuleJoinInvitation", + "x-didit-sdk": { + "resource": "travel_rule.join", + "method": "get_invitation", + "auth": "public", + "server": false + }, "tags": [ - "Biometric Templates" + "Travel Rule" ], "security": [ + {} + ], + "parameters": [ { - "ApiKeyAuth": [] + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The invitation token from the join link." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl https://verification.didit.me/v3/travel-rule/join/{token}/" + } + ], + "responses": { + "200": { + "description": "The invitation.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleJoinInvitation" + } + } + } + }, + "404": { + "description": "No invitation has this link." + }, + "410": { + "description": "The invitation is no longer open: expired, already used, or withdrawn." } + } + }, + "post": { + "summary": "Register a free Travel Rule account", + "description": "Public, unauthenticated. Creates a free Travel Rule account for a counterparty VASP that received a request. The email must be the address the invitation went to. A 6-character code is emailed to it; send it to the verify endpoint to finish. Registration attempts are rate limited per address and per IP.", + "operationId": "registerTravelRuleJoin", + "x-didit-sdk": { + "resource": "travel_rule.join", + "method": "register", + "auth": "public", + "server": false + }, + "tags": [ + "Travel Rule" + ], + "security": [ + {} ], "parameters": [ { + "name": "token", "in": "path", - "name": "template_uuid", "required": true, - "description": "Template id, as returned in `biometric_template_uuid` by the session deletion endpoints, `biometric_template_id` on a Face Search match, or the list endpoint.", "schema": { - "type": "string", - "format": "uuid", - "example": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b" + "type": "string" + }, + "description": "The invitation token from the join link." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleJoinRegisterRequest" + }, + "example": { + "email": "compliance@example.com", + "password": "a-long-passphrase" + } } } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/join/{token}/ \\\n -H 'Content-Type: application/json' \\\n -d '{\"email\": \"compliance@example.com\", \"password\": \"a-long-passphrase\"}'" + } ], "responses": { - "200": { - "description": "Purge outcome.", + "202": { + "description": "The account was created and a code was emailed.", "content": { "application/json": { "schema": { "type": "object", "properties": { - "uuid": { + "message": { "type": "string", - "format": "uuid" + "description": "Tells the counterparty to check their email for the verification code." }, - "outcome": { + "email": { "type": "string", - "enum": [ - "purged", - "already_purged" - ] - } - } - }, - "examples": { - "Purged": { - "value": { - "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", - "outcome": "purged" - } - }, - "Already purged": { - "value": { - "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", - "outcome": "already_purged" + "format": "email", + "description": "The address the code was sent to." } } } } } }, - "403": { - "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", + "400": { + "description": "The email is not the invited address, or the password or address was refused." + }, + "404": { + "description": "No invitation has this link." + }, + "410": { + "description": "The invitation is no longer open: expired, already used, or withdrawn." + }, + "429": { + "description": "Too many attempts to join. The `X-RateLimit-*` headers name the budget that was spent." + } + } + } + }, + "/v3/travel-rule/join/{token}/verify/": { + "post": { + "summary": "Verify a free Travel Rule account", + "description": "Public, unauthenticated. Spends the emailed code, creates the free organization and answers with the credentials its application uses on the workspace endpoints. Receiving Travel Rule data is free.", + "operationId": "verifyTravelRuleJoin", + "x-didit-sdk": { + "resource": "travel_rule.join", + "method": "verify", + "auth": "public", + "server": false + }, + "tags": [ + "Travel Rule" + ], + "security": [ + {} + ], + "parameters": [ + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The invitation token from the join link." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleJoinVerifyRequest" + }, + "example": { + "email": "compliance@example.com", + "code": "123456" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/join/{token}/verify/ \\\n -H 'Content-Type: application/json' \\\n -d '{\"email\": \"compliance@example.com\", \"code\": \"123456\"}'" + } + ], + "responses": { + "201": { + "description": "The organization and application credentials.", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." - } - } + "schema": { + "$ref": "#/components/schemas/TravelRuleJoinCredentials" } } } }, + "400": { + "description": "The code or email was refused." + }, "404": { - "description": "Unknown template, a template of another application, or (on `GET`) a purged template.", + "description": "No invitation has this link." + }, + "410": { + "description": "The invitation is no longer open: expired, already used, or withdrawn." + }, + "429": { + "description": "Too many attempts to join. The `X-RateLimit-*` headers name the budget that was spent." + } + } + } + }, + "/v3/travel-rule/workspace/": { + "get": { + "summary": "Get the free Travel Rule workspace", + "description": "Reads the free workspace of an application that joined free: directory claim, domain proof, billing and Didit network listing.", + "operationId": "getTravelRuleWorkspace", + "x-didit-sdk": { + "resource": "travel_rule.workspace", + "method": "get", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET https://verification.didit.me/v3/travel-rule/workspace/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "The workspace.", "content": { "application/json": { - "examples": { - "Not found": { - "value": { - "detail": "Biometric template not found." - } - } + "schema": { + "$ref": "#/components/schemas/TravelRuleWorkspace" } } } }, - "503": { - "description": "A biometric store was unavailable while retaining or purging a biometric template. Nothing is deleted; repeat the same request. Retries are idempotent and converge on a single template.", + "404": { + "description": "This application did not join free, so it has no free workspace." + } + } + } + }, + "/v3/travel-rule/workspace/claim/": { + "post": { + "summary": "Claim a directory entry", + "description": "Claims the directory entry this workspace is. When the work email is at a domain the entry lists, the claim is proved and applies at once: the VASP profile and Travel Rule settings are filled from the entry and Travel Rule is turned on, so the next transfer to it routes over the Didit network. Otherwise the claim waits for review (`pending_review`).", + "operationId": "claimTravelRuleWorkspace", + "x-didit-sdk": { + "resource": "travel_rule.workspace", + "method": "claim", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleWorkspaceClaimRequest" + }, + "example": { + "vasp_id": "0b0c9d3e-5a53-4a3e-9d6e-1f2f6f6a1c11" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/workspace/claim/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"vasp_id\": \"0b0c9d3e-5a53-4a3e-9d6e-1f2f6f6a1c11\"}'" + } + ], + "responses": { + "200": { + "description": "The workspace with its claim status.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleWorkspace" + } + } + } + }, + "400": { + "description": "No directory entry has this id." + }, + "404": { + "description": "This application did not join free, so it has no free workspace." + } + } + } + }, + "/v3/travel-rule/workspace/domain/code/": { + "post": { + "summary": "Email a domain verification code", + "description": "Emails a single-use code to the workspace's work address to prove it controls the email domain. Throttled, and a limited number of wrong attempts is accepted per code. Also returns the TXT record for the DNS alternative.", + "operationId": "sendTravelRuleWorkspaceDomainCode", + "x-didit-sdk": { + "resource": "travel_rule.workspace", + "method": "send_domain_code", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/workspace/domain/code/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "The domain, its status and the TXT record.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleWorkspaceDomainCodeResponse" + } + } + } + }, + "404": { + "description": "This application did not join free, so it has no free workspace." + } + } + } + }, + "/v3/travel-rule/workspace/domain/confirm/": { + "post": { + "summary": "Confirm the domain verification code", + "description": "Spends the emailed code and marks the email domain verified.", + "operationId": "confirmTravelRuleWorkspaceDomain", + "x-didit-sdk": { + "resource": "travel_rule.workspace", + "method": "confirm_domain", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleWorkspaceDomainConfirmRequest" + }, + "example": { + "code": "123456" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/workspace/domain/confirm/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"code\": \"123456\"}'" + } + ], + "responses": { + "200": { + "description": "The workspace.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, - "examples": { - "Retryable": { - "value": { - "detail": "The biometric-template operation could not finish because an external store is unavailable. The operation is retryable." - } - } + "$ref": "#/components/schemas/TravelRuleWorkspace" } } } + }, + "400": { + "description": "The code is wrong, expired or already used." + }, + "404": { + "description": "This application did not join free, so it has no free workspace." } + } + } + }, + "/v3/travel-rule/workspace/domain/dns/": { + "post": { + "summary": "Verify the domain from its TXT record", + "description": "Checks for the `didit-tr-verify.` TXT record returned by the code endpoint and marks the domain verified when it matches. One of the two proofs, the emailed code or the TXT record, is enough.", + "operationId": "verifyTravelRuleWorkspaceDomainDns", + "x-didit-sdk": { + "resource": "travel_rule.workspace", + "method": "verify_domain_dns", + "auth": "api_key", + "server": true }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], "x-codeSamples": [ { "lang": "curl", "label": "curl", - "source": "curl -X DELETE https://verification.didit.me/v3/biometric-templates/9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b/ \\\n -H 'x-api-key: YOUR_API_KEY'" + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/workspace/domain/dns/ \\\n -H 'x-api-key: YOUR_API_KEY'" } - ] + ], + "responses": { + "200": { + "description": "The workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleWorkspace" + } + } + } + }, + "400": { + "description": "The TXT record was not found or does not match." + }, + "404": { + "description": "This application did not join free, so it has no free workspace." + } + } } }, - "/v3/biometric-templates/delete/": { + "/v3/travel-rule/workspace/billing/": { "post": { - "summary": "Bulk purge retained biometric templates", - "description": "An opted-in application keeps one separately managed, image-free face biometric template anchored to the User when a session is deleted (see `DELETE /v3/session/{sessionId}/delete/`). Templates are biometric data with a finite `expires_at`; they never contain images, session data, or identity fields, and they are purged on User deletion, privacy erasure, expiry, or an explicit purge. These endpoints accept application API keys (`x-api-key`) only; Business Console user tokens are rejected with `403`.\n\nPurges many templates in one call, by explicit id list or with `delete_all: true` optionally narrowed by the same filters as the list endpoint. Templates are purged one by one and each result reports `purged` or `already_purged`. If a biometric store is unavailable the call returns `503` at the first failing template: templates purged earlier in the request stay purged, the failing one moves to `purge_failed` (already excluded from matching), and repeating the request finishes the job.\n\n**`delete_all: true` without filters purges every biometric template in the application and cannot be undone.** Run the count endpoint with the same filters first.", - "operationId": "bulk_purge_biometric_templates", + "summary": "Enable outbound billing", + "description": "Turns outbound Travel Rule billing on for the workspace. Receiving stays free. This cannot be turned back off through the API: contact Didit support.", + "operationId": "enableTravelRuleWorkspaceBilling", + "x-didit-sdk": { + "resource": "travel_rule.workspace", + "method": "enable_billing", + "auth": "api_key", + "server": true + }, "tags": [ - "Biometric Templates" + "Travel Rule" ], "security": [ { @@ -47159,22561 +70615,33041 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "template_uuids": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "minItems": 1, - "description": "Templates to purge. Required unless `delete_all` is `true`. Already purged ids are reported as `already_purged`." - }, - "delete_all": { - "type": "boolean", - "default": false, - "description": "When `true`, purges every template matching the filters below (all of the application's templates when no filter is given). `template_uuids` is ignored. Irreversible." - }, - "vendor_data": { - "type": "string", - "description": "Only with `delete_all`: case-insensitive substring match on the owning User's `vendor_data`." - }, - "vendor_user_uuid": { - "type": "string", - "format": "uuid", - "description": "Only with `delete_all`: exact match on the owning User." - }, - "status": { - "type": "string", - "enum": [ - "pending", - "active", - "expired", - "held_out_of_use", - "purge_pending", - "purge_failed" - ], - "description": "Only with `delete_all`." - }, - "source_type": { - "type": "string", - "enum": [ - "session_delete", - "automatic_retention", - "backfill" - ], - "description": "Only with `delete_all`." - }, - "retained_from": { - "type": "string", - "format": "date-time", - "description": "Only with `delete_all`." - }, - "retained_to": { - "type": "string", - "format": "date-time", - "description": "Only with `delete_all`. Must not be earlier than `retained_from`." - } - } + "$ref": "#/components/schemas/TravelRuleWorkspaceBillingRequest" }, - "examples": { - "By id": { - "value": { - "template_uuids": [ - "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", - "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d" - ] - } - }, - "Every template of one User": { - "value": { - "delete_all": true, - "vendor_user_uuid": "2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f" - } - }, - "Every template in the application": { - "value": { - "delete_all": true - } - } + "example": { + "enabled": true } } } }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/workspace/billing/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"enabled\": true}'" + } + ], "responses": { "200": { - "description": "Per-template purge outcomes.", + "description": "The workspace with `billing_enabled` true.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "outcome": { - "type": "string", - "enum": [ - "purged", - "already_purged" - ] - } - } - } - } - } - }, - "examples": { - "Mixed": { - "value": { - "results": [ - { - "uuid": "9d2f4b6e-1c3a-4e8f-b7d5-0a1c2e3f4a5b", - "outcome": "purged" - }, - { - "uuid": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", - "outcome": "already_purged" - } - ] - } - } + "$ref": "#/components/schemas/TravelRuleWorkspace" } } } }, "400": { - "description": "Validation error — nothing is purged.", + "description": "`enabled` was false." + }, + "404": { + "description": "This application did not join free, so it has no free workspace." + } + } + } + }, + "/v3/investigator/summary/": { + "get": { + "operationId": "investigator_summary", + "summary": "Investigator summary", + "description": "Stage, queue counts, reports prepared and decided today, 30-day agreement, prices and the autonomy level of each queue. Restricted keys need the `decisions` scope (read); transaction alerts also need `transactions`.", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", "content": { "application/json": { - "examples": { - "Missing selector": { - "value": { - "template_uuids": [ - "This field is required unless delete_all is true." - ] - } - }, - "Bad range": { - "value": { - "retained_to": [ - "Must be on or after retained_from." - ] + "schema": { + "type": "object", + "additionalProperties": true, + "properties": { + "stage": { + "type": "string" + }, + "last_report_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "prepared_today": { + "type": "integer" + }, + "ready_to_accept": { + "type": "integer" + }, + "open": { + "type": "integer" + }, + "decided_today": { + "type": "integer" + }, + "automation_rate_month": { + "type": "number", + "nullable": true + }, + "preparing": { + "type": "integer" } } } } } }, + "401": { + "description": "Missing or invalid API key." + }, "403": { - "description": "Console user token, or the API key is not allowed. These endpoints accept application API keys only.", + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "parameters": [ + { + "in": "query", + "name": "scope", + "schema": { + "type": "string", + "enum": [ + "all", + "identity", + "transactions" + ], + "default": "all" + }, + "description": "`identity` (sessions, and cases not opened by a transaction rule) or `transactions` (transaction alerts, and cases a transaction rule opened)." + }, + { + "in": "query", + "name": "timezone", + "schema": { + "type": "string" + }, + "description": "IANA time zone (for example `Europe/Madrid`) that \"today\" counts from. UTC by default." + } + ], + "x-didit-sdk": { + "resource": "investigator", + "method": "retrieve_summary", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/queue/": { + "get": { + "operationId": "investigator_queue", + "summary": "List the Investigator queue", + "description": "Open reports, **Ready to accept** first, then by deadline, risk and wait. `counts` covers every kind in scope. `count` is capped at 100. Restricted keys need the `decisions` scope (read); transaction alerts also need `transactions`.", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", "content": { "application/json": { - "examples": { - "Forbidden": { - "value": { - "detail": "You do not have permission to perform this action." + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "counts": { + "type": "object", + "additionalProperties": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InvestigatorQueueRow" + } } } } } } }, - "503": { - "description": "A biometric store was unavailable while retaining or purging a biometric template. Nothing is deleted; repeat the same request. Retries are idempotent and converge on a single template.", + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "parameters": [ + { + "in": "query", + "name": "scope", + "schema": { + "type": "string", + "enum": [ + "all", + "identity", + "transactions" + ], + "default": "all" + }, + "description": "`identity` (sessions, and cases not opened by a transaction rule) or `transactions` (transaction alerts, and cases a transaction rule opened)." + }, + { + "in": "query", + "name": "kind", + "schema": { + "type": "string" + }, + "description": "`session`, `business_session`, `transaction` or `case`, or several separated by commas." + }, + { + "in": "query", + "name": "queue", + "schema": { + "type": "string", + "enum": [ + "kyc", + "kyb", + "aml", + "transactions", + "cases" + ] + } + }, + { + "in": "query", + "name": "recommendation", + "schema": { + "type": "string", + "enum": [ + "approve", + "decline", + "escalate", + "request" + ] + } + }, + { + "in": "query", + "name": "confidence", + "schema": { + "type": "string", + "enum": [ + "high", + "moderate", + "low" + ] + } + }, + { + "in": "query", + "name": "ready", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "search", + "schema": { + "type": "string" + }, + "description": "Name, company, case title, number, transaction ID or item ID." + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "offset", + "schema": { + "type": "integer" + } + } + ], + "x-didit-sdk": { + "resource": "investigator", + "method": "list_queue", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/runs/{run_id}/": { + "get": { + "operationId": "investigator_run_retrieve", + "summary": "Get an Investigator report", + "description": "The full report of one run. 404 when the run has no completed report, or its item was deleted. Restricted keys need the `decisions` scope (read); transaction alerts also need `transactions`.", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } - }, - "examples": { - "Retryable": { - "value": { - "detail": "The biometric-template operation could not finish because an external store is unavailable. The operation is retryable." - } - } + "$ref": "#/components/schemas/InvestigatorRunDetail" } } } + }, + "404": { + "description": "Not found, or no report." + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." } }, - "x-codeSamples": [ + "parameters": [ { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/biometric-templates/delete/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\"delete_all\": true, \"vendor_user_uuid\": \"2f7c1a9e-8b4d-4c6a-9e1f-3d5b7a9c1e2f\"}'" + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } } - ] + ], + "x-didit-sdk": { + "resource": "investigator", + "method": "retrieve_run", + "auth": "api_key", + "server": true + } } }, - "/v3/kyb/shareholders/": { + "/v3/investigator/runs/{run_id}/accept/": { "post": { - "summary": "KYB registry company with shareholders (standalone)", - "operationId": "post_v3_kyb_shareholders", + "operationId": "investigator_run_accept", + "summary": "Accept a recommendation", + "description": "Applies the recommended action through the item's normal path, so your usual webhooks fire. A `narrative` makes it edited and accepted. Restricted keys need the `decisions` scope (write); transaction alerts also need `transactions` (write). Escalating a verification or transaction opens a case.", "tags": [ - "Standalone APIs" + "Investigator" ], "security": [ { "ApiKeyAuth": [] } ], - "parameters": [], - "description": "Retrieve the Lite profile **plus the direct shareholders** (natural persons and corporate entities, with their ownership bands) of one candidate returned by `POST /v3/kyb/search/`.\n\n**Pairing with search.** Pass the candidate's `kyb_response_id` exactly as returned by a search of the same application. The search runs on the Lite registry chain; when the country's ownership data lives at another registry Didit re-finds the company there by registration number, at no extra cost.\n\n**Billing.** Billed at the country's Shareholders price - see [KYB registry pricing](/getting-started/kyb-registry-pricing). When the registry holds no shareholder record for the company (or the country offers no ownership data at all) the Lite profile is delivered and billed instead: read `registry_tier_delivered` and `registry_ownership_status`. The balance is checked against the Shareholders price before any registry call (`403` when short).\n\n**Availability.** v3 only. Every call creates a business session (its id is the `request_id`).\n\n**Pending reports.** When a report is still being prepared, or the ownership section has not been retrieved from the registry yet, the endpoint returns 202 Accepted, kyb_registry.fetch_status=pending, kyb_registry.registry_ownership_status=pending, and registry_tier_delivered=null. Keep request_id and retrieve GET /v3/session/{request_id}/decision/ until registry_checks[0].company.fetch_status is resolved. Didit completes the report automatically and charges the delivered tier once it is ready. Do not repeat the purchase while it is pending; every new POST creates a separate request.", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvestigatorDecision" + } + } + } + }, + "409": { + "description": "Already decided, report not ready, or a guardrail holds the item." + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "parameters": [ + { + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "kyb_response_id" - ], - "properties": { - "kyb_response_id": { - "type": "string", - "maxLength": 255, - "description": "Candidate handle from `POST /v3/kyb/search/` (`kyb_registry.companies[].kyb_response_id`). Ephemeral — use it shortly after the search that produced it.", - "example": "69aeeb95febb0f1704042259" - }, - "vendor_data": { - "type": "string", - "description": "Your identifier for this company; echoed back and stored with the business session." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Free-form JSON stored with the business session and echoed back." - }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "Must be `true` (the default). KYB registry selects are always persisted so billing and retrieval can be tracked; `false` returns `400`." - } + "$ref": "#/components/schemas/InvestigatorAcceptRequest" + } + } + } + }, + "x-didit-sdk": { + "resource": "investigator", + "method": "accept_run", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/runs/{run_id}/override/": { + "post": { + "operationId": "investigator_run_override", + "summary": "Override a recommendation", + "description": "Takes another action with a category and a reason. Also overrides a decision the Investigator took on its own while it still stands. Restricted keys need the `decisions` scope (write); transaction alerts also need `transactions` (write). Escalating a verification or transaction opens a case.", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvestigatorDecision" } } } + }, + "409": { + "description": "Already decided, or a guardrail holds the item." + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "parameters": [ + { + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvestigatorOverrideRequest" + } + } } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/kyb/shareholders/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"kyb_response_id\": \"69aeeb95febb0f1704042259\",\n \"vendor_data\": \"company-1234\"\n }'" - }, - { - "lang": "python", - "label": "Python", - "source": "import os, time, requests\n\nsearch = requests.post(\n \"https://verification.didit.me/v3/kyb/search/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n timeout=60,\n).json()\nkyb_response_id = search[\"kyb_registry\"][\"companies\"][0][\"kyb_response_id\"]\n\nresp = requests.post(\n \"https://verification.didit.me/v3/kyb/shareholders/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"kyb_response_id\": kyb_response_id, \"vendor_data\": \"company-1234\"},\n timeout=90,\n)\nresp.raise_for_status()\ndata = resp.json()\nrequest_id = data[\"request_id\"]\nprint(\"Keep this request ID:\", request_id)\ncompany = data[\"kyb_registry\"]\nif resp.status_code == 202 or company.get(\"fetch_status\") == \"pending\":\n # This client wait limit does not cancel the report or create a new purchase.\n deadline = time.monotonic() + 300\n while company.get(\"fetch_status\") != \"resolved\" and time.monotonic() < deadline:\n time.sleep(5)\n decision = requests.get(\n f\"https://verification.didit.me/v3/session/{request_id}/decision/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n timeout=30,\n )\n decision.raise_for_status()\n checks = decision.json().get(\"registry_checks\") or []\n if checks:\n company = checks[0].get(\"company\") or company\nif company.get(\"fetch_status\") == \"resolved\":\n print(company[\"company_name\"], company[\"registry_tier_delivered\"])\nelse:\n print(\"Still processing; retrieve this request later:\", request_id)" - }, + "x-didit-sdk": { + "resource": "investigator", + "method": "override_run", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/runs/batch-accept/": { + "post": { + "operationId": "investigator_batch_accept", + "summary": "Batch accept recommendations", + "description": "Accepts up to 50 reports marked Ready to accept. Each is its own decision: one that cannot be accepted is skipped with its reason. Restricted keys need the `decisions` scope (write); transaction alerts also need `transactions` (write). Escalating a verification or transaction opens a case.", + "tags": [ + "Investigator" + ], + "security": [ { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/kyb/shareholders/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n kyb_response_id: '69aeeb95febb0f1704042259',\n vendor_data: 'company-1234',\n }),\n});\nif (!res.ok) throw new Error(`Request failed: ${res.status}`);\nconst data = await res.json();\nconst requestId = data.request_id;\nconsole.log('Keep this request ID:', requestId);\nlet company = data.kyb_registry;\nif (res.status === 202 || company.fetch_status === 'pending') {\n // This client wait limit does not cancel the report or create a new purchase.\n const deadline = Date.now() + 300000;\n while (company.fetch_status !== 'resolved' && Date.now() < deadline) {\n await new Promise(resolve => setTimeout(resolve, 5000));\n const decisionResponse = await fetch(\n `https://verification.didit.me/v3/session/${requestId}/decision/`,\n { headers: { 'x-api-key': 'YOUR_API_KEY' }, signal: AbortSignal.timeout(30000) },\n );\n if (!decisionResponse.ok) throw new Error(`Retrieval failed: ${decisionResponse.status}`);\n const decision = await decisionResponse.json();\n company = decision.registry_checks?.[0]?.company ?? company;\n }\n}\nif (company.fetch_status === 'resolved') {\n console.log(company.company_name, company.registry_tier_delivered);\n} else {\n console.log('Still processing; retrieve this request later:', requestId);\n}" + "ApiKeyAuth": [] } ], "responses": { "200": { - "description": "Company report returned and billed at the delivered tier. Ownership reports still being prepared can return 202; keep request_id to retrieve their saved decision.", + "description": "", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Id of the business session created for this retrieval. The stored record (visible in the console) keeps updating if the registry resolves after the response." - }, - "registry_tier_requested": { - "type": "string", - "enum": [ - "basic", - "shareholders", - "ubo" - ], - "description": "The tier this endpoint sells: `basic` (Lite) for select, `shareholders`, `ubo`." - }, - "registry_tier_delivered": { - "type": "string", - "enum": [ - "basic", - "shareholders", - "ubo", - null - ], - "description": "The tier actually delivered and billed. Lower than requested when the country's registries hold no ownership data, or the registry has no ownership record for this company.", - "nullable": true - }, - "kyb_registry": { - "type": "object", - "description": "Full registry company profile.", - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Didit's id for this company record." - }, - "node_id": { - "type": "string", - "example": "feature_kyb_registry" - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review" - ], - "description": "Didit's assessment of the registry check (e.g. `In Review` when the company is not active in the registry or ownership data could not be derived)." - }, - "registry_status": { - "type": "string", - "nullable": true, - "enum": [ - "active", - "dissolved", - "deregistered", - "see full details", - "authorised", - "appointed representative", - "unauthorised", - "inactive", - "no longer authorised", - "closed", - "struck off", - null - ], - "description": "Normalized company status as reported by the registry." - }, - "data_resolved": { - "type": "boolean", - "description": "`true` once the registry returned the full profile. `false` when the registry was still processing after polling — the stored record completes automatically once the registry resolves." - }, - "company_name": { - "type": "string", - "example": "Tesco PLC" - }, - "registration_number": { - "type": "string", - "nullable": true, - "example": "00445790" - }, - "country_code": { - "type": "string", - "description": "ISO 3166-1 alpha-2.", - "example": "GB" - }, - "region": { - "type": "string", - "nullable": true, - "description": "Subdivision code when the registry is regional (e.g. `CA` for `US-CA`); empty otherwise." - }, - "company_type": { - "type": "string", - "nullable": true, - "example": "Public Limited Company" - }, - "incorporation_date": { - "type": "string", - "format": "date", - "nullable": true, - "example": "1947-11-27" - }, - "registered_address": { - "type": "string", - "nullable": true, - "example": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA" - }, - "tax_number": { - "type": "string", - "nullable": true, - "example": "GB412 8925 93" - }, - "risk_level": { - "type": "string", - "nullable": true, - "description": "Registry-reported risk level, when available." - }, - "verification_status": { - "type": "string", - "nullable": true, - "description": "Registry verification status, when available." - }, - "is_from_registry": { - "type": "boolean", - "description": "Always `true` for this endpoint — the profile was retrieved from an official registry." - }, - "fetch_status": { - "type": "string", - "enum": [ - "pending", - "resolved" - ], - "description": "Registry fetch lifecycle. Mirrored by `data_resolved`." - }, - "alternative_names": { - "type": "string", - "nullable": true, - "description": "Alternative, trading, former or original-language company names as text, when reported." - }, - "nature_of_business": { - "type": "string", - "nullable": true, - "description": "Activity description or classification code, when reported." - }, - "registered_capital": { - "type": "string", - "nullable": true - }, - "registered_capital_amount": { - "type": "string", - "nullable": true, - "description": "Decimal amount as a string, when reported." - }, - "registered_capital_currency": { - "type": "string", - "nullable": true, - "description": "ISO 4217 code, when reported." - }, - "website": { - "type": "string", - "nullable": true, - "example": "https://www.tesco.com/" - }, - "email": { - "type": "string", - "nullable": true, - "example": "customer.service@tesco.co.uk" - }, - "phone": { - "type": "string", - "nullable": true, - "example": "03301231688" - }, - "legal_entity_identifier": { - "type": "string", - "nullable": true, - "description": "LEI, when reported." - }, - "location_of_registration": { - "type": "string", - "nullable": true, - "description": "Registry/jurisdiction the company is registered with, when reported." - }, - "vat_number": { - "type": "string", - "nullable": true, - "description": "EU VAT number, when collected during the hosted registry step or edited afterwards. At select time this is typically not yet set — VAT and tax ID validation runs when the end user submits the registry step in the hosted flow." - }, - "vat_validation_status": { - "type": "string", - "enum": [ - "valid", - "invalid", - "format_valid", - "invalid_format", - "could_not_validate", - "not_applicable" - ], - "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." - }, - "vat_validated_number": { - "type": "string", - "nullable": true, - "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." - }, - "vat_validation_source": { - "type": "string", - "nullable": true, - "enum": [ - "eu_vies", - "pl_vat_white_list", - "no_brreg", - "ch_uid_register", - "br_receita_federal", - "co_rues", - "cr_hacienda", - "ec_sri", - "py_dnit", - "format_rules", - null - ], - "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." - }, - "vat_validated_name": { - "type": "string", - "nullable": true, - "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_validated_address": { - "type": "string", - "nullable": true, - "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_checked_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the check. `null` when no check ran." - }, - "financial_summary": { - "type": "object", - "nullable": true, - "description": "Registry-reported share/capital details (currency, total shares, share classes), when available." - }, - "officers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string", - "example": "MELISSA BETHELL" - }, - "designation": { - "type": "string", - "nullable": true, - "description": "Raw registry designation (e.g. `Director`, `Secretary`)." - }, - "role": { - "type": "string", - "description": "Normalized role derived from the designation. Full 17-value model set; registry parsing on this endpoint typically emits director, non_executive_director, secretary, chairman, or company_officer.", - "enum": [ - "director", - "non_executive_director", - "secretary", - "chairman", - "ubo", - "shareholder", - "representative", - "founder", - "legal_advisor", - "authorized_signatory", - "trustee", - "beneficiary", - "company_officer", - "settlor", - "protector", - "investor", - "other" - ] - }, - "nationality": { - "type": "string", - "nullable": true - }, - "is_active": { - "type": "boolean", - "description": "`false` when the registry marks the appointment as resigned." - }, - "kyc_status": { - "type": "string", - "nullable": true, - "enum": [ - "Approved", - "Declined", - "Pending", - null - ], - "description": "Status of an associated KYC session, when one was started for this officer. `null` otherwise." - }, - "kyc_session_url": { - "type": "string", - "nullable": true, - "description": "Verification URL of the associated KYC session, when one exists." - }, - "first_name": { - "type": "string", - "nullable": true, - "description": "Given name when explicitly reported by the registry; not inferred from the full name." - }, - "last_name": { - "type": "string", - "nullable": true, - "description": "Family name when explicitly reported by the registry." - }, - "address": { - "type": "string", - "nullable": true, - "description": "Officer address reported by the registry." - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Date the officer was appointed.", - "format": "date" - }, - "termination_date": { - "type": "string", - "nullable": true, - "description": "Date the appointment ended, when reported.", - "format": "date" - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", - "format": "date" - }, - "date_of_birth_year": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 9999, - "description": "Registry-reported birth year, including when the full date is unavailable." - }, - "date_of_birth_month": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 12, - "description": "Registry-reported birth month. Null when only the year is known." - } - } - }, - "description": "Directors, secretaries, and other officers extracted from the registry." - }, - "beneficial_owners": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "first_name": { - "type": "string", - "nullable": true - }, - "last_name": { - "type": "string", - "nullable": true - }, - "entity_type": { - "type": "string", - "enum": [ - "person", - "company" - ], - "description": "Whether the owner is a natural person or a corporate entity." - }, - "roles": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Normalized roles (e.g. `ubo`, `shareholder`)." - }, - "ownership_min_shares": { - "type": "string", - "nullable": true, - "description": "Lower bound of the ownership band reported by the registry (e.g. `25%`)." - }, - "ownership_max_shares": { - "type": "string", - "nullable": true, - "description": "Upper bound of the ownership band reported by the registry." - }, - "is_active": { - "type": "boolean" - }, - "kyc_status": { - "type": "string", - "nullable": true, - "enum": [ - "Approved", - "Declined", - "Pending", - null - ] - }, - "kyc_session_url": { - "type": "string", - "nullable": true - }, - "effective_ownership_percent": { - "type": "number", - "nullable": true, - "description": "Parsed ownership percentage (max bound, falling back to min bound)." - }, - "company_name": { - "type": "string", - "nullable": true - }, - "registration_number": { - "type": "string", - "nullable": true - }, - "country_of_birth": { - "type": "string", - "nullable": true - }, - "email": { - "type": "string", - "nullable": true - }, - "nationality": { - "type": "string", - "nullable": true - }, - "designation": { - "type": "string", - "nullable": true - }, - "voting_min_shares": { - "type": "string", - "nullable": true - }, - "voting_max_shares": { - "type": "string", - "nullable": true - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete birth date when reported.", - "format": "date" - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Appointment date when reported.", - "format": "date" - }, - "shareholding": { - "$ref": "#/components/schemas/KYBRegistryShareholding" - } - } + "$ref": "#/components/schemas/InvestigatorBatchAcceptResponse" + } + } + } + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvestigatorBatchAcceptRequest" + } + } + } + }, + "x-didit-sdk": { + "resource": "investigator", + "method": "batch_accept_runs", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/transactions/{transaction_id}/": { + "get": { + "operationId": "investigator_transaction_retrieve", + "summary": "Get a transaction's Investigator review", + "description": "The transaction's latest review, or `investigator: null` when it never had one. Restricted keys need the `decisions` and `transactions` scopes (read).", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "transaction_id": { + "type": "string", + "format": "uuid" + }, + "investigator": { + "$ref": "#/components/schemas/InvestigatorBlock" + } + } + } + } + } + }, + "404": { + "description": "Not this application's transaction." + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "parameters": [ + { + "in": "path", + "name": "transaction_id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "x-didit-sdk": { + "resource": "investigator", + "method": "retrieve_transaction", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/guardrails/": { + "get": { + "operationId": "investigator_guardrails", + "summary": "List the locked guardrails", + "description": "The five guardrails that apply to every queue. They cannot be switched off. Restricted keys need the `decisions` scope (read); transaction alerts also need `transactions`.", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "enum": [ + "sanctions_to_person", + "no_tipping_off", + "no_protected_characteristics", + "never_files_reports", + "every_decision_logged" + ] }, - "description": "Beneficial owners / persons with significant control extracted from the registry. Can be empty when the registry does not expose ownership." - }, - "addresses": { - "type": "array", - "nullable": true, - "description": "Every address the registry states, one canonical shape. Null when it states none.", - "items": { - "$ref": "#/components/schemas/KYBRegistryAddress" - } - }, - "industries": { - "type": "array", - "nullable": true, - "description": "The activities the registry classifies the company under, one canonical shape. Null when it states none.", - "items": { - "$ref": "#/components/schemas/KYBRegistryIndustry" + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "locked": { + "type": "boolean" } - }, - "accounts": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." - }, - "registry_data": { - "type": "object", - "description": "Raw record returned by the registry source, kept for reviewers. Its shape differs by country and source and is not a stable contract: read the canonical fields (`addresses`, `industries`, `ownership`, `beneficial_owners[].shareholding`) instead." - }, - "user_provided_data": { - "type": "object", - "nullable": true, - "description": "Always `null` on this endpoint (used by the hosted KYB flow when end users edit registry data)." - }, - "confirmed_by_user_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Always `null` on this endpoint." - }, - "last_console_edit_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Set when a console reviewer edits the record later." - }, - "is_editable": { - "type": "boolean", - "description": "Whether the registry-sourced data can still be edited in the hosted flow/console." - }, - "registry_tier_delivered": { - "type": "string", - "enum": [ - "basic", - "shareholders", - "ubo", - null - ], - "description": "Same as the top-level `registry_tier_delivered`.", - "nullable": true - }, - "registry_ownership_status": { - "type": "string", - "nullable": true, - "enum": [ - "not_requested", - "delivered", - "unavailable", - "pending", - null - ], - "description": "`delivered` when the ownership data came back, `unavailable` when an ownership tier was requested but the registry had none for this company (see `registry_data.ownership_error`), `pending` when the ownership section has not been retrieved from the registry yet and Didit is still completing it, `not_requested` on the Lite tier." - }, - "ownership": { - "$ref": "#/components/schemas/KYBRegistryOwnership" - }, - "registry_tier": { - "type": "string", - "nullable": true, - "description": "The registry tier the workflow ordered: `basic`, `shareholders` or `ubo`. Also in `ownership.tier_requested`." } } + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "x-didit-sdk": { + "resource": "investigator", + "method": "list_guardrails", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/dashboard/": { + "get": { + "operationId": "investigator_dashboard", + "summary": "Investigator dashboard", + "description": "The last 30 days: reviews, decisions, automation rate, hours saved, spend, overrides, quality samples since go-live, per-queue and weekly figures. Restricted keys need the `decisions` scope (read); transaction alerts also need `transactions`.", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "properties": { + "stage": { + "type": "string" + }, + "window_days": { + "type": "integer" + }, + "reviews": { + "type": "integer" + }, + "decided": { + "type": "integer" + }, + "automated": { + "type": "integer" + }, + "automation_rate": { + "type": "number", + "nullable": true + }, + "hours_saved": { + "type": "number", + "nullable": true + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "parameters": [ + { + "in": "query", + "name": "scope", + "schema": { + "type": "string", + "enum": [ + "all", + "identity", + "transactions" + ], + "default": "all" + }, + "description": "`identity` (sessions, and cases not opened by a transaction rule) or `transactions` (transaction alerts, and cases a transaction rule opened)." + } + ], + "x-didit-sdk": { + "resource": "investigator", + "method": "retrieve_dashboard", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/performance/": { + "get": { + "operationId": "investigator_performance", + "summary": "Investigator performance", + "description": "Agreement with your team, volume, time saved, confidence calibration, per-queue results, override reasons and disagreements for a period. Restricted keys need the `decisions` scope (read); transaction alerts also need `transactions`.", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "properties": { + "period": { + "type": "integer" + }, + "start": { + "type": "string", + "format": "date" + }, + "end": { + "type": "string", + "format": "date" + }, + "investigated": { + "type": "integer" + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "parameters": [ + { + "in": "query", + "name": "scope", + "schema": { + "type": "string", + "enum": [ + "all", + "identity", + "transactions" + ], + "default": "all" + }, + "description": "`identity` (sessions, and cases not opened by a transaction rule) or `transactions` (transaction alerts, and cases a transaction rule opened)." + }, + { + "in": "query", + "name": "period", + "schema": { + "type": "string", + "enum": [ + "7", + "30", + "90" + ], + "default": "30" + }, + "description": "Days, ending today." + } + ], + "x-didit-sdk": { + "resource": "investigator", + "method": "retrieve_performance", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/activity/": { + "get": { + "operationId": "investigator_activity", + "summary": "List Investigator activity", + "description": "The append-only audit trail, newest first. A batch accept is one entry. Adds `counts` per group and `actors`. Restricted keys need the `decisions` scope (read); transaction alerts also need `transactions`.", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "counts": { + "type": "object", + "additionalProperties": true + }, + "actors": { + "type": "array", + "items": {} + }, + "results": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "description": "`id`, `at`, `kind`, `group`, `queue`, `run_id`, `item`, `actor`, `agent_version`, `data`, `report_version`, `steps`, `sources`, `batch`." + } + } + } + } + } + } + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "parameters": [ + { + "in": "query", + "name": "scope", + "schema": { + "type": "string", + "enum": [ + "all", + "identity", + "transactions" + ], + "default": "all" + }, + "description": "`identity` (sessions, and cases not opened by a transaction rule) or `transactions` (transaction alerts, and cases a transaction rule opened)." + }, + { + "in": "query", + "name": "group", + "schema": { + "type": "string", + "enum": [ + "agent", + "decisions", + "overrides", + "autonomy" + ] + } + }, + { + "in": "query", + "name": "actor", + "schema": { + "type": "string" + }, + "description": "`agent` for the Investigator, or a person's email." + }, + { + "in": "query", + "name": "since", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "in": "query", + "name": "until", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "offset", + "schema": { + "type": "integer" + } + } + ], + "x-didit-sdk": { + "resource": "investigator", + "method": "list_activity", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/activity/export/": { + "get": { + "operationId": "investigator_activity_export", + "summary": "Export Investigator activity", + "description": "The same entries as CSV, one row per decision, newest first, up to 10,000 entries. Restricted keys need the `decisions` scope (read); transaction alerts also need `transactions`.", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", + "content": { + "text/csv": { + "schema": { + "type": "string" + } + } + } + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "parameters": [ + { + "in": "query", + "name": "scope", + "schema": { + "type": "string", + "enum": [ + "all", + "identity", + "transactions" + ], + "default": "all" + }, + "description": "`identity` (sessions, and cases not opened by a transaction rule) or `transactions` (transaction alerts, and cases a transaction rule opened)." + }, + { + "in": "query", + "name": "group", + "schema": { + "type": "string", + "enum": [ + "agent", + "decisions", + "overrides", + "autonomy" + ] + } + }, + { + "in": "query", + "name": "actor", + "schema": { + "type": "string" + }, + "description": "`agent` for the Investigator, or a person's email." + }, + { + "in": "query", + "name": "since", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "in": "query", + "name": "until", + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "x-didit-sdk": { + "resource": "investigator", + "method": "export_activity", + "auth": "api_key", + "server": true + } + } + }, + "/v3/investigator/settings/": { + "get": { + "operationId": "investigator_settings_retrieve", + "summary": "Get Investigator settings", + "description": "The stage (`none`, `shadow` or `live`) and the monthly spend limit with this month's spend. Restricted keys need the `applications` scope (read).", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvestigatorSettings" + } + } + } + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "x-didit-sdk": { + "resource": "investigator", + "method": "retrieve_settings", + "auth": "api_key", + "server": true + } + }, + "patch": { + "operationId": "investigator_settings_update", + "summary": "Update Investigator settings", + "description": "Change the stage, the monthly spend limit, or both. Moving past `none` needs a plan that includes the Investigator (Growth or higher); `live` also needs a contract that prices it, if you have one. Restricted keys need the `applications` scope (write).", + "tags": [ + "Investigator" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvestigatorSettings" + } + } + } + }, + "400": { + "description": "Neither field sent, or an invalid value." + }, + "401": { + "description": "Missing or invalid API key." + }, + "403": { + "description": "The key lacks the scope, the item kind is not readable, or the plan does not include the Investigator." + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvestigatorSettings" + } + } + } + }, + "x-didit-sdk": { + "resource": "investigator", + "method": "update_settings", + "auth": "api_key", + "server": true + } + } + }, + "/v3/travel-rule/wallet-addresses/import/": { + "post": { + "summary": "Import wallet addresses in bulk", + "description": "Add up to 10,000 wallets to your Travel Rule address book in one call, as JSON or as a CSV file. A wallet can be sent by its address, by the SHA-256 of its lowercased address (`address_hash`) to keep the address private, or both. The import is idempotent: a wallet already in the book is reported as `existing` and left untouched, so the same file can be sent again. Each row is validated on its own, so one bad row never fails the others. A wallet imported by its hash only matches transfers to that address and can be proven with the wallet-ownership widget like any other entry.", + "operationId": "importTravelRuleWalletAddresses", + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "x-didit-sdk": { + "resource": "travel_rule.wallet_addresses", + "method": "bulk_import", + "auth": "api_key", + "server": true + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "required": [ + "entries" + ], + "properties": { + "entries": { + "type": "array", + "minItems": 1, + "maxItems": 10000, + "items": { + "$ref": "#/components/schemas/WalletAddressImportRow" + } + } + } + }, + { + "type": "array", + "minItems": 1, + "maxItems": 10000, + "items": { + "$ref": "#/components/schemas/WalletAddressImportRow" + } + } + ] + }, + "example": { + "entries": [ + { + "address": "0xBeneficiaryWallet01", + "chain": "ETH", + "holder_name": "Ana Diaz", + "holder_vendor_data": "user-042" + }, + { + "address_hash": "4f7d2e1c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e", + "chain": "bitcoin", + "holder_vendor_data": "user-043" + }, + { + "address": "rExchangeSharedAddress01", + "chain": "XRP", + "memo": "104729", + "holder_name": "Ana Diaz" + } + ] + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file" + ], + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "A UTF-8 CSV file of at most 5 MB with a header row naming its columns (the row fields or their aliases). Empty cells count as not sent; columns the import does not read are ignored." + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl (JSON)", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/wallet-addresses/import/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"entries\": [{\"address\": \"0xBeneficiaryWallet01\", \"chain\": \"ETH\", \"holder_name\": \"Ana Diaz\"}, {\"address\": \"rExchangeSharedAddress01\", \"chain\": \"XRP\", \"memo\": \"104729\"}]}'" + }, + { + "lang": "curl", + "label": "curl (CSV)", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/wallet-addresses/import/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -F 'file=@wallets.csv;type=text/csv'" + } + ], + "responses": { + "200": { + "description": "Every row was created, already in the book, or a repeat of an earlier row.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WalletAddressImportReport" + }, + "example": { + "total": 3, + "created": 2, + "existing": 1, + "duplicate": 0, + "failed": 0, + "rows": [ + { + "row": 1, + "status": "existing", + "entry_id": "11111111-2222-3333-4444-555555555555", + "errors": {}, + "duplicate_of_row": null + }, + { + "row": 2, + "status": "created", + "entry_id": "22222222-3333-4444-5555-666666666666", + "errors": {}, + "duplicate_of_row": null + }, + { + "row": 3, + "status": "created", + "entry_id": "33333333-4444-5555-6666-777777777777", + "errors": {}, + "duplicate_of_row": null + } + ] + } + } + } + }, + "207": { + "description": "At least one row failed; the others were imported. Each failed row names its errors.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WalletAddressImportReport" + }, + "example": { + "total": 2, + "created": 1, + "existing": 0, + "duplicate": 0, + "failed": 1, + "rows": [ + { + "row": 1, + "status": "created", + "entry_id": "44444444-5555-6666-7777-888888888888", + "errors": {}, + "duplicate_of_row": null + }, + { + "row": 2, + "status": "failed", + "entry_id": null, + "errors": { + "address": [ + "Send address, address_hash or both." + ] + }, + "duplicate_of_row": null + } + ] + } + } + } + }, + "400": { + "description": "The request itself cannot be read: both `entries` and a `file`, or neither; more than 10,000 rows; a file over 5 MB, not UTF-8 CSV, without a header row or without rows.", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "example": { + "file": [ + "At most 10000 rows per import." + ] + } + } + } + } + } + } + }, + "/v3/travel-rule/counterparty-contacts/": { + "get": { + "summary": "List counterparty contacts", + "description": "The compliance addresses the EMAIL rail may write to for your application: Didit's platform contacts plus your application's own private ones, bounced ones included, newest first. Paginated with `limit` (default 50) and `offset`; `count` stops at 100, so 100 means 100 or more.", + "operationId": "listTravelRuleCounterpartyContacts", + "x-didit-sdk": { + "resource": "travel_rule.counterparty_contacts", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "counterparty", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Only the contacts of this directory entry. Must be a UUID; an empty value means no filter." + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1 + }, + "description": "Page size, default 50." + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + }, + "description": "Rows to skip." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/travel-rule/counterparty-contacts/?counterparty={vasp_id}' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "The contacts.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TravelRuleCounterpartyContact" + } + } + } + }, + "example": { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "uuid": "6c1f9b8e-2d4a-4f7e-8b3c-5a9d0e1f2a3b", + "counterparty": "0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91", + "counterparty_name": "Example Counterparty VASP", + "email": "compliance@counterparty.example", + "name": "Compliance desk", + "source": "application", + "scope": "application", + "created_by": "", + "verified_at": null, + "is_invalid": false, + "invalid_reason": "", + "invalid_detail": "", + "invalid_at": null, + "created_at": "2026-10-19T09:00:00Z" + } + ] + } + } + } + }, + "400": { + "description": "`counterparty` is not a UUID.", + "content": { + "application/json": { + "example": { + "counterparty": [ + "Must be a valid UUID." + ] + } + } + } + } + } + }, + "post": { + "summary": "Add a counterparty contact", + "description": "Adds a private compliance address for one directory entry. Only your application sees and uses it, and the EMAIL rail writes to it before any platform contact. Adding an address the entry already has among your own contacts returns that contact.", + "operationId": "createTravelRuleCounterpartyContact", + "x-didit-sdk": { + "resource": "travel_rule.counterparty_contacts", + "method": "create", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyContactCreate" + }, + "example": { + "counterparty": "0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91", + "email": "compliance@counterparty.example", + "name": "Compliance desk" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X POST https://verification.didit.me/v3/travel-rule/counterparty-contacts/ \\\n -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n -d '{\"counterparty\": \"0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91\", \"email\": \"compliance@counterparty.example\", \"name\": \"Compliance desk\"}'" + } + ], + "responses": { + "201": { + "description": "The contact was added.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleCounterpartyContact" + }, + "example": { + "uuid": "6c1f9b8e-2d4a-4f7e-8b3c-5a9d0e1f2a3b", + "counterparty": "0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91", + "counterparty_name": "Example Counterparty VASP", + "email": "compliance@counterparty.example", + "name": "Compliance desk", + "source": "application", + "scope": "application", + "created_by": "", + "verified_at": null, + "is_invalid": false, + "invalid_reason": "", + "invalid_detail": "", + "invalid_at": null, + "created_at": "2026-10-19T09:00:00Z" + } + } + } + }, + "400": { + "description": "A field is missing or invalid, or no directory entry has this id.", + "content": { + "application/json": { + "example": { + "counterparty": [ + "No directory entry with this id." + ] + } + } + } + } + } + } + }, + "/v3/travel-rule/counterparty-contacts/{contact_uuid}/": { + "delete": { + "summary": "Delete a counterparty contact", + "description": "Deletes one of your application's own contacts. The EMAIL rail stops writing to it at once. Platform contacts cannot be deleted.", + "operationId": "deleteTravelRuleCounterpartyContact", + "x-didit-sdk": { + "resource": "travel_rule.counterparty_contacts", + "method": "delete", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "contact_uuid", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X DELETE https://verification.didit.me/v3/travel-rule/counterparty-contacts/{contact_uuid}/ \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "204": { + "description": "Deleted." + }, + "404": { + "description": "No contact of your application has this id. A platform contact answers 404 too." + } + } + } + }, + "/v3/travel-rule/transfers/": { + "get": { + "summary": "List Travel Rule transfers", + "description": "Your application's Travel Rule transfers, newest first, paginated with `limit` (default 50) and `offset`. Rows carry VASP names, wallets and memos only, never person data. Comma-separated values select several. An unknown value answers 400 naming the parameter, for example `{\"direction\": [\"Unknown value(s): inbound.\"]}`.", + "operationId": "listTravelRuleTransfers", + "x-didit-sdk": { + "resource": "travel_rule.transfers", + "method": "list", + "auth": "api_key", + "server": true + }, + "tags": [ + "Travel Rule" + ], + "security": [ + { + "ApiKeyAuth": [] + } + ], + "parameters": [ + { + "name": "direction", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "INBOUND, OUTBOUND, or both comma-separated. Case-sensitive." + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "One or more transfer statuses, comma-separated." + }, + { + "name": "rail", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "One or more of INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL, TEST_VASP, comma-separated." + }, + { + "name": "kind", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "withdrawal, deposit or message, comma-separated." + }, + { + "name": "currency", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "The transaction's currency, case-insensitive." + }, + { + "name": "start_date", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date" + }, + "description": "Transfers created on or after this day (UTC), YYYY-MM-DD." + }, + { + "name": "end_date", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date" + }, + "description": "Transfers created on or before this day (UTC), YYYY-MM-DD." + }, + { + "name": "counterparty", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The `id` of a directory entry or Didit network member from GET /v3/travel-rule/vasps/: your transfers with that counterparty. A value that is not a valid id answers 400." + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1 + }, + "description": "Page size, default 50." + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0 + }, + "description": "Rows to skip." + } + ], + "x-codeSamples": [ + { + "lang": "curl", + "label": "curl", + "source": "curl -X GET 'https://verification.didit.me/v3/travel-rule/transfers/?direction=OUTBOUND&status=AWAITING_COUNTERPARTY' \\\n -H 'x-api-key: YOUR_API_KEY'" + } + ], + "responses": { + "200": { + "description": "A page of transfers.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TravelRuleTransferRow" + } + } + } + }, + "example": { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "id": "5b0f6c2e-2a4d-4f0e-9a41-0c3f9a7e1d22", + "transaction_id": "a3c1e8f4-7b62-4d1a-8e0b-2f9d6c4b7a10", + "txn_id": "wd-2026-10-19-0042", + "txn_date": "2026-10-19T08:41:12Z", + "kind": "withdrawal", + "direction": "OUTBOUND", + "status": "AWAITING_COUNTERPARTY", + "rail": "TRP", + "asset": "ETH", + "amount": "1.25", + "originator_vasp": "Origin CASP SL", + "beneficiary_vasp": "Example Counterparty VASP", + "originator_wallet": "0x77ccff0cc624a53cd8b8d8703ced826710211ec7", + "beneficiary_wallet": "0xede2d7e4f5b097e6391ab6bbcf2f47a53f4778cb", + "originator_memo": "", + "beneficiary_memo": "", + "originator_wallet_type": "vasp", + "beneficiary_wallet_type": "vasp", + "matched_transaction_id": null, + "deadline_at": "2026-10-21T08:41:13Z", + "created_at": "2026-10-19T08:41:13Z" + } + ] + } + } + } + }, + "400": { + "description": "A filter value is not valid.", + "content": { + "application/json": { + "example": { + "start_date": [ + "Use the YYYY-MM-DD format." + ] + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "DocumentAIDetectedCode": { + "type": "object", + "required": [ + "type", + "page_number", + "position", + "x", + "y", + "width", + "height", + "page_width", + "page_height", + "raw_payload", + "parsed_payload" + ], + "properties": { + "type": { + "type": "string", + "description": "Detected symbology, for example `QR_CODE`, `CODE_128`, `EAN_13`, `DATA_MATRIX`, or `PDF_417`." + }, + "page_number": { + "type": "integer", + "minimum": 1, + "description": "1-based page number." + }, + "position": { + "type": "array", + "nullable": true, + "description": "Detected polygon in rendered-page pixel coordinates. `null` when no reliable location is available.", + "items": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + } + }, + "x": { + "type": "integer", + "nullable": true, + "description": "Left edge of the bounding box in rendered-page pixels." + }, + "y": { + "type": "integer", + "nullable": true, + "description": "Top edge of the bounding box in rendered-page pixels." + }, + "width": { + "type": "integer", + "nullable": true, + "description": "Bounding-box width in rendered-page pixels." + }, + "height": { + "type": "integer", + "nullable": true, + "description": "Bounding-box height in rendered-page pixels." + }, + "page_width": { + "type": "integer", + "description": "Rendered page width in pixels." + }, + "page_height": { + "type": "integer", + "description": "Rendered page height in pixels." + }, + "raw_payload": { + "type": "string", + "description": "Raw decoder payload as a base64 string when available; otherwise an empty string." + }, + "parsed_payload": { + "nullable": true, + "description": "Decoded string or structured value. `null` when the code was located but could not be decoded." + } + } + }, + "SessionImportJob": { + "type": "object", + "required": [ + "uuid", + "status", + "import_type", + "source_format", + "source_filename", + "total_rows", + "processed_rows", + "created_count", + "updated_count", + "skipped_count", + "failed_count" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Import job id. Use this value to poll progress and fetch row errors." + }, + "status": { + "type": "string", + "enum": [ + "PENDING", + "PROCESSING", + "COMPLETED", + "FAILED", + "PAUSED", + "CANCELLED" + ] + }, + "import_type": { + "type": "string", + "enum": [ + "user_verification", + "business_verification", + "status_rules", + "transactions" + ] + }, + "source_format": { + "type": "string", + "enum": [ + "csv", + "ndjson" + ] + }, + "source_filename": { + "type": "string" + }, + "total_rows": { + "type": "integer", + "format": "int64" + }, + "processed_rows": { + "type": "integer", + "format": "int64" + }, + "checkpoint_row": { + "type": "integer", + "format": "int64" + }, + "created_count": { + "type": "integer", + "format": "int64" + }, + "updated_count": { + "type": "integer", + "format": "int64" + }, + "skipped_count": { + "type": "integer", + "format": "int64" + }, + "failed_count": { + "type": "integer", + "format": "int64" + }, + "total_media_bytes": { + "type": "integer", + "format": "int64" + }, + "last_error": { + "type": "string", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "started_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "completed_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "SessionImportError": { + "type": "object", + "required": [ + "uuid", + "row_number", + "error", + "raw_row" + ], + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "row_number": { + "type": "integer", + "format": "int64" + }, + "external_id": { + "type": "string", + "nullable": true + }, + "error": { + "type": "string" + }, + "raw_row": { + "type": "object", + "additionalProperties": true + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "BrandingCustomizationUpdate": { + "type": "object", + "description": "Partial branding update body. Provide only the fields you want to change. Hex colors accept `#RRGGBB` or `#RRGGBBAA`. Custom domain and white-label email are not accepted here.", + "properties": { + "color_primary": { + "type": "string", + "description": "Headers / primary text color." + }, + "color_secondary": { + "type": "string", + "description": "Paragraph / secondary text color." + }, + "color_background": { + "type": "string", + "description": "Page background color." + }, + "color_panel": { + "type": "string", + "description": "Panel / accent color." + }, + "color_panel_10": { + "type": "string", + "description": "Panel color at 10% opacity." + }, + "color_on_panel_1": { + "type": "string", + "description": "Support text color on panels." + }, + "color_on_panel_2": { + "type": "string", + "description": "Pill / badge color on panels." + }, + "color_on_background": { + "type": "string", + "description": "Text color on background." + }, + "color_button_1": { + "type": "string", + "description": "Primary button color." + }, + "color_button_2": { + "type": "string", + "description": "Secondary button color." + }, + "color_button_text_1": { + "type": "string", + "description": "Primary button text color." + }, + "color_button_text_2": { + "type": "string", + "description": "Secondary button text color." + }, + "color_pill_text": { + "type": "string", + "description": "Text color for pills, inputs and selectors." + }, + "border_radius_panel": { + "type": "integer", + "minimum": 0, + "description": "Panel corner radius in pixels." + }, + "border_radius_buttons": { + "type": "integer", + "minimum": 0, + "description": "Button corner radius in pixels." + }, + "font_family": { + "type": "string", + "description": "Google Font family name (e.g. `Inter`, `AR One Sans`)." + }, + "font_weight": { + "type": "string", + "description": "Font weight available for the selected family (e.g. `regular`, `500`, `700`)." + }, + "app_public_name": { + "type": "string", + "nullable": true, + "description": "Public name shown in the verification flow." + }, + "privacy_policy_url": { + "type": "string", + "nullable": true, + "description": "Privacy policy URL shown to the end user." + }, + "skip_welcome_screen": { + "type": "boolean", + "description": "Skip the initial welcome screen." + }, + "disable_login_with_didit": { + "type": "boolean", + "description": "Continue as guest instead of showing the login-with-Didit screen. Cannot be set back to `false` while a custom domain is configured (400)." + }, + "hide_progress_bar": { + "type": "boolean", + "description": "Hide the progress bar during the flow." + }, + "callback_seconds": { + "type": "integer", + "minimum": 0, + "description": "Delay (seconds) before redirecting to the callback URL." + }, + "delete_logo_square": { + "type": "boolean", + "description": "When `true`, removes the current square logo." + }, + "delete_logo_rectangular": { + "type": "boolean", + "description": "When `true`, removes the current rectangular logo." + }, + "delete_logo_rectangular_dark": { + "type": "boolean", + "description": "When `true`, removes the current dark-mode logo, and `logo_rectangular_dark` then reads `null`. If the same multipart request also uploads `image_rectangular_dark`, the upload wins." + }, + "delete_favicon": { + "type": "boolean", + "description": "When `true`, removes the current favicon." + } + } + }, + "BrandingCustomization": { + "type": "object", + "description": "White-label branding for an application: colors, fonts, radii, logo URLs and UI/UX options. Logo URLs are read-only (set them by uploading images). Custom domain and white-label email are not included.", + "properties": { + "color_primary": { + "type": "string" + }, + "color_secondary": { + "type": "string" + }, + "color_background": { + "type": "string" + }, + "color_panel": { + "type": "string" + }, + "color_panel_10": { + "type": "string" + }, + "color_on_panel_1": { + "type": "string" + }, + "color_on_panel_2": { + "type": "string" + }, + "color_on_background": { + "type": "string" + }, + "color_button_1": { + "type": "string" + }, + "color_button_2": { + "type": "string" + }, + "color_button_text_1": { + "type": "string" + }, + "color_button_text_2": { + "type": "string" + }, + "color_pill_text": { + "type": "string" + }, + "border_radius_panel": { + "type": "integer" + }, + "border_radius_buttons": { + "type": "integer" + }, + "font_family": { + "type": "string" + }, + "font_weight": { + "type": "string" + }, + "font_url": { + "type": "string", + "nullable": true, + "description": "Read-only. Resolved Google Fonts stylesheet URL for the selected family." + }, + "logo_square": { + "type": "string", + "nullable": true, + "description": "Read-only URL of the square logo. Upload via `image_square`." + }, + "logo_rectangular": { + "type": "string", + "nullable": true, + "description": "Read-only URL of the rectangular logo. Upload via `image_rectangular`." + }, + "logo_rectangular_dark": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Read-only. Temporary signed URL of the rectangular logo for dark mode, shown on a dark background. `null` when no dark logo is set or rectangular logos are not shown for your account. Upload it with `image_rectangular_dark`." + }, + "favicon": { + "type": "string", + "nullable": true, + "description": "Read-only URL of the favicon. Upload via `image_favicon`." + }, + "app_public_name": { + "type": "string", + "nullable": true + }, + "privacy_policy_url": { + "type": "string", + "nullable": true + }, + "skip_welcome_screen": { + "type": "boolean" + }, + "disable_login_with_didit": { + "type": "boolean" + }, + "hide_progress_bar": { + "type": "boolean" + }, + "callback_seconds": { + "type": "integer" + } + } + }, + "WebhookEvent": { + "type": "string", + "description": "Supported webhook event name. Use these exact strings in `subscribed_events`; unsupported values are rejected.", + "enum": [ + "status.updated", + "data.updated", + "user.status.updated", + "user.data.updated", + "business.status.updated", + "business.data.updated", + "activity.created", + "transaction.created", + "transaction.status.updated", + "travel_rule.status.updated", + "workflow.compliance_changed", + "auth.signup", + "auth.login", + "auth.login_failed", + "auth.password_reset_requested", + "auth.password_reset_completed", + "auth.mfa_changed", + "auth.new_device", + "auth.account_locked", + "investigator.report.ready", + "investigator.decision.automated", + "investigator.decision.overridden", + "investigator.decision.contested", + "investigator.question.raised", + "investigator.automation.paused", + "investigator.rfi.answered", + "investigator.rfi.overdue", + "investigator.drift.detected", + "investigator.sla.breached", + "investigator.qa.failed", + "investigator.external.report.ready" + ] + }, + "WebhookSubscribedEvents": { + "type": "array", + "description": "Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit `[]` is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged.", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/components/schemas/WebhookEvent" + }, + "example": [ + "status.updated", + "data.updated" + ] + }, + "PhoneVerificationSendResponse": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Session id of the verification. A `Retry` send returns the same `request_id` as the original send. This id appears in the Business Console, is returned again by a finalized `POST /v3/phone/check/`, and can be passed to `GET /v3/session/{sessionId}/decision/`." + }, + "status": { + "type": "string", + "enum": [ + "Success", + "Retry", + "Blocked" + ], + "description": "`Success` — OTP sent to a new verification. `Retry` — OTP re-sent for the pending verification created by a previous send. `Blocked` — the anti-fraud layer refused to send; the verification is immediately finalized as `Declined` and nothing is billed." + }, + "reason": { + "type": "string", + "nullable": true, + "description": "Why a send was `Blocked` (e.g., `repeated_attempts`, `suspicious`, `spam`). `null` on `Success` and `Retry`." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` stored on the session (from the first send)." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Echo of the `metadata` stored on the session (from the first send)." + } + } + }, + "PhoneVerificationCheckResponse": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "On `Approved`/`Declined`: the session id of the matched verification — identical to the `request_id` returned by `POST /v3/phone/send/`. On `Failed` and `Expired or Not Found`: a random one-off UUID that cannot be looked up later." + }, + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "Failed", + "Expired or Not Found" + ], + "description": "`Approved` — correct code, no declining risk. `Declined` — terminal: a declining risk matched or the attempt budget (3) was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — no pending verification for this number in the last 5 minutes." + }, + "message": { + "type": "string", + "description": "Human-readable explanation of the outcome, including the number of attempts remaining after a wrong code." + }, + "phone": { + "type": "object", + "nullable": true, + "description": "Full phone report. Present (non-null) only on finalized outcomes (`Approved`/`Declined`); `null` on `Failed` and absent on `Expired or Not Found`.", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "In Review", + "Not Finished", + "Expired" + ], + "description": "Final status of the phone verification. Matches the top-level `status` for finalized checks." + }, + "phone_number_prefix": { + "type": "string", + "description": "Country calling code with leading `+` (e.g., `+1`).", + "example": "+1" + }, + "phone_number": { + "type": "string", + "description": "National number without the country calling code.", + "example": "4155552671" + }, + "full_number": { + "type": "string", + "description": "Complete number in E.164 format.", + "example": "+14155552671" + }, + "country_code": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country of the number.", + "example": "US" + }, + "country_name": { + "type": "string", + "description": "Full country name derived from `country_code`.", + "example": "United States" + }, + "carrier": { + "type": "object", + "description": "Current network serving the number, from the carrier lookup run at finalization.", + "properties": { + "name": { + "type": "string", + "description": "Carrier name. Empty string when the lookup could not identify it." + }, + "type": { + "type": "string", + "description": "Line type reported by the lookup: `mobile`, `fixed_line`, `voip`, `isp`, `vpn`, `toll_free`, `premium_rate`, `pager`, `payphone`, `satellite`, `service`, `shared_cost`, `short_codes_commercial`, `calling_cards`, `local_rate`, `universal_access`, `voice_mail`, `other`, or `unknown`." + } + } + }, + "is_disposable": { + "type": "boolean", + "description": "`true` when the number belongs to a temporary/burner number service." + }, + "is_virtual": { + "type": "boolean", + "description": "`true` when the line type is virtual (`voip`, `isp`, or `vpn`)." + }, + "verification_method": { + "type": "string", + "description": "Channel that actually delivered the OTP once delivery is confirmed (e.g., `whatsapp`, `sms`); until then, the requested channel." + }, + "verification_attempts": { + "type": "integer", + "description": "Number of OTP sends for this verification (1, or 2 after a retry)." + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the correct code was entered. `null` if the verification was never approved." + }, + "warnings": { + "type": "array", + "description": "Risk warnings recorded during the verification (duplicate, disposable, VoIP, attempts exceeded, blocklist, etc.). Empty array when there are none.", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "description": "Feature that produced the warning. Always `PHONE` here." }, - "vendor_data": { + "risk": { "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent." + "description": "Machine-readable risk code." }, - "metadata": { + "additional_data": { "type": "object", "nullable": true, - "description": "Echo of the `metadata` you sent." + "description": "Risk-specific context (e.g., the duplicated session id for duplicate risks). `null` when the risk carries no extra data." }, - "created_at": { + "log_type": { "type": "string", - "format": "date-time" - } - } - }, - "examples": { - "Resolved company": { - "summary": "Full registry profile (officer list truncated for brevity)", - "value": { - "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", - "kyb_registry": { - "uuid": "f1e2d3c4-b5a6-4789-9c0d-1e2f3a4b5c6d", - "node_id": "feature_kyb_registry", - "status": "In Review", - "registry_status": "active", - "data_resolved": true, - "company_name": "Tesco PLC", - "registration_number": "00445790", - "country_code": "GB", - "region": "", - "company_type": "Public Limited Company", - "incorporation_date": "1947-11-27", - "registered_address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", - "tax_number": "GB412 8925 93", - "risk_level": "low risk", - "verification_status": "verified", - "is_from_registry": true, - "fetch_status": "resolved", - "alternative_names": "TESCO STORES (HOLDINGS) PUBLIC LIMITED COMPANY", - "nature_of_business": null, - "registered_capital": null, - "registered_capital_amount": null, - "registered_capital_currency": null, - "website": "https://www.tesco.com/", - "email": "customer.service@tesco.co.uk", - "phone": "03301231688", - "legal_entity_identifier": null, - "location_of_registration": null, - "financial_summary": { - "currency": "Pound (£)", - "total_number_of_shares": "8174378721", - "meta_detail": { - "Class_Of_Share": "Ordinary" - } - }, - "officers": [ - { - "uuid": "a1b2c3d4-e5f6-4789-9a0b-1c2d3e4f5a6b", - "name": "CHRISTOPHER JON TAYLOR", - "designation": "Secretary", - "role": "secretary", - "nationality": null, - "is_active": true, - "kyc_status": null, - "kyc_session_url": null - }, - { - "uuid": "b2c3d4e5-f6a7-4890-8b1c-2d3e4f5a6b7c", - "name": "MELISSA BETHELL", - "designation": "Director", - "role": "director", - "nationality": "British", - "is_active": true, - "kyc_status": null, - "kyc_session_url": null - } - ], - "beneficial_owners": [], - "addresses": [ - { - "address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", - "type": "legal_entity_registry_address", - "description": "registered office address" - } - ], - "industries": null, - "accounts": { - "last_account": "26 February 2025", - "next_account": "26 February 2026", - "due_by_date": "26 August 2026" - }, - "registry_data": { - "name": "Tesco PLC", - "registration_number": "00445790", - "status": "active", - "type": "Public Limited Company", - "incorporation_date": "1947-11-27", - "tax_number": "GB412 8925 93", - "country_code": "GB", - "fetch_status": "resolved", - "industries_detail": [ - { - "code": "47110", - "name": "Retail Sale In Non-specialised Stores With Food, Beverages Or Tobacco Predominating", - "description": null, - "meta_detail": [] - } - ] - }, - "user_provided_data": null, - "confirmed_by_user_at": null, - "last_console_edit_at": null, - "is_editable": true - }, - "vendor_data": "company-1234", - "metadata": null, - "created_at": "2026-06-11T10:45:00.000000+00:00" - } - } - } - } - } - }, - "400": { - "description": "Validation error. Field-level problems use DRF's standard envelope.", - "content": { - "application/json": { - "examples": { - "Missing kyb_response_id": { - "summary": "`kyb_response_id` is required", - "value": { - "kyb_response_id": [ - "This field is required." - ] - } - }, - "save_api_request=false rejected": { - "summary": "Selects are always persisted", - "value": { - "save_api_request": [ - "KYB registry select must be saved so billing and retrieval can be tracked." - ] + "enum": [ + "error", + "warning", + "information" + ], + "description": "Severity. `error` risks decline the verification, `warning` flags it for review, `information` is recorded without affecting the status." + }, + "short_description": { + "type": "string", + "description": "Human-readable one-line summary of the risk." + }, + "long_description": { + "type": "string", + "description": "Human-readable explanation of the risk." } } } - } - } - }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", - "content": { - "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" + }, + "lifecycle": { + "type": "array", + "description": "Chronological audit trail of the verification. Event `type` is one of `PHONE_VERIFICATION_MESSAGE_SENT`, `PHONE_VERIFICATION_RETRY_MESSAGE_SENT`, `PHONE_VERIFICATION_BLOCKED`, `PHONE_DELIVERY_DELIVERED`, `PHONE_DELIVERY_UNDELIVERABLE`, `VALID_CODE_ENTERED`, `INVALID_CODE_ENTERED`, `PHONE_VERIFICATION_APPROVED`, `PHONE_VERIFICATION_DECLINED`, `PHONE_VERIFICATION_IN_REVIEW`, `PHONE_VERIFICATION_EXPIRED`.", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Event type." + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "When the event happened." + }, + "details": { + "type": "object", + "nullable": true, + "description": "Event-specific payload. Send events: `{status, reason, channel, actual_channel}` (`actual_channel` is filled once delivery is confirmed). Delivery events: `{channel, status}`. Code-entry events: `{code_tried, status}`. Final-status events: `null`, or `{reason}` when declined or in review." + }, + "fee": { + "type": "number", + "description": "USD amount billed for this event. Non-zero only on send events (the delivery price); 0 for code entries, delivery confirmations, and status events." } } } - } - } - }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", - "content": { - "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } - }, - "schema": { + }, + "matches": { + "type": "array", + "description": "Other sessions of your application (KYC, KYB, or API) where the same phone number was used by a different user (different `vendor_data`). Up to 5 entries, a synthetic list-entry blocklist match first when present (session-derived matches keep query order). Empty array when there are no matches.", + "items": { "type": "object", "properties": { - "detail": { - "type": "string" + "session_id": { + "type": "string", + "format": "uuid", + "description": "Session id of the other verification that used this phone number." + }, + "session_number": { + "type": "integer", + "description": "Human-friendly sequential number of that session." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of that session." + }, + "verification_date": { + "type": "string", + "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)." + }, + "phone_number": { + "type": "string", + "description": "The matched phone number." + }, + "status": { + "type": "string", + "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)." + }, + "is_blocklisted": { + "type": "boolean", + "description": "Whether the phone number is on your application's blocklist." + }, + "api_service": { + "type": "string", + "nullable": true, + "description": "For API-type sessions, the standalone API that created it (e.g., `PHONE_VERIFICATION`); `null` for regular verification sessions." + }, + "source": { + "type": "string", + "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry prepends a synthetic match with `source: \"list_entry\"` and null `session_id`/`session_number`." } } } + }, + "phone_social": { + "$ref": "#/components/schemas/PhoneSocialFootprint" } } }, - "202": { - "description": "Report accepted and still processing. When a report is still being prepared, the endpoint returns 202 Accepted, kyb_registry.fetch_status=pending, and registry_tier_delivered=null. Keep request_id and retrieve GET /v3/session/{request_id}/decision/ until registry_checks[0].company.fetch_status is resolved. Didit completes the report automatically and charges the delivered tier once it is ready. Do not repeat the purchase while it is pending; every new POST creates a separate request.", - "content": { - "application/json": { - "schema": { - "$ref": "#/paths/~1v3~1kyb~1shareholders~1/post/responses/200/content/application~1json/schema" - }, - "examples": { - "Pending (registry still resolving)": { - "summary": "Ownership report is still processing; retrieve the saved decision", - "value": { - "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", - "kyb_registry": { - "uuid": "7c0a4f7e-2b1d-4f6a-9c3e-8d5b2a1f0e9d", - "node_id": "feature_kyb_registry", - "status": "Not Finished", - "registry_status": null, - "data_resolved": false, - "company_name": "Example Holdings Limited", - "registration_number": "12345678", - "country_code": "HK", - "region": null, - "company_type": null, - "incorporation_date": null, - "registered_address": null, - "tax_number": null, - "risk_level": null, - "verification_status": null, - "is_from_registry": true, - "fetch_status": "pending", - "alternative_names": null, - "nature_of_business": null, - "registered_capital": null, - "registered_capital_amount": null, - "registered_capital_currency": null, - "website": null, - "email": null, - "phone": null, - "legal_entity_identifier": null, - "location_of_registration": null, - "financial_summary": {}, - "officers": [], - "beneficial_owners": [], - "addresses": null, - "industries": null, - "accounts": null, - "registry_data": {}, - "user_provided_data": null, - "confirmed_by_user_at": null, - "last_console_edit_at": null, - "is_editable": true, - "registry_tier_delivered": null, - "registry_ownership_status": "pending" - }, - "vendor_data": "company-1234", - "metadata": null, - "created_at": "2026-06-11T10:45:00.000000+00:00", - "registry_tier_delivered": null, - "registry_tier_requested": "shareholders" - } - } - } - } - } + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of the matched verification's session. `null` on `Expired or Not Found`." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "`metadata` of the matched verification's session. `null` on `Expired or Not Found`." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp of this check response." } } - } - }, - "/v3/kyb/ubo/": { - "post": { - "summary": "KYB registry company with ultimate beneficial owners (standalone)", - "operationId": "post_v3_kyb_ubo", - "tags": [ - "Standalone APIs" - ], - "security": [ - { - "ApiKeyAuth": [] - } - ], - "parameters": [], - "description": "Retrieve the Lite profile, the direct shareholders **and the ultimate beneficial owners** resolved through the ownership chain (`registry_data.ubo_resolution` carries the corporate parent chain) of one candidate returned by `POST /v3/kyb/search/`.\n\n**Pairing with search.** Pass the candidate's `kyb_response_id` exactly as returned by a search of the same application. The search runs on the Lite registry chain; when the country's ownership data lives at another registry Didit re-finds the company there by registration number, at no extra cost.\n\n**Billing.** Billed at the country's UBO price - see [KYB registry pricing](/getting-started/kyb-registry-pricing). The tier delivered is the highest the country and the registry can serve: Shareholders when the resolved report names no natural person as a beneficial owner, Lite when there is no ownership record at all; `registry_tier_delivered` names what was billed. The balance is checked against the UBO price before any registry call (`403` when short).\n\n**Availability.** v3 only. Every call creates a business session (its id is the `request_id`).\n\n**Pending reports.** When a report is still being prepared, or the ownership section has not been retrieved from the registry yet, the endpoint returns 202 Accepted, kyb_registry.fetch_status=pending, kyb_registry.registry_ownership_status=pending, and registry_tier_delivered=null. Keep request_id and retrieve GET /v3/session/{request_id}/decision/ until registry_checks[0].company.fetch_status is resolved. Didit completes the report automatically and charges the delivered tier once it is ready. Do not repeat the purchase while it is pending; every new POST creates a separate request.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "kyb_response_id" - ], - "properties": { - "kyb_response_id": { - "type": "string", - "maxLength": 255, - "description": "Candidate handle from `POST /v3/kyb/search/` (`kyb_registry.companies[].kyb_response_id`). Ephemeral — use it shortly after the search that produced it.", - "example": "69aeeb95febb0f1704042259" - }, - "vendor_data": { - "type": "string", - "description": "Your identifier for this company; echoed back and stored with the business session." - }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Free-form JSON stored with the business session and echoed back." - }, - "save_api_request": { - "type": "boolean", - "default": true, - "description": "Must be `true` (the default). KYB registry selects are always persisted so billing and retrieval can be tracked; `false` returns `400`." - } - } - } - } - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "curl", - "source": "curl -X POST https://verification.didit.me/v3/kyb/ubo/ \\\n -H 'x-api-key: YOUR_API_KEY' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"kyb_response_id\": \"69aeeb95febb0f1704042259\",\n \"vendor_data\": \"company-1234\"\n }'" + }, + "EmailVerificationSendResponse": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Session id of the verification. A `Retry` send returns the same `request_id` as the original send. This id appears in the Business Console, is returned again by a finalized `POST /v3/email/check/`, and can be passed to `GET /v3/session/{sessionId}/decision/`." }, - { - "lang": "python", - "label": "Python", - "source": "import os, time, requests\n\nsearch = requests.post(\n \"https://verification.didit.me/v3/kyb/search/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n timeout=60,\n).json()\nkyb_response_id = search[\"kyb_registry\"][\"companies\"][0][\"kyb_response_id\"]\n\nresp = requests.post(\n \"https://verification.didit.me/v3/kyb/ubo/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n json={\"kyb_response_id\": kyb_response_id, \"vendor_data\": \"company-1234\"},\n timeout=90,\n)\nresp.raise_for_status()\ndata = resp.json()\nrequest_id = data[\"request_id\"]\nprint(\"Keep this request ID:\", request_id)\ncompany = data[\"kyb_registry\"]\nif resp.status_code == 202 or company.get(\"fetch_status\") == \"pending\":\n # This client wait limit does not cancel the report or create a new purchase.\n deadline = time.monotonic() + 300\n while company.get(\"fetch_status\") != \"resolved\" and time.monotonic() < deadline:\n time.sleep(5)\n decision = requests.get(\n f\"https://verification.didit.me/v3/session/{request_id}/decision/\",\n headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n timeout=30,\n )\n decision.raise_for_status()\n checks = decision.json().get(\"registry_checks\") or []\n if checks:\n company = checks[0].get(\"company\") or company\nif company.get(\"fetch_status\") == \"resolved\":\n print(company[\"company_name\"], company[\"registry_tier_delivered\"])\nelse:\n print(\"Still processing; retrieve this request later:\", request_id)" + "status": { + "type": "string", + "enum": [ + "Success", + "Retry", + "Undeliverable" + ], + "description": "`Success` — OTP emailed to a new verification (billed). `Retry` — fresh OTP emailed for the pending verification created by a previous send (free). `Undeliverable` — the address failed deliverability validation or the message could not be sent; the verification is immediately finalized as `Declined` and nothing is billed." }, - { - "lang": "javascript", - "label": "JavaScript", - "source": "const res = await fetch('https://verification.didit.me/v3/kyb/ubo/', {\n method: 'POST',\n headers: {\n 'x-api-key': 'YOUR_API_KEY',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n kyb_response_id: '69aeeb95febb0f1704042259',\n vendor_data: 'company-1234',\n }),\n});\nif (!res.ok) throw new Error(`Request failed: ${res.status}`);\nconst data = await res.json();\nconst requestId = data.request_id;\nconsole.log('Keep this request ID:', requestId);\nlet company = data.kyb_registry;\nif (res.status === 202 || company.fetch_status === 'pending') {\n // This client wait limit does not cancel the report or create a new purchase.\n const deadline = Date.now() + 300000;\n while (company.fetch_status !== 'resolved' && Date.now() < deadline) {\n await new Promise(resolve => setTimeout(resolve, 5000));\n const decisionResponse = await fetch(\n `https://verification.didit.me/v3/session/${requestId}/decision/`,\n { headers: { 'x-api-key': 'YOUR_API_KEY' }, signal: AbortSignal.timeout(30000) },\n );\n if (!decisionResponse.ok) throw new Error(`Retrieval failed: ${decisionResponse.status}`);\n const decision = await decisionResponse.json();\n company = decision.registry_checks?.[0]?.company ?? company;\n }\n}\nif (company.fetch_status === 'resolved') {\n console.log(company.company_name, company.registry_tier_delivered);\n} else {\n console.log('Still processing; retrieve this request later:', requestId);\n}" + "reason": { + "type": "string", + "nullable": true, + "enum": [ + "email_can_not_be_delivered", + null + ], + "description": "`email_can_not_be_delivered` when `status` is `Undeliverable`; `null` otherwise." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` stored on the session (from the first send)." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Echo of the `metadata` stored on the session (from the first send)." } - ], - "responses": { - "200": { - "description": "Company report returned and billed at the delivered tier. Ownership reports still being prepared can return 202; keep request_id to retrieve their saved decision.", - "content": { - "application/json": { - "schema": { + } + }, + "EmailVerificationCheckResponse": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "On `Approved`/`Declined`: the session id of the matched verification — identical to the `request_id` returned by `POST /v3/email/send/`. On `Failed` and `Expired or Not Found`: a random one-off UUID that cannot be looked up later." + }, + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "Failed", + "Expired or Not Found" + ], + "description": "`Approved` — correct code, no declining risk. `Declined` — terminal: a declining risk matched or the attempt budget (3) was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — no pending verification for this address in the last 5 minutes." + }, + "message": { + "type": "string", + "description": "Human-readable explanation of the outcome, including the number of attempts remaining after a wrong code." + }, + "email": { + "type": "object", + "nullable": true, + "description": "Full email report. Present (non-null) only on finalized outcomes (`Approved`/`Declined`); `null` on `Failed` and absent on `Expired or Not Found`.", + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "In Review", + "Not Finished", + "Expired" + ], + "description": "Final status of the email verification. Matches the top-level `status` for finalized checks." + }, + "email": { + "type": "string", + "format": "email", + "description": "The verified email address.", + "example": "alice@example.com" + }, + "is_breached": { + "type": "boolean", + "description": "`true` when the address appears in known data breaches (breach intelligence lookup run at finalization)." + }, + "breaches": { + "type": "array", + "description": "Up to 5 most recent known breaches containing this address. Empty array (or omitted) when `is_breached` is `false`.", + "items": { "type": "object", "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Id of the business session created for this retrieval. The stored record (visible in the console) keeps updating if the registry resolves after the response." - }, - "registry_tier_requested": { - "type": "string", - "enum": [ - "basic", - "shareholders", - "ubo" - ], - "description": "The tier this endpoint sells: `basic` (Lite) for select, `shareholders`, `ubo`." - }, - "registry_tier_delivered": { - "type": "string", - "enum": [ - "basic", - "shareholders", - "ubo", - null - ], - "description": "The tier actually delivered and billed. Lower than requested when the country's registries hold no ownership data, when the registry has no ownership record for this company, or when the resolved report of a `ubo` request names no natural person as a beneficial owner.", - "nullable": true - }, - "kyb_registry": { - "type": "object", - "description": "Full registry company profile.", - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Didit's id for this company record." - }, - "node_id": { - "type": "string", - "example": "feature_kyb_registry" - }, - "status": { - "type": "string", - "enum": [ - "Not Finished", - "Approved", - "Declined", - "In Review" - ], - "description": "Didit's assessment of the registry check (e.g. `In Review` when the company is not active in the registry or ownership data could not be derived)." - }, - "registry_status": { - "type": "string", - "nullable": true, - "enum": [ - "active", - "dissolved", - "deregistered", - "see full details", - "authorised", - "appointed representative", - "unauthorised", - "inactive", - "no longer authorised", - "closed", - "struck off", - null - ], - "description": "Normalized company status as reported by the registry." - }, - "data_resolved": { - "type": "boolean", - "description": "`true` once the registry returned the full profile. `false` when the registry was still processing after polling — the stored record completes automatically once the registry resolves." - }, - "company_name": { - "type": "string", - "example": "Tesco PLC" - }, - "registration_number": { - "type": "string", - "nullable": true, - "example": "00445790" - }, - "country_code": { - "type": "string", - "description": "ISO 3166-1 alpha-2.", - "example": "GB" - }, - "region": { - "type": "string", - "nullable": true, - "description": "Subdivision code when the registry is regional (e.g. `CA` for `US-CA`); empty otherwise." - }, - "company_type": { - "type": "string", - "nullable": true, - "example": "Public Limited Company" - }, - "incorporation_date": { - "type": "string", - "format": "date", - "nullable": true, - "example": "1947-11-27" - }, - "registered_address": { - "type": "string", - "nullable": true, - "example": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA" - }, - "tax_number": { - "type": "string", - "nullable": true, - "example": "GB412 8925 93" - }, - "risk_level": { - "type": "string", - "nullable": true, - "description": "Registry-reported risk level, when available." - }, - "verification_status": { - "type": "string", - "nullable": true, - "description": "Registry verification status, when available." - }, - "is_from_registry": { - "type": "boolean", - "description": "Always `true` for this endpoint — the profile was retrieved from an official registry." - }, - "fetch_status": { - "type": "string", - "enum": [ - "pending", - "resolved" - ], - "description": "Registry fetch lifecycle. Mirrored by `data_resolved`." - }, - "alternative_names": { - "type": "string", - "nullable": true, - "description": "Alternative, trading, former or original-language company names as text, when reported." - }, - "nature_of_business": { - "type": "string", - "nullable": true, - "description": "Activity description or classification code, when reported." - }, - "registered_capital": { - "type": "string", - "nullable": true - }, - "registered_capital_amount": { - "type": "string", - "nullable": true, - "description": "Decimal amount as a string, when reported." - }, - "registered_capital_currency": { - "type": "string", - "nullable": true, - "description": "ISO 4217 code, when reported." - }, - "website": { - "type": "string", - "nullable": true, - "example": "https://www.tesco.com/" - }, - "email": { - "type": "string", - "nullable": true, - "example": "customer.service@tesco.co.uk" - }, - "phone": { - "type": "string", - "nullable": true, - "example": "03301231688" - }, - "legal_entity_identifier": { - "type": "string", - "nullable": true, - "description": "LEI, when reported." - }, - "location_of_registration": { - "type": "string", - "nullable": true, - "description": "Registry/jurisdiction the company is registered with, when reported." - }, - "vat_number": { - "type": "string", - "nullable": true, - "description": "EU VAT number, when collected during the hosted registry step or edited afterwards. At select time this is typically not yet set — VAT and tax ID validation runs when the end user submits the registry step in the hosted flow." - }, - "vat_validation_status": { - "type": "string", - "enum": [ - "valid", - "invalid", - "format_valid", - "invalid_format", - "could_not_validate", - "not_applicable" - ], - "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." - }, - "vat_validated_number": { - "type": "string", - "nullable": true, - "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." - }, - "vat_validation_source": { - "type": "string", - "nullable": true, - "enum": [ - "eu_vies", - "pl_vat_white_list", - "no_brreg", - "ch_uid_register", - "br_receita_federal", - "co_rues", - "cr_hacienda", - "ec_sri", - "py_dnit", - "format_rules", - null - ], - "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." - }, - "vat_validated_name": { - "type": "string", - "nullable": true, - "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_validated_address": { - "type": "string", - "nullable": true, - "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." - }, - "vat_checked_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the check. `null` when no check ran." - }, - "financial_summary": { - "type": "object", - "nullable": true, - "description": "Registry-reported share/capital details (currency, total shares, share classes), when available." - }, - "officers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string", - "example": "MELISSA BETHELL" - }, - "designation": { - "type": "string", - "nullable": true, - "description": "Raw registry designation (e.g. `Director`, `Secretary`)." - }, - "role": { - "type": "string", - "description": "Normalized role derived from the designation. Full 17-value model set; registry parsing on this endpoint typically emits director, non_executive_director, secretary, chairman, or company_officer.", - "enum": [ - "director", - "non_executive_director", - "secretary", - "chairman", - "ubo", - "shareholder", - "representative", - "founder", - "legal_advisor", - "authorized_signatory", - "trustee", - "beneficiary", - "company_officer", - "settlor", - "protector", - "investor", - "other" - ] - }, - "nationality": { - "type": "string", - "nullable": true - }, - "is_active": { - "type": "boolean", - "description": "`false` when the registry marks the appointment as resigned." - }, - "kyc_status": { - "type": "string", - "nullable": true, - "enum": [ - "Approved", - "Declined", - "Pending", - null - ], - "description": "Status of an associated KYC session, when one was started for this officer. `null` otherwise." - }, - "kyc_session_url": { - "type": "string", - "nullable": true, - "description": "Verification URL of the associated KYC session, when one exists." - }, - "first_name": { - "type": "string", - "nullable": true, - "description": "Given name when explicitly reported by the registry; not inferred from the full name." - }, - "last_name": { - "type": "string", - "nullable": true, - "description": "Family name when explicitly reported by the registry." - }, - "address": { - "type": "string", - "nullable": true, - "description": "Officer address reported by the registry." - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Date the officer was appointed.", - "format": "date" - }, - "termination_date": { - "type": "string", - "nullable": true, - "description": "Date the appointment ended, when reported.", - "format": "date" - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", - "format": "date" - }, - "date_of_birth_year": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 9999, - "description": "Registry-reported birth year, including when the full date is unavailable." - }, - "date_of_birth_month": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 12, - "description": "Registry-reported birth month. Null when only the year is known." - } - } - }, - "description": "Directors, secretaries, and other officers extracted from the registry." - }, - "beneficial_owners": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "first_name": { - "type": "string", - "nullable": true - }, - "last_name": { - "type": "string", - "nullable": true - }, - "entity_type": { - "type": "string", - "enum": [ - "person", - "company" - ], - "description": "Whether the owner is a natural person or a corporate entity." - }, - "roles": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Normalized roles (e.g. `ubo`, `shareholder`)." - }, - "ownership_min_shares": { - "type": "string", - "nullable": true, - "description": "Lower bound of the ownership band reported by the registry (e.g. `25%`)." - }, - "ownership_max_shares": { - "type": "string", - "nullable": true, - "description": "Upper bound of the ownership band reported by the registry." - }, - "is_active": { - "type": "boolean" - }, - "kyc_status": { - "type": "string", - "nullable": true, - "enum": [ - "Approved", - "Declined", - "Pending", - null - ] - }, - "kyc_session_url": { - "type": "string", - "nullable": true - }, - "effective_ownership_percent": { - "type": "number", - "nullable": true, - "description": "Parsed ownership percentage (max bound, falling back to min bound)." - }, - "company_name": { - "type": "string", - "nullable": true - }, - "registration_number": { - "type": "string", - "nullable": true - }, - "country_of_birth": { - "type": "string", - "nullable": true - }, - "email": { - "type": "string", - "nullable": true - }, - "nationality": { - "type": "string", - "nullable": true - }, - "designation": { - "type": "string", - "nullable": true - }, - "voting_min_shares": { - "type": "string", - "nullable": true - }, - "voting_max_shares": { - "type": "string", - "nullable": true - }, - "date_of_birth": { - "type": "string", - "nullable": true, - "description": "Complete birth date when reported.", - "format": "date" - }, - "appointment_date": { - "type": "string", - "nullable": true, - "description": "Appointment date when reported.", - "format": "date" - }, - "shareholding": { - "$ref": "#/components/schemas/KYBRegistryShareholding" - } - } - }, - "description": "Beneficial owners / persons with significant control extracted from the registry. Can be empty when the registry does not expose ownership." - }, - "addresses": { - "type": "array", - "nullable": true, - "description": "Every address the registry states, one canonical shape. Null when it states none.", - "items": { - "$ref": "#/components/schemas/KYBRegistryAddress" - } - }, - "industries": { - "type": "array", - "nullable": true, - "description": "The activities the registry classifies the company under, one canonical shape. Null when it states none.", - "items": { - "$ref": "#/components/schemas/KYBRegistryIndustry" - } - }, - "accounts": { - "type": "object", - "nullable": true, - "additionalProperties": true, - "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." + "name": { + "type": "string", + "description": "Breach identifier." + }, + "domain": { + "type": "string", + "description": "Domain of the breached service." + }, + "breach_date": { + "type": "string", + "format": "date", + "description": "Date the breach occurred." + }, + "breach_emails_count": { + "type": "integer", + "description": "Total number of accounts exposed in the breach." + }, + "description": { + "type": "string", + "description": "Human-readable description of the breach (may contain HTML markup)." + }, + "logo_path": { + "type": "string", + "description": "URL of the breached service's logo." + }, + "data_classes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Categories of data exposed, in snake_case (e.g., `email_addresses`, `passwords`)." + }, + "is_verified": { + "type": "boolean", + "description": "Whether the breach has been verified as legitimate." + } + } + } + }, + "is_disposable": { + "type": "boolean", + "description": "`true` when the domain belongs to a known disposable/temporary-mail provider." + }, + "is_undeliverable": { + "type": "boolean", + "description": "`true` when the address cannot receive mail (failed syntax or DNS/MX validation, or the message bounced)." + }, + "verification_attempts": { + "type": "integer", + "description": "Number of OTP sends for this verification (1, or 2 after a retry)." + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the correct code was entered. `null` if the verification was never approved." + }, + "warnings": { + "type": "array", + "description": "Risk warnings recorded during the verification (duplicate, breached, disposable, undeliverable, attempts exceeded, blocklist, etc.). Empty array when there are none.", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "description": "Feature that produced the warning. Always `EMAIL` here." + }, + "risk": { + "type": "string", + "description": "Machine-readable risk code." + }, + "additional_data": { + "type": "object", + "nullable": true, + "description": "Risk-specific context (e.g., the duplicated session id for duplicate risks). `null` when the risk carries no extra data." + }, + "log_type": { + "type": "string", + "enum": [ + "error", + "warning", + "information" + ], + "description": "Severity. `error` risks decline the verification, `warning` flags it for review, `information` is recorded without affecting the status." + }, + "short_description": { + "type": "string", + "description": "Human-readable one-line summary of the risk." + }, + "long_description": { + "type": "string", + "description": "Human-readable explanation of the risk." + } + } + } + }, + "lifecycle": { + "type": "array", + "description": "Chronological audit trail of the verification. Event `type` is one of `EMAIL_VERIFICATION_MESSAGE_SENT`, `EMAIL_VERIFICATION_RETRY_MESSAGE_SENT`, `VALID_CODE_ENTERED`, `INVALID_CODE_ENTERED`, `EMAIL_VERIFICATION_APPROVED`, `EMAIL_VERIFICATION_DECLINED`, `EMAIL_VERIFICATION_IN_REVIEW`, `EMAIL_VERIFICATION_EXPIRED`.", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Event type." + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "When the event happened." + }, + "details": { + "type": "object", + "nullable": true, + "description": "Event-specific payload. Send events: `{status, reason}`. Code-entry events: `{code_tried, status}`. Final-status events: `null`, or `{reason}` when declined or in review." + }, + "fee": { + "type": "number", + "description": "USD amount billed for this event. Non-zero only on the first successful send; 0 for retries, code entries, and status events." + } + } + } + }, + "matches": { + "type": "array", + "description": "Other sessions of your application (KYC, KYB, or API) where the same email address was used by a different user (different `vendor_data`). Up to 5 entries; a synthetic list-entry blocklist match is prepended when present (and only when no session-derived blocklisted email exists) — session-derived matches keep created_at order. Empty array when there are no matches.", + "items": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "description": "Session id of the other verification that used this email address." + }, + "session_number": { + "type": "integer", + "description": "Human-friendly sequential number of that session." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of that session." + }, + "verification_date": { + "type": "string", + "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)." + }, + "email": { + "type": "string", + "description": "The matched email address." + }, + "status": { + "type": "string", + "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)." + }, + "is_blocklisted": { + "type": "boolean", + "description": "Whether the email address is on your application's blocklist." + }, + "api_service": { + "type": "string", + "nullable": true, + "description": "For API-type sessions, the standalone API that created it (e.g., `PHONE_VERIFICATION`); `null` for regular verification sessions." + }, + "source": { + "type": "string", + "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry prepends a synthetic match with `source: \"list_entry\"` and null `session_id`/`session_number`." + } + } + } + }, + "email_social": { + "$ref": "#/components/schemas/EmailSocialFootprint" + } + } + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of the matched verification's session. `null` on `Expired or Not Found`." + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "`metadata` of the matched verification's session. `null` on `Expired or Not Found`." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp of this check response." + } + } + }, + "WorkflowListItem": { + "type": "object", + "description": "One workflow version in the list view.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Per-version UUID. Use as `{settings_uuid}` for get/update/delete, and as `workflow_id` when creating sessions." + }, + "workflow_id": { + "type": "string", + "format": "uuid", + "description": "Stable group ID shared by every version of the same workflow. Equals `uuid` for version 1." + }, + "workflow_label": { + "type": "string", + "description": "Display name for the workflow." + }, + "workflow_type": { + "type": "string", + "nullable": true, + "description": "Base type for simple workflows: `kyc`, `kyb`, `adaptive_age_verification`, `biometric_authentication`, etc. `null` for graph-based workflows without a KYB feature." + }, + "is_default": { + "type": "boolean", + "description": "Whether this is the default workflow for new sessions." + }, + "is_archived": { + "type": "boolean" + }, + "is_white_label_enabled": { + "type": "boolean", + "description": "Whether white-label customization applies to sessions created with this workflow." + }, + "total_price": { + "type": "number", + "description": "Fixed price per verification in USD (JSON number, excludes variable-priced features such as DATABASE_VALIDATION and PHONE_VERIFICATION)." + }, + "min_price": { + "type": "number", + "description": "Cheapest fixed price across workflow-graph paths, in USD." + }, + "max_price": { + "type": "number", + "description": "Most expensive fixed price across workflow-graph paths, in USD." + }, + "features": { + "type": "string", + "description": "Enabled features as a single `\" + \"`-joined string, e.g. `\"OCR + LIVENESS + FACE_MATCH\"`. Split on `\" + \"` to get a list." + }, + "is_simple_workflow": { + "type": "boolean", + "description": "True when the workflow has no custom workflow graph." + }, + "is_editable": { + "type": "boolean", + "description": "True when this version is a `draft` (drafts are editable in place; published versions are immutable in the Console)." + }, + "workflow_url": { + "type": "string", + "format": "uri", + "description": "Public shareable verification URL for this workflow (`https://verify.didit.me/u/`)." + }, + "max_retry_attempts": { + "type": "integer", + "description": "Maximum retry attempts allowed after a declined verification (0-10)." + }, + "retry_window_days": { + "type": "integer", + "nullable": true, + "description": "Rolling window in days used to count retries (1-365). `null` = all-time limit." + }, + "session_expiration_time": { + "type": "integer", + "description": "Seconds before an unfinished session expires (3600-2419200)." + }, + "version": { + "type": "integer", + "description": "Version number within the `workflow_id` group, starting at 1." + }, + "status": { + "type": "string", + "enum": [ + "draft", + "published" + ], + "description": "Version status. New sessions use the latest `published` version of a group." + }, + "has_draft": { + "type": "boolean", + "description": "True when any version in this `workflow_id` group is currently a draft." + }, + "created_by": { + "type": "object", + "nullable": true, + "description": "Who created this version: `{name, email, type}` where `type` is `user` (Console user, `email` set) or `api_key` (`name` is the client id, `email` is null). `null` when no creator was recorded.", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "enum": [ + "user", + "api_key" + ] + } + } + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + }, + "SimpleWorkflowRequest": { + "type": "object", + "description": "Create a simple linear workflow. The API converts this feature list into a node-based workflow graph internally. STRICT field whitelist: any key not listed here (including `workflow_type`) is rejected with a 400.", + "required": [ + "features" + ], + "properties": { + "workflow_label": { + "type": "string", + "maxLength": 50, + "default": "ID Verification", + "description": "Display name for the workflow (max 50 characters). Defaults to `\"ID Verification\"` when omitted — always set it so workflows stay distinguishable.", + "example": "Standard KYC" + }, + "is_default": { + "type": "boolean", + "description": "Set this workflow as the default for new sessions." + }, + "status": { + "type": "string", + "enum": [ + "draft", + "published" + ], + "description": "Omit this field to create a published workflow ready for sessions. Use `draft` only when you want to save without publishing." + }, + "features": { + "type": "array", + "minItems": 1, + "description": "Verification features in execution order. The API links each item to the next one and adds one final status node automatically.", + "items": { + "$ref": "#/components/schemas/SimpleWorkflowFeature" + } + }, + "is_white_label_enabled": { + "type": "boolean", + "description": "Enable white-label customization for sessions created with this workflow.", + "default": false + }, + "is_desktop_allowed": { + "type": "boolean", + "description": "Allow the verification flow to run on desktop browsers.", + "default": false + }, + "max_retry_attempts": { + "type": "integer", + "minimum": 0, + "maximum": 10, + "default": 3, + "description": "Maximum retry attempts allowed after a declined verification. `0` blocks the user after the first decline." + }, + "retry_window_days": { + "type": "integer", + "minimum": 1, + "maximum": 365, + "nullable": true, + "default": 7, + "description": "Rolling window in days used to count retries. `null` enforces an all-time limit." + }, + "face_liveness_max_attempts": { + "type": "integer", + "minimum": 1, + "maximum": 3, + "default": 3, + "description": "Maximum liveness submissions per session. Default 3: one initial attempt plus two retries. This value is also copied into generated liveness and age-estimation workflow nodes unless the feature-level config overrides it." + }, + "face_match_max_attempts": { + "type": "integer", + "minimum": 1, + "maximum": 3, + "default": 3, + "description": "Maximum face-match submissions per session. Default 3: one initial attempt plus two retries. This value is also copied into generated face-match workflow nodes unless the feature-level config overrides it." + }, + "session_expiration_time": { + "type": "integer", + "minimum": 3600, + "maximum": 2419200, + "default": 604800, + "description": "Seconds before an unfinished session expires. Minimum 1 hour (3600), maximum 4 weeks (2419200)." + } + } + }, + "SimpleWorkflowUpdateRequest": { + "type": "object", + "description": "Update workflow metadata or replace the full linear feature list. STRICT field whitelist: any key not listed here is rejected with a 400.", + "properties": { + "workflow_label": { + "type": "string", + "maxLength": 50, + "description": "Display name for the workflow (max 50 characters)." + }, + "is_default": { + "type": "boolean", + "description": "Set this workflow as the default for new sessions." + }, + "status": { + "type": "string", + "enum": [ + "draft", + "published" + ], + "description": "Controls versioning. Omit to update the current version in place (drafts auto-publish). `published` on an already-published version creates and returns a NEW version. `draft` is only valid while the version is still a draft." + }, + "features": { + "type": "array", + "minItems": 1, + "description": "If provided, this replaces the full feature order and configuration.", + "items": { + "$ref": "#/components/schemas/SimpleWorkflowFeature" + } + }, + "is_white_label_enabled": { + "type": "boolean", + "description": "Enable white-label customization for sessions created with this workflow." + }, + "is_desktop_allowed": { + "type": "boolean", + "description": "Allow the verification flow to run on desktop browsers." + }, + "max_retry_attempts": { + "type": "integer", + "minimum": 0, + "maximum": 10, + "description": "Maximum retry attempts allowed after a declined verification. `0` blocks the user after the first decline." + }, + "retry_window_days": { + "type": "integer", + "minimum": 1, + "maximum": 365, + "nullable": true, + "description": "Rolling window in days used to count retries. `null` enforces an all-time limit." + }, + "face_liveness_max_attempts": { + "type": "integer", + "minimum": 1, + "maximum": 3, + "description": "Maximum liveness submissions per session. This value is also copied into generated liveness and age-estimation workflow nodes unless the feature-level config overrides it." + }, + "face_match_max_attempts": { + "type": "integer", + "minimum": 1, + "maximum": 3, + "description": "Maximum face-match submissions per session. This value is also copied into generated face-match workflow nodes unless the feature-level config overrides it." + }, + "session_expiration_time": { + "type": "integer", + "minimum": 3600, + "maximum": 2419200, + "description": "Seconds before an unfinished session expires. Minimum 1 hour (3600), maximum 4 weeks (2419200)." + } + } + }, + "SimpleWorkflowFeature": { + "type": "object", + "required": [ + "feature" + ], + "properties": { + "feature": { + "type": "string", + "description": "Feature to run. Use the uppercase values shown here.", + "enum": [ + "OCR", + "NFC", + "LIVENESS", + "FACE_MATCH", + "PROOF_OF_ADDRESS", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "PHONE_VERIFICATION", + "EMAIL_VERIFICATION", + "DATABASE_VALIDATION", + "AML", + "IP_ANALYSIS", + "AGE_ESTIMATION", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE", + "SIGNATURE" + ] + }, + "config": { + "type": "object", + "description": "Feature-specific settings. For `QUESTIONNAIRE`, include `questionnaire_uuid` with the `questionnaire_id` returned by the questionnaire create endpoint. For `OCR`, `methods` selects which ID Verification methods each country may use (document capture, non-doc lookup against a government or other authoritative source, and digital identity wallets); omit it and every country is document capture only. For `SIGNATURE`, see [Electronic Signature settings](/management-api/workflows/feature-configs#electronic-signature): `signature_level` accepts `aes` only for now, and workflow documents and their fields are managed in the console, so through the API send documents and fields per session with `signature_documents_source: \"session\"`.", + "additionalProperties": true + }, + "label": { + "type": "string", + "description": "Optional internal label for this workflow node." + } + } + }, + "ReviewItem": { + "type": "object", + "description": "One entry in a session's review / activity feed. Status changes carry `previous_status`, `new_status`, `previous_value`, `new_value`, and `changed_fields`; comments carry `comment` and (for Console comments) `mentioned_emails`; the remaining fields are `null` or empty when they do not apply to the activity type.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Unique identifier of this feed entry.", + "example": "de0561bc-5583-4409-9171-1a31a4c8a5f4" + }, + "activity_type": { + "type": "string", + "enum": [ + "COMMENT", + "STATUS_UPDATED", + "KYC_DATA_UPDATED", + "POA_DATA_UPDATED", + "FILE_ADDED", + "FILE_REMOVED", + "AML_ONGOING_MATCH", + "AML_STATUS_UPDATED", + "AML_HIT_STATUS_UPDATED", + "KYB_DATA_UPDATED", + "KYB_DOCUMENT_UPLOADED", + "TAG_ADDED", + "TAG_REMOVED", + "FEATURE_OVERRIDE_PENDING", + "FEATURE_OVERRIDE_APPLIED", + "FEATURE_OVERRIDE_CANCELLED" + ], + "description": "What kind of activity this entry records. `STATUS_UPDATED` rows come from manual decisions (`PATCH /v3/session/{session_id}/update-status/`) and from entries added via `POST …/reviews/` (which default to this type even when no status is recorded). `COMMENT` rows come from Console reviewers; Console detail-page views are also stored as `COMMENT` rows flagged with `metadata.system_event = \"DETAIL_VIEWED\"`. The remaining types track data edits, file changes, tags, and AML monitoring activity. STATUS_UPDATED rows are also written by system actors (e.g. AML ongoing monitoring with actor_type SYSTEM, and automatic feature-status updates), not only by the update-status endpoint and manual review posts. `FEATURE_OVERRIDE_PENDING`, `FEATURE_OVERRIDE_APPLIED` and `FEATURE_OVERRIDE_CANCELLED` record an override of one check's status (`PATCH /v3/session/{session_id}/features/{node_id}/update-status/`): requested to wait for the flow to finish, applied, or cancelled. They carry the check's status in `previous_value` / `new_value` (`{\"status\": ...}`), leave `previous_status` and `new_status` `null`, and name the override in `metadata` (`feature_override_id`, `node_id`, `feature_model`, `forced`, `state`, `cancel_reason`). The entry for the request carries the reviewer's `comment`; an override applied when the flow finishes is recorded with `actor_type` `SYSTEM`.", + "example": "STATUS_UPDATED" + }, + "actor_display": { + "type": "string", + "description": "Human-readable author label, resolved in order: actor's full name → Console account identifier → local part of `actor_email` → `\"API Client\"` (for `API_KEY` actors) → `\"System\"` (for `SYSTEM` actors) → `\"Unknown\"`. Entries created via `POST …/reviews/` always display `\"Unknown\"` because that endpoint records no actor attribution.", + "example": "API Client" + }, + "actor_email": { + "type": "string", + "nullable": true, + "description": "Email of the Console user who performed the action. `null` for API-key and system actors.", + "example": null + }, + "actor_type": { + "type": "string", + "enum": [ + "API_KEY", + "CONSOLE_USER", + "SYSTEM" + ], + "description": "Who performed the action: an API key, a Console user, or Didit itself (`SYSTEM`, e.g. AML ongoing monitoring). Entries created via `POST …/reviews/` carry the default `CONSOLE_USER` regardless of the caller.", + "example": "API_KEY" + }, + "new_status": { + "type": "string", + "nullable": true, + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "description": "Session status recorded by this entry (for status updates). `null` for comments and data-change entries.", + "example": "Approved" + }, + "previous_status": { + "type": "string", + "nullable": true, + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "description": "Status the session had before the change. Populated on `STATUS_UPDATED` entries written by the update-status endpoint; `null` on entries added via `POST …/reviews/`. Also populated on system-written STATUS_UPDATED rows.", + "example": "In Review" + }, + "comment": { + "type": "string", + "nullable": true, + "description": "Free-text note attached to the entry, or `null`.", + "example": "Document re-checked manually; address matches." + }, + "mentioned_emails": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "description": "Console users `@email`-mentioned in the comment. Always empty for entries created via this API — mention processing is a Console-only feature.", + "example": [] + }, + "previous_value": { + "type": "object", + "nullable": true, + "description": "Snapshot of the changed data before the action, e.g. `{\"status\": \"In Review\"}` on a status change. `null` when nothing was changed.", + "example": { + "status": "In Review" + } + }, + "new_value": { + "type": "object", + "nullable": true, + "description": "Snapshot of the changed data after the action, e.g. `{\"status\": \"Approved\"}`. `null` when nothing was changed.", + "example": { + "status": "Approved" + } + }, + "changed_fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Names of the fields modified by this activity (e.g. `[\"status\"]`). Empty for comments and for entries added via `POST …/reviews/`.", + "example": [ + "status" + ] + }, + "metadata": { + "type": "object", + "nullable": true, + "description": "Extra context for system-generated entries. Console detail-page views carry `{\"system_event\": \"DETAIL_VIEWED\", \"detail_type\": \"verification\"}` so they can be filtered out of notification logic. `null` for ordinary entries.", + "example": null + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the entry was written, ISO 8601 UTC.", + "example": "2026-06-12T00:19:31.918735Z" + } + } + }, + "QuestionnaireListItem": { + "type": "object", + "description": "One questionnaire version in the list view.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Per-version UUID. Use as `{questionnaire_uuid}` for get/update/delete and as `questionnaire_uuid` in a workflow's QUESTIONNAIRE feature config." + }, + "title": { + "type": "string" + }, + "description": { + "type": "string", + "nullable": true + }, + "languages": { + "type": "array", + "items": { + "type": "string" + } + }, + "default_language": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_simple_questionnaire": { + "type": "boolean", + "description": "True for linear questionnaires created through this API; false for graph-based questionnaires built in the Console." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "question_types": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Uppercase element types of the answerable questions, in creation order (e.g. `SHORT_TEXT`, `MULTIPLE_CHOICE`, `FILE_UPLOAD`). Structural `SECTION_HEADER` and `SEPARATOR` elements are excluded. One entry per question, so values can repeat." + }, + "workflow_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Names of workflows using this questionnaire. Currently always `[]` on this endpoint (populated only in the Console listing)." + }, + "questionnaire_group_id": { + "type": "string", + "format": "uuid", + "description": "Stable identifier that groups all versions of the same questionnaire." + }, + "version": { + "type": "integer", + "description": "Version number within the group, starting at 1." + }, + "status": { + "type": "string", + "enum": [ + "draft", + "published" + ] + }, + "published_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When this version was published. `null` for drafts." + }, + "is_editable": { + "type": "boolean", + "description": "True when this version is a `draft`." + } + } + }, + "SimpleQuestionnaireRequest": { + "type": "object", + "description": "Create a simple linear questionnaire. The API converts `form_elements` into the internal questionnaire graph.", + "required": [ + "title", + "languages", + "form_elements" + ], + "properties": { + "title": { + "type": "string", + "description": "Internal name for the questionnaire." + }, + "description": { + "type": "string", + "nullable": true + }, + "languages": { + "type": "array", + "description": "Languages included in every label. Must include `en`.", + "items": { + "type": "string" + }, + "example": [ + "en" + ] + }, + "default_language": { + "type": "string", + "description": "Default language. Must be included in `languages`.", + "example": "en" + }, + "is_active": { + "type": "boolean", + "default": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "published" + ], + "description": "Omit this field to create a published questionnaire ready for workflows." + }, + "form_elements": { + "type": "array", + "minItems": 1, + "description": "Questions in the exact order users should see them. Branching and conditional fields are not supported.", + "items": { + "$ref": "#/components/schemas/SimpleQuestionnaireElement" + } + } + } + }, + "SimpleQuestionnaireUpdateRequest": { + "type": "object", + "description": "Update questionnaire metadata or replace the full linear `form_elements` list.", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string", + "nullable": true + }, + "languages": { + "type": "array", + "items": { + "type": "string" + } + }, + "default_language": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "status": { + "type": "string", + "enum": [ + "draft", + "published" + ] + }, + "form_elements": { + "type": "array", + "minItems": 1, + "description": "If provided, this replaces the full linear question list.", + "items": { + "$ref": "#/components/schemas/SimpleQuestionnaireElement" + } + } + } + }, + "SimpleQuestionnaireElement": { + "type": "object", + "required": [ + "element_type", + "label" + ], + "properties": { + "id": { + "type": "string", + "description": "Optional stable question id. If omitted, the API generates `q1`, `q2`, etc. Use lowercase letters, numbers, and underscores for readability.", + "example": "source_of_funds" + }, + "element_type": { + "type": "string", + "description": "Question type. The API accepts these lowercase values and their uppercase equivalents.", + "enum": [ + "short_text", + "long_text", + "dropdown", + "single_choice", + "multiple_choice", + "number", + "image", + "file_upload", + "time", + "email", + "address", + "phone", + "country", + "date_picker", + "consent", + "section_header", + "separator", + "heading", + "paragraph" + ] + }, + "label": { + "type": "object", + "description": "Question label by language code, for example `{ \"en\": \"Source of funds\" }`.", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "object", + "nullable": true, + "additionalProperties": { + "type": "string" + } + }, + "placeholder": { + "type": "object", + "nullable": true, + "additionalProperties": { + "type": "string" + } + }, + "is_required": { + "type": "boolean", + "default": false + }, + "options": { + "type": "array", + "description": "Use for `dropdown`, `single_choice`, and `multiple_choice`. If `value` is omitted, the API generates one from the option label.", + "items": { + "type": "object", + "required": [ + "label" + ], + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "max_files": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "description": "Use with `file_upload` or `image`." + }, + "validation": { + "type": "object", + "nullable": true, + "description": "Optional format validation for `short_text`, `long_text`, or `number`. Enforced while the user fills the field (they cannot proceed until it passes) and re-checked by the API on submission.", + "properties": { + "pattern": { + "type": "string", + "description": "Regular expression (ECMA-262 subset) the value must fully match. For `short_text` and `long_text`. Do not add ^/$ anchors; the whole value is matched." + }, + "min_length": { + "type": "integer", + "minimum": 0, + "description": "Minimum number of characters. For `short_text` and `long_text`." + }, + "max_length": { + "type": "integer", + "minimum": 0, + "description": "Maximum number of characters. For `short_text` and `long_text`." + }, + "min": { + "type": "number", + "description": "Minimum numeric value. For `number`." + }, + "max": { + "type": "number", + "description": "Maximum numeric value. For `number`." + }, + "error_message": { + "type": "object", + "description": "Translated message shown when validation fails, keyed by language code.", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "QuestionnaireDetail": { + "type": "object", + "description": "Full questionnaire detail returned from GET, POST (create) and PATCH (update) endpoints.", + "properties": { + "questionnaire_id": { + "type": "string", + "format": "uuid", + "description": "Unique identifier of the questionnaire. Use this id when referencing the questionnaire from a workflow or in subsequent update/delete requests." + }, + "title": { + "type": "string" + }, + "description": { + "type": "string", + "nullable": true + }, + "languages": { + "type": "array", + "items": { + "type": "string" + } + }, + "default_language": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_simple_questionnaire": { + "type": "boolean" + }, + "graph": { + "type": "object", + "description": "Graph structure with start_node and nodes map." + }, + "sections": { + "type": "array", + "description": "Questionnaire content grouped into sections (derived from the graph).", + "items": { + "type": "object" + } + }, + "questionnaire_group_id": { + "type": "string", + "format": "uuid", + "description": "Stable identifier that groups all versions of the same questionnaire." + }, + "version": { + "type": "integer" + }, + "status": { + "type": "string", + "enum": [ + "draft", + "published" + ] + }, + "published_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "is_editable": { + "type": "boolean", + "description": "True when the questionnaire can still be edited in place (draft versions)." + } + } + }, + "UserListItem": { + "type": "object", + "description": "A verified user.", + "properties": { + "didit_internal_id": { + "type": "string", + "format": "uuid", + "description": "Didit's stable internal UUID for this user." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your unique identifier for this user (passed when creating sessions). This can be null when no vendor identifier was supplied." + }, + "display_name": { + "type": "string", + "nullable": true, + "description": "Custom display name set by you" + }, + "full_name": { + "type": "string", + "nullable": true, + "description": "Full name extracted from verified documents" + }, + "date_of_birth": { + "type": "string", + "format": "date", + "nullable": true + }, + "effective_name": { + "type": "string", + "nullable": true, + "description": "Best available name: display_name if set, otherwise full_name" + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "FLAGGED", + "BLOCKED" + ], + "description": "Lifecycle status of the user record (NOT a session status). `ACTIVE` is the default, `FLAGGED` marks the user for manual attention, `BLOCKED` prevents new sessions for this `vendor_data`." + }, + "portrait_image_url": { + "type": "string", + "nullable": true, + "description": "Presigned URL of the user's portrait photo (expires after a few hours)" + }, + "session_count": { + "type": "integer", + "description": "Total number of verification sessions for this user" + }, + "approved_count": { + "type": "integer", + "description": "Number of approved sessions" + }, + "declined_count": { + "type": "integer", + "description": "Number of declined sessions" + }, + "in_review_count": { + "type": "integer", + "description": "Number of sessions in review" + }, + "issuing_states": { + "type": "array", + "items": { + "type": "string" + }, + "description": "ISO 3166-1 alpha-3 codes of issuing countries seen on this user's approved ID documents, e.g. `[\"USA\", \"ESP\"]`. Empty array when none." + }, + "approved_emails": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Verified email addresses collected from this user's approved sessions, e.g. `[\"john@example.com\"]`." + }, + "approved_phones": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Verified phone numbers collected from this user's approved sessions, e.g. `[\"+14155551234\"]`." + }, + "features": { + "type": "object", + "description": "Aggregated per-feature status across all of this user's sessions. Possible keys: `ID_VERIFICATION`, `NFC`, `LIVENESS`, `FACE_MATCH`, `POA`, `QUESTIONNAIRE`, `EMAIL_VERIFICATION`, `PHONE`, `AML`, `IP_ANALYSIS`, `AGE_ESTIMATION`, `DATABASE_VALIDATION`. Possible values: `Approved`, `Declined`, `In Review`, `Not Finished`, `Resub Requested`." + }, + "features_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "description": "Same data as `features`, as an ordered array of `{feature, status}` objects." + }, + "last_session_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the most recent session" + }, + "first_session_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the first session" + }, + "last_activity_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the most recent activity on this user (session, transaction, status change, data edit, etc.)." + }, + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "color": { + "type": "string" + } + } + }, + "description": "Tags assigned to this user" + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "BlocklistItem": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "face", + "document", + "phone", + "email" + ] + }, + "session_id": { + "type": "string", + "format": "uuid" + }, + "session_number": { + "type": "integer" + }, + "blocklisted_at": { + "type": "string", + "format": "date-time" + }, + "face_image": { + "type": "string", + "nullable": true + }, + "document_type": { + "type": "string", + "nullable": true + }, + "document_number": { + "type": "string", + "nullable": true + }, + "phone_number": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + } + } + }, + "UserDetailItem": { + "type": "object", + "description": "Full user detail. Extends UserListItem with metadata, comments, and updated_at.", + "allOf": [ + { + "$ref": "#/components/schemas/UserListItem" + }, + { + "type": "object", + "properties": { + "metadata": { + "type": "object", + "description": "Custom metadata JSON you attached to this user. Defaults to `{}`." + }, + "tags": { + "type": "array", + "description": "Tag assignments. NOTE: on detail responses each entry is a tag *link* object (`{uuid, tag: {...}, added_by_email, added_by_name, created_at}`), unlike the flat `{uuid, name, color}` shape used on list responses.", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "UUID of the tag assignment (not the tag itself)." + }, + "tag": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - "registry_data": { - "type": "object", - "description": "Raw record returned by the registry source, kept for reviewers. Its shape differs by country and source and is not a stable contract: read the canonical fields (`addresses`, `industries`, `ownership`, `beneficial_owners[].shareholding`) instead." + "name": { + "type": "string" }, - "user_provided_data": { - "type": "object", - "nullable": true, - "description": "Always `null` on this endpoint (used by the hosted KYB flow when end users edit registry data)." + "color": { + "type": "string" }, - "confirmed_by_user_at": { + "description": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "Always `null` on this endpoint." + "nullable": true }, - "last_console_edit_at": { + "source": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "Set when a console reviewer edits the record later." + "enum": [ + "didit", + "custom" + ] }, - "is_editable": { - "type": "boolean", - "description": "Whether the registry-sourced data can still be edited in the hosted flow/console." + "created_at": { + "type": "string", + "format": "date-time" }, - "registry_tier_delivered": { + "updated_at": { + "type": "string", + "format": "date-time" + } + } + }, + "added_by_email": { + "type": "string", + "nullable": true + }, + "added_by_name": { + "type": "string", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + } + }, + "comments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "comment_type": { + "type": "string", + "enum": [ + "COMMENT", + "STATUS_CHANGED", + "METADATA_UPDATED", + "TAG_ADDED", + "TAG_REMOVED", + "PROFILE_UPDATED" + ] + }, + "comment": { + "type": "string", + "nullable": true + }, + "actor_email": { + "type": "string", + "nullable": true + }, + "actor_name": { + "type": "string", + "nullable": true + }, + "previous_status": { + "type": "string", + "nullable": true, + "enum": [ + "ACTIVE", + "FLAGGED", + "BLOCKED", + null + ] + }, + "new_status": { + "type": "string", + "nullable": true, + "enum": [ + "ACTIVE", + "FLAGGED", + "BLOCKED", + null + ] + }, + "previous_value": { + "type": "object", + "nullable": true, + "description": "Previous field values for PROFILE_UPDATED entries." + }, + "new_value": { + "type": "object", + "nullable": true, + "description": "New field values for PROFILE_UPDATED entries." + }, + "changed_fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Names of the fields changed by a PROFILE_UPDATED entry." + }, + "metadata": { + "type": "object", + "nullable": true + }, + "mentioned_emails": { + "type": "array", + "items": { + "type": "string" + } + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "description": "Activity log and comments for this user (status changes, profile edits, manual notes)." + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + } + ] + }, + "BusinessListItem": { + "type": "object", + "description": "A verified business.", + "properties": { + "didit_internal_id": { + "type": "string", + "format": "uuid", + "description": "Didit's stable internal UUID for this business." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your unique identifier for this business (passed when creating sessions). This can be null when no vendor identifier was supplied." + }, + "display_name": { + "type": "string", + "nullable": true, + "description": "Custom display name set by you" + }, + "legal_name": { + "type": "string", + "nullable": true, + "description": "Official legal name from registry or manual entry" + }, + "registration_number": { + "type": "string", + "nullable": true, + "description": "Company registration or incorporation number" + }, + "country_code": { + "type": "string", + "nullable": true, + "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `US`)." + }, + "effective_name": { + "type": "string", + "nullable": true, + "description": "Best available name: display_name if set, otherwise legal_name" + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "FLAGGED", + "BLOCKED" + ], + "description": "Lifecycle status of the business record (NOT a session status). `ACTIVE` is the default, `FLAGGED` marks it for manual attention, `BLOCKED` prevents new sessions for this `vendor_data`." + }, + "session_count": { + "type": "integer", + "description": "Total number of verification sessions for this business" + }, + "approved_count": { + "type": "integer", + "description": "Number of approved sessions" + }, + "declined_count": { + "type": "integer", + "description": "Number of declined sessions" + }, + "in_review_count": { + "type": "integer", + "description": "Number of sessions in review" + }, + "features": { + "type": "object", + "description": "Aggregated per-feature status across all of this business's KYB sessions. Currently the only key is `KYB` (the registry company check status). Possible values: `Approved`, `Declined`, `In Review`, `Not Finished`, `Resub Requested`." + }, + "features_list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "description": "Same data as `features`, as an ordered array of `{feature, status}` objects." + }, + "last_session_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the most recent session" + }, + "first_session_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the first session" + }, + "last_activity_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the most recent activity (status change, session update, etc.)" + }, + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "color": { + "type": "string" + } + } + }, + "description": "Tags assigned to this business" + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "BusinessDetailItem": { + "type": "object", + "description": "Full business detail. Extends BusinessListItem with metadata and updated_at.", + "allOf": [ + { + "$ref": "#/components/schemas/BusinessListItem" + }, + { + "type": "object", + "properties": { + "metadata": { + "type": "object", + "description": "Custom metadata JSON you attached to this business. Defaults to `{}`." + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + } + ] + }, + "TransactionDetail": { + "type": "object", + "description": "Full monitoring record for one transaction, as returned by `GET /v3/transactions/{transaction_id}/` and `POST /v3/transactions/`.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid", + "description": "Didit-stable transaction identifier. Use as `{transaction_id}` for follow-up calls." + }, + "environment": { + "type": "string", + "enum": [ + "live", + "sandbox" + ], + "description": "Environment of the application that owns the transaction. A `sandbox` transaction is stored but never evaluated, screened, notified or billed, so its empty `rule_runs` means \"not evaluated\"." + }, + "transaction_number": { + "type": "integer", + "description": "Application-scoped sequential transaction number shown in Console (e.g. `4123`)." + }, + "txn_id": { + "type": "string", + "description": "The `transaction_id` you supplied at create time (max 128 chars). Unique per application." + }, + "txn_date": { + "type": "string", + "format": "date-time", + "description": "When the transaction occurred (from `transaction_at`; defaults to submission time)." + }, + "zone_id": { + "type": "string", + "nullable": true, + "description": "IANA time zone identifier provided at create time." + }, + "transaction_type": { + "type": "string", + "description": "Top-level category as stored: `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`, `gamblingLimitChange`, `gamblingBonusChange`, `auditTrailEvent`. Note multi-word values are echoed back in camelCase even when submitted in snake_case." + }, + "action_type": { + "type": "string", + "description": "Sub-type within the category (e.g. `deposit`, `withdrawal`, `transfer`). Defaults to the category when not supplied." + }, + "direction": { + "type": "string", + "enum": [ + "INBOUND", + "OUTBOUND" + ], + "description": "Direction relative to the subject. Stored uppercase regardless of the casing submitted (`in`/`out`/`inbound`/`outbound` are accepted on input)." + }, + "status": { + "type": "string", + "enum": [ + "APPROVED", + "IN_REVIEW", + "DECLINED", + "AWAITING_USER" + ], + "description": "Current monitoring verdict. Transactions are created `APPROVED`; rules may flip them to `IN_REVIEW`/`DECLINED` synchronously." + }, + "source": { + "type": "string", + "enum": [ + "LIVE", + "IMPORTED" + ], + "description": "How the record originated: `LIVE` for transactions submitted through the API or SDK, `IMPORTED` for transactions loaded through the console import." + }, + "amount": { + "type": "string", + "description": "Transaction amount as a decimal string with trailing zeros stripped (e.g. `\"1500\"`, `\"0.5\"`, `\"0.123456789012345678\"`). Up to 18 decimal places." + }, + "currency": { + "type": "string", + "description": "Currency code of `amount`. Any ISO 4217 alphabetic code is accepted (e.g. `EUR`, `USD`, `NGN`), as is a crypto asset symbol (e.g. `BTC`). Prefer a currently-circulating code. Not every accepted code has an exchange rate: when none is available for the code (for example the ISO accounting unit `XSU`, or a crypto symbol Didit does not price), automatic conversion fails and `preferred_currency_amount` is returned null unless the request supplied a pre-converted `amount_in_default_currency`." + }, + "currency_type": { + "type": "string", + "nullable": true, + "description": "`fiat` or `crypto`, as submitted in `currency_kind`." + }, + "amount_in_default_currency": { + "type": "string", + "nullable": true, + "description": "Pre-converted amount you supplied, as a decimal string with trailing zeros stripped." + }, + "default_currency_code": { + "type": "string", + "nullable": true + }, + "preferred_currency_amount": { + "type": "string", + "nullable": true, + "description": "`amount` converted to the application's preferred currency, when available." + }, + "preferred_currency_code": { + "type": "string", + "nullable": true + }, + "payment_details": { + "type": "string", + "nullable": true, + "description": "Free-text payment reference or memo from the submission." + }, + "payment_txn_id": { + "type": "string", + "nullable": true, + "description": "External payment system reference (e.g. blockchain transaction hash) from `payment_reference_id`." + }, + "score": { + "type": "integer", + "description": "Risk score accumulated by rules (typically 0–100). Higher = riskier." + }, + "severity": { + "type": "string", + "nullable": true, + "enum": [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL" + ], + "description": "Categorical risk severity set by rules/providers (`UNKNOWN`, `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`). `null` until something sets it." + }, + "decision_reason_code": { + "type": "string", + "nullable": true, + "description": "Machine-readable reason for the current `status`." + }, + "decision_reason_label": { + "type": "string", + "nullable": true, + "description": "Human-readable label for `decision_reason_code`." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Convenience copy of the applicant's `vendor_data`." + }, + "metadata": { + "type": "object", + "description": "Snapshot of the `subject` and `counterparty` payloads as submitted at create time." + }, + "props": { + "type": "object", + "description": "The `custom_properties` you supplied at create time. Each key is addressable in rule conditions as `custom_values.`." + }, + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "color": { + "type": "string" + }, + "description": { + "type": "string", + "nullable": true + }, + "source": { + "type": "string", + "enum": [ + "didit", + "custom" + ], + "description": "Whether the tag is a Didit preset or a custom tag." + } + } + }, + "description": "Tags attached to the transaction (manually or by rules)." + }, + "parties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "role": { + "type": "string", + "enum": [ + "APPLICANT", + "REMITTER", + "BENEFICIARY", + "COUNTERPARTY" + ], + "description": "Party role. The create endpoint assigns `APPLICANT` (the subject) and `COUNTERPARTY`." + }, + "entity_type": { + "type": "string", + "enum": [ + "individual", + "company", + "external", + "unhostedWallet" + ] + }, + "kind": { + "type": "string", + "enum": [ + "USER", + "BUSINESS", + "EXTERNAL" + ], + "description": "`USER`/`BUSINESS` parties link back to a Didit-owned entity via `vendor_data`. `EXTERNAL` parties have no Didit entity behind them." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your internal user/business identifier." + }, + "full_name": { + "type": "string", + "nullable": true + }, + "first_name": { + "type": "string", + "nullable": true + }, + "last_name": { + "type": "string", + "nullable": true + }, + "country_code": { + "type": "string", + "nullable": true, + "description": "Country code from the submitted `address.country` (typically ISO 3166-1 alpha-3)." + }, + "dob": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Date of birth from the submitted `date_of_birth`." + }, + "address": { + "type": "object", + "description": "Address object as submitted. `{}` when omitted." + }, + "institution_info": { + "type": "object", + "description": "Institution details as submitted. `{}` when omitted." + }, + "device": { + "type": "object", + "description": "Normalized device context, including server-side IP enrichment under network_context (geolocation, VPN/data-center flags) when an IP was provided.", + "properties": { + "fingerprint": { + "type": "string", + "description": "Unique device fingerprint." + }, + "persistent_device_id": { + "type": "string", + "description": "Stable cross-session device identifier (v2 fingerprint schema)." + }, + "user_agent": { + "type": "string", + "description": "Browser or app user agent string." + }, + "browser_family": { + "type": "string", + "description": "Browser family (e.g. Chrome, Firefox, Safari)." + }, + "browser_version": { + "type": "string", + "description": "Browser version string." + }, + "platform": { + "type": "string", + "description": "Platform identifier (web, ios, android, macos, windows, linux)." + }, + "os_family": { + "type": "string", + "description": "Operating system family (e.g. iOS, Android, Windows)." + }, + "os_version": { + "type": "string", + "description": "Operating system version string." + }, + "accept_language": { + "type": "string", + "description": "Accept-Language header value." + }, + "session_id": { + "type": "string", + "description": "Session identifier (stored in metadata)." + }, + "session_age_ms": { + "type": "number", + "description": "Session age in milliseconds." + }, + "network_context": { + "type": "object", + "description": "Network context with IP address and geo-location.", + "properties": { + "ip_address": { "type": "string", - "enum": [ - "basic", - "shareholders", - "ubo", - null - ], - "description": "Same as the top-level `registry_tier_delivered`.", - "nullable": true + "description": "Client IP address." }, - "registry_ownership_status": { + "ip_country_code": { "type": "string", - "nullable": true, - "enum": [ - "not_requested", - "delivered", - "unavailable", - "pending", - null - ], - "description": "`delivered` when the ownership data came back, `unavailable` when an ownership tier was requested but the registry had none for this company (see `registry_data.ownership_error`), `pending` when the ownership section has not been retrieved from the registry yet and Didit is still completing it, `not_requested` on the Lite tier." + "description": "Geo-resolved country of the IP (ISO 3166-1 alpha-3)." }, - "ownership": { - "$ref": "#/components/schemas/KYBRegistryOwnership" + "latitude": { + "type": "number", + "description": "Geographic latitude of the IP." }, - "registry_tier": { - "type": "string", - "nullable": true, - "description": "The registry tier the workflow ordered: `basic`, `shareholders` or `ubo`. Also in `ownership.tier_requested`." + "longitude": { + "type": "number", + "description": "Geographic longitude of the IP." } } + } + } + }, + "external_party_snapshot": { + "type": "object", + "nullable": true, + "description": "Frozen copy of the party data for `EXTERNAL` parties. `null` while the party is linked to a Didit user or business." + } + } + }, + "description": "Transaction parties (applicant, counterparty)." + }, + "payment_methods": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "payment_method_type": { + "type": "string", + "enum": [ + "bank_card", + "bank_account", + "e_wallet", + "crypto_wallet", + "unhosted_wallet", + "other" + ] + }, + "label": { + "type": "string", + "nullable": true + }, + "fingerprint": { + "type": "string", + "nullable": true, + "description": "SHA-256 over `type:account_id:issuing_country` — stable identifier to correlate reuse of the same instrument." + }, + "account_id": { + "type": "string", + "nullable": true, + "description": "Account identifier (IBAN, wallet address, card token, …)." + }, + "issuing_country": { + "type": "string", + "nullable": true + }, + "owner_kind": { + "type": "string", + "enum": [ + "USER", + "BUSINESS", + "EXTERNAL" + ] + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your identifier for the owning user/business." + }, + "owner_name": { + "type": "string", + "nullable": true + }, + "institution_info": { + "type": "object" + }, + "details": { + "type": "object", + "description": "Raw `payment_method` object as submitted." + }, + "external_owner_snapshot": { + "type": "object", + "nullable": true + }, + "role": { + "type": "string", + "enum": [ + "SOURCE", + "DESTINATION", + "FUNDING", + "BENEFICIARY" + ], + "description": "How this method was used in the transaction. The create endpoint assigns `SOURCE`/`DESTINATION` from the direction (outbound: subject = SOURCE; inbound: subject = DESTINATION)." + } + } + }, + "description": "Payment methods linked to the transaction." + }, + "documents": { + "type": "array", + "description": "Files attached to the transaction from the console. `[]` when none.", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "label": { + "type": "string", + "nullable": true + }, + "note": { + "type": "string", + "nullable": true + }, + "uploaded_by": { + "type": "string", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "file_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Short-lived signed download URL; fetch it when needed rather than storing it." + }, + "original_filename": { + "type": "string", + "nullable": true + }, + "file_size": { + "type": "integer", + "nullable": true + } + } + } + } + } + } + }, + "activities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "activity_type": { + "type": "string", + "description": "E.g. `TRANSACTION_CREATED`, `TRANSACTION_NOTE_ADDED`, `TRANSACTION_STATUS_CHANGED`." + }, + "source": { + "type": "string" + }, + "status": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "metadata": { + "type": "object" + }, + "occurred_at": { + "type": "string", + "format": "date-time" + } + } + }, + "description": "Activity log entries (creation, notes, manual reviews, status changes)." + }, + "alerts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string", + "nullable": true + }, + "severity": { + "type": "string", + "enum": [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL" + ] + }, + "status": { + "type": "string", + "enum": [ + "OPEN", + "INVESTIGATING", + "AWAITING_USER", + "PENDING_SAR", + "SAR_FILED", + "RESOLVED", + "DISMISSED" + ] + }, + "source": { + "type": "string", + "enum": [ + "RULE", + "PROVIDER", + "MANUAL" + ] + }, + "metadata": { + "type": "object" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "due_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "description": "Alerts raised by rules and providers." + }, + "rule_runs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "rule": { + "type": "string", + "format": "uuid", + "description": "UUID of the rule that ran." + }, + "rule_title": { + "type": "string" + }, + "rule_description": { + "type": "string", + "nullable": true + }, + "rule_category": { + "type": "string" + }, + "matched": { + "type": "boolean", + "description": "Whether the rule's conditions matched this transaction. A run is recorded only for matched rules, so live evaluations always carry `true`." + }, + "is_test": { + "type": "boolean", + "description": "True when the rule ran in TEST mode (no effect on status/score)." + }, + "mode": { + "type": "string", + "enum": [ + "ACTIVE", + "DISABLED", + "TEST" + ] + }, + "severity": { + "type": "string", + "nullable": true + }, + "score_delta": { + "type": "integer", + "description": "Score added by this rule's `add_score` actions." + }, + "status_target": { + "type": "string", + "nullable": true, + "description": "Status set by a `change_status` action, if any." + }, + "action_summary": { + "type": "string", + "nullable": true, + "description": "Summary of the actions the rule executed, joined with `; ` (e.g. `score +30; change_status:IN_REVIEW; add_tag:high_risk_country`). Parts: `score +N`, `change_status:`, `add_tag:`, `add_note`, `add_to_list`, `open_case`. `null` when the rule executed no action." + }, + "metadata": { + "type": "object", + "description": "Evaluation detail: `scope_match`, `conditions[]` and `aggregations[]`. Each condition entry carries `field`, `operator`, `expected_value`, the `current_value` read from the transaction, `matched`, `group_index` and `group_logic`. Each aggregation entry (velocity / count / sum) carries `metric`, `field`, `operator`, `expected_value`, the `computed_value` the aggregate evaluated to, `window`, `filters`, `matched` and `sample_references`; deferred async aggregations have `computed_value: null` and `deferred: true`. A nested condition group is one `conditions[]` entry with `operator` `any_of` or `all_of`, `field` null, and its members' results under its own `conditions`." + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "description": "Per-rule execution results — which rule fired, with what score impact." + }, + "provider_results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "provider": { + "type": "string" + }, + "result_type": { + "type": "string", + "enum": [ + "FIAT_MONITORING", + "WALLET_SCREENING", + "TRANSACTION_SCREENING", + "TRAVEL_RULE", + "NETWORK_GRAPH", + "CUSTOM" + ] + }, + "status": { + "type": "string" + }, + "severity": { + "type": "string", + "nullable": true + }, + "score": { + "type": "integer" + }, + "summary": { + "type": "string", + "nullable": true + }, + "payload": { + "type": "object", + "description": "Raw provider response payload." + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "description": "Raw provider payloads (AML screening, blockchain analytics, etc.)." + }, + "travel_rule": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/TransactionTravelRule" + } + ], + "description": "Travel Rule transfer or check. Populated when `travel_rule_details` was submitted, and also when Travel Rule is enabled and the transaction was created with `transaction_category: travelRule` (Didit initiates the exchange itself, even without `travel_rule_details`). `null` when no transfer or check exists. On an inbound transfer, the counterparty's `originator_data` and `beneficiary_data` stay masked (`data_masked: true`) until wallet ownership is confirmed." + }, + "network_snapshot": { + "type": "object", + "nullable": true, + "description": "Graph snapshot of related transactions/parties used by the rules engine. Either the `network_snapshot` submitted with the transaction or the graph Didit builds from a successful AML wallet screening (which replaces a submitted one). `null` when neither exists.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "nodes": { + "type": "array", + "items": { + "type": "object" + } + }, + "edges": { + "type": "array", + "items": { + "type": "object" + } + }, + "metrics": { + "type": "object" + } + } + }, + "remediation": { + "type": "object", + "nullable": true, + "description": "Remediation session offered to the user when re-verification is required. Superseded by `action_required`, which is the canonical block; kept for backward compatibility.", + "properties": { + "session_id": { + "type": "string" + }, + "session_token": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "status": { + "type": "string" + } + } + }, + "action_required": { + "type": "object", + "nullable": true, + "description": "Pending end-user action required to complete this transaction, or null when there is none. The Didit SDKs auto-launch the action by default and refresh the transaction when it finishes. Two variants, discriminated by `type`: `verification_session` (a hosted verification session created by a rule action) and `wallet_ownership` (a wallet-ownership widget session auto-minted for a Travel Rule transfer that needs proof of wallet control, gated by the `auto_wallet_verification` Travel Rule setting). When both could apply, `wallet_ownership` takes precedence because it blocks the Travel Rule exchange.", + "oneOf": [ + { + "$ref": "#/components/schemas/TransactionVerificationSessionAction" + }, + { + "$ref": "#/components/schemas/TransactionWalletOwnershipAction" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "verification_session": "#/components/schemas/TransactionVerificationSessionAction", + "wallet_ownership": "#/components/schemas/TransactionWalletOwnershipAction" + } + } + }, + "cost_breakdown": { + "type": "object", + "nullable": true, + "description": "Cost of this transaction: `total_price` and `items`, one per charge, each with `usage_type`, `price`, `details` and `created_at`. A re-screen of the same transaction is a new item." + } + } + }, + "TravelRuleSettingsDetail": { + "type": "object", + "properties": { + "is_enabled": { + "type": "boolean", + "description": "Whether the managed Travel Rule negotiation flow is active for outbound `travelRule` transactions on this application." + }, + "jurisdiction": { + "type": "string", + "maxLength": 8, + "description": "Your VASP's operating jurisdiction code. Default `\"EU\"`. Didit maps it to the regime it stamps on your Travel Rule transactions as `travel_rule_regime` (for example `EU` or an EU member code becomes `eu`), which transaction rules can key on. Other Didit customers see it as your jurisdiction in the VASP directory once the Didit network lists your profile." + }, + "name_matching_strictness": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ], + "description": "How strictly beneficiary names must match before a transfer can reach COMPLETED. `CONTAINS` accepts one name whose words all appear in the other." + }, + "confirmation_timeout_hours": { + "type": "integer", + "minimum": 1, + "description": "Hours an AWAITING_COUNTERPARTY transfer may remain unconfirmed before the periodic sweeper expires it. Default 48." + }, + "timeout_outcome": { + "type": "string", + "enum": [ + "HOLD", + "REJECT", + "PROCEED" + ], + "description": "What happens to the underlying transaction when a transfer expires. Default HOLD. sunrise_policy and non_responsive_policy take precedence when they apply and are set." + }, + "threshold_amount": { + "type": "string", + "description": "Decimal string, in your application's default currency. Minimum transfer amount that triggers a managed exchange; transfers below it resolve straight to NOT_APPLICABLE. Didit compares the transaction amount converted to your default currency, or the `amount_in_default_currency` you send; when no converted amount is available, the threshold does not apply. Defaults to \"0.00\" (EU TFR applies no minimum amount)." + }, + "ownership_validation_mode": { + "type": "string", + "enum": [ + "FULL", + "SIMPLIFIED", + "AUTOMATED" + ], + "description": "How much proof a wallet's ownership takes. `FULL` always needs a proof (signed message, test deposit or reviewed screenshot), never the end user's word. `SIMPLIFIED` accepts a self-declaration below `simplified_validation_threshold` and a proof above it. `AUTOMATED` lets the address book and earlier proofs answer on their own, self-declarations included while `allow_self_declaration` is true. Default `AUTOMATED`." + }, + "simplified_validation_threshold": { + "type": "string", + "description": "Decimal string, default \"0.00\". In `SIMPLIFIED` mode a transfer below this amount (in the threshold currency) accepts a self-declaration. A transfer with no amount in that currency counts as above it." + }, + "data_sharing_fields": { + "$ref": "#/components/schemas/TravelRuleDataSharingFields" + }, + "matching_rules": { + "$ref": "#/components/schemas/TravelRuleMatchingRules" + }, + "legal_name": { + "type": "string", + "description": "Your VASP's legal name. Required (non-blank) to set is_enabled to true." + }, + "lei": { + "type": "string", + "description": "Your Legal Entity Identifier, surfaced in the VASP directory." + }, + "compliance_email": { + "type": "string", + "format": "email", + "description": "Compliance contact email for your VASP profile. The Didit network lists your profile only once this address is confirmed through an emailed link (POST /v3/travel-rule/settings/compliance-email/confirmation/); changing it needs a new confirmation." + }, + "is_discoverable": { + "type": "boolean", + "description": "Whether your VASP profile can be listed in the Didit network: found in GET /v3/travel-rule/vasps/ by other Didit customers and reached on the INTERNAL rail. Listing also needs a live application, Travel Rule on, a legal name and a confirmed compliance email; didit_network says which are missing. Default true." + }, + "inbound_auto_accept": { + "type": "boolean", + "description": "When true, an inbound TRP inquiry whose beneficiary wallet is a verified address-book entry with a matching name is approved automatically. When false, that inquiry waits in ON_HOLD for you to approve or deny it with the ownership endpoint. INTERNAL-rail and after-deposit transfers are not affected. Default true." + }, + "allow_self_declaration": { + "type": "boolean", + "description": "When true, the wallet-ownership widget offers self-declaration as a fallback proof method. Default true." + }, + "allow_screenshot_proof": { + "type": "boolean", + "description": "When true, the wallet-ownership widget offers screenshot upload as an ownership evidence method. Default true." + }, + "auto_wallet_verification": { + "type": "boolean", + "description": "When true, submitting an outbound Travel Rule transfer that needs end-user proof of wallet control auto-mints a wallet-ownership widget session bound to the transaction and exposes it in the transaction's action_required block. Default true." + }, + "vasp_attribution_enabled": { + "type": "boolean", + "description": "When true, an outbound Travel Rule transfer that no rail can route is attributed through blockchain analytics: the destination wallet is resolved to its owning VASP, the entity is recorded on the transfer and in the VASP directory, and the rails are re-run once with the attributed name. Default true." + }, + "counterparty_screening_enabled": { + "type": "boolean", + "description": "Opt-in sanctions and adverse-media screening of counterparty VASPs, billed per screening at the Company AML Screening price. When on, Didit screens the counterparty an outbound exchange reaches (reusing a screening younger than 30 days) and you can screen one with POST /v3/travel-rule/vasps/{vasp_id}/screening/. Default false: nothing is screened or billed." + }, + "sunrise_policy": { + "type": "string", + "enum": [ + "", + "HOLD", + "REJECT", + "PROCEED" + ], + "description": "What an expired transfer does to its transaction when the counterparty is a directory entry in a jurisdiction on sunrise_jurisdictions, which does not enforce the Travel Rule yet: HOLD moves it to review, REJECT declines it, PROCEED leaves its status untouched. Empty (the default) follows timeout_outcome. Applies before non_responsive_policy." + }, + "sunrise_jurisdictions": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true, + "description": "Read-only. The countries (ISO 3166 alpha-2) Didit treats as in a Travel Rule sunrise period, where sunrise_policy applies. Currently CO, KH and VN. Any other jurisdiction, and an empty or unknown one, follows timeout_outcome." + }, + "non_responsive_policy": { + "type": "string", + "enum": [ + "", + "HOLD", + "REJECT", + "PROCEED" + ], + "description": "What an expired transfer does to its transaction when the counterparty is a directory entry whose responsiveness is UNRESPONSIVE: HOLD, REJECT or PROCEED, as for timeout_outcome. Empty (the default) follows timeout_outcome. sunrise_policy applies first when both fit." + }, + "travel_address": { + "type": "string", + "description": "Read-only. Your VASP's own travel address - the encoded inbound TRP inquiry endpoint other VASPs use to reach you. Empty while `is_enabled` is false." + }, + "disabled_networks": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA" + ] + }, + "description": "Networks opted out of platform membership. Does not disable your connected own memberships or provision unavailable networks.", + "default": [] + }, + "networks": { + "type": "object", + "readOnly": true, + "description": "Effective membership configuration for each network. No network, including GTR, is included by default. This is not a live health or delivery check.", + "properties": { + "gtr": { + "type": "object", + "properties": { + "rail": { + "type": "string", + "enum": [ + "GTR" + ] + }, + "mode": { + "type": "string", + "enum": [ + "own", + "didit", + "off", + "unavailable" + ], + "description": "own: connected customer membership takes precedence; didit: configured platform membership allowed; off: platform routing disabled with no own membership; unavailable: neither membership available." + }, + "platform_available": { + "type": "boolean", + "description": "Whether platform membership is configured; not a live health check." + }, + "connected": { + "type": "boolean", + "description": "Whether a connected customer membership exists. Credentials are validated locally; network validation occurs on a live exchange." + } + } + }, + "trust": { + "type": "object", + "properties": { + "rail": { + "type": "string", + "enum": [ + "TRUST" + ] + }, + "mode": { + "type": "string", + "enum": [ + "own", + "didit", + "off", + "unavailable" + ], + "description": "own: connected customer membership takes precedence; didit: configured platform membership allowed; off: platform routing disabled with no own membership; unavailable: neither membership available." + }, + "platform_available": { + "type": "boolean", + "description": "Whether platform membership is configured; not a live health check." + }, + "connected": { + "type": "boolean", + "description": "Whether a connected customer membership exists. Credentials are validated locally; network validation occurs on a live exchange." + } + } + }, + "verifyvasp": { + "type": "object", + "properties": { + "rail": { + "type": "string", + "enum": [ + "VERIFYVASP" + ] + }, + "mode": { + "type": "string", + "enum": [ + "own", + "didit", + "off", + "unavailable" + ], + "description": "own: connected customer membership takes precedence; didit: configured platform membership allowed; off: platform routing disabled with no own membership; unavailable: neither membership available." + }, + "platform_available": { + "type": "boolean", + "description": "Whether platform membership is configured; not a live health check." + }, + "connected": { + "type": "boolean", + "description": "Whether a connected customer membership exists. Credentials are validated locally; network validation occurs on a live exchange." + } + } + }, + "sygna": { + "type": "object", + "properties": { + "rail": { + "type": "string", + "enum": [ + "SYGNA" + ] + }, + "mode": { + "type": "string", + "enum": [ + "own", + "didit", + "off", + "unavailable" + ], + "description": "own: connected customer membership takes precedence; didit: configured platform membership allowed; off: platform routing disabled with no own membership; unavailable: neither membership available." + }, + "platform_available": { + "type": "boolean", + "description": "Whether platform membership is configured; not a live health check." + }, + "connected": { + "type": "boolean", + "description": "Whether a connected customer membership exists. Credentials are validated locally; network validation occurs on a live exchange." + } + } + } + } + }, + "didit_network": { + "type": "object", + "readOnly": true, + "description": "Whether the Didit network lists your VASP profile, so other Didit customers find it in the directory and reach it on the INTERNAL rail, and each condition it still misses.", + "properties": { + "listed": { + "type": "boolean" + }, + "missing": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "travel_rule_off", + "sandbox_application", + "not_discoverable", + "legal_name_missing", + "compliance_email_missing", + "compliance_email_unconfirmed", + "excluded_by_staff", + "internal_organization" + ] + }, + "description": "Empty when listed. compliance_email_unconfirmed: send the confirmation with POST /v3/travel-rule/settings/compliance-email/confirmation/ and open the emailed link. excluded_by_staff: Didit marked the application as a test, demo or internal one." + }, + "compliance_email_confirmed": { + "type": "boolean", + "description": "Whether the current compliance_email was confirmed through the emailed link." + } + } + } + } + }, + "TravelRuleSettingsUpdate": { + "type": "object", + "properties": { + "is_enabled": { + "type": "boolean" + }, + "legal_name": { + "type": "string", + "maxLength": 255 + }, + "lei": { + "type": "string", + "maxLength": 20 + }, + "jurisdiction": { + "type": "string", + "maxLength": 8 + }, + "name_matching_strictness": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ], + "description": "`CONTAINS` accepts one name whose words all appear in the other." + }, + "confirmation_timeout_hours": { + "type": "integer", + "minimum": 1 + }, + "timeout_outcome": { + "type": "string", + "enum": [ + "HOLD", + "REJECT", + "PROCEED" + ] + }, + "compliance_email": { + "type": "string", + "format": "email", + "description": "Compliance contact email for your VASP profile. The Didit network lists your profile only once this address is confirmed through an emailed link (POST /v3/travel-rule/settings/compliance-email/confirmation/); changing it needs a new confirmation." + }, + "is_discoverable": { + "type": "boolean", + "description": "Whether your VASP profile can be listed in the Didit network: found in GET /v3/travel-rule/vasps/ by other Didit customers and reached on the INTERNAL rail. Listing also needs a live application, Travel Rule on, a legal name and a confirmed compliance email; didit_network says which are missing. Default true." + }, + "threshold_amount": { + "type": "string", + "description": "Decimal string, zero or greater, in your application's default currency. Minimum transfer amount that triggers a managed exchange." + }, + "ownership_validation_mode": { + "type": "string", + "enum": [ + "FULL", + "SIMPLIFIED", + "AUTOMATED" + ], + "description": "See the response field of the same name." + }, + "simplified_validation_threshold": { + "type": "string", + "description": "Decimal string, 0 or more." + }, + "data_sharing_fields": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleDataSharingFields" + } + ], + "description": "Merged per entity type: an entity type you leave out keeps its current choice. A choice that cannot meet the originator obligations of your `jurisdiction` regime (EU TFR or FATF Recommendation 16), an empty list included, is refused with `400`." + }, + "matching_rules": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleMatchingRules" + } + ], + "description": "Merged per entity type, and per field inside it. A field that entity type does not compare (for example `date_of_birth` for `company`) is refused with `400`." + }, + "inbound_auto_accept": { + "type": "boolean" + }, + "allow_self_declaration": { + "type": "boolean" + }, + "allow_screenshot_proof": { + "type": "boolean", + "description": "When true, the wallet-ownership widget offers screenshot upload as an ownership evidence method. Default true." + }, + "auto_wallet_verification": { + "type": "boolean" + }, + "vasp_attribution_enabled": { + "type": "boolean" + }, + "counterparty_screening_enabled": { + "type": "boolean", + "description": "Opt-in sanctions and adverse-media screening of counterparty VASPs, billed per screening at the Company AML Screening price. When on, Didit screens the counterparty an outbound exchange reaches (reusing a screening younger than 30 days) and you can screen one with POST /v3/travel-rule/vasps/{vasp_id}/screening/. Default false: nothing is screened or billed." + }, + "sunrise_policy": { + "type": "string", + "enum": [ + "", + "HOLD", + "REJECT", + "PROCEED" + ], + "description": "HOLD, REJECT, PROCEED, or empty to follow timeout_outcome. See the response field of the same name." + }, + "non_responsive_policy": { + "type": "string", + "enum": [ + "", + "HOLD", + "REJECT", + "PROCEED" + ], + "description": "HOLD, REJECT, PROCEED, or empty to follow timeout_outcome. See the response field of the same name." + }, + "disabled_networks": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA" + ] + }, + "description": "Networks opted out of platform membership. Does not disable your connected own memberships or provision unavailable networks.", + "default": [] + } + }, + "description": "All fields optional; PUT applies a partial update." + }, + "WalletAddressEntry": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "address": { + "type": "string", + "description": "The wallet address; `\"\"` for an entry imported by its hash only. Unique per application, chain and memo, comparing the address without regard to case. After you delete an entry, you can register the same address again." + }, + "chain": { + "type": "string", + "description": "Chain token for the address, for example `ETH` or `BTC`. When Didit routes or registers a transfer, it matches address book entries on the wallet address and, when the transfer carries a chain, on this value too, ignoring case. A transfer's chain is `beneficiary_data.chain`, filled from the transaction's crypto chain or currency symbol (for example `ETH`) when you do not send it, or the `chain` of an after-deposit registration, so use the same token everywhere. An entry with an empty chain matches any chain. A Lightning payee (a node id or a Lightning Address) registered on bitcoin is stored lowercased on `LIGHTNING`, where Lightning transfers are matched." + }, + "holder_vendor_data": { + "type": "string", + "description": "Your internal identifier for the holder." + }, + "holder_name": { + "type": "string", + "description": "Name of the wallet holder, used for the beneficiary name match." + }, + "entity_type": { + "type": "string", + "description": "Free text, defaults to \"individual\"." + }, + "is_ownership_verified": { + "type": "boolean", + "description": "Read-only. Whether ownership of this address has been proven." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "travel_address": { + "type": "string", + "description": "The counterparty VASP's travel address for this wallet, if known. Lets Didit route this beneficiary's transfers over the TRP rail, for the assets TRP carries (BTC, ETH, and USDC on a named network)." + }, + "address_hash": { + "type": "string", + "description": "Read-only. SHA-256 of the lowercased address, in lowercase hex. An entry imported by its hash only is matched on it." + }, + "memo": { + "type": "string", + "description": "The destination tag or memo that is part of the wallet on a memo network (XRP, XLM, EOS, ATOM, HBAR); `\"\"` for the whole address. An entry with a memo matches only transfers naming that memo; one without matches the whole address." + }, + "receiving_travel_address": { + "type": "string", + "readOnly": true, + "description": "Your per-wallet travel address for this wallet: give it to the sending VASP so its TRP inquiry names this wallet. Empty for a wallet recorded as another VASP's, while Travel Rule is off, and before your VASP profile is saved." + } + } + }, + "WalletAddressEntryCreate": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string", + "maxLength": 128, + "description": "The wallet address. An XRP X-address is stored as its classic address and its tag, so it matches transfers that name either form." + }, + "chain": { + "type": "string", + "maxLength": 32, + "description": "Chain token for the address, for example `ETH` or `BTC`. When Didit routes or registers a transfer, it matches address book entries on the wallet address and, when the transfer carries a chain, on this value too, ignoring case. A transfer's chain is `beneficiary_data.chain`, filled from the transaction's crypto chain or currency symbol (for example `ETH`) when you do not send it, or the `chain` of an after-deposit registration, so use the same token everywhere. An entry with an empty chain matches any chain." + }, + "holder_vendor_data": { + "type": "string", + "maxLength": 255 + }, + "holder_name": { + "type": "string", + "maxLength": 255 + }, + "entity_type": { + "type": "string", + "default": "individual", + "maxLength": 16 + }, + "self_declared": { + "type": "boolean", + "default": false, + "description": "Write-only. When true, the entry is created already ownership-verified via a SELF_DECLARATION proof. Returns 400 when `allow_self_declaration` is off in your Travel Rule settings." + }, + "travel_address": { + "type": "string", + "description": "Optional. The counterparty VASP's travel address for this wallet, enabling TRP routing for the assets TRP carries (BTC, ETH, and USDC on a named network). An invalid travel address returns 400.", + "maxLength": 512 + }, + "memo": { + "type": "string", + "maxLength": 256, + "description": "Optional. The destination tag or memo that is part of the wallet. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a `400`. Alias: `destinationTag` or `destination_tag`." + } + } + }, + "WalletAddressEntryUpdate": { + "type": "object", + "properties": { + "holder_name": { + "type": "string", + "maxLength": 255 + }, + "holder_vendor_data": { + "type": "string", + "maxLength": 255 + }, + "entity_type": { + "type": "string", + "maxLength": 16 + }, + "travel_address": { + "type": "string", + "maxLength": 512 + } + }, + "description": "PATCH is a partial update; the response echoes only holder_name, holder_vendor_data, entity_type, travel_address, receiving_travel_address, label, and owner_type." + }, + "TravelRuleTransferDetail": { + "type": "object", + "description": "Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. While an inbound transfer's wallet ownership is unconfirmed, the counterparty's `originator_data` and `beneficiary_data` come back masked (the first letter of each word is kept; the wallet, chain and VASP routing stay readable) and `data_masked` is true.", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "AWAITING_COUNTERPARTY", + "ON_HOLD", + "COMPLETED", + "FINISHED", + "CANCELLED", + "EXPIRED", + "COUNTERPARTY_MISMATCHED_DATA", + "COUNTERPARTY_VASP_NOT_FOUND", + "COUNTERPARTY_VASP_NOT_REACHABLE", + "UNCONFIRMED_OWNERSHIP", + "COUNTERPARTY_VASP_GENERAL_DECLINE", + "NOT_ENOUGH_COUNTERPARTY_DATA", + "NOT_APPLICABLE" + ] + }, + "direction": { + "type": "string", + "enum": [ + "INBOUND", + "OUTBOUND" + ] + }, + "rail": { + "type": "string", + "nullable": true, + "enum": [ + "INTERNAL", + "TRP", + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA", + "EMAIL", + "TEST_VASP", + null + ], + "description": "The rail the transfer was routed over, or null when no rail was selected (for example below the threshold, a self-hosted counterparty wallet, an after-deposit registration, or no reachable counterparty). INTERNAL means both sides are Didit applications; EMAIL means the counterparty was reached by an emailed pickup link. TEST_VASP means the Didit Test VASP answered a sandbox application's transfer. Over GTR, VERIFYVASP and SYGNA, Didit reads the counterparty's answer from the network's response or by polling it every 5 minutes. TRUST only sends, so a TRUST transfer stays AWAITING_COUNTERPARTY until it expires. A network rail is used only when you have connected your own membership for that network. It is chosen after the transaction create returns, so it is null in that response." + }, + "protocol": { + "type": "string", + "nullable": true + }, + "ivms_version": { + "type": "string", + "example": "ivms101.2023" + }, + "required": { + "type": "boolean" + }, + "ownership_confirmed": { + "type": "boolean", + "nullable": true + }, + "deadline_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "timeline": { + "type": "array", + "items": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "detail": { + "type": "string" + }, + "name_match": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleNameMatch" + } + ], + "nullable": true, + "description": "The name comparison this entry decided, next to `detail`. Absent on entries that compared no name." + }, + "reason": { + "type": "string", + "description": "Present only on the entry of a transition a failed delivery caused: `email_undeliverable` or `delivery_failed`, the value the transfer's `reason` reports." + } + } + } + }, + "counterparty_vasp": { + "type": "object", + "nullable": true, + "properties": { + "name": { + "type": "string" + }, + "lei": { + "type": "string" + }, + "dd_score": { + "type": "integer", + "description": "Due-diligence score from 0 to 100, where higher is better. Same scale as in the VASP directory." + }, + "dd_score_breakdown": { + "type": "object", + "description": "Per-component contributions to dd_score, one object per component as in the VASP directory (jurisdiction, verification, protocol, lei, history, essentials, responsiveness), each {points, max, reason, detail}. {} until the counterparty has been assessed.", + "additionalProperties": { + "type": "object", + "properties": { + "points": { + "type": "number", + "description": "Points awarded for this component." }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent." + "max": { + "type": "integer", + "description": "Maximum points this component can award." }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Echo of the `metadata` you sent." + "reason": { + "type": "string", + "description": "Machine-readable code of the component's verdict." }, - "created_at": { + "detail": { "type": "string", - "format": "date-time" - } - } - }, - "examples": { - "Resolved company": { - "summary": "Full registry profile (officer list truncated for brevity)", - "value": { - "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", - "kyb_registry": { - "uuid": "f1e2d3c4-b5a6-4789-9c0d-1e2f3a4b5c6d", - "node_id": "feature_kyb_registry", - "status": "In Review", - "registry_status": "active", - "data_resolved": true, - "company_name": "Tesco PLC", - "registration_number": "00445790", - "country_code": "GB", - "region": "", - "company_type": "Public Limited Company", - "incorporation_date": "1947-11-27", - "registered_address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", - "tax_number": "GB412 8925 93", - "risk_level": "low risk", - "verification_status": "verified", - "is_from_registry": true, - "fetch_status": "resolved", - "alternative_names": "TESCO STORES (HOLDINGS) PUBLIC LIMITED COMPANY", - "nature_of_business": null, - "registered_capital": null, - "registered_capital_amount": null, - "registered_capital_currency": null, - "website": "https://www.tesco.com/", - "email": "customer.service@tesco.co.uk", - "phone": "03301231688", - "legal_entity_identifier": null, - "location_of_registration": null, - "financial_summary": { - "currency": "Pound (£)", - "total_number_of_shares": "8174378721", - "meta_detail": { - "Class_Of_Share": "Ordinary" - } - }, - "officers": [ - { - "uuid": "a1b2c3d4-e5f6-4789-9a0b-1c2d3e4f5a6b", - "name": "CHRISTOPHER JON TAYLOR", - "designation": "Secretary", - "role": "secretary", - "nationality": null, - "is_active": true, - "kyc_status": null, - "kyc_session_url": null - }, - { - "uuid": "b2c3d4e5-f6a7-4890-8b1c-2d3e4f5a6b7c", - "name": "MELISSA BETHELL", - "designation": "Director", - "role": "director", - "nationality": "British", - "is_active": true, - "kyc_status": null, - "kyc_session_url": null - } - ], - "beneficial_owners": [], - "addresses": [ - { - "address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA", - "type": "legal_entity_registry_address", - "description": "registered office address" - } - ], - "industries": null, - "accounts": { - "last_account": "26 February 2025", - "next_account": "26 February 2026", - "due_by_date": "26 August 2026" - }, - "registry_data": { - "name": "Tesco PLC", - "registration_number": "00445790", - "status": "active", - "type": "Public Limited Company", - "incorporation_date": "1947-11-27", - "tax_number": "GB412 8925 93", - "country_code": "GB", - "fetch_status": "resolved", - "industries_detail": [ - { - "code": "47110", - "name": "Retail Sale In Non-specialised Stores With Food, Beverages Or Tobacco Predominating", - "description": null, - "meta_detail": [] - } - ] - }, - "user_provided_data": null, - "confirmed_by_user_at": null, - "last_console_edit_at": null, - "is_editable": true - }, - "vendor_data": "company-1234", - "metadata": null, - "created_at": "2026-06-11T10:45:00.000000+00:00" + "description": "Human-readable reason for the awarded points." } } } + }, + "dd_assessed_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the due-diligence assessment last ran. Null until the counterparty has been assessed." + } + } + }, + "originator_data": { + "type": "object", + "description": "Originator party. For an inbound transfer the counterparty's values are masked until ownership is confirmed." + }, + "beneficiary_data": { + "type": "object", + "description": "Beneficiary party. For an inbound transfer the counterparty's values are masked until ownership is confirmed." + }, + "name_match": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleNameMatch" + } + ], + "nullable": true, + "description": "The name comparison that decided the transfer, or null until one ran. While the parties are masked, and on the other customer's copy of an INTERNAL exchange, `transfer_name`, `compared_name`, `matched_names` and `mismatched_names` are hidden. `score` and `matched` can disagree: `matched` follows the strictness, `score` is the similarity of the two full names." + }, + "data_match": { + "$ref": "#/components/schemas/TravelRuleDataMatch" + }, + "mismatched_fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Shortcut for `data_match.mismatched_fields`. Empty when nothing differs." + }, + "data_unmasked": { + "$ref": "#/components/schemas/TravelRuleDataUnmasked" + }, + "data_masked": { + "type": "boolean", + "description": "True while the counterparty's personal data is masked in this response." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "delivery": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleDelivery" } + ], + "description": "Where the latest message this transfer owes the other side stands. Null when the transfer never owed one (for example below `threshold_amount`, a declared self-hosted wallet, or a sunrise registration)." + }, + "discovery": { + "type": "array", + "description": "How Didit looked for the counterparty, one entry per step that ran. Empty when no discovery ran, for example on an inbound transfer.", + "items": { + "$ref": "#/components/schemas/TravelRuleDiscoveryStep" + } + }, + "reason": { + "type": "string", + "description": "Why a `COUNTERPARTY_VASP_NOT_FOUND` or `COUNTERPARTY_VASP_NOT_REACHABLE` transfer is stuck; `\"\"` for any other status. A failed delivery decides it first, on `COUNTERPARTY_VASP_NOT_REACHABLE` only: `email_undeliverable` (the mail provider refused the compliance email and no address is left to try: when it was sent, on a reminder, or as a permanent bounce reported later) or `delivery_failed` (the rail that was delivering failed for good: a network rail refused the message or ran out of retries, a `TRP` identity host did not answer, or the counterparty's `TRP` host did not accept the inquiry). Otherwise the `discovery` trace decides it: `awaiting_end_user` (no step named a counterparty Didit can reach), `directory_match_no_route` (a step named a directory VASP that has no route), `picked_member_without_proven_wallet` (the end user picked a Didit customer that has not proven control of the wallet), or `no_directory_match` when the trace gives no other reason. A resend moves the transfer on, so the reason clears. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck)." + }, + "next_actions": { + "type": "array", + "description": "What moves the transfer on; `[]` for any status but `COUNTERPARTY_VASP_NOT_FOUND` or `COUNTERPARTY_VASP_NOT_REACHABLE`. `email_undeliverable` offers `update_compliance_email`, then `resend`. `delivery_failed` offers `resend`. A discovery reason offers `select_counterparty_vasp`, `declare_self_hosted`, `add_vasp_hint` and `resend`.", + "items": { + "$ref": "#/components/schemas/TravelRuleNextAction" } }, - "400": { - "description": "Validation error. Field-level problems use DRF's standard envelope.", - "content": { - "application/json": { - "examples": { - "Missing kyb_response_id": { - "summary": "`kyb_response_id` is required", - "value": { - "kyb_response_id": [ - "This field is required." - ] - } - }, - "save_api_request=false rejected": { - "summary": "Selects are always persisted", - "value": { - "save_api_request": [ - "KYB registry select must be saved so billing and retrieval can be tracked." - ] - } - } + "chain_details": { + "$ref": "#/components/schemas/TravelRuleChainDetails" + } + } + }, + "TravelRuleOwnershipConfirmRequest": { + "type": "object", + "required": [ + "confirmed" + ], + "properties": { + "confirmed": { + "type": "boolean", + "description": "true to confirm ownership, false to deny it." + }, + "wallet_address": { + "type": "string", + "default": "", + "description": "Inbound TRP transfers: the payment address Didit returns to the originating VASP when you approve. Defaults to the matched address book entry's address, then beneficiary_data.wallet_address. On a declared self-hosted wallet transfer that names no counterparty wallet, the wallet whose ownership you verified; without it the request is rejected with 400. Ignored for other transfers." + } + } + }, + "TravelRuleTransferPatchRequest": { + "type": "object", + "description": "Provide exactly one of: payment_txn_id (finish), action=cancel, action=resend, action=update_compliance_email with compliance_email, or action=update_originator_wallet with wallet_address.", + "properties": { + "payment_txn_id": { + "type": "string", + "description": "The on-chain transaction hash. Requires the transfer to be an outbound transfer in COMPLETED status." + }, + "action": { + "type": "string", + "enum": [ + "cancel", + "resend", + "update_compliance_email", + "update_originator_wallet" + ], + "description": "\"cancel\" cancels a non-terminal transfer. \"resend\" re-runs routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA. \"update_compliance_email\" replaces the compliance email the request goes to, on an outbound transfer in one of those three statuses; send \"resend\" afterwards. \"update_originator_wallet\" records the wallet the funds actually left from (`wallet_address`, `memo`)." + }, + "compliance_email": { + "type": "string", + "format": "email", + "maxLength": 254, + "description": "Required with `action: update_compliance_email`. Kept as this transfer's `beneficiary_data.counterparty_vasp_email`: it belongs to this transfer only and joins no directory entry and no contact list." + }, + "wallet_address": { + "type": "string", + "maxLength": 128, + "description": "With `action: update_originator_wallet`: the wallet the funds left from. When the transfer's UTXO inputs are known it must be one of them." + }, + "memo": { + "type": "string", + "maxLength": 256, + "description": "With `action: update_originator_wallet`: the wallet's memo or tag on a memo network. Optional." + } + } + }, + "VaspDirectoryEntry": { + "type": "object", + "description": "A VASP of the directory or a member of the Didit network, with the routes your application has to it. A customer is a member of the Didit network only while its application is live, its Travel Rule is on, its profile is discoverable, has a legal name and a confirmed compliance email, and Didit has not marked the application as a test, demo or internal one. Each row also carries the shared due-diligence assessment (dd_score, risk_level, responsiveness, essential_checks) and your application's own decision, entity screening and questionnaires, which no other customer sees.", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "The entry's id, for GET /v3/travel-rule/vasps/{vasp_id}/." + }, + "kind": { + "type": "string", + "enum": [ + "directory", + "didit" + ], + "description": "directory for an entry of the shared directory; didit for a member of the Didit network." + }, + "name": { + "type": "string" + }, + "lei": { + "type": "string" + }, + "jurisdiction": { + "type": "string", + "description": "ISO 3166 country code, as the source gives it." + }, + "domains": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The VASP's web domains. Always empty for Didit network members." + }, + "reachable": { + "type": "boolean", + "description": "Whether your application has an available route to this VASP, or to one of its entities for a brand. Always true for a Didit network member, except on a sandbox application, where every row is false." + }, + "aliases": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "trading_name", + "other_legal_name", + "network_name", + "attribution", + "merged", + "staff" + ], + "description": "trading_name, other_legal_name (from the global LEI register), network_name (the name on a Travel Rule network), attribution (the entity name blockchain analytics gives the VASP's wallets), merged (the name of a duplicate entry merged into this one) or staff." } } - } + }, + "description": "Other names the VASP is found by." }, - "403": { - "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.", - "content": { - "application/json": { - "examples": { - "Missing or invalid API key": { - "summary": "No `x-api-key` header, or the key is invalid/revoked", - "value": { - "detail": "You do not have permission to perform this action." - } - }, - "Not enough credits": { - "summary": "Organization balance cannot cover the call", - "value": { - "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" - } - } + "networks": { + "type": "array", + "items": { + "type": "object", + "description": "A current listing of this VASP in a Travel Rule network's member directory. A TRISA listing says who the VASP is on TRISA; Didit has no TRISA rail, so it is never a route.", + "properties": { + "network": { + "type": "string", + "enum": [ + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA", + "TRISA" + ] + }, + "code": { + "type": "string", + "description": "The VASP's identifier on that network." + }, + "listed_name": { + "type": "string", + "description": "The name the network lists the VASP under." + }, + "country": { + "type": "string", + "description": "The country the network lists, when it gives one." + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When Didit staff verified the listing. Null for listings read from a public directory." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the source last showed the listing. A listing the source has not shown for three weeks is removed." + }, + "source": { + "type": "string", + "enum": [ + "trisa_directory", + "staff" + ], + "description": "Where the listing comes from: the public TRISA directory, or Didit staff." } } } }, - "429": { - "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.", - "content": { - "application/json": { - "examples": { - "Write rate limit exceeded": { - "summary": "More than 300 write requests in the current minute", - "value": { - "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute." - } - } + "routes": { + "type": "array", + "items": { + "type": "object", + "description": "One way your application can deliver Travel Rule data to this VASP, in the order dispatch tries them. A network route needs a membership on that network: your own (connected in the Marketplace) or Didit's once Didit holds one. Without it the route is listed as unavailable with the reason, never as a way to reach the VASP.", + "properties": { + "rail": { + "type": "string", + "enum": [ + "INTERNAL", + "TRP", + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA", + "EMAIL" + ] }, - "schema": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } + "status": { + "type": "string", + "enum": [ + "available", + "unavailable" + ] + }, + "reason": { + "type": "string", + "enum": [ + "", + "no_membership", + "network_switched_off", + "sandbox_application" + ], + "description": "Why an unavailable route cannot be used: no_membership (neither you nor Didit holds a membership on that network), network_switched_off (you turned that network off in your settings) or sandbox_application (a sandbox application sends only to the Didit Test VASP, so no route is available to it). Empty when the route is available." + }, + "via": { + "type": "string", + "enum": [ + "", + "own_membership", + "didit_membership", + "directory", + "contact", + "didit_network" + ], + "description": "How an available route is reached: own_membership or didit_membership for a network rail, directory for a TRP endpoint the directory verified, contact for the EMAIL rail, didit_network for a Didit network member. Empty when the route is unavailable." } } } }, - "202": { - "description": "Report accepted and still processing. When a report is still being prepared, the endpoint returns 202 Accepted, kyb_registry.fetch_status=pending, and registry_tier_delivered=null. Keep request_id and retrieve GET /v3/session/{request_id}/decision/ until registry_checks[0].company.fetch_status is resolved. Didit completes the report automatically and charges the delivered tier once it is ready. Do not repeat the purchase while it is pending; every new POST creates a separate request.", - "content": { - "application/json": { - "schema": { - "$ref": "#/paths/~1v3~1kyb~1ubo~1/post/responses/200/content/application~1json/schema" - }, - "examples": { - "Pending (registry still resolving)": { - "summary": "Ownership report is still processing; retrieve the saved decision", - "value": { - "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e", - "kyb_registry": { - "uuid": "7c0a4f7e-2b1d-4f6a-9c3e-8d5b2a1f0e9d", - "node_id": "feature_kyb_registry", - "status": "Not Finished", - "registry_status": null, - "data_resolved": false, - "company_name": "Example Holdings Limited", - "registration_number": "12345678", - "country_code": "HK", - "region": null, - "company_type": null, - "incorporation_date": null, - "registered_address": null, - "tax_number": null, - "risk_level": null, - "verification_status": null, - "is_from_registry": true, - "fetch_status": "pending", - "alternative_names": null, - "nature_of_business": null, - "registered_capital": null, - "registered_capital_amount": null, - "registered_capital_currency": null, - "website": null, - "email": null, - "phone": null, - "legal_entity_identifier": null, - "location_of_registration": null, - "financial_summary": {}, - "officers": [], - "beneficial_owners": [], - "addresses": null, - "industries": null, - "accounts": null, - "registry_data": {}, - "user_provided_data": null, - "confirmed_by_user_at": null, - "last_console_edit_at": null, - "is_editable": true, - "registry_tier_delivered": null, - "registry_ownership_status": "pending" - }, - "vendor_data": "company-1234", - "metadata": null, - "created_at": "2026-06-11T10:45:00.000000+00:00", - "registry_tier_delivered": null, - "registry_tier_requested": "ubo" - } - } - } + "umbrella": { + "type": "object", + "nullable": true, + "description": "The brand this entry is a regulated entity of. Null when it is not part of one.", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" } } - } - } - } - }, - "/v3/id-verification/wallets/": { - "get": { - "operationId": "list_wallets", - "description": "Every Digital ID wallet with its start endpoint, countries, required fields and whether this application can start it in this environment now. Listed does not mean live.", - "summary": "List Digital ID wallets", - "tags": [ - "Digital ID wallets" - ], - "security": [ - { - "ApiKeyAuth": [] }, - { - "BearerAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletCatalogResponse" + "entities": { + "type": "array", + "items": { + "type": "object", + "description": "A regulated entity of a brand: the legal entity that receives a transfer.", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "lei": { + "type": "string" + }, + "jurisdiction": { + "type": "string" + }, + "reachable": { + "type": "boolean", + "description": "Whether your application has an available route to it." } } }, - "description": "" + "description": "For a brand, the regulated entities that receive its transfers. A transfer naming the brand goes to the reachable entity in the jurisdiction it states (beneficiary_data.counterparty_vasp_jurisdiction), else to the entity blockchain analytics attribution linked to the destination wallet, else to the only reachable entity when no jurisdiction is stated. Two entities that fit equally are never guessed between." }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" + "contacts": { + "type": "array", + "items": { + "type": "object", + "description": "A compliance contact the EMAIL rail can write to.", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "email": { + "type": "string", + "format": "email" + }, + "name": { + "type": "string" + }, + "source": { + "type": "string", + "enum": [ + "staff", + "application", + "learned" + ], + "description": "staff: curated by Didit; application: added by your application; learned: an address that answered a Travel Rule request through its response link." + }, + "scope": { + "type": "string", + "enum": [ + "platform", + "application" + ], + "description": "platform contacts are used for every Didit customer's transfers; application contacts are private to your application, which sees only its own." + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When someone at this address answered a request. Null when it has not yet." } } }, - "description": "Missing or invalid credentials (x-api-key or Authorization: Bearer)." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request GET \\\n --url https://verification.didit.me/v3/id-verification/wallets/ \\\n --header 'x-api-key: YOUR_API_KEY'" + "description": "The platform's contacts for this VASP and your application's own." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"]\n}\n\nresponse = requests.get(\n \"https://verification.didit.me/v3/id-verification/wallets/\",\n headers=headers,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ] - } - }, - "/v3/id-verification/wallets/verifications/{request_id}/": { - "get": { - "operationId": "get_wallet_verification", - "description": "Status and result of a wallet verification: the normalized identity and the wallet's own attributes once verified, the error code once declined, the next action while pending. A verification past expires_at is decided timeout.", - "summary": "Get a wallet verification", - "parameters": [ - { - "in": "path", - "name": "request_id", - "schema": { + "dd_status": { + "type": "string", + "enum": [ + "NOT_STARTED", + "IN_PROGRESS", + "COMPLETED", + "REJECTED" + ], + "description": "Didit's verification status of the entry, scored by the verification component of dd_score. Always COMPLETED for a Didit network member." + }, + "dd_score": { + "type": "integer", + "nullable": true, + "minimum": 0, + "maximum": 100, + "description": "The shared due-diligence score of a directory entry, 0 to 100, where higher is safer (see dd_score_semantic): the sum of the points in dd_score_breakdown. Null for a Didit network member, which is not scored. An entry not assessed yet (dd_assessed_at null) shows 0 with an empty risk_level, so read risk_level rather than the raw score." + }, + "reachable_rails": { + "type": "array", + "items": { "type": "string", - "format": "uuid" + "enum": [ + "INTERNAL", + "TRP", + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA", + "EMAIL" + ] }, - "description": "The request_id the start endpoint returned.", - "required": true - } - ], - "tags": [ - "Digital ID wallets" - ], - "security": [ - { - "ApiKeyAuth": [] + "description": "Rails recorded on the entry. Read routes for what your application can use today." }, - { - "BearerAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + "is_didit_customer": { + "type": "boolean", + "description": "true for a member of the Didit network (dd_status is always COMPLETED, dd_score null and risk_level empty for these) and for a directory entry linked to a Didit customer." + }, + "dd_score_breakdown": { + "type": "object", + "description": "One object per component of dd_score. Components, their weight (max) and their reason codes: jurisdiction, 25 (jurisdiction_not_high_risk, jurisdiction_high_risk, jurisdiction_unknown); verification, 15 (dd_status_completed, dd_status_in_progress, dd_status_not_started, dd_status_rejected); protocol, 15 (native_rail, email_only, no_rail); lei, 10 (lei_valid, lei_missing); history, 10 (history, no_history); essentials, 15 (essential_checks, no_essential_checks); responsiveness, 10 (responsive, unresponsive, responsiveness_unknown). Empty for a Didit network member.", + "additionalProperties": { + "type": "object", + "properties": { + "points": { + "type": "number", + "description": "Points the component contributes, rounded to one decimal." }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "ftn", - "name": "Finnish Trust Network", - "issuing_authority": "Finnish banks and mobile operators (FTN)" - }, - "country": "FIN", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" + "max": { + "type": "integer", + "description": "The component's weight: the points it contributes at best." + }, + "reason": { + "type": "string", + "description": "Machine-readable code of the component's verdict." + }, + "detail": { + "type": "string", + "description": "One-line English explanation." } } - }, - "description": "" + } }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" - } - } - }, - "description": "Missing or invalid credentials (x-api-key or Authorization: Bearer)." + "dd_assessed_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When dd_score, risk_level and responsiveness were last computed: after an exchange with the entry completes, finishes, expires, mismatches or is declined, when Didit refreshes or curates the entry, and in the weekly directory refresh. Null for a Didit network member." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error not_found: no wallet verification of this application has this request_id." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request GET \\\n --url https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/ \\\n --header 'x-api-key: YOUR_API_KEY'" + "dd_score_semantic": { + "type": "string", + "enum": [ + "higher_is_safer" + ], + "description": "Which way dd_score points: always higher_is_safer." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"]\n}\n\nresponse = requests.get(\n \"https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/\",\n headers=headers,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ] - } - }, - "/v3/id-verification/wallets/verifications/{request_id}/poll/": { - "post": { - "operationId": "poll_wallet_verification", - "description": "Smart-ID and Mobile-ID: ask the provider whether the user confirmed, refused or let the prompt lapse, and verify the signed identity when they confirmed. Redirect wallets complete through the user's browser, so for them this only reads the verification back. Answers the verification as it is afterwards.", - "summary": "Poll a wallet verification", - "parameters": [ - { - "in": "path", - "name": "request_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "The request_id the start endpoint returned.", - "required": true - } - ], - "tags": [ - "Digital ID wallets" - ], - "security": [ - { - "ApiKeyAuth": [] + "risk_level": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH", + "" + ], + "description": "The band dd_score falls in: LOW (70 or more), MEDIUM (40 to 69) or HIGH (below 40). Empty for a Didit network member and for an entry not assessed yet. Filter on it with risk. Your own flagged screening never changes this shared value: it makes counterparty_risk_level HIGH in your rules instead." + }, + "responsiveness": { + "type": "object", + "description": "Whether the VASP answers Travel Rule requests: the share of the requests Didit customers started towards it in the last 90 days that it answered. Only requests sent over a network or protocol rail, or by email to a shared address (the entry's compliance address or a platform contact), count; a request sent to an address a customer typed, or to a customer's private contact, never does. An expired request is unanswered; a completed exchange, a decline or a data mismatch is an answer; a cancelled request or an unreachable rail is neither. Always UNKNOWN with zero counts for a Didit network member.", + "properties": { + "status": { + "type": "string", + "enum": [ + "RESPONSIVE", + "UNRESPONSIVE", + "UNKNOWN" + ], + "description": "UNKNOWN with fewer than 5 decided requests in the window; UNRESPONSIVE when less than 10 percent of them were answered; RESPONSIVE otherwise." + }, + "answered": { + "type": "integer", + "description": "Decided requests in the window that the VASP answered." + }, + "total": { + "type": "integer", + "description": "Decided requests in the window: answered plus expired." + }, + "computed_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } }, - { - "BearerAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "smart_id", - "name": "Smart-ID", - "issuing_authority": "SK ID Solutions" + "essential_checks": { + "type": "object", + "description": "The essential-checks report of a directory entry, built only from the platform's verified sources: registration (an official register lists the VASP: the EU's interim MiCA register, the public TRISA directory or the global LEI register), licence (a MiCA authorisation is on record and current), domain (the entry's domain matches its TRP endpoint or the website its register gives) and lei (its LEI record is active, issued and consistent with the entry's names and country). The essentials component of dd_score is the share of decided checks that pass. Empty until the weekly directory refresh first checks the entry, and always empty for a Didit network member.", + "properties": { + "level": { + "type": "string", + "enum": [ + "essential" + ] + }, + "checked_at": { + "type": "string", + "format": "date-time" + }, + "checks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "enum": [ + "registration", + "licence", + "domain", + "lei" + ] }, - "country": "EST", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "in_app", - "simulated": true, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "confirm_in_app", - "verification_code": "1234", - "poll_interval_seconds": 2 + "status": { + "type": "string", + "enum": [ + "pass", + "fail", + "unknown" + ], + "description": "unknown when no source on record can decide the check." }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" + "reason": { + "type": "string", + "description": "Machine-readable code of the verdict." + }, + "detail": { + "type": "string", + "description": "One-line English explanation." + } + } } + }, + "passed": { + "type": "integer" + }, + "failed": { + "type": "integer" + }, + "unknown": { + "type": "integer" + }, + "total": { + "type": "integer" } - }, - "description": "" + } }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" - } + "decision": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleCounterpartyDecision" } - }, - "description": "Missing or invalid credentials (x-api-key or Authorization: Bearer)." + ], + "nullable": true, + "description": "Your application's decision on this VASP, or for an entity with no decision of its own its brand's (inherited_from names the brand). Null when there is none. Private to your application." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } + "entity_screening": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleCounterpartyScreening" } - }, - "description": "error not_found: no wallet verification of this application has this request_id." + ], + "nullable": true, + "description": "Your application's latest sanctions and adverse-media screening of this VASP. Null when you never screened it. Private to your application." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } + "questionnaires": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TravelRuleCounterpartyQuestionnaire" }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." + "description": "Your application's due-diligence questionnaires with this VASP, newest first. Private to your application." } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/poll/ \\\n --header 'x-api-key: YOUR_API_KEY'" + "example": { + "id": "0d6f3c2a-6a1e-4c55-9a39-1f2b0c7d8e91", + "kind": "directory", + "name": "Example Counterparty VASP", + "lei": "529900EXAMPLECPTY001", + "jurisdiction": "DE", + "domains": [ + "counterparty.example" + ], + "reachable": true, + "aliases": [ + { + "name": "Example Counterparty", + "kind": "trading_name" + } + ], + "networks": [ + { + "network": "GTR", + "code": "EXAMPLE01", + "listed_name": "Example Counterparty VASP", + "country": "DE", + "verified_at": "2026-10-12T09:30:00Z", + "last_seen_at": "2026-10-12T09:30:00Z", + "source": "staff" + } + ], + "routes": [ + { + "rail": "GTR", + "status": "unavailable", + "reason": "no_membership", + "via": "" + }, + { + "rail": "EMAIL", + "status": "available", + "reason": "", + "via": "contact" + } + ], + "umbrella": null, + "entities": [], + "contacts": [ + { + "id": "6c1f9b8e-2d4a-4f7e-8b3c-5a9d0e1f2a3b", + "email": "travelrule@counterparty.example", + "name": "Travel Rule desk", + "source": "staff", + "scope": "platform", + "verified_at": null + } + ], + "dd_status": "NOT_STARTED", + "dd_score": 62, + "dd_score_breakdown": { + "jurisdiction": { + "points": 25.0, + "max": 25, + "reason": "jurisdiction_not_high_risk", + "detail": "DEU is not on the high-risk jurisdiction list" + }, + "verification": { + "points": 3.8, + "max": 15, + "reason": "dd_status_not_started", + "detail": "due diligence status NOT_STARTED" + }, + "protocol": { + "points": 6.0, + "max": 15, + "reason": "email_only", + "detail": "reachable over the email fallback only" + }, + "lei": { + "points": 10.0, + "max": 10, + "reason": "lei_valid", + "detail": "valid LEI on record" + }, + "history": { + "points": 5.0, + "max": 10, + "reason": "no_history", + "detail": "no exchange history" + }, + "essentials": { + "points": 7.5, + "max": 15, + "reason": "no_essential_checks", + "detail": "no decided essential check yet" + }, + "responsiveness": { + "points": 5.0, + "max": 10, + "reason": "responsiveness_unknown", + "detail": "0 decided recent requests, too few to judge" + } + }, + "dd_assessed_at": "2026-10-12T09:30:00Z", + "dd_score_semantic": "higher_is_safer", + "risk_level": "MEDIUM", + "responsiveness": { + "status": "UNKNOWN", + "answered": 0, + "total": 0, + "computed_at": "2026-10-12T09:30:00Z" + }, + "essential_checks": {}, + "reachable_rails": [ + "GTR", + "EMAIL" + ], + "is_didit_customer": false, + "decision": { + "decision": "APPROVED", + "reason": "Licensed in DE, questionnaire reviewed", + "decided_by": "", + "updated_at": "2026-10-14T10:05:00Z", + "inherited_from": null + }, + "entity_screening": { + "id": "7a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d", + "status": "Approved", + "total_hits": 0, + "score": null, + "screened_at": "2026-10-14T09:58:00Z", + "fresh": true, + "flagged": false + }, + "questionnaires": [ + { + "id": "8b7a6c5d-4e3f-4a2b-9c1d-0e9f8a7b6c5d", + "status": "ANSWERED", + "form_version": "1", + "sent_to": "travelrule@counterparty.example", + "sent_at": "2026-09-02T08:00:00Z", + "expires_at": "2026-10-02T08:00:00Z", + "answered_at": "2026-09-04T15:20:00Z", + "valid_until": "2027-09-04T15:20:00Z", + "current": true, + "answers": { + "legal_name": "Example Counterparty VASP, incorporated in the example jurisdiction.", + "licensing": "Crypto-asset service provider authorised under MiCA by its national regulator, licence active.", + "jurisdictions": "EU and EEA. We do not serve the United States.", + "aml_program": "Written AML/CFT policy, annual independent review, last completed in March 2026.", + "compliance_officer": "Head of Compliance, reachable at compliance@counterparty.example.", + "sanctions_screening": "Customers at onboarding and daily; counterparties on every transfer.", + "transaction_monitoring": "Rule-based monitoring with analyst review; suspicious activity reports filed with the FIU.", + "travel_rule": "TRP and email; we answer requests within one business day.", + "custody": "Segregated customer wallets held with a qualified custodian, insured.", + "ownership": "No shareholder holds 25 percent or more; parent company Example Holding Group.", + "record_keeping": "Five-year record-keeping for Travel Rule and transaction data, as our regulator requires.", + "data_protection": "Encrypted at rest and in transit, stored in the EU." + } + } + ] + } + }, + "TravelRuleCounterpartyDecision": { + "type": "object", + "description": "Your application's decision on a counterparty VASP. Private to your application: it never changes the shared directory entry and another customer never sees it. It acts only through your transaction rules (the counterparty_dd_status rule field and the travel-rule-counterparty-blocked and travel-rule-counterparty-restricted presets); it never changes how a transfer is routed.", + "properties": { + "decision": { + "type": "string", + "enum": [ + "APPROVED", + "RESTRICTED", + "BLOCKED" + ], + "description": "APPROVED: your team cleared the counterparty. RESTRICTED: transfers to it may go ahead after review. BLOCKED: your team refused it." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"]\n}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/poll/\",\n headers=headers,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + "reason": { + "type": "string", + "maxLength": 255, + "description": "Free-text reason you gave, empty when none." + }, + "decided_by": { + "type": "string", + "description": "The console user who made or last changed the decision. Empty when an API key made it." + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "inherited_from": { + "type": "object", + "nullable": true, + "description": "The brand whose decision this entity follows because you made none on the entity itself. Null when the decision is the counterparty's own.", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + } + } } - ] - } - }, - "/v3/id-verification/wallets/verifications/{request_id}/cancel/": { - "post": { - "operationId": "cancel_wallet_verification", - "description": "Withdraw a pending verification: it is declined as cancelled and never billed. A decided one is returned unchanged. Smart-ID and Mobile-ID: a cancel that arrives while a poll is checking the sign-in waits a few seconds for that poll, then declines it, or returns it verified if the user had already confirmed. A verification the user is completing right now cannot be cancelled (409 verification_in_progress with Retry-After): retry after that many seconds to read its outcome.", - "summary": "Cancel a wallet verification", - "parameters": [ - { - "in": "path", - "name": "request_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "The request_id the start endpoint returned.", - "required": true + } + }, + "TravelRuleCounterpartyDecisionResponse": { + "type": "object", + "properties": { + "decision": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleCounterpartyDecision" + } + ], + "nullable": true, + "description": "The decision that applies to the counterparty for your application: its own, else (for an entity of a brand) the brand's. Null when there is none." } + } + }, + "TravelRuleCounterpartyDecisionRequest": { + "type": "object", + "required": [ + "decision" ], - "tags": [ - "Digital ID wallets" - ], - "security": [ - { - "ApiKeyAuth": [] + "properties": { + "decision": { + "type": "string", + "enum": [ + "APPROVED", + "RESTRICTED", + "BLOCKED" + ] }, - { - "BearerAuth": [] + "reason": { + "type": "string", + "maxLength": 255, + "description": "Optional. Replaces the previous reason; omit it to clear it." } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "ftn", - "name": "Finnish Trust Network", - "issuing_authority": "Finnish banks and mobile operators (FTN)" - }, - "country": "FIN", - "status": "Declined", - "outcome": "cancelled", - "error": "cancelled_by_customer", - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": null, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + } + }, + "TravelRuleCounterpartyScreening": { + "type": "object", + "description": "One sanctions and adverse-media screening of the counterparty entity, run for and billed to your application. Private to your application: another customer never sees it.", + "properties": { + "id": { + "type": "string", + "format": "uuid" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" - } - } - }, - "description": "Missing or invalid credentials (x-api-key or Authorization: Bearer)." + "status": { + "type": "string", + "enum": [ + "Approved", + "In Review", + "Declined" + ], + "description": "Approved: no possible match needs attention. In Review or Declined: a possible sanctions or adverse-media match." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error not_found: no wallet verification of this application has this request_id." + "total_hits": { + "type": "integer", + "description": "How many possible matches the screening returned." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } + "score": { + "type": "number", + "nullable": true, + "description": "The highest risk score among the possible matches. Null when nothing matched." + }, + "screened_at": { + "type": "string", + "format": "date-time" + }, + "fresh": { + "type": "boolean", + "description": "Whether the screening is younger than fresh_days (30 days). A fresh screening is reused instead of billed again." + }, + "flagged": { + "type": "boolean", + "description": "true while the screening is fresh and its status is In Review or Declined. While your latest screening is flagged and counterparty screening stays on, counterparty_risk_level is HIGH in your rules." + } + } + }, + "TravelRuleCounterpartyScreeningStatus": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Your counterparty_screening_enabled Travel Rule setting. Off by default; nothing is screened or billed while it is off." + }, + "price": { + "type": "string", + "description": "Decimal string, in USD: what one screening costs your organization, the Company AML Screening price." + }, + "fresh_days": { + "type": "integer", + "description": "How many days a screening stays fresh and is reused." + }, + "screening": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleCounterpartyScreening" } - }, - "description": "error verification_in_progress: the user is completing this sign-in right now. The Retry-After header says when to retry; the retry returns the decided verification." + ], + "nullable": true, + "description": "Your latest screening of the counterparty. Null when you never screened it." } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/cancel/ \\\n --header 'x-api-key: YOUR_API_KEY'" + } + }, + "TravelRuleCounterpartyScreeningRequest": { + "type": "object", + "properties": { + "refresh": { + "type": "boolean", + "default": false, + "description": "true screens again (and bills again) even when a fresh screening exists." + } + } + }, + "TravelRuleCounterpartyScreeningResult": { + "type": "object", + "properties": { + "outcome": { + "type": "string", + "enum": [ + "screened", + "reused" + ], + "description": "screened: a new screening ran and was billed (201). reused: a fresh screening was returned and nothing was billed (200)." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"]\n}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/verifications/REQUEST_ID/cancel/\",\n headers=headers,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + "screening": { + "$ref": "#/components/schemas/TravelRuleCounterpartyScreening" } - ] - } - }, - "/v3/id-verification/wallets/mitid/": { - "post": { - "operationId": "start_mitid_wallet_verification", - "description": "Start a MitID sign-in (Danish Agency for Digital Government; DNK) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start MitID verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { + } + }, + "TravelRuleCounterpartyQuestionnaire": { + "type": "object", + "description": "One due-diligence questionnaire your application sent the counterparty: Didit's standard questionnaire, emailed to a compliance address on record with a single-use answer link. Private to your application.", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "SENT", + "ANSWERED", + "EXPIRED" + ], + "description": "SENT: waiting for answers. ANSWERED: the counterparty answered every question. EXPIRED: the answer link closed unanswered (its deadline passed, or a newer questionnaire replaced it), or answers passed valid_until." + }, + "form_version": { + "type": "string", + "description": "Version of Didit's standard questionnaire the answers belong to. Currently 1." + }, + "sent_to": { + "type": "string", + "format": "email" + }, + "sent_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "The answer-by deadline, 30 days after sending. The link stops working after it." + }, + "answered_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "valid_until": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "How long the answers stay current: 365 days after answering. Null until answered." + }, + "current": { + "type": "boolean", + "description": "true while the questionnaire is ANSWERED and valid_until has not passed." + }, + "answers": { + "type": "object", + "additionalProperties": { "type": "string" }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + "description": "The answers keyed by question id: legal_name, licensing, jurisdictions, aml_program, compliance_officer, sanctions_screening, transaction_monitoring, travel_rule, custody, ownership, record_keeping, data_protection. Each answer is up to 2,000 characters. Empty until answered." } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiMitidWalletVerificationStartRequest" - }, - "example": { - "country": "DNK", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiMitidWalletVerificationStartRequest" - } + } + }, + "TravelRuleCounterpartyQuestionnaireRequest": { + "type": "object", + "properties": { + "sent_to": { + "type": "string", + "format": "email", + "description": "Optional. A compliance address on record for the counterparty: for a directory entry its compliance address or a contact your application sees, for a Didit network member its confirmed compliance email. Default: the address a Travel Rule email to the counterparty goes to (your own contact first, then a platform contact, then the entry's compliance address)." + } + } + }, + "TravelRuleCounterpartyQuestionnaireList": { + "type": "object", + "properties": { + "questionnaires": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TravelRuleCounterpartyQuestionnaire" }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiMitidWalletVerificationStartRequest" - } - } + "description": "Newest first." + } + } + }, + "TravelRulePickupInfo": { + "type": "object", + "description": "The pending exchange behind an emailed pickup link, as the counterparty's response page shows it.", + "properties": { + "reference": { + "type": "string", + "format": "uuid", + "description": "The transfer's uuid, as the sending VASP's console shows it." }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "requested_at": { + "type": "string", + "format": "date-time" }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "mitid", - "name": "MitID", - "issuing_authority": "Danish Agency for Digital Government" - }, - "country": "DNK", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "originator_vasp_name": { + "type": "string" + }, + "originator_vasp_lei": { + "type": "string" + }, + "recipient_vasp_name": { + "type": "string", + "description": "The counterparty VASP the request is addressed to." + }, + "language": { + "type": "string", + "description": "The language of the request email and the response page, from the counterparty's jurisdiction." }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "amount": { + "type": "string" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "currency": { + "type": "string" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "fiat_amount": { + "type": "string", + "description": "The converted value the sender gave, or empty." }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } + "fiat_currency": { + "type": "string" + }, + "chain": { + "type": "string" + }, + "beneficiary_wallet": { + "type": "string" + }, + "status": { + "type": "string" + }, + "ivms_version": { + "type": "string" + }, + "deadline_at": { + "type": "string", + "format": "date-time", + "description": "When the link stops answering." + }, + "reminders_sent": { + "type": "integer" + }, + "contact": { + "type": "string", + "description": "The `?contact=` value of the link, echoed back; empty when the link has none." + }, + "decline_reasons": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "data_mismatch", + "not_our_customer", + "wallet_not_hosted", + "sanctions", + "risk_appetite", + "jurisdiction", + "insufficient_data", + "other" + ] }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "description": "The values `decline_reason` accepts." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } + "join_invitation": { + "type": "object", + "description": "Whether the counterparty holds a live invitation to join Didit for free. The invitation link travels only in the email to the invited address, so `url` is empty here.", + "properties": { + "eligible": { + "type": "boolean" + }, + "url": { + "type": "string" } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/mitid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"DNK\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + } }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'DNK',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/mitid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + "originator_data": { + "type": "object", + "description": "The originator fields the sending VASP shares (`data_sharing_fields`). The name and the wallet are always included." + }, + "beneficiary_data": { + "type": "object" + }, + "ivms_request": { + "type": "object", + "description": "The same request as an IVMS 101 object. Left out while `data_masked` is true." + }, + "data_masked": { + "type": "boolean", + "description": "Always false on this endpoint: an email request is outbound, and the sender's own user is never masked." } + } + }, + "TravelRulePickupRespondRequest": { + "type": "object", + "required": [ + "decision" ], - "x-didit-wallet-id": "mitid" - } - }, - "/v3/id-verification/wallets/bankid-se/": { - "post": { - "operationId": "start_bankid_se_wallet_verification", - "description": "Start a BankID (SWE) sign-in (Finansiell ID-Teknik / bank consortium; SWE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start BankID (SWE) verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + "properties": { + "decision": { + "type": "string", + "enum": [ + "accept", + "decline" + ] + }, + "beneficiary_name": { + "type": "string", + "description": "The beneficiary's name as your records hold it, run through the sending VASP's name rule. `ivms_response` takes precedence when it names the beneficiary.", + "maxLength": 255 + }, + "decline_reason": { + "type": "string", + "enum": [ + "", + "data_mismatch", + "not_our_customer", + "wallet_not_hosted", + "sanctions", + "risk_appetite", + "jurisdiction", + "insufficient_data", + "other" + ], + "description": "Why you decline. Recorded on the sending VASP's transfer timeline. Empty (the default) when you give no reason." + }, + "decline_detail": { + "type": "string", + "maxLength": 512, + "description": "Free text; required when `decision` is `decline` and `decline_reason` is `other`." + }, + "ivms_response": { + "type": "object", + "description": "Your full IVMS 101 response. Its beneficiary name wins over `beneficiary_name`." + }, + "contact": { + "type": "string", + "format": "uuid", + "description": "The contact the link was sent to, from the link's `?contact=` value. When the body leaves it out, the `contact` query parameter is used." } + } + }, + "TravelRuleInboundRequest": { + "type": "object", + "required": [ + "chain", + "txid", + "wallet_address", + "amount", + "currency" ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBankidSeWalletVerificationStartRequest" + "properties": { + "chain": { + "type": "string", + "description": "Chain the deposit settled on, for example `ETH`. Stored lowercase and compared case-insensitively with your wallet address book entries, so use the same chain token there.", + "maxLength": 32 + }, + "txid": { + "type": "string", + "description": "On-chain transaction hash. Deduped together with wallet_address.", + "maxLength": 128 + }, + "wallet_address": { + "type": "string", + "description": "The destination wallet you received the deposit on. An XRP X-address is split into its classic address and tag.", + "maxLength": 128 + }, + "amount": { + "type": "string", + "description": "Deposit amount as a decimal string, zero or greater, with up to 18 decimal places." + }, + "currency": { + "type": "string", + "description": "Deposit asset, e.g. \"USDC\".", + "maxLength": 16 + }, + "originator_data": { + "type": "object", + "description": "Originator details from the sending VASP, if known, as a flat object such as `{\"name\": \"...\"}`. Didit reads name (or full_name), first_name, last_name and vendor_data from it.", + "additionalProperties": true + }, + "beneficiary_data": { + "type": "object", + "description": "Beneficiary details (your customer), as a flat object such as `{\"name\": \"...\"}`. Didit matches name against the holder_name of the matched address book entry, and sets wallet_address and chain from the request.", + "additionalProperties": true + }, + "originating_vasp": { + "type": "object", + "description": "Optional description of the sending VASP. It stays on this transfer only (`metadata.originating_vasp`, and the transfer's originator VASP in GET /v3/travel-rule/transfers/); it never creates, edits or links a shared VASP directory entry.", + "properties": { + "name": { + "type": "string", + "maxLength": 255 }, - "example": { - "country": "SWE", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBankidSeWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBankidSeWalletVerificationStartRequest" + "lei": { + "type": "string", + "maxLength": 20, + "description": "Optional. A valid 20-character ISO 17442 LEI, or empty." + }, + "travel_address": { + "type": "string", + "description": "The sender's travel address, recorded on this transfer.", + "maxLength": 512 } } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "memo": { + "type": "string", + "maxLength": 256, + "description": "Optional. The deposit's destination tag or memo on a memo network: on a shared deposit address it is what tells your customers apart, and the address-book entry for that memo is matched. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a `400`. Alias: `destinationTag` or `destination_tag`." }, - { - "BearerAuth": [] + "utxo": { + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleUtxo" + } + ], + "description": "Optional, UTXO chains only. The deposit transaction's inputs and outputs: the sender's wallet is the declared `originator_data.wallet_address`, else the largest input." + } + } + }, + "TravelRuleInboundResponse": { + "type": "object", + "properties": { + "transaction_id": { + "type": "string", + "format": "uuid", + "description": "Didit transaction UUID for the inbound deposit." + }, + "txn_id": { + "type": "string", + "description": "txn_id of the inbound transaction: Didit-generated when Didit created the transaction, or your own when an unclaimed deposit was reused." + }, + "created": { + "type": "boolean", + "description": "true when this call registered the transfer (HTTP 201); false when a transfer for the same txid and wallet_address already existed (HTTP 200)." + }, + "travel_rule": { + "$ref": "#/components/schemas/TravelRuleTransferDetail" } + } + }, + "TravelRuleTestVaspInboundRequest": { + "type": "object", + "required": [ + "beneficiary_wallet_address" ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "bankid_se", - "name": "BankID", - "issuing_authority": "Finansiell ID-Teknik / bank consortium" - }, - "country": "SWE", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "properties": { + "beneficiary_wallet_address": { + "type": "string", + "description": "The destination wallet address. The Didit Test VASP sends to it, matched against your wallet address book on the chain of the asset. A wallet with no matching entry is accepted and the inbound transaction is booked ON_HOLD." }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "scenario": { + "type": "string", + "enum": [ + "accept", + "name_mismatch" + ], + "default": "accept", + "description": "What the Didit Test VASP declares the beneficiary to be. `accept` declares the holder's own name; `name_mismatch` declares a name no holder agrees with. The decision itself is always yours." }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "chain": { + "type": "string", + "default": "ETH", + "description": "Chain of the asset." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "amount": { + "type": "string", + "default": "1", + "description": "Amount as a decimal string." }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "currency": { + "type": "string", + "default": "ETH", + "description": "Asset code." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." + "reference": { + "type": "string", + "description": "Optional idempotency key. A repeat with the same value returns the transfer the first call booked." } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/bankid-se/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"SWE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + } + }, + "TravelRuleTestVaspInboundResponse": { + "type": "object", + "properties": { + "transaction_id": { + "type": "string", + "format": "uuid", + "description": "Didit transaction UUID of the inbound transfer." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'SWE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/bankid-se/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + "txn_id": { + "type": "string", + "description": "Didit-generated txn_id." + }, + "scenario": { + "type": "string", + "description": "The scenario that ran." + }, + "travel_rule_status": { + "type": "string", + "description": "The status your application's own decision resolved to." + }, + "detail": { + "type": "string", + "description": "Why the transfer resolved to that status." + }, + "test_vasp": { + "type": "boolean", + "description": "Always true: the sender was the Didit Test VASP." + }, + "travel_rule": { + "$ref": "#/components/schemas/TravelRuleTransferDetail" } - ], - "x-didit-wallet-id": "bankid_se" - } - }, - "/v3/id-verification/wallets/bankid-no/": { - "post": { - "operationId": "start_bankid_no_wallet_verification", - "description": "Start a BankID (NOR) sign-in (BankID BankAxept AS; NOR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start BankID (NOR) verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + } + }, + "TravelRuleWidgetSessionCreate": { + "type": "object", + "properties": { + "wallet_address": { + "type": "string", + "description": "The wallet address to verify. Optional; when empty, the address must be supplied when a proof is started.", + "maxLength": 128 + }, + "chain": { + "type": "string", + "description": "Chain token for the address, for example `ETH`, `BTC` or `SOL`. Use the same token as in your wallet address book. Message signing and the Satoshi test are offered only for a supported chain.", + "maxLength": 32 + }, + "holder_name": { + "type": "string", + "description": "Name of the wallet holder, used for the beneficiary name match.", + "maxLength": 255 + }, + "vendor_data": { + "type": "string", + "description": "Your internal identifier for the holder, carried onto the created address-book entry.", + "maxLength": 255 + }, + "transaction_id": { + "type": "string", + "format": "uuid", + "description": "Optional. Didit transaction UUID of a Travel Rule transfer to advance when the proof verifies.", + "nullable": true + }, + "satoshi_deposit_address": { + "type": "string", + "description": "Optional. A deposit address you control; supplying it enables the SATOSHI_TEST proof method.", + "maxLength": 128 + }, + "callback_url": { + "type": "string", + "description": "Optional. Absolute http(s) URL where the widget returns the customer after completion.", + "maxLength": 512 + }, + "expires_in_minutes": { + "type": "integer", + "description": "Optional. Lifetime of the minted link, in minutes. Automatically extended to cover the Satoshi test window when SATOSHI_TEST is offered.", + "default": 60, + "minimum": 5, + "maximum": 10080 } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBankidNoWalletVerificationStartRequest" - }, - "example": { - "country": "NOR", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBankidNoWalletVerificationStartRequest" - } + } + }, + "TravelRuleWidgetSessionResponse": { + "type": "object", + "properties": { + "widget_session_id": { + "type": "string", + "format": "uuid" + }, + "token": { + "type": "string", + "description": "Opaque session token embedded in the widget URL." + }, + "url": { + "type": "string", + "description": "The hosted wallet-ownership widget URL. Open it verbatim in the system browser (never inside a mobile SDK webview)." + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } + }, + "TravelRuleWidgetContext": { + "type": "object", + "properties": { + "vasp_name": { + "type": "string", + "description": "Legal name from your VASP profile, shown to the customer in the widget. Empty when no profile exists." + }, + "wallet_address": { + "type": "string" + }, + "chain": { + "type": "string" + }, + "holder_name": { + "type": "string" + }, + "allowed_methods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "MESSAGE_SIGNING", + "SCREENSHOT", + "SATOSHI_TEST", + "SELF_DECLARATION" + ] }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBankidNoWalletVerificationStartRequest" - } + "description": "Proof methods available for this session. MESSAGE_SIGNING requires a supported chain family, SATOSHI_TEST requires a satoshi_deposit_address on the session, SCREENSHOT requires the allow_screenshot_proof setting, and SELF_DECLARATION requires the allow_self_declaration setting and an `ownership_validation_mode` that accepts a declaration for the linked transfer (never in `FULL`; in `SIMPLIFIED` only below `simplified_validation_threshold`). Initiating a SELF_DECLARATION the mode does not accept answers 403." + }, + "transfer_status": { + "type": "string", + "nullable": true, + "description": "Status of the linked Travel Rule transfer. Null when the session was minted without a transaction_id." + }, + "callback_url": { + "type": "string", + "description": "Where the widget returns the customer after completion. Empty when not set." + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "completed_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Non-null once a proof has been verified." + }, + "proofs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TravelRuleWidgetProofSummary" } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "max_attempts": { + "type": "integer", + "description": "Rejected wallet proofs this session allows." }, - { - "BearerAuth": [] + "attempts_used": { + "type": "integer", + "description": "Rejected proofs so far." + }, + "attempts_remaining": { + "type": "integer", + "description": "When 0, starting another proof answers 409 and you mint a new widget session." + }, + "customization": { + "type": "object", + "nullable": true, + "description": "The application's whitelabel customization (colors, logo, font family) when white label is enabled - the widget renders with the same branding as the verification flow. Null otherwise." } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "bankid_no", - "name": "BankID", - "issuing_authority": "BankID BankAxept AS" - }, - "country": "NOR", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + } + }, + "TravelRuleWidgetProofSummary": { + "type": "object", + "properties": { + "proof_id": { + "type": "string", + "format": "uuid" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "method": { + "type": "string", + "enum": [ + "MESSAGE_SIGNING", + "SATOSHI_TEST", + "SCREENSHOT", + "SELF_DECLARATION" + ] }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "status": { + "type": "string", + "enum": [ + "PENDING", + "VERIFIED", + "REJECTED", + "EXPIRED" + ], + "description": "PENDING on a SATOSHI_TEST proof means the deposit has been seen on-chain but has not yet reached enough confirmations; a background job automatically rechecks it and moves it to VERIFIED once it confirms, with no resubmission needed. REJECTED means a genuinely wrong deposit was submitted (absent, wrong sender, or wrong amount). EXPIRED means the proof's own test window closed before a valid deposit was seen." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "wallet_address": { + "type": "string" }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "chain": { + "type": "string" }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/bankid-no/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"NOR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "deposit_address": { + "type": "string", + "nullable": true, + "description": "Deposit address the customer must send the Satoshi test amount to. Present only on a SATOSHI_TEST proof." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'NOR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/bankid-no/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "bankid_no" - } - }, - "/v3/id-verification/wallets/vipps/": { - "post": { - "operationId": "start_vipps_wallet_verification", - "description": "Start a Vipps sign-in (Vipps MobilePay / BankID NO; NOR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Vipps verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiVippsWalletVerificationStartRequest" - }, - "example": { - "country": "NOR", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiVippsWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiVippsWalletVerificationStartRequest" - } - } + "expected_amount": { + "type": "string", + "nullable": true, + "description": "Exact on-chain amount, in the deposit's native currency, the Satoshi test deposit must match. Present only on a SATOSHI_TEST proof." }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Deadline for this proof's own Satoshi test window. Present only on a SATOSHI_TEST proof; the widget session's own expires_at is extended to cover it when SATOSHI_TEST is offered." }, - { - "BearerAuth": [] + "challenge": { + "type": "string", + "nullable": true, + "description": "Didit-issued message the customer signs with their wallet. Present only on a MESSAGE_SIGNING proof." + }, + "rejection_reason": { + "type": "string", + "description": "Why the proof was rejected. Present only when `status` is REJECTED." + }, + "created_at": { + "type": "string", + "format": "date-time" } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "vipps", - "name": "Vipps", - "issuing_authority": "Vipps MobilePay / BankID NO" - }, - "country": "NOR", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + } + }, + "CaseSubject": { + "type": "object", + "nullable": true, + "description": "The single user or business the case is anchored to.", + "properties": { + "type": { + "type": "string", + "enum": [ + "user", + "business" + ] }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "uuid": { + "type": "string", + "format": "uuid" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "name": { + "type": "string" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "external_id": { + "type": "string", + "nullable": true, + "description": "The subject's vendor_data." }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "country": { + "type": "string", + "nullable": true, + "description": "Three-letter country code of the subject (ISO 3166-1 alpha-3), or null when it is not known. For a business, its country of incorporation. For a person, the issuing country of an identity document from their verifications; a person with documents from several countries shows one of them, and ICAO document codes such as `XXA` can appear.", + "example": "ESP" }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." + "portrait": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Short-lived signed URL of the person's portrait image. Always null for a business, and null for a person without a portrait. The URL expires, so download the image when you read the case instead of storing the URL." } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/vipps/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"NOR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + } + }, + "CaseAssignee": { + "type": "object", + "nullable": true, + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'NOR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/vipps/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "vipps" - } - }, - "/v3/id-verification/wallets/buypass/": { - "post": { - "operationId": "start_buypass_wallet_verification", - "description": "Start a Buypass ID sign-in (Buypass AS; NOR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Buypass ID verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBuypassWalletVerificationStartRequest" - }, - "example": { - "country": "NOR", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBuypassWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBuypassWalletVerificationStartRequest" - } - } + "full_name": { + "type": "string" }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "email": { + "type": "string", + "format": "email" + } + } + }, + "CaseBlueprintSummary": { + "type": "object", + "nullable": true, + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - { - "BearerAuth": [] + "name": { + "type": "string", + "maxLength": 24 } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "buypass", - "name": "Buypass ID", - "issuing_authority": "Buypass AS" - }, - "country": "NOR", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" + } + }, + "CaseContentConfig": { + "type": "object", + "description": "The six togglable sections that drive which tabs and fields appear on the case page. Unknown sections or keys are rejected.", + "properties": { + "checklist": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "optional": { + "type": "boolean", + "description": "When true, a case can be resolved with incomplete checklist items." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string", + "maxLength": 255 + }, + "order": { + "type": "integer" + } + } } } - }, - "description": "" + } }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } + "aml_control": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "financial": { + "type": "boolean" + }, + "identity": { + "type": "boolean" } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + } }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } + "identity": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "personal_information": { + "type": "boolean" + }, + "applicant_tags": { + "type": "boolean" + }, + "contact_information": { + "type": "boolean" + }, + "risk_overview": { + "type": "boolean" + }, + "poi_documents": { + "type": "boolean" + }, + "poa_documents": { + "type": "boolean" } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + } }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } + "verifications": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "summary": { + "type": "boolean" } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + } }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } + "reporting": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "fiu_reports": { + "type": "boolean" } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + } }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } + "financial": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "payment_methods": { + "type": "boolean" + }, + "transactions": { + "type": "boolean" } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." + } } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/buypass/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"NOR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + } + }, + "CaseNote": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'NOR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/buypass/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "buypass" - } - }, - "/v3/id-verification/wallets/itsme/": { - "post": { - "operationId": "start_itsme_wallet_verification", - "description": "Start a itsme sign-in (Belgian Mobile ID; BEL, LUX, NLD) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start itsme verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiItsmeWalletVerificationStartRequest" - }, - "example": { - "country": "BEL", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiItsmeWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiItsmeWalletVerificationStartRequest" - } + "comment": { + "type": "string" + }, + "actor_email": { + "type": "string", + "format": "email", + "nullable": true + }, + "actor_name": { + "type": "string", + "nullable": true + }, + "mentioned_emails": { + "type": "array", + "items": { + "type": "string", + "format": "email" } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "is_internal": { + "type": "boolean" }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + "attachments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "s3_key": { + "type": "string" }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "itsme", - "name": "itsme", - "issuing_authority": "Belgian Mobile ID" - }, - "country": "BEL", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" + "filename": { + "type": "string" + }, + "file_size": { + "type": "integer" + }, + "url": { + "type": "string", + "format": "uri" } } - }, - "description": "" + } }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "tags": { + "type": "array", + "items": { + "type": "string" + } }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "created_at": { + "type": "string", + "format": "date-time" + } + } + }, + "CaseEvent": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "event_type": { + "type": "string", + "enum": [ + "CREATED", + "STATUS_CHANGED", + "ASSIGNED", + "UNASSIGNED", + "RESOLVED", + "REOPENED", + "LINK_ADDED", + "LINK_REMOVED", + "NOTE_ADDED", + "NOTE_PINNED", + "NOTE_UNPINNED", + "PRIORITY_CHANGED", + "DUE_DATE_CHANGED", + "TAG_ADDED", + "TAG_REMOVED", + "ESCALATED", + "TRANSFERRED", + "CHECKLIST_COMPLETED", + "REPORT_CREATED", + "REPORT_FINALIZED", + "SUBMITTED_FOR_APPROVAL", + "APPROVAL_GRANTED", + "APPROVAL_REJECTED", + "ENTITY_ACTION", + "APPROVAL_INVALIDATED", + "CUSTOMER_REQUEST", + "STAGE_CHANGED", + "TEAM_CHANGED", + "CHECKER_ASSIGNED", + "CHECKER_UNASSIGNED" + ] }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "actor_email": { + "type": "string", + "format": "email", + "nullable": true }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." + "actor_name": { + "type": "string", + "nullable": true + }, + "previous_status": { + "type": "string", + "enum": [ + "OPEN", + "AWAITING_USER", + "RESOLVED" + ], + "nullable": true + }, + "new_status": { + "type": "string", + "enum": [ + "OPEN", + "AWAITING_USER", + "RESOLVED" + ], + "nullable": true + }, + "metadata": { + "type": "object" + }, + "created_at": { + "type": "string", + "format": "date-time" } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/itsme/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"BEL\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + } + }, + "CaseLink": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'BEL',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/itsme/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + "linked_entity_type": { + "type": "string", + "enum": [ + "session", + "business_session", + "transaction" + ], + "nullable": true + }, + "linked_entity_uuid": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "linked_entity_summary": { + "type": "object", + "nullable": true, + "description": "session_id/session_number/status for session and business_session links; uuid/txn_id/status for transaction links." + }, + "created_at": { + "type": "string", + "format": "date-time" } - ], - "x-didit-wallet-id": "itsme" - } - }, - "/v3/id-verification/wallets/idin/": { - "post": { - "operationId": "start_idin_wallet_verification", - "description": "Start a iDIN sign-in (Dutch banks (Currence iDIN); NLD) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start iDIN verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + } + }, + "CaseChecklistItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "label": { + "type": "string", + "maxLength": 255 + }, + "is_completed": { + "type": "boolean" + }, + "completed_by": { + "type": "string", + "format": "email", + "nullable": true + }, + "completed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "order": { + "type": "integer" } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiIdinWalletVerificationStartRequest" - }, - "example": { - "country": "NLD", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiIdinWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiIdinWalletVerificationStartRequest" - } + } + }, + "CaseBlueprintListItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "maxLength": 24 + }, + "description": { + "type": "string", + "nullable": true + }, + "assignment_method": { + "type": "string", + "enum": [ + "MANUAL", + "AUTOMATIC" + ] + }, + "assignee_pool": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "max_active_cases": { + "type": "integer", + "nullable": true }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "idin", - "name": "iDIN", - "issuing_authority": "Dutch banks (Currence iDIN)" - }, - "country": "NLD", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "escalation_enabled": { + "type": "boolean" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "transfer_enabled": { + "type": "boolean" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "require_resolution_note": { + "type": "boolean" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "is_active": { + "type": "boolean" }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "preset_key": { + "type": "string", + "nullable": true }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/idin/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"NLD\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "created_at": { + "type": "string", + "format": "date-time" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'NLD',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/idin/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "idin" - } - }, - "/v3/id-verification/wallets/ftn/": { - "post": { - "operationId": "start_ftn_wallet_verification", - "description": "Start a Finnish Trust Network sign-in (Finnish banks and mobile operators (FTN); FIN) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Finnish Trust Network verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + "version": { + "type": "integer", + "readOnly": true + }, + "default_deadline_hours": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 8760 } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiFtnWalletVerificationStartRequest" - }, - "example": { - "country": "FIN", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiFtnWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiFtnWalletVerificationStartRequest" - } + } + }, + "CaseBlueprintDetail": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "maxLength": 24 + }, + "description": { + "type": "string", + "nullable": true + }, + "assignment_method": { + "type": "string", + "enum": [ + "MANUAL", + "AUTOMATIC" + ] + }, + "assignee_pool": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "max_active_cases": { + "type": "integer", + "nullable": true }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "ftn", - "name": "Finnish Trust Network", - "issuing_authority": "Finnish banks and mobile operators (FTN)" - }, - "country": "FIN", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "escalation_enabled": { + "type": "boolean" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "transfer_enabled": { + "type": "boolean" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "require_resolution_note": { + "type": "boolean" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "is_active": { + "type": "boolean" }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "preset_key": { + "type": "string", + "nullable": true }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/ftn/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"FIN\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "created_at": { + "type": "string", + "format": "date-time" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'FIN',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/ftn/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "ftn" - } - }, - "/v3/id-verification/wallets/personalausweis/": { - "post": { - "operationId": "start_personalausweis_wallet_verification", - "description": "Start a Personalausweis sign-in (Bundesministerium des Innern (eID); DEU) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Personalausweis verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiPersonalausweisWalletVerificationStartRequest" - }, - "example": { - "country": "DEU", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiPersonalausweisWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiPersonalausweisWalletVerificationStartRequest" - } - } + "escalation_blueprint": { + "type": "string", + "format": "uuid", + "nullable": true }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "escalation_blueprint_name": { + "type": "string", + "nullable": true }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "personalausweis", - "name": "Personalausweis", - "issuing_authority": "Bundesministerium des Innern (eID)" - }, - "country": "DEU", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" + "four_eyes_checker_blueprint": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "four_eyes_checker_blueprint_name": { + "type": "string", + "nullable": true + }, + "content_config": { + "type": "object", + "description": "The six togglable sections that drive which tabs and fields appear on the case page. Unknown sections or keys are rejected.", + "properties": { + "checklist": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "optional": { + "type": "boolean", + "description": "When true, a case can be resolved with incomplete checklist items." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string", + "maxLength": 255 + }, + "order": { + "type": "integer" + } + } + } + } + } + }, + "aml_control": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "financial": { + "type": "boolean" + }, + "identity": { + "type": "boolean" + } + } + }, + "identity": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "personal_information": { + "type": "boolean" }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" + "applicant_tags": { + "type": "boolean" }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" + "contact_information": { + "type": "boolean" + }, + "risk_overview": { + "type": "boolean" + }, + "poi_documents": { + "type": "boolean" + }, + "poa_documents": { + "type": "boolean" + } } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" + }, + "verifications": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "summary": { + "type": "boolean" + } } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + }, + "reporting": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "fiu_reports": { + "type": "boolean" + } } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + }, + "financial": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "payment_methods": { + "type": "boolean" + }, + "transactions": { + "type": "boolean" + } } } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + } }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + "creation_sources": { + "type": "array", + "description": "Connectors currently routing new cases into this blueprint.", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "aml", + "rule", + "workflow" + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" } } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/personalausweis/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"DEU\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + } }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'DEU',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/personalausweis/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "personalausweis" - } - }, - "/v3/id-verification/wallets/frejaid/": { - "post": { - "operationId": "start_frejaid_wallet_verification", - "description": "Start a Freja eID sign-in (Freja eID Group; SWE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Freja eID verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiFrejaidWalletVerificationStartRequest" - }, - "example": { - "country": "SWE", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" + "receives_escalation_from": { + "type": "array", + "description": "Other blueprints whose Escalation target points at this one.", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiFrejaidWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiFrejaidWalletVerificationStartRequest" + "name": { + "type": "string" + } } } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "updated_at": { + "type": "string", + "format": "date-time" }, - { - "BearerAuth": [] + "version": { + "type": "integer", + "readOnly": true + }, + "default_deadline_hours": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 8760 } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "frejaid", - "name": "Freja eID", - "issuing_authority": "Freja eID Group" - }, - "country": "SWE", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + } + }, + "CaseListItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "case_number": { + "type": "string", + "nullable": true, + "description": "Auto-assigned, e.g. CS-0001." }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "title": { + "type": "string" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "status": { + "type": "string", + "enum": [ + "OPEN", + "AWAITING_USER", + "RESOLVED" + ] }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "resolution": { + "type": "string", + "enum": [ + "FALSE_POSITIVE", + "VALID_THREAT", + "INCONCLUSIVE", + "REQUIREMENTS_NOT_MET", + "REQUIREMENTS_SATISFIED" + ], + "nullable": true }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } + "pending_resolution": { + "type": "object", + "nullable": true, + "description": "Set only while a 4-eyes resolution is staged on the checker blueprint, awaiting approve or reject-approval.", + "properties": { + "resolution": { + "type": "string", + "enum": [ + "FALSE_POSITIVE", + "VALID_THREAT", + "INCONCLUSIVE", + "REQUIREMENTS_NOT_MET", + "REQUIREMENTS_SATISFIED" + ] + }, + "resolution_notes": { + "type": "string", + "nullable": true + }, + "maker_email": { + "type": "string", + "format": "email", + "nullable": true, + "description": "Email of the officer who submitted the resolution. Null when an API key submitted it: that resolution has no recorded maker, so approve and reject-approval return 403 for every caller." + }, + "maker_name": { + "type": "string", + "nullable": true + }, + "maker_user_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Member user uuid of the officer who submitted the resolution. Null when an API key submitted it. That member cannot be named as `checker`." + }, + "maker_blueprint_id": { + "type": "string", + "format": "uuid" + }, + "staged_at": { + "type": "string", + "format": "date-time" } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/frejaid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"SWE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + } }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'SWE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/frejaid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "frejaid" - } - }, - "/v3/id-verification/wallets/uae-pass/": { - "post": { - "operationId": "start_uae_pass_wallet_verification", - "description": "Start a UAE PASS sign-in (UAE Digital Government Authority; ARE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start UAE PASS verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { + "checker": { + "$ref": "#/components/schemas/CaseChecker" + }, + "priority": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, + "source": { + "type": "string", + "enum": [ + "MANUAL", + "AML_SCREENING", + "TRANSACTION_RULE", + "WORKFLOW_BUILDER" + ] + }, + "tags": { + "type": "array", + "items": { "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiUaePassWalletVerificationStartRequest" - }, - "example": { - "country": "ARE", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiUaePassWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiUaePassWalletVerificationStartRequest" - } } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "subject": { + "$ref": "#/components/schemas/CaseSubject" }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "uae_pass", - "name": "UAE PASS", - "issuing_authority": "UAE Digital Government Authority" - }, - "country": "ARE", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "assigned_to": { + "$ref": "#/components/schemas/CaseAssignee" + }, + "assigned_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "team": { + "$ref": "#/components/schemas/CaseTeamSummary" + }, + "due_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "resolved_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "created_by_email": { + "type": "string", + "nullable": true + }, + "created_by_name": { + "type": "string", + "nullable": true }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "blueprint": { + "$ref": "#/components/schemas/CaseBlueprintSummary" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "links_count": { + "type": "integer" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "notes_count": { + "type": "integer" }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "created_at": { + "type": "string", + "format": "date-time" }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/uae-pass/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"ARE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "updated_at": { + "type": "string", + "format": "date-time" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'ARE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/uae-pass/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "uae_pass" - } - }, - "/v3/id-verification/wallets/govbr/": { - "post": { - "operationId": "start_govbr_wallet_verification", - "description": "Start a gov.br sign-in (Governo Federal do Brasil; BRA) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start gov.br verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiGovbrWalletVerificationStartRequest" - }, - "example": { - "country": "BRA", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiGovbrWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiGovbrWalletVerificationStartRequest" - } - } + "application_id": { + "type": "string", + "format": "uuid" }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "application_name": { + "type": "string" }, - { - "BearerAuth": [] + "organization_id": { + "type": "string", + "format": "uuid" + }, + "organization_name": { + "type": "string" } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "govbr", - "name": "gov.br", - "issuing_authority": "Governo Federal do Brasil" - }, - "country": "BRA", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + } + }, + "CaseDetail": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "case_number": { + "type": "string", + "nullable": true }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "title": { + "type": "string" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "status": { + "type": "string", + "enum": [ + "OPEN", + "AWAITING_USER", + "RESOLVED" + ] }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "resolution": { + "type": "string", + "enum": [ + "FALSE_POSITIVE", + "VALID_THREAT", + "INCONCLUSIVE", + "REQUIREMENTS_NOT_MET", + "REQUIREMENTS_SATISFIED" + ], + "nullable": true }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/govbr/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"BRA\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "resolution_notes": { + "type": "string", + "nullable": true }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'BRA',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/govbr/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "govbr" - } - }, - "/v3/id-verification/wallets/oneid/": { - "post": { - "operationId": "start_oneid_wallet_verification", - "description": "Start a OneID sign-in (OneID (UK bank-verified identity); GBR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start OneID verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { + "priority": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ] + }, + "source": { + "type": "string", + "enum": [ + "MANUAL", + "AML_SCREENING", + "TRANSACTION_RULE", + "WORKFLOW_BUILDER" + ] + }, + "tags": { + "type": "array", + "items": { "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiOneidWalletVerificationStartRequest" - }, - "example": { - "country": "GBR", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiOneidWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiOneidWalletVerificationStartRequest" - } } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "subject": { + "$ref": "#/components/schemas/CaseSubject" }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "oneid", - "name": "OneID", - "issuing_authority": "OneID (UK bank-verified identity)" - }, - "country": "GBR", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "assigned_to": { + "$ref": "#/components/schemas/CaseAssignee" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "assigned_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "team": { + "$ref": "#/components/schemas/CaseTeamSummary" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "due_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "resolved_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/oneid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"GBR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "resolved_by_email": { + "type": "string", + "nullable": true }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'GBR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/oneid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "oneid" - } - }, - "/v3/id-verification/wallets/govuk-wallet/": { - "post": { - "operationId": "start_govuk_wallet_wallet_verification", - "description": "Start a GOV.UK Wallet sign-in (UK Government Digital Service; GBR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start GOV.UK Wallet verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiGovukWalletWalletVerificationStartRequest" + "pending_resolution": { + "type": "object", + "nullable": true, + "description": "Set only while a 4-eyes resolution is staged on the checker blueprint, awaiting approve or reject-approval.", + "properties": { + "resolution": { + "type": "string", + "enum": [ + "FALSE_POSITIVE", + "VALID_THREAT", + "INCONCLUSIVE", + "REQUIREMENTS_NOT_MET", + "REQUIREMENTS_SATISFIED" + ] + }, + "resolution_notes": { + "type": "string", + "nullable": true + }, + "maker_email": { + "type": "string", + "format": "email", + "nullable": true, + "description": "Email of the officer who submitted the resolution. Null when an API key submitted it: that resolution has no recorded maker, so approve and reject-approval return 403 for every caller." + }, + "maker_name": { + "type": "string", + "nullable": true + }, + "maker_user_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Member user uuid of the officer who submitted the resolution. Null when an API key submitted it. That member cannot be named as `checker`." + }, + "maker_blueprint_id": { + "type": "string", + "format": "uuid" }, - "example": { - "country": "GBR", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiGovukWalletWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiGovukWalletWalletVerificationStartRequest" + "staged_at": { + "type": "string", + "format": "date-time" } } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "checker": { + "$ref": "#/components/schemas/CaseChecker" }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "govuk_wallet", - "name": "GOV.UK Wallet", - "issuing_authority": "UK Government Digital Service" - }, - "country": "GBR", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "metadata": { + "type": "object" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "blueprint": { + "$ref": "#/components/schemas/CaseBlueprintDetail" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } + "notes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseNote" }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "description": "Newest 50 notes." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } + "events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseEvent" }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "description": "Newest 100 events." }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseLink" + } }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/govuk-wallet/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"GBR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "links_count": { + "type": "integer" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'GBR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/govuk-wallet/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "govuk_wallet" - } - }, - "/v3/id-verification/wallets/smart-id/": { - "post": { - "operationId": "start_smart_id_wallet_verification", - "description": "Start a Smart-ID sign-in (SK ID Solutions; EST, LVA, LTU, BEL) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=confirm_in_app with the comparison code to show the user; the user confirms it and enters their PIN in the app (never collect the PIN). Poll POST /v3/id-verification/wallets/verifications/{request_id}/poll/ every poll_interval_seconds, or rely on the webhook: Didit polls in the background too. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Smart-ID verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiSmartIdWalletVerificationStartRequest" - }, - "example": { - "country": "EST", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "personal_code": "00000000000" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiSmartIdWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiSmartIdWalletVerificationStartRequest" - } + "notes_count": { + "type": "integer" + }, + "checklist_items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseChecklistItem" } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "created_at": { + "type": "string", + "format": "date-time" }, - { - "BearerAuth": [] + "updated_at": { + "type": "string", + "format": "date-time" } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "smart_id", - "name": "Smart-ID", - "issuing_authority": "SK ID Solutions" - }, - "country": "EST", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "in_app", - "simulated": true, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "confirm_in_app", - "verification_code": "1234", - "poll_interval_seconds": 2 - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + } + }, + "ReportTemplateDetail": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "name": { + "type": "string" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "jurisdiction": { + "type": "string", + "minLength": 2, + "maxLength": 2, + "description": "ISO 3166-1 alpha-2 country code." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "report_type": { + "type": "string", + "enum": [ + "SAR", + "STR", + "CTR", + "TTR", + "UTR", + "IFT", + "CBR", + "TFR" + ] }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "report_type_display": { + "type": "string" }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/smart-id/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"EST\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"personal_code\": \"00000000000\"\n}'" + "field_config": { + "type": "object", + "description": "Reusable goAML header defaults. Template-level obligatory fields: rentity_id, submission_code, and currency_code_local (3-letter). Optional: enabled_fields (which optional report blocks to include) plus reporting_person, location, reason, and action defaults. Per-filing header fields (action, submission_date, transactions, jurisdiction) are enforced when the report is validated or finalized, not on the template." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'EST',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'personal_code': '00000000000'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/smart-id/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "smart_id" - } - }, - "/v3/id-verification/wallets/mobile-id/": { - "post": { - "operationId": "start_mobile_id_wallet_verification", - "description": "Start a Mobile-ID sign-in (SK ID Solutions with the national mobile operators; EST, LTU) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=confirm_in_app with the comparison code to show the user; the user confirms it and enters their PIN in the app (never collect the PIN). Poll POST /v3/id-verification/wallets/verifications/{request_id}/poll/ every poll_interval_seconds, or rely on the webhook: Didit polls in the background too. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Mobile-ID verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiMobileIdWalletVerificationStartRequest" - }, - "example": { - "country": "EST", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "personal_code": "00000000000", - "phone_number": "+37200000000" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiMobileIdWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiMobileIdWalletVerificationStartRequest" - } - } + } + }, + "RegulatoryReportDetail": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "case_uuid": { + "type": "string", + "format": "uuid" }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "mobile_id", - "name": "Mobile-ID", - "issuing_authority": "SK ID Solutions with the national mobile operators" - }, - "country": "EST", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "in_app", - "simulated": true, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "confirm_in_app", - "verification_code": "1234", - "poll_interval_seconds": 2 - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "case_number": { + "type": "string", + "nullable": true + }, + "report_type": { + "type": "string", + "enum": [ + "SAR", + "STR", + "CTR", + "TTR", + "UTR", + "IFT", + "CBR", + "TFR" + ] + }, + "report_type_display": { + "type": "string" + }, + "report_code": { + "type": "string", + "description": "goAML report code derived from report_type, e.g. STR, CTR, BCR." + }, + "report_number": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "DRAFT", + "VALIDATED", + "FINALIZED" + ] + }, + "status_display": { + "type": "string" + }, + "jurisdiction": { + "type": "string", + "minLength": 2, + "maxLength": 2 + }, + "rentity_id": { + "type": "string", + "nullable": true, + "description": "Reporting entity ID issued by the FIU." }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "rentity_branch": { + "type": "string", + "nullable": true }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "submission_code": { + "type": "string", + "enum": [ + "E", + "M" + ] }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "entity_reference": { + "type": "string", + "nullable": true }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "fiu_ref_number": { + "type": "string", + "nullable": true }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/mobile-id/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"EST\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"personal_code\": \"00000000000\",\n \"phone_number\": \"+37200000000\"\n}'" + "currency_code_local": { + "type": "string", + "minLength": 3, + "maxLength": 3, + "nullable": true }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'EST',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'personal_code': '00000000000',\n 'phone_number': '+37200000000'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/mobile-id/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "mobile_id" - } - }, - "/v3/id-verification/wallets/idcard-ee/": { - "post": { - "operationId": "start_idcard_ee_wallet_verification", - "description": "Start a Estonian ID-card sign-in (Police and Border Guard Board (Estonia); EST) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Estonian ID-card verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiIdcardEeWalletVerificationStartRequest" + "submission_date": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reporting_person": { + "type": "object" + }, + "location": { + "type": "object" + }, + "reason": { + "type": "string", + "nullable": true + }, + "action": { + "type": "string", + "nullable": true + }, + "activity": { + "type": "string", + "nullable": true + }, + "narrative": { + "type": "object", + "description": "Five Ws narrative sections.", + "properties": { + "who": { + "type": "string" }, - "example": { - "country": "EST", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiIdcardEeWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiIdcardEeWalletVerificationStartRequest" + "what": { + "type": "string" + }, + "when": { + "type": "string" + }, + "where": { + "type": "string" + }, + "why": { + "type": "string" + }, + "how": { + "type": "string" } } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "report_indicators": { + "type": "array", + "items": { + "type": "string" + } }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + "included_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "transactions": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + "validation_errors": { + "type": "array", + "description": "Result of the most recent validation; each entry is a {field, message} object.", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "idcard_ee", - "name": "Estonian ID-card", - "issuing_authority": "Police and Border Guard Board (Estonia)" - }, - "country": "EST", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" + "message": { + "type": "string" } } - }, - "description": "" + } }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "validated_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "finalized_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "created_by_email": { + "type": "string", + "nullable": true }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "created_by_name": { + "type": "string", + "nullable": true }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/idcard-ee/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"EST\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "created_at": { + "type": "string", + "format": "date-time" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'EST',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/idcard-ee/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "idcard_ee" - } - }, - "/v3/id-verification/wallets/eparaksts-mobile/": { - "post": { - "operationId": "start_eparaksts_mobile_wallet_verification", - "description": "Start a eParaksts mobile sign-in (LVRTC (Latvian State Radio and Television Centre); LVA) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start eParaksts mobile verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + "updated_at": { + "type": "string", + "format": "date-time" } + } + }, + "TransactionRuleCondition": { + "type": "object", + "required": [ + "field", + "operator" ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiEparakstsMobileWalletVerificationStartRequest" - }, - "example": { - "country": "LVA", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiEparakstsMobileWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiEparakstsMobileWalletVerificationStartRequest" - } - } + "properties": { + "field": { + "type": "string", + "description": "Transaction field path to evaluate. Core fields: amount, currency, direction, action_type, score, severity, transaction_type, payment_details, preferred_currency_amount (falls back to amount_in_default_currency), default_currency_code, subject_country, counterparty_country, subject_vendor_data, counterparty_vendor_data, subject_device_fingerprint, counterparty_device_fingerprint, subject_browser_family, subject_browser_version, subject_platform, subject_accept_language, subject_session_age_ms, subject_ip_country, counterparty_ip_country, subject_ip_address, counterparty_ip_address, subject_payment_method_type, subject_payment_method_country, subject_payment_method_fingerprint, subject_payment_method_account_id (and the counterparty_payment_method_* equivalents), travel_rule_status, travel_rule_required, travel_rule_obligations_count, counterparty_dd_status (your decision on the Travel Rule counterparty: APPROVED, RESTRICTED or BLOCKED, an entity following its brand's), counterparty_risk_level (LOW, MEDIUM or HIGH; HIGH while your own counterparty screening is flagged), counterparty_jurisdiction (the counterparty VASP's country), counterparty_responsiveness (RESPONSIVE, UNRESPONSIVE or UNKNOWN), subject_days_since_previous_transaction, tags, and custom_values. for any key submitted in custom_properties on transaction create. Country-code fields accept ISO-2 or ISO-3; both sides are normalized to ISO-3 before comparison." }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "operator": { + "type": "string", + "enum": [ + "eq", + "ne", + "gt", + "gte", + "lt", + "lte", + "in", + "not_in", + "contains", + "not_contains", + "contains_any", + "regex", + "fuzzy_match", + "exists", + "is_not_empty", + "is_not_null", + "not_exists", + "is_empty", + "is_null" + ], + "description": "Comparison operator. in/not_in test membership (value may be a list or a single scalar). contains/not_contains are case-insensitive substring checks against a string value. contains_any takes a list of strings and matches when any is a case-insensitive substring of the field value. regex matches the value pattern against the field. fuzzy_match compares against the value string with the required `score` threshold (0-100). gt/gte/lt/lte never match when either side is missing. exists/is_not_empty/is_not_null (aliases) and not_exists/is_empty/is_null (aliases) are presence checks that ignore `value`." }, - { - "BearerAuth": [] + "value": { + "description": "Comparison value. Its shape depends on the operator and value_type; omit for the presence-check operators." + }, + "value_type": { + "type": "string", + "enum": [ + "list", + "field", + "relative_date" + ], + "description": "Optional value interpretation. `list`: value is a List UUID and the list's entries become the comparison value. `field`: value is another field path, compared field-to-field. `relative_date`: value is a relative-date object, e.g. {\"direction\": \"past\", \"unit\": \"days\", \"amount\": 30}." + }, + "score": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "Required when operator is fuzzy_match: the 0-100 similarity threshold." + }, + "group_index": { + "type": "integer", + "description": "Optional grouped logic. When any condition carries group_index, conditions sharing an index form a group and grouped evaluation replaces evaluation_mode." + }, + "group_logic": { + "type": "string", + "enum": [ + "AND", + "OR" + ], + "default": "AND", + "description": "How conditions combine WITHIN a group." + }, + "groups_logic": { + "type": "string", + "enum": [ + "AND", + "OR" + ], + "default": "OR", + "description": "How group outcomes combine ACROSS groups. Read from the first condition." } + }, + "description": "A condition reads only the transaction being evaluated - it never filters the aggregation window." + }, + "TransactionRuleAggregation": { + "type": "object", + "required": [ + "value" ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "eparaksts_mobile", - "name": "eParaksts mobile", - "issuing_authority": "LVRTC (Latvian State Radio and Television Centre)" - }, - "country": "LVA", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "properties": { + "metric": { + "type": "string", + "enum": [ + "count", + "sum", + "max", + "min", + "avg", + "distinct_count" + ], + "default": "count", + "description": "How the matching historical transactions are aggregated. unique_count is accepted as an alias of distinct_count." + }, + "field": { + "type": "string", + "default": "amount", + "description": "Field aggregated by sum/max/min/avg/distinct_count. Ignored for count." }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "operator": { + "type": "string", + "enum": [ + "eq", + "ne", + "gt", + "gte", + "lt", + "lte" + ], + "default": "eq", + "description": "Comparison applied to the computed metric." }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "value": { + "type": "number", + "description": "Threshold the computed metric is compared against." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } + "window": { + "type": "string", + "pattern": "^\\d+(m|h|d)$", + "default": "1d", + "description": "Look-back window ending at the transaction's txn_date: m (MINUTES, not months), h (hours) or d (days), e.g. 30m, 24h, 7d, 30d." + }, + "filters": { + "type": "object", + "additionalProperties": true, + "description": "Narrows which historical transactions are aggregated. Keys are field paths from the condition field catalog; a value of \"__current__\" resolves to the same field's value on the transaction being evaluated (e.g. {\"subject_vendor_data\": \"__current__\"} scopes the window to the same subject), and a list value means membership. Filters compare by equality or membership ONLY - there is no lt/gte filter, so a range such as \"historical transactions under 10,000\" cannot be expressed here. Put the range on the metric instead (metric max with operator lt keeps every transaction in the window below the threshold)." + } + }, + "description": "Velocity check over the application's historical transactions inside the window. Every aggregation entry must match for the rule to match, regardless of evaluation_mode. An aggregation constrains only the window; the transaction being evaluated is constrained by scope and conditions, so repeat direction, currency or transaction_type on both halves when the rule promises them." + }, + "TransactionRuleScope": { + "type": "object", + "properties": { + "transaction_types": { + "type": "array", + "items": { + "type": "string" }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "description": "Restrict to transaction types, e.g. finance, kyc, travel_rule, user_event. Empty or missing = no restriction." }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } + "directions": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "INBOUND", + "OUTBOUND" + ] }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "description": "Restrict to transaction directions (case-insensitive on write)." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } + "action_types": { + "type": "array", + "items": { + "type": "string" }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." + "description": "Restrict to your action_type values, e.g. withdrawal, deposit." } }, - "x-codeSamples": [ + "description": "Which transactions the rule applies to. Only these three keys are accepted." + }, + "TransactionRuleAction": { + "oneOf": [ { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/eparaksts-mobile/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"LVA\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "$ref": "#/components/schemas/TransactionRuleActionAddScore" }, { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'LVA',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/eparaksts-mobile/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "eparaksts_mobile" - } - }, - "/v3/id-verification/wallets/bankid-cz/": { - "post": { - "operationId": "start_bankid_cz_wallet_verification", - "description": "Start a Bank iD sign-in (Bankovní identita, a.s.; CZE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Bank iD verification", - "parameters": [ + "$ref": "#/components/schemas/TransactionRuleActionChangeStatus" + }, { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBankidCzWalletVerificationStartRequest" - }, - "example": { - "country": "CZE", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBankidCzWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiBankidCzWalletVerificationStartRequest" - } - } + "$ref": "#/components/schemas/TransactionRuleActionAddTags" }, - "required": true - }, - "security": [ { - "ApiKeyAuth": [] + "$ref": "#/components/schemas/TransactionRuleActionAddNote" }, { - "BearerAuth": [] + "$ref": "#/components/schemas/TransactionRuleActionAddToList" + }, + { + "$ref": "#/components/schemas/TransactionRuleActionOpenCase" } ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "bankid_cz", - "name": "Bank iD", - "issuing_authority": "Bankovní identita, a.s." - }, - "country": "CZE", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "discriminator": { + "propertyName": "type", + "mapping": { + "add_score": "#/components/schemas/TransactionRuleActionAddScore", + "change_status": "#/components/schemas/TransactionRuleActionChangeStatus", + "add_tags": "#/components/schemas/TransactionRuleActionAddTags", + "add_note": "#/components/schemas/TransactionRuleActionAddNote", + "add_to_list": "#/components/schemas/TransactionRuleActionAddToList", + "open_case": "#/components/schemas/TransactionRuleActionOpenCase" + } + }, + "description": "One rule action. A rule can carry several; all are applied together when it matches. Rules in TEST mode record what they would have done without applying any action." + }, + "TransactionRuleCreateRequest": { + "type": "object", + "required": [ + "title", + "category", + "mode" + ], + "properties": { + "title": { + "type": "string", + "description": "Rule name. Unique per application among non-deleted rules; duplicates return 400." }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "description": { + "type": "string", + "nullable": true }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "category": { + "type": "string", + "description": "Risk theme used for grouping and reporting, e.g. finance, aml_ctf, anomaly_detection, fatf, device_intelligence, crypto_monitoring, travel_rule, responsible_gaming, e_commerce." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "mode": { + "type": "string", + "enum": [ + "ACTIVE", + "DISABLED", + "TEST" + ], + "description": "ACTIVE evaluates live. TEST evaluates and records rule runs but never touches score or status - the recommended starting mode. DISABLED is skipped." }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "evaluation_mode": { + "type": "string", + "enum": [ + "ALL", + "ANY" + ], + "default": "ALL", + "description": "Whether every condition must match (ALL) or at least one (ANY). Ignored when conditions use group_index." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." + "scope": { + "$ref": "#/components/schemas/TransactionRuleScope" + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleCondition" + } + }, + "aggregation": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleAggregation" + } + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleAction" + } + }, + "metadata": { + "type": "object", + "description": "Free-form metadata stored on the rule." } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/bankid-cz/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"CZE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "description": "Creates a CUSTOM rule. Rules in ACTIVE mode take effect immediately for new transactions - create in TEST mode and backtest first." + }, + "TransactionRuleUpdateRequest": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Rule name. Unique per application among non-deleted rules; duplicates return 400." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'CZE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/bankid-cz/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "bankid_cz" - } - }, - "/v3/id-verification/wallets/mojeid/": { - "post": { - "operationId": "start_mojeid_wallet_verification", - "description": "Start a MojeID sign-in (CZ.NIC; CZE) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start MojeID verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + "description": { + "type": "string", + "nullable": true + }, + "category": { + "type": "string", + "description": "Risk theme used for grouping and reporting, e.g. finance, aml_ctf, anomaly_detection, fatf, device_intelligence, crypto_monitoring, travel_rule, responsible_gaming, e_commerce." + }, + "mode": { + "type": "string", + "enum": [ + "ACTIVE", + "DISABLED", + "TEST" + ], + "description": "ACTIVE evaluates live. TEST evaluates and records rule runs but never touches score or status - the recommended starting mode. DISABLED is skipped." + }, + "evaluation_mode": { + "type": "string", + "enum": [ + "ALL", + "ANY" + ], + "default": "ALL", + "description": "Whether every condition must match (ALL) or at least one (ANY). Ignored when conditions use group_index." + }, + "scope": { + "$ref": "#/components/schemas/TransactionRuleScope" + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleCondition" + } + }, + "aggregation": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleAggregation" + } + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleAction" + } + }, + "metadata": { + "type": "object", + "description": "Free-form metadata stored on the rule." } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiMojeidWalletVerificationStartRequest" - }, - "example": { - "country": "CZE", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiMojeidWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiMojeidWalletVerificationStartRequest" - } + }, + "description": "Partial update. Custom rules accept every field; library (PRESET) rules accept only mode - anything else returns 400." + }, + "TransactionRuleDetail": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "title": { + "type": "string", + "description": "Rule name. Unique per application among non-deleted rules; duplicates return 400." + }, + "description": { + "type": "string", + "nullable": true + }, + "category": { + "type": "string", + "description": "Risk theme used for grouping and reporting, e.g. finance, aml_ctf, anomaly_detection, fatf, device_intelligence, crypto_monitoring, travel_rule, responsible_gaming, e_commerce." + }, + "mode": { + "type": "string", + "enum": [ + "ACTIVE", + "DISABLED", + "TEST" + ], + "description": "ACTIVE evaluates live. TEST evaluates and records rule runs but never touches score or status - the recommended starting mode. DISABLED is skipped." + }, + "evaluation_mode": { + "type": "string", + "enum": [ + "ALL", + "ANY" + ], + "default": "ALL", + "description": "Whether every condition must match (ALL) or at least one (ANY). Ignored when conditions use group_index." + }, + "scope": { + "$ref": "#/components/schemas/TransactionRuleScope" + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleCondition" + } + }, + "aggregation": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleAggregation" + } + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleAction" } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "metadata": { + "type": "object", + "description": "Free-form metadata stored on the rule." + }, + "severity": { + "type": "string", + "enum": [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL" + ], + "nullable": true, + "description": "Informational severity carried by library rules; not settable on custom rules." + }, + "source": { + "type": "string", + "enum": [ + "PRESET", + "CUSTOM" + ], + "description": "PRESET rules come from the library (install/uninstall, mode-only edits); CUSTOM rules are fully yours." + }, + "library_key": { + "type": "string", + "nullable": true, + "description": "Stable library identifier; null for custom rules." + }, + "latest_triggered_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "run_count": { + "type": "integer", + "description": "How many transactions matched this rule." }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "mojeid", - "name": "MojeID", - "issuing_authority": "CZ.NIC" - }, - "country": "CZE", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + "approved_count": { + "type": "integer" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "reviewed_count": { + "type": "integer" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "declined_count": { + "type": "integer" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "awaiting_user_count": { + "type": "integer" }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "approved_pct": { + "type": "number" }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/mojeid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"CZE\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "reviewed_pct": { + "type": "number" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'CZE',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/mojeid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "mojeid" - } - }, - "/v3/id-verification/wallets/diia/": { - "post": { - "operationId": "start_diia_wallet_verification", - "description": "Start a Diia sign-in (Ministry of Digital Transformation of Ukraine; UKR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Diia verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiDiiaWalletVerificationStartRequest" - }, - "example": { - "country": "UKR", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiDiiaWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiDiiaWalletVerificationStartRequest" - } - } + "declined_pct": { + "type": "number" }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "created_at": { + "type": "string", + "format": "date-time" }, - { - "BearerAuth": [] + "updated_at": { + "type": "string", + "format": "date-time" } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "diia", - "name": "Diia", - "issuing_authority": "Ministry of Digital Transformation of Ukraine" - }, - "country": "UKR", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } + } + }, + "TransactionRuleBacktestRequest": { + "type": "object", + "properties": { + "conditions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleCondition" }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "default": [] }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } + "aggregation": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleAggregation" }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "default": [] }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "evaluation_mode": { + "type": "string", + "enum": [ + "ALL", + "ANY" + ], + "default": "ALL" }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "scope": { + "$ref": "#/components/schemas/TransactionRuleScope" }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." + "period_days": { + "type": "integer", + "minimum": 1, + "maximum": 365, + "default": 90 } }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/diia/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"UKR\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "description": "Hypothetical rule configuration to evaluate against recent transactions. Read-only and free: nothing is created or modified. Send the same conditions, aggregation, evaluation_mode AND scope as the rule you intend to activate. Omitted conditions, aggregation and scope add no constraints of their own; dropping scope can include directions or transaction types the saved rule skips. Omitted evaluation_mode defaults to ALL (which can reduce matches for an ANY rule), and omitted period_days defaults to 90 days, not unlimited history." + }, + "TransactionRuleBacktestResult": { + "type": "object", + "properties": { + "evaluated": { + "type": "integer", + "description": "Transactions evaluated (most recent first, capped)." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'UKR',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/diia/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "diia" - } - }, - "/v3/id-verification/wallets/franceconnect/": { - "post": { - "operationId": "start_franceconnect_wallet_verification", - "description": "Start a FranceConnect sign-in (DINUM (French state); FRA) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start FranceConnect verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiFranceconnectWalletVerificationStartRequest" - }, - "example": { - "country": "FRA", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiFranceconnectWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiFranceconnectWalletVerificationStartRequest" - } - } + "matched": { + "type": "integer", + "description": "Transactions the configuration would have matched." }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "affected_entities": { + "type": "integer", + "description": "Distinct subjects (by vendor_data) among the matches." }, - { - "BearerAuth": [] + "period_days": { + "type": "integer" } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "franceconnect", - "name": "FranceConnect", - "issuing_authority": "DINUM (French state)" - }, - "country": "FRA", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + } + }, + "TransactionRuleLibraryEntry": { + "type": "object", + "properties": { + "library_key": { + "type": "string" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "title": { + "type": "string" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "description": { + "type": "string", + "nullable": true }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "category": { + "type": "string" }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "evaluation_mode": { + "type": "string", + "enum": [ + "ALL", + "ANY" + ] + }, + "scope": { + "$ref": "#/components/schemas/TransactionRuleScope" + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleCondition" + } + }, + "aggregation": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleAggregation" + } + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TransactionRuleAction" + } + }, + "metadata": { + "type": "object" + }, + "bundle": { + "type": "string", + "nullable": true, + "description": "Functional family, e.g. finance, aml_ctf, crypto_monitoring, travel_rule, responsible_gaming." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/franceconnect/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"FRA\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "industries": { + "type": "array", + "items": { + "type": "string" + } }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'FRA',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/franceconnect/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" + "source": { + "type": "string" + }, + "is_installed": { + "type": "boolean", + "description": "Whether this preset is currently installed on the application." } - ], - "x-didit-wallet-id": "franceconnect" - } - }, - "/v3/id-verification/wallets/audkenni/": { - "post": { - "operationId": "start_audkenni_wallet_verification", - "description": "Start a Auðkenni sign-in (Auðkenni (Icelandic electronic ID); ISL) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start Auðkenni verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { + } + }, + "TransactionRuleInstallRequest": { + "type": "object", + "properties": { + "library_keys": { + "type": "array", + "items": { "type": "string" }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." + "description": "Specific presets to install or uninstall." + }, + "bundle": { + "type": "string", + "description": "Install or uninstall every preset in this bundle instead." } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiAudkenniWalletVerificationStartRequest" - }, - "example": { - "country": "ISL", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiAudkenniWalletVerificationStartRequest" - } + }, + "description": "Provide library_keys OR bundle." + }, + "TransactionRuleInstallResult": { + "type": "object", + "properties": { + "installed_library_keys": { + "type": "array", + "items": { + "type": "string" }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiAudkenniWalletVerificationStartRequest" - } - } + "description": "POST (install) only: presets installed by this call." }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] + "installed_count": { + "type": "integer", + "description": "POST (install) only." }, - { - "BearerAuth": [] + "uninstalled_count": { + "type": "integer", + "description": "DELETE (uninstall) only: presets removed by this call." } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "audkenni", - "name": "Auðkenni", - "issuing_authority": "Auðkenni (Icelandic electronic ID)" - }, - "country": "ISL", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + } + }, + "WalletScreeningResult": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "description": "Provider that performed the screening (e.g. `merklescience`, `crystal`)." }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "screening_type": { + "type": "string", + "enum": [ + "WALLET_SCREENING" + ], + "description": "Always `WALLET_SCREENING` for this endpoint." }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } + "risk_score": { + "type": "integer", + "description": "Normalised 0-100 risk score. Higher means greater exposure to risky entities. Merkle Science grades an address 0-5, which maps to 0, 15, 35, 55, 75 or 95." + }, + "severity": { + "type": "string", + "enum": [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL" + ], + "description": "Risk bucket derived from `risk_score`: `0-9` UNKNOWN, `10-39` LOW, `40-69` MEDIUM, `70-89` HIGH, `90-100` CRITICAL. `UNKNOWN` is the lowest band, not a separate no-data state: `risk_score` 0 (the common clean-address case) means no adverse assessment, while a non-zero score in the 1-9 range is a real but sub-LOW signal - read the `risk_score`, not just the band. Never treat `UNKNOWN` as an affirmative low-risk or clear rating; do not display it as a pass." + }, + "status": { + "type": "string", + "enum": [ + "SCREENED", + "PENDING", + "ERROR" + ], + "description": "Screening outcome status." + }, + "summary": { + "type": "string", + "description": "Human-readable summary of the screening result." + }, + "wallet_address": { + "type": "string", + "description": "The screened address, echoed back." + }, + "transaction_hash": { + "type": "string", + "nullable": true, + "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." + }, + "blockchain": { + "type": "string", + "description": "The blockchain that was screened." + }, + "block_timestamp": { + "type": "string", + "nullable": true, + "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." + }, + "total_value_usd": { + "type": "number", + "nullable": true, + "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." + }, + "fee_usd": { + "type": "number", + "nullable": true, + "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." + }, + "sender_rows": { + "type": "array", + "items": { + "type": "object" }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "description": "Transaction screening field, shared with the result shape. Always empty on a wallet screening result." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } + "receiver_rows": { + "type": "array", + "items": { + "type": "object" }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "description": "Transaction screening field, shared with the result shape. Always empty on a wallet screening result." }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } + "transaction_flows": { + "type": "array", + "items": { + "type": "object" }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "description": "Transaction screening field, shared with the result shape. Always empty on a wallet screening result." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + "transaction_flows_status": { + "type": "string", + "nullable": true, + "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." + }, + "transaction_flows_error": { + "type": "string", + "nullable": true, + "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." + }, + "source_of_funds": { + "type": "array", + "description": "Where the address received funds from, attributed by entity. Each entry is an exposure breakdown.", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "description": "Normalised risk category, e.g. `exchange_licensed`, `dex`, `defi`, `payment_processor`, `mixer`, `darknet_market`, `sanctioned`, `stolen_funds`, `high_risk_exchange`." + }, + "percentage": { + "type": "number", + "description": "Share of the direction's total exposure, 0-100." + }, + "amount_usd": { + "type": "number", + "nullable": true, + "description": "USD value attributed to this entity." + }, + "entity_name": { + "type": "string", + "nullable": true, + "description": "Name of the attributed entity, for example an exchange, a mixer or a sanctioned group." + }, + "entity_type": { + "type": "string", + "nullable": true, + "description": "Provider entity type (e.g. `Exchange`, `DeFi`, `Sanctions`, `Theft`)." + }, + "entity_subtype": { + "type": "string", + "nullable": true, + "description": "More granular subtype (e.g. `Mandatory KYC and AML`, `OFAC`, `Yield Aggregator`)." + }, + "hops": { + "type": "integer", + "nullable": true, + "description": "Number of hops to the entity (1 for a direct counterparty), when the provider reports one. Always `null` for Merkle Science, which reports only whether exposure is direct or indirect: use `exposure_type`." + }, + "description": { + "type": "string", + "nullable": true, + "description": "`entity_type / entity_subtype`, as the provider labels the entity." + }, + "is_direct": { + "type": "boolean", + "description": "True when `exposure_type` is `direct`." + }, + "exposure_direction": { + "type": "string", + "enum": [ + "incoming", + "outgoing", + "connected" + ], + "description": "Whether the funds came from (`incoming`) or went to (`outgoing`) this entity." + }, + "exposure_type": { + "type": "string", + "enum": [ + "direct", + "indirect" + ], + "description": "`direct` (1 hop) or `indirect` (multi-hop) exposure." + }, + "provider_entity_id": { + "type": "string", + "nullable": true, + "description": "The provider's own identifier for the entity." + }, + "provider_entity_type_id": { + "type": "string", + "nullable": true, + "description": "The provider's own identifier for `entity_type`." + }, + "provider_entity_subtype_id": { + "type": "string", + "nullable": true, + "description": "The provider's own identifier for `entity_subtype`." + }, + "country": { + "type": "string", + "nullable": true, + "description": "Primary ISO 3166-1 alpha-2 country of the entity. Empty or `null` when the provider does not know it." + }, + "raw_amount": { + "type": "number", + "nullable": true, + "description": "Amount in the asset's own unit, when the provider reports it." + }, + "raw_currency": { + "type": "string", + "nullable": true, + "description": "Unit of `raw_amount`, when the provider reports it." } } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/audkenni/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"ISL\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + } }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'ISL',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/audkenni/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "audkenni" - } - }, - "/v3/id-verification/wallets/connectid/": { - "post": { - "operationId": "start_connectid_wallet_verification", - "description": "Start a ConnectID sign-in (Australian Payments Plus; AUS) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start ConnectID verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiConnectidWalletVerificationStartRequest" - }, - "example": { - "country": "AUS", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" + "destination_of_funds": { + "type": "array", + "description": "Where the address sent funds to, attributed by entity. Same item shape as `source_of_funds` with `exposure_direction` = `outgoing`.", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "description": "Normalised risk category, e.g. `exchange_licensed`, `dex`, `defi`, `payment_processor`, `mixer`, `darknet_market`, `sanctioned`, `stolen_funds`, `high_risk_exchange`." + }, + "percentage": { + "type": "number", + "description": "Share of the direction's total exposure, 0-100." + }, + "amount_usd": { + "type": "number", + "nullable": true, + "description": "USD value attributed to this entity." + }, + "entity_name": { + "type": "string", + "nullable": true, + "description": "Name of the attributed entity, for example an exchange, a mixer or a sanctioned group." + }, + "entity_type": { + "type": "string", + "nullable": true, + "description": "Provider entity type (e.g. `Exchange`, `DeFi`, `Sanctions`, `Theft`)." + }, + "entity_subtype": { + "type": "string", + "nullable": true, + "description": "More granular subtype (e.g. `Mandatory KYC and AML`, `OFAC`, `Yield Aggregator`)." + }, + "hops": { + "type": "integer", + "nullable": true, + "description": "Number of hops to the entity (1 for a direct counterparty), when the provider reports one. Always `null` for Merkle Science, which reports only whether exposure is direct or indirect: use `exposure_type`." + }, + "description": { + "type": "string", + "nullable": true, + "description": "`entity_type / entity_subtype`, as the provider labels the entity." + }, + "is_direct": { + "type": "boolean", + "description": "True when `exposure_type` is `direct`." + }, + "exposure_direction": { + "type": "string", + "enum": [ + "incoming", + "outgoing", + "connected" + ], + "description": "Whether the funds came from (`incoming`) or went to (`outgoing`) this entity." + }, + "exposure_type": { + "type": "string", + "enum": [ + "direct", + "indirect" + ], + "description": "`direct` (1 hop) or `indirect` (multi-hop) exposure." + }, + "provider_entity_id": { + "type": "string", + "nullable": true, + "description": "The provider's own identifier for the entity." + }, + "provider_entity_type_id": { + "type": "string", + "nullable": true, + "description": "The provider's own identifier for `entity_type`." + }, + "provider_entity_subtype_id": { + "type": "string", + "nullable": true, + "description": "The provider's own identifier for `entity_subtype`." }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiConnectidWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiConnectidWalletVerificationStartRequest" + "country": { + "type": "string", + "nullable": true, + "description": "Primary ISO 3166-1 alpha-2 country of the entity. Empty or `null` when the provider does not know it." + }, + "raw_amount": { + "type": "number", + "nullable": true, + "description": "Amount in the asset's own unit, when the provider reports it." + }, + "raw_currency": { + "type": "string", + "nullable": true, + "description": "Unit of `raw_amount`, when the provider reports it." + } } } }, - "required": true - }, - "security": [ - { - "ApiKeyAuth": [] - }, - { - "BearerAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" + "counterparty_connections": { + "type": "array", + "description": "Entities the provider attributes the screened address to. For Merkle Science these come from the address's own owner and user tags, so the same entity can appear twice, and they carry no fund flows: `received_usd`, `sent_usd`, `received_hops` and `sent_hops` are `null` and `percentage` is 0. Do not route on `risk_level` or `is_direct` here: use `sanctions_hit`, `dominant_risk_category` and `risk_factors[].is_high_risk` for the verdict, and `exposure_type` in `source_of_funds`/`destination_of_funds` for directness.", + "items": { + "type": "object", + "properties": { + "entity_name": { + "type": "string", + "nullable": true, + "description": "Attributed entity name." }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "connectid", - "name": "ConnectID", - "issuing_authority": "Australian Payments Plus" - }, - "country": "AUS", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" + "entity_type": { + "type": "string", + "nullable": true, + "description": "Provider entity type." + }, + "entity_subtype": { + "type": "string", + "nullable": true, + "description": "Provider entity subtype." + }, + "risk_level": { + "type": "string", + "description": "Lowercase and provider-dependent. For Merkle Science it is the address's own 0-5 risk grade (`no_risk`, `low`, `medium_low`, `medium`, `high`, `critical`), raised to `high` when the entity's category is high risk; it is not a separate grade of the entity." + }, + "categories": { + "type": "array", + "items": { + "type": "string" }, - "created_at": "2026-01-01T10:00:00Z" + "description": "Normalised risk categories of the entity." + }, + "received_usd": { + "type": "number", + "nullable": true, + "description": "USD received from the entity, when the provider reports it." + }, + "sent_usd": { + "type": "number", + "nullable": true, + "description": "USD sent to the entity, when the provider reports it." + }, + "received_hops": { + "type": "integer", + "nullable": true, + "description": "Hops to the entity on the receiving side, when the provider reports them." + }, + "sent_hops": { + "type": "integer", + "nullable": true, + "description": "Hops to the entity on the sending side, when the provider reports them." + }, + "percentage": { + "type": "number", + "description": "Share of the address's exposure, when the provider reports it; otherwise 0." + }, + "is_direct": { + "type": "boolean", + "description": "Always `true` for Merkle Science, whose connections are attributions rather than counterparties." + }, + "country": { + "type": "string", + "nullable": true, + "description": "Country of the entity, when known." + }, + "provider_entity_id": { + "type": "string", + "nullable": true, + "description": "The provider's own identifier for the entity." } } - }, - "description": "" + } }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } + "network_nodes": { + "type": "array", + "items": { + "type": "object" }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "description": "Exposure graph nodes for transaction screening. Empty on a wallet screening result." }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } + "network_edges": { + "type": "array", + "items": { + "type": "object" }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "description": "Exposure graph edges for transaction screening. Empty on a wallet screening result." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "sanctions_hit": { + "type": "boolean", + "description": "True if the address has direct or indirect sanctions exposure." }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "pep_counterparty": { + "type": "boolean", + "description": "Politically exposed person exposure. `null` when no PEP finding is available, which is every result today because no provider reports one; `false` only for an explicit negative screening result. Do not read `null` as a completed PEP check.", + "nullable": true }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/connectid/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"AUS\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "dominant_risk_category": { + "type": "string", + "nullable": true, + "description": "Highest-weighted high-risk category, or `null` when none is dominant (e.g. `sanctioned`, `mixer`, `stolen_funds`)." }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'AUS',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/connectid/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "connectid" - } - }, - "/v3/id-verification/wallets/eudi/": { - "post": { - "operationId": "start_eudi_wallet_verification", - "description": "Start a EUDI Wallet sign-in (The user's own member state; the issuer differs per country; AUT, BEL, BGR, HRV, CYP, CZE, DNK, EST, FIN, FRA, DEU, GRC, HUN, IRL, ITA, LVA, LTU, LUX, MLT, NLD, POL, PRT, ROU, SVK, SVN, ESP, SWE, ISL, LIE, NOR) without any workflow. Billed once, only when the identity is verified. The response carries next_action.type=redirect with the URL to send the user's browser to; the user returns to return_url with request_id and result appended. Read the outcome with GET /v3/id-verification/wallets/verifications/{request_id}/ or from the webhook, never from the URL. Sandbox applications get a deterministic simulation (simulated: true) that never contacts the provider.", - "summary": "Start EUDI Wallet verification", - "parameters": [ - { - "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Optional retry-safety key (max 255 characters). Retrying with the same key, wallet and body returns the original verification without starting a second sign-in; the same key with another wallet or body returns 409 idempotency_key_reused." - } - ], - "tags": [ - "Digital ID wallets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiEudiWalletVerificationStartRequest" - }, - "example": { - "country": "AUT", - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" + "risk_factors": { + "type": "array", + "description": "Ranked explanation of `risk_score`: what produced this verdict. Providers grade an address on their own model and return only a verdict (Merkle Science returns an integer 0-5), so the score alone is not actionable. Ordered high-risk first, then by the value that moved through each driver, capped at the five strongest; `source_of_funds` and `destination_of_funds` always carry the complete breakdown. A high `risk_score` with no `is_high_risk` entry means the provider reported no sanctions or attributed-entity match and graded the address purely on its own exposure model - `summary` states this explicitly.", + "items": { + "type": "object", + "properties": { + "source": { + "type": "string", + "enum": [ + "attribution", + "exposure" + ], + "description": "`attribution` is what the provider says the address itself is (its own tag or cluster); `exposure` is where its funds actually moved." }, - "return_url": "https://app.example.com/identity/complete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiEudiWalletVerificationStartRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiEudiWalletVerificationStartRequest" + "category": { + "type": "string", + "description": "Normalised risk category, same vocabulary as the exposure breakdown." + }, + "label": { + "type": "string", + "description": "The provider's own wording for the driver (e.g. `Gambling / Crypto only`), which is what you would also see in the provider's console." + }, + "entity_name": { + "type": "string", + "nullable": true, + "description": "Attributed entity behind the driver, for example a gambling site or an exchange." + }, + "direction": { + "type": "string", + "enum": [ + "incoming", + "outgoing", + "connected", + "unknown", + "attribution" + ], + "description": "Direction of the fund flow, `attribution` when the driver is the address's own tag, or `unknown` when the provider did not report a direction." + }, + "exposure_type": { + "type": "string", + "enum": [ + "direct", + "indirect", + "unknown", + "attribution" + ], + "description": "`direct` (1 hop) or `indirect` (multi-hop), `attribution` for the address's own tag, or `unknown` when the provider did not state how many hops away the counterparty is. Treat `unknown` as unclassified, not as `direct`." + }, + "amount_usd": { + "type": "number", + "format": "float", + "nullable": true, + "description": "USD value attributed to this driver." + }, + "percentage": { + "type": "number", + "format": "float", + "description": "Share of the address's TOTAL screened volume, both directions combined - unlike `percentage` inside `source_of_funds`/`destination_of_funds`, which is normalised per direction. Comparable and summable across the whole list." + }, + "is_high_risk": { + "type": "boolean", + "description": "Whether the category is one Didit treats as high risk (`sanctioned`, `mixer`, `stolen_funds`, `ransomware`, `scam`, `darknet_market`, `terrorist_financing`, `child_exploitation`, `hacked_exchange`, `high_risk_exchange`, `seized_funds`)." + }, + "description": { + "type": "string", + "description": "One-line rendering of the driver, e.g. `9.01% indirect outgoing exposure to Gambling / Crypto only (Example Gambling)`." + } } } }, - "required": true + "raw_response": { + "type": "object", + "additionalProperties": true, + "description": "The provider's own payload. Its shape is provider-specific and can change without notice: read the normalised fields instead of parsing it." + }, + "report_signature": { + "type": "string", + "description": "Opaque HMAC-SHA256 signature over this exact result, scoped to your application. Not meaningful on its own — repost the full response body, unmodified and including this field, to `POST /v3/wallet-screening/pdf/` to render it as a PDF. The PDF endpoint rejects the request with `400` if `report_signature` is missing or if any signed field (e.g. `risk_score`, `severity`, `sanctions_hit`) was edited before repost." + } }, - "security": [ - { - "ApiKeyAuth": [] + "required": [ + "provider", + "screening_type", + "risk_score", + "severity", + "status", + "summary", + "wallet_address", + "blockchain", + "sanctions_hit", + "dominant_risk_category", + "source_of_funds", + "destination_of_funds", + "counterparty_connections", + "report_signature" + ] + }, + "NetworkRiskDisplay": { + "type": "object", + "properties": { + "score": { + "type": "integer", + "description": "Risk score, 0-100." }, - { - "BearerAuth": [] + "band": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "description": "Band the score renders as. high >= 70, medium >= 40, otherwise low." + }, + "bar": { + "type": "number", + "format": "float", + "description": "Normalized 0-1 value for rendering a risk bar." } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResponse" - }, - "example": { - "request_id": "00000000-0000-4000-8000-000000000001", - "wallet_verification": { - "wallet": { - "id": "eudi", - "name": "EUDI Wallet", - "issuing_authority": "The user's own member state; the issuer differs per country" - }, - "country": "AUT", - "status": "Not Finished", - "outcome": "pending", - "error": null, - "interaction": "redirect", - "simulated": false, - "expires_at": "2026-01-01T10:10:00Z", - "next_action": { - "type": "redirect", - "url": "https://wallet.example.com/authorize" - }, - "identity": null, - "attributes": null, - "level_of_assurance": null, - "verified_at": null, - "signature_valid": null, - "credential_type": "person_identification" - }, - "vendor_data": "your-customer-reference", - "metadata": { - "reference": "example" - }, - "created_at": "2026-01-01T10:00:00Z" - } - } - }, - "description": "" + } + }, + "NetworkNodeTag": { + "type": "object", + "properties": { + "id": { + "type": "string" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartBadRequest" - } - } - }, - "description": "Field validation errors ({field: [messages]}), or error return_url_invalid, return_url_not_allowed, invalid_personal_code, invalid_phone_number or idempotency_key_invalid." + "name": { + "type": "string" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationStartForbidden" - } - } - }, - "description": "Missing or invalid credentials ({detail}), or error wallet_unavailable, insufficient_credits or contract_rate_missing." + "color": { + "type": "string", + "nullable": true, + "description": "Null for guard tags, whose color token the renderer owns." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error idempotency_key_reused or idempotency_key_in_progress (with Retry-After)." + "source": { + "type": "string", + "enum": [ + "subject_tag", + "session_tag", + "transaction_tag", + "signal_guard" + ], + "description": "Where the tag came from: a tag on the user/business, a tag on a session in this network, a tag on a transaction a party belongs to, or a signal guard condition (e.g. hub, datacenter IP)." }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_provider_unavailable: the provider could not start; nothing was created." + "category": { + "type": "string", + "enum": [ + "customer", + "quality" + ], + "nullable": true, + "description": "customer for customer-defined tags, quality for signal-quality guard tags. Null when not applicable." + } + } + }, + "NetworkListItem": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "network_id": { + "type": "string", + "description": "Human-readable per-organization sequential id, e.g. N-0001." + }, + "name": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "active", + "in_review", + "resolved", + "dismissed" + ], + "description": "Network lifecycle status. Distinct from the per-applicant outcome_status inside a network (approved/in_review/declined/not_completed)." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - } - }, - "description": "error wallet_not_configured: this environment cannot reach the wallet." - } - }, - "x-codeSamples": [ - { - "lang": "curl", - "label": "cURL", - "source": "curl --request POST \\\n --url https://verification.didit.me/v3/id-verification/wallets/eudi/ \\\n --header 'x-api-key: YOUR_API_KEY' \\\n --header 'Idempotency-Key: your-unique-attempt-key' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"country\": \"AUT\",\n \"vendor_data\": \"your-customer-reference\",\n \"metadata\": {\n \"reference\": \"example\"\n },\n \"return_url\": \"https://app.example.com/identity/complete\"\n}'" + "risk": { + "$ref": "#/components/schemas/NetworkRiskDisplay" }, - { - "lang": "python", - "label": "Python", - "source": "import os\nimport requests\n\nheaders = {\n \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n \"Idempotency-Key\": \"your-unique-attempt-key\"\n}\npayload = {'country': 'AUT',\n 'vendor_data': 'your-customer-reference',\n 'metadata': {'reference': 'example'},\n 'return_url': 'https://app.example.com/identity/complete'}\n\nresponse = requests.post(\n \"https://verification.didit.me/v3/id-verification/wallets/eudi/\",\n headers=headers,\n json=payload,\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" - } - ], - "x-didit-wallet-id": "eudi" - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/teams/": { - "get": { - "summary": "List review teams", - "description": "Requires `list:cases` for the requested application.", - "operationId": "list_case_teams", - "tags": [ - "Case Assignment" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { + "pattern_types": { + "type": "array", + "items": { "type": "string", - "format": "uuid" + "enum": [ + "exact_same_device", + "similar_device", + "same_ip_address", + "same_address", + "similar_selfie_backgrounds", + "similar_poa_documents", + "same_document_number", + "same_phone_number", + "same_email" + ] }, - "description": "Organization UUID." + "description": "The clustering patterns detected in this network." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { + "signal_types": { + "type": "array", + "items": { "type": "string", - "format": "uuid" + "enum": [ + "ip_address", + "device", + "face", + "document_number", + "phone", + "email", + "address" + ] }, - "description": "Application UUID." + "description": "The distinct signal types present in this network." + }, + "member_counts": { + "type": "object", + "description": "Counts of members by category (e.g. total, per outcome_status)." + }, + "member_status": { + "type": "object", + "description": "Breakdown of members by outcome_status." + }, + "signal_coverage": { + "type": "object", + "description": "Per signal_type coverage of this network's members." + }, + "first_activity_at": { + "type": "string", + "format": "date-time", + "nullable": true }, + "last_activity_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "NetworkDetail": { + "allOf": [ { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 50 - } + "$ref": "#/components/schemas/NetworkListItem" }, { - "name": "offset", - "in": "query", - "schema": { - "type": "integer", - "minimum": 0, - "maximum": 1000000, - "default": 0 + "type": "object", + "properties": { + "stats": { + "type": "object", + "description": "Aggregate stats for the network (e.g. member totals)." + }, + "fraud_tags": { + "type": "array", + "items": { + "type": "object" + }, + "description": "Fraud-pattern tags surfaced on the network." + }, + "risk_signals": { + "type": "object", + "description": "Risk-signal summaries: residential_proxy_reuse, email_intelligence, phone_intelligence." + }, + "reviewed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reviewed_by": { + "type": "string", + "nullable": true + }, + "review_reason": { + "type": "string", + "nullable": true + } } } ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseReviewTeam" - } - }, - "count": { - "type": "integer", - "description": "Matching count, capped at 100. Follow next to continue beyond the cap." - }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { - "type": "string", - "nullable": true - } - } - } - } - } - }, - "400": { - "description": "Invalid input." + "properties": { + "notes_count": { + "type": "integer", + "minimum": 0, + "readOnly": true, + "description": "Number of private analyst notes in this investigation." + } + } + }, + "NetworkGraphNode": { + "type": "object", + "description": "A subject node or a signal node, distinguished by kind. Subject nodes (kind: vendor_user | vendor_business | transaction_party) carry id, kind, subject, node_status (approved | in_review | declined | not_completed), and tags. Signal nodes (kind: signal) carry id, kind, uuid, signal_type, subject_count, coverage, is_hub, is_datacenter_ip, is_high_collision_device, node_status (signal_active | signal_guarded), tags, risk_evidence, first_seen_at, and last_seen_at.", + "properties": { + "id": { + "type": "string" }, - "403": { - "description": "Insufficient permission." + "kind": { + "type": "string", + "enum": [ + "vendor_user", + "vendor_business", + "transaction_party", + "signal" + ] } } }, - "post": { - "summary": "Create review team", - "description": "Requires `write:cases` for the requested application.", - "operationId": "create_case_teams", - "tags": [ - "Case Assignment" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "NetworkGraphEdge": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "source": { + "type": "string", + "description": "Subject node id." + }, + "target": { + "type": "string", + "description": "Signal node id." + }, + "subject_kind": { + "type": "string", + "enum": [ + "vendor_user", + "vendor_business", + "transaction_party" + ] + }, + "signal_type": { + "type": "string", + "enum": [ + "ip_address", + "device", + "face", + "document_number", + "phone", + "email", + "address" + ] + }, + "confidence": { + "type": "number", + "format": "float", + "description": "1.0 for an exact match." + }, + "observation_count": { + "type": "integer" + }, + "first_seen_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "nullable": true } - ], - "responses": { - "201": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseReviewTeam" - } - } + } + }, + "NetworkGraph": { + "type": "object", + "properties": { + "network": { + "$ref": "#/components/schemas/NetworkDetail" + }, + "depth": { + "type": "integer", + "nullable": true, + "description": "Effective depth applied, or null when unbounded (depth=all)." + }, + "truncated": { + "type": "boolean", + "description": "True if the neighborhood was cut off by depth." + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkGraphNode" } }, - "400": { - "description": "Invalid input." + "edges": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkGraphEdge" + } }, - "403": { - "description": "Insufficient permission." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseReviewTeamCreate" + "metrics": { + "type": "object", + "properties": { + "node_count": { + "type": "integer" + }, + "edge_count": { + "type": "integer" + }, + "hub_signal_count": { + "type": "integer" } } } } - } - }, - "/v3/fraud/checks/{check_id}/": { - "get": { - "operationId": "root_retrieve", - "description": "Fetch a previously created check by its `check_id`.", - "summary": "Retrieve a Fraud Check", - "parameters": [ - { - "in": "path", - "name": "check_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudCheckResponse" - } - } - }, - "description": "" + }, + "NetworkMember": { + "type": "object", + "properties": { + "subject": { + "type": "object", + "description": "The vendor_user, vendor_business, or transaction_party this member represents." }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudClientCredentialsErrorMessage" - } - } - }, - "description": "" + "outcome_status": { + "type": "string", + "description": "The subject's verification/decision outcome." }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudClientCredentialsForbiddenErrorMessage" + "node_status": { + "type": "string", + "enum": [ + "approved", + "in_review", + "declined", + "not_completed", + "signal_active", + "signal_guarded" + ], + "description": "For a member row this is always one of the subject-outcome values: approved, in_review, declined, or not_completed." + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkNodeTag" + } + }, + "risk": { + "$ref": "#/components/schemas/NetworkRiskDisplay" + }, + "signal_coverage": { + "type": "object" + }, + "shared_infrastructure": { + "type": "object" + }, + "joined_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "provenance": { + "type": "object", + "properties": { + "session_ids": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + "business_session_ids": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + "transaction_ids": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" } } - }, - "description": "" - } - }, - "security": [ - { - "ApiKeyAuth": [] - }, - { - "BearerAuth": [] + } } - ], - "tags": [ - "Fraud Check" - ] - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/": { - "get": { - "operationId": "organization_application_cases_requests_list", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + } + }, + "NetworkSignal": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "signal_type": { + "type": "string", + "enum": [ + "ip_address", + "device", + "face", + "document_number", + "phone", + "email", + "address" + ] }, - { - "name": "ordering", - "required": false, - "in": "query", - "description": "Which field to use when ordering the results.", - "schema": { - "type": "string" - } + "subject_count": { + "type": "integer" }, - { - "name": "limit", - "required": false, - "in": "query", - "description": "Number of results to return per page.", - "schema": { - "type": "integer" - } + "coverage": { + "type": "number", + "format": "float" }, - { - "name": "offset", - "required": false, - "in": "query", - "description": "The initial index from which to return the results.", - "schema": { - "type": "integer" - } - } - ], - "tags": [ - "Case Management" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingPaginatedCaseRequestList" - } - } - }, - "description": "" + "is_hub": { + "type": "boolean", + "description": "True when this signal links an unusually large number of subjects." }, - "404": { - "description": "The request or case is unavailable for this caller." + "is_datacenter_ip": { + "type": "boolean" }, - "403": { - "description": "The caller lacks the required cases permission." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - }, - "post": { - "operationId": "organization_application_cases_requests_create", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "is_high_collision_device": { + "type": "boolean" }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "node_status": { + "type": "string", + "enum": [ + "signal_active", + "signal_guarded" + ] }, - { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Case Management" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestConfirmRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestConfirmRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestConfirmRequest" - } + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkNodeTag" } }, - "required": true - }, - "responses": { - "404": { - "description": "The request or case is unavailable for this caller." + "risk_evidence": { + "type": "object" }, - "403": { - "description": "The caller lacks the required cases permission." + "first_seen_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "400": { - "description": "Invalid inputs, unsupported purpose, unavailable verified contact or an existing immutable response." + "last_seen_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "NetworkTimelineEvent": { + "type": "object", + "properties": { + "event_type": { + "type": "string", + "description": "e.g. network_detected, signal_observed, network_in_review, network_resolved, network_dismissed." }, - "409": { - "description": "The preview changed or expired. Preview again before confirming." + "occurred_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequest" - } - } - }, - "description": "" - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Confirm the exact signed preview within 15 minutes. Only fixed templates and an OTP-verified contact belonging to the case subject are accepted. The request ID makes retries idempotent. Delivery is queued; SENT, SIMULATED and UNKNOWN are distinct outcomes. An actionable request sets the case to Awaiting user. A review update leaves its status unchanged." - } - }, - "/v3/fraud/checks/": { - "post": { - "operationId": "create_fraud_check", - "description": "Submit one or more typed identifiers and receive normalization, fresh enrichment, authoritative validation where the country and identifier type are covered, independent risk dimensions and a recommendation in one response.\n\nThese profiles support identifier checks. Selfie-based identity binding (`biometric_identity`) requires profile version `2026-09-14.2` and a Brazilian CPF; it runs only with the user's consent and when your organization is entitled to it. Earlier versions report it as `not_available_in_profile_version`. Modules that cannot run are reported as skipped with a deterministic reason; a source that could not answer is never reported as an identity mismatch.", - "summary": "Run a Fraud Check", - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/FraudCheckRequestRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudCheckRequestRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/FraudCheckRequestRequest" - } - } + "label": { + "type": "string" }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudCheckResponse" - } - } - }, - "description": "" + "metadata": { + "type": "object" + } + } + }, + "NetworkMap": { + "type": "object", + "properties": { + "network": { + "$ref": "#/components/schemas/NetworkDetail" }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudClientCredentialsErrorMessage" - } - } - }, - "description": "" + "convergence_count": { + "type": "integer", + "description": "Number of distinct address/location points members converge on." }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudClientCredentialsForbiddenErrorMessage" - } - } - }, - "description": "" + "countries": { + "type": "array", + "items": { + "type": "string" + } }, - "409": { - "description": "Conflict, returned without a charge: the `client_reference` was already used with a different request body; a check with the same `client_reference` is still running (retry after `Retry-After` seconds with the same reference); or the verification this check records into was deleted before the check completed.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "error": { - "type": "string" - } - }, - "required": [ - "error" - ] + "points": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - "examples": { - "different_body": { - "summary": "Reference reused with a different body", - "value": { - "error": "client_reference has already been used with a different request body." - } - }, - "still_running": { - "summary": "A check with this reference is still running", - "value": { - "error": "A check with this client_reference is already running. Retry with the same reference later." - } - }, - "verification_deleted": { - "summary": "The verification was deleted before the check completed", - "value": { - "error": "This verification was deleted before the check completed." - } - } - } - } - }, - "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying, sent when a check with the same `client_reference` is still running.", - "schema": { - "type": "integer" + "country": { + "type": "string", + "nullable": true + }, + "country_code": { + "type": "string", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true + }, + "latitude": { + "type": "number", + "format": "float", + "nullable": true + }, + "longitude": { + "type": "number", + "format": "float", + "nullable": true + }, + "observed_at": { + "type": "string", + "format": "date-time", + "nullable": true } } } } - }, - "security": [ - { - "ApiKeyAuth": [] + } + }, + "EntityNetworkMembership": { + "type": "object", + "properties": { + "network": { + "$ref": "#/components/schemas/NetworkListItem" }, - { - "BearerAuth": [] - } - ], - "tags": [ - "Fraud Check" - ] - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/cancel/": { - "post": { - "operationId": "organization_application_cases_requests_cancel_create", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "subject": { + "type": "object" }, - { - "in": "path", - "name": "application_id", - "schema": { + "signal_types": { + "type": "array", + "items": { "type": "string", - "format": "uuid" - }, - "required": true + "enum": [ + "ip_address", + "device", + "face", + "document_number", + "phone", + "email", + "address" + ] + } }, - { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "signal_count": { + "type": "integer" }, - { - "in": "path", - "name": "request_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "joined_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "nullable": true } - ], - "tags": [ - "Case Management" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequest" - } - } - }, - "description": "" + } + }, + "RiskSignals": { + "type": "object", + "description": "Optional device and network context observed by your application. All fields are optional. Passing them feeds cross-session match detection in Networks.", + "properties": { + "ip": { + "type": "string", + "format": "ip", + "description": "IPv4 or IPv6 address observed for the end user.", + "example": "192.0.2.1" }, - "404": { - "description": "The request or case is unavailable for this caller." + "device_id": { + "type": "string", + "maxLength": 255, + "description": "Stable device identifier collected by your application (Android `ANDROID_ID`, iOS `identifierForVendor`)." }, - "403": { - "description": "The caller lacks the required cases permission." + "device_platform": { + "type": "string", + "enum": [ + "android", + "ios", + "ipados", + "tvos", + "web" + ], + "description": "Platform of the end user's device." }, - "400": { - "description": "Invalid inputs, unsupported purpose, unavailable verified contact or an existing immutable response." + "device_model": { + "type": "string", + "maxLength": 255, + "description": "Device model reported by your application." }, - "409": { - "description": "The preview changed or expired. Preview again before confirming." - } - }, - "security": [ - { - "ApiKeyAuth": [] + "os_version": { + "type": "string", + "maxLength": 64, + "description": "Operating-system version." + }, + "app_version": { + "type": "string", + "maxLength": 64, + "description": "Version of your application." + }, + "user_agent": { + "type": "string", + "maxLength": 512, + "description": "Browser or app user agent." } - ] - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/options/": { - "get": { - "operationId": "organization_application_cases_requests_options_retrieve", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + } + }, + "PhoneRiskRequest": { + "type": "object", + "required": [ + "phone_number" + ], + "properties": { + "phone_number": { + "type": "string", + "maxLength": 20, + "description": "Phone number to score. It is validated and normalized to E.164.", + "example": "+14155552671" + }, + "country_code": { + "type": "string", + "maxLength": 3, + "description": "Optional ISO 3166-1 alpha-2 or alpha-3 country hint for intelligence coverage.", + "example": "US" }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "signals": { + "$ref": "#/components/schemas/RiskSignals" }, - { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "vendor_data": { + "type": "string", + "description": "Your stable reference for this request. Stored on the API session and echoed in the response." + }, + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Your non-sensitive JSON metadata. Stored on the API session and echoed in the response." + }, + "enable_social": { + "type": "boolean", + "default": false, + "description": "When true, also look up the social footprint of the number - the messaging, social, e-commerce and professional platforms it is registered on. Billed as one `phone_social` unit per request in addition to this endpoint's own price.", + "example": true } + } + }, + "EmailRiskRequest": { + "type": "object", + "required": [ + "email" ], - "tags": [ - "Case Management" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestOptions" - } - } - }, - "description": "" + "properties": { + "email": { + "type": "string", + "format": "email", + "description": "Email address to score.", + "example": "alice@example.com" }, - "404": { - "description": "The request or case is unavailable for this caller." + "country_code": { + "type": "string", + "maxLength": 3, + "description": "Optional ISO 3166-1 alpha-2 or alpha-3 country hint for intelligence coverage.", + "example": "US" }, - "403": { - "description": "The caller lacks the required cases permission." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/attachment/": { - "get": { - "operationId": "organization_application_cases_requests_attachment_retrieve", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "signals": { + "$ref": "#/components/schemas/RiskSignals" }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "vendor_data": { + "type": "string", + "description": "Your stable reference for this request. Stored on the API session and echoed in the response." }, - { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Your non-sensitive JSON metadata. Stored on the API session and echoed in the response." }, - { - "in": "path", - "name": "request_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "enable_social": { + "type": "boolean", + "default": false, + "description": "When true, also look up the social footprint of the address - the email, social, professional, e-commerce and entertainment platforms it is registered on. Billed as one `email_social` unit per request in addition to this endpoint's own price.", + "example": true } + } + }, + "IPRiskRequest": { + "type": "object", + "required": [ + "ip_address" ], - "tags": [ - "Case Management" - ], - "responses": { - "200": { - "content": { - "image/jpeg": { - "schema": { - "type": "string", - "format": "binary" - } - } - }, - "description": "" + "properties": { + "ip_address": { + "type": "string", + "format": "ip", + "description": "IPv4 or IPv6 address to score.", + "example": "8.8.8.8" }, - "404": { - "description": "The request or case is unavailable for this caller." + "claimed_country": { + "type": "string", + "maxLength": 3, + "description": "Optional ISO 3166-1 alpha-2 or alpha-3 country claimed by the user or session. Used to calculate `country_mismatch`.", + "example": "ESP" }, - "403": { - "description": "The caller lacks the required cases permission." - } - }, - "security": [ - { - "ApiKeyAuth": [] + "user_agent": { + "type": "string", + "maxLength": 512, + "description": "User agent observed by your backend. Stored as request context; it does not provide device fingerprinting." + }, + "vendor_data": { + "type": "string", + "description": "Your stable reference for this request. Stored on the API session and echoed in the response." + }, + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Your non-sensitive JSON metadata. Stored on the API session and echoed in the response." } - ] - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/history/": { - "get": { - "operationId": "list_case_routing_rule_history", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + } + }, + "RiskWarning": { + "type": "object", + "required": [ + "feature", + "risk", + "log_type", + "short_description", + "long_description" + ], + "properties": { + "feature": { + "type": "string", + "description": "Feature that produced the warning." }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "risk": { + "type": "string", + "description": "Machine-readable risk code. New values can be added over time." }, - { - "in": "path", - "name": "record_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "additional_data": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Risk-specific context, or `null`." }, - { - "name": "ordering", - "required": false, - "in": "query", - "description": "Which field to use when ordering the results.", - "schema": { - "type": "string" - } + "log_type": { + "type": "string", + "enum": [ + "error", + "warning", + "information" + ], + "description": "Severity used by the decision rules." }, - { - "name": "limit", - "required": false, - "in": "query", - "description": "Number of results to return per page.", - "schema": { - "type": "integer" - } + "short_description": { + "type": "string", + "description": "Human-readable one-line summary." }, - { - "name": "offset", - "required": false, - "in": "query", - "description": "The initial index from which to return the results.", - "schema": { - "type": "integer" - } + "long_description": { + "type": "string", + "description": "Human-readable explanation." } + } + }, + "RiskLifecycleEvent": { + "type": "object", + "required": [ + "type", + "timestamp", + "details", + "fee" ], - "tags": [ - "history" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPaginatedPolicyRevisionList" - } - } - }, - "description": "" + "properties": { + "type": { + "type": "string", + "description": "Machine-readable event type. New values can be added over time." }, - "403": { - "description": "Case access is required." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - } - }, - "/v3/session/{sessionId}/resubmission-preview/": { - "post": { - "summary": "Preview a resubmission email", - "description": "Render the email that would accompany a KYC or KYB resubmission without changing the session, resetting checks, extending its expiry, running checks or sending mail. Requires write:sessions and an application-scoped session in Approved, Declined, In Review, Kyc Expired or Abandoned status. The server validates the selected workflow steps and renders its fixed translated template with the applicable verified white-label sender and branding. The request cannot set a subject, message body or action URL.\n\nThe response includes the recipient, sender, subject, HTML, resolved steps and a signed preview_token. requires_user_action describes whether the selected steps include an interactive check. Email delivery remains conditional: if automatic checks finalize the verification during the subsequent resubmission, no action-request email is sent.\n\nThis preview does not reserve session state or authorize delivery. After reviewing it, call Update Session Status with new_status=Resubmitted, the same selection, recipient and language, and email_preview_token set to the returned preview_token. The receipt expires after 15 minutes. When a receipt is supplied, changed inputs, message content, sender or session state return 409 before any mutation or send. Existing integrations may omit the receipt. Regenerate the preview after a conflict. Treat the response as sensitive verification content; responses use Cache-Control: no-store. Render HTML in a sandboxed frame, never directly in your application DOM.", - "security": [ - { - "ApiKeyAuth": [] - } - ], - "parameters": [ - { - "in": "path", - "name": "sessionId", - "required": true, - "description": "UUID of the verification session to update. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.", - "schema": { - "type": "string", - "format": "uuid", - "example": "11111111-2222-3333-4444-555555555555" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "email_address" - ], - "properties": { - "nodes_to_resubmit": { - "type": "array", - "description": "Steps to preview, using the same validation, selection and workflow ordering as a resubmission. Omit to auto-select recorded attempts needing resubmission.", - "items": { - "type": "object", - "required": [ - "node_id", - "feature" - ], - "properties": { - "node_id": { - "type": "string", - "description": "Node identifier as it appears in the session's workflow definition (for example `feature_ocr`, `feature_liveness`).", - "example": "feature_ocr" - }, - "feature": { - "type": "string", - "description": "Feature type of the node. The aliases `ID_VERIFICATION`, `POA`, `PHONE`, and `EMAIL` are normalized server-side to `OCR`, `PROOF_OF_ADDRESS`, `PHONE_VERIFICATION`, and `EMAIL_VERIFICATION`. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and `KYB` pass schema validation but are always rejected with the business-rule `400` shown in the examples.", - "enum": [ - "OCR", - "OCR_BACK", - "NFC", - "AML", - "FACE", - "LIVENESS", - "FACE_MATCH", - "IP_ANALYSIS", - "AGE_ESTIMATION", - "PROOF_OF_ADDRESS", - "PHONE_VERIFICATION", - "EMAIL_VERIFICATION", - "FACE_SEARCH", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_DOCUMENTS", - "ID_VERIFICATION", - "POA", - "PHONE", - "EMAIL", - "KYB_REGISTRY", - "KYB_KEY_PEOPLE", - "KYB" - ], - "example": "OCR" - } - } - } - }, - "email_address": { - "type": "string", - "format": "email", - "description": "Recipient shown in the preview. No email is sent.", - "example": "user@example.com" - }, - "email_language": { - "type": "string", - "description": "Language for the notification email. Accepts any string at schema level; unsupported codes silently fall back to English (`en`).", - "default": "en", - "example": "en" - } - } - } - } + "timestamp": { + "type": "string", + "format": "date-time", + "description": "When the event happened." + }, + "details": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Event-specific payload, or `null`." + }, + "fee": { + "type": "number", + "description": "USD amount billed for this event." } - }, - "responses": { - "200": { - "description": "Preview only. The session is unchanged and no email is sent.", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "recipient", - "sender", - "subject", - "html", - "nodes_to_resubmit", - "requires_user_action", - "preview_token" - ], - "properties": { - "recipient": { - "type": "string", - "format": "email" - }, - "sender": { - "type": "string" - }, - "subject": { - "type": "string" - }, - "html": { - "type": "string", - "description": "Rendered email. Inline image attachments use data URLs for preview." - }, - "nodes_to_resubmit": { - "type": "array", - "items": { - "type": "object", - "required": [ - "node_id", - "feature" - ], - "properties": { - "node_id": { - "type": "string", - "description": "Node identifier as it appears in the session's workflow definition (for example `feature_ocr`, `feature_liveness`).", - "example": "feature_ocr" - }, - "feature": { - "type": "string", - "description": "Feature type of the node. The aliases `ID_VERIFICATION`, `POA`, `PHONE`, and `EMAIL` are normalized server-side to `OCR`, `PROOF_OF_ADDRESS`, `PHONE_VERIFICATION`, and `EMAIL_VERIFICATION`. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and `KYB` pass schema validation but are always rejected with the business-rule `400` shown in the examples.", - "enum": [ - "OCR", - "OCR_BACK", - "NFC", - "AML", - "FACE", - "LIVENESS", - "FACE_MATCH", - "IP_ANALYSIS", - "AGE_ESTIMATION", - "PROOF_OF_ADDRESS", - "PHONE_VERIFICATION", - "EMAIL_VERIFICATION", - "FACE_SEARCH", - "DATABASE_VALIDATION", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "KYB_DOCUMENTS", - "ID_VERIFICATION", - "POA", - "PHONE", - "EMAIL", - "KYB_REGISTRY", - "KYB_KEY_PEOPLE", - "KYB" - ], - "example": "OCR" - } - } - } - }, - "requires_user_action": { - "type": "boolean", - "description": "Whether at least one selected check requires user interaction. This does not predict the final workflow outcome." - }, - "preview_token": { - "type": "string", - "description": "Signed confirmation receipt, valid for 15 minutes. Pass it as email_preview_token when confirming the same message." - } - } - } - } - } + } + }, + "PhoneMatchItem": { + "type": "object", + "description": "Another session of your application that used the same phone number with a different `vendor_data`.", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "description": "Session id of the other verification that used this phone number." }, - "400": { - "description": "Invalid email, state or workflow selection." + "session_number": { + "type": "integer", + "description": "Human-friendly sequential number of that session." }, - "403": { - "description": "Authentication or write:sessions permission is missing." + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of that session." }, - "404": { - "description": "No session exists in the requested application." + "verification_date": { + "type": "string", + "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)." + }, + "phone_number": { + "type": "string", + "description": "The matched phone number." + }, + "status": { + "type": "string", + "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)." + }, + "is_blocklisted": { + "type": "boolean", + "description": "Whether the phone number is on your application's blocklist." + }, + "api_service": { + "type": "string", + "nullable": true, + "description": "For API-type sessions, the standalone API that created it; `null` for regular verification sessions." + }, + "source": { + "type": "string", + "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry uses `list_entry`." } } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/": { - "get": { - "summary": "Retrieve routing rule", - "description": "Requires `read:cases` for the requested application.", - "operationId": "retrieve_case_routing_rules", - "tags": [ - "Case Assignment" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + }, + "EmailMatchItem": { + "type": "object", + "description": "Another session of your application that used the same email address with a different `vendor_data`.", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "description": "Session id of the other verification that used this email address." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "session_number": { + "type": "integer", + "description": "Human-friendly sequential number of that session." }, - { - "name": "record_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseRoutingRule" - } - } - } + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of that session." }, - "400": { - "description": "Invalid input." + "verification_date": { + "type": "string", + "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)." }, - "403": { - "description": "Insufficient permission." + "email": { + "type": "string", + "description": "The matched email address." }, - "404": { - "description": "Record not found in this application." + "status": { + "type": "string", + "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)." + }, + "is_blocklisted": { + "type": "boolean", + "description": "Whether the email address is on your application's blocklist." + }, + "api_service": { + "type": "string", + "nullable": true, + "description": "For API-type sessions, the standalone API that created it; `null` for regular verification sessions." + }, + "source": { + "type": "string", + "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry uses `list_entry`." } } }, - "patch": { - "summary": "Update routing rule", - "description": "Requires `write:cases` for the requested application.", - "operationId": "update_case_routing_rules", - "tags": [ - "Case Assignment" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "PhoneIntelligence": { + "type": "object", + "description": "Normalized phone-intelligence block. Present only when the intelligence add-on ran for this lookup; when present it is also exposed under the `enrichment` alias with identical values.", + "properties": { + "score": { + "type": "number", + "nullable": true, + "description": "Normalized 0-100 risk score. Higher is riskier." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "phone_type_risk": { + "type": "string", + "nullable": true, + "description": "`Low`, `Medium` or `High`, derived from the score." }, - { - "name": "record_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseRoutingRule" - } - } - } + "phone_trust_index": { + "type": "number", + "nullable": true, + "description": "Inverse of the score, for dashboards that prefer higher is better." }, - "400": { - "description": "Invalid input." + "network_stability_index": { + "type": "number", + "nullable": true, + "description": "Stability of the line's network." }, - "403": { - "description": "Insufficient permission." + "is_disposable": { + "type": "boolean", + "nullable": true, + "description": "`true` when the number looks disposable or virtual." }, - "404": { - "description": "Record not found in this application." + "is_ported": { + "type": "boolean", + "nullable": true, + "description": "`true` when the number has moved carrier." }, - "409": { - "description": "The routing rule changed since expected_version. Review the latest rule before retrying." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseRoutingRuleUpdate" - } - } + "is_recently_ported": { + "type": "boolean", + "nullable": true, + "description": "`true` when the port happened recently." + }, + "carrier_type": { + "type": "string", + "nullable": true, + "description": "Line classification reported by the lookup (e.g., `mobile`)." + }, + "reason_codes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Machine-readable codes explaining the score. Values can grow over time, so do not reject unknown codes." } } - } - }, - "/v3/case-requests/{request_uuid}/": { - "get": { - "operationId": "case_requests_retrieve", - "parameters": [ - { - "in": "path", - "name": "request_uuid", - "schema": { - "type": "string", - "format": "uuid" + }, + "EmailIntelligence": { + "type": "object", + "description": "Normalized email-intelligence block. Present only when the intelligence add-on ran for this email; when present it is also exposed as the `enrichment` alias with identical values.", + "properties": { + "score": { + "type": "number", + "nullable": true, + "description": "Normalized 0-100 risk score. Higher is riskier." + }, + "deliverable": { + "type": "string", + "nullable": true, + "description": "Deliverability band (e.g., `high`)." + }, + "breach_risk_level": { + "type": "string", + "nullable": true, + "description": "`Low`, `Medium` or `High`." + }, + "breach_risk_index": { + "type": "number", + "nullable": true, + "description": "Numeric breach-risk index." + }, + "is_recent_breach": { + "type": "boolean", + "nullable": true, + "description": "`true` when the address appears in a recent breach." + }, + "is_frequent_breached": { + "type": "boolean", + "nullable": true, + "description": "`true` when the address appears in many breaches." + }, + "is_disposable": { + "type": "boolean", + "nullable": true, + "description": "`true` when the domain is a throwaway provider." + }, + "is_free_domain": { + "type": "boolean", + "nullable": true, + "description": "`true` when the domain is a free mail provider." + }, + "is_breached": { + "type": "boolean", + "nullable": true, + "description": "`true` when the address appears in known breaches." + }, + "no_of_breaches": { + "type": "number", + "nullable": true, + "description": "Number of known breaches containing the address." + }, + "reason_codes": { + "type": "array", + "items": { + "type": "string" }, - "required": true + "description": "Machine-readable codes explaining the score. Values can grow over time, so do not reject unknown codes." } + } + }, + "IPRiskResult": { + "type": "object", + "description": "IP risk result. Individual enrichment fields can be `null` when the lookup returned no usable data.", + "required": [ + "status", + "threat" ], - "tags": [ - "Case Management" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestPublic" - } - } - }, - "description": "" + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "In Review" + ], + "description": "`In Review` when the resolved country conflicts with `claimed_country`, or when the address is associated with VPN, Tor, or data-center infrastructure. Otherwise `Approved`." }, - "404": { - "description": "The request or case is unavailable for this caller." - } - }, - "security": [ - { - "CaseRequestToken": [] + "ip_address": { + "type": "string", + "nullable": true, + "description": "The scored address. `null` when no enrichment source returned usable data." + }, + "ip_country": { + "type": "string", + "nullable": true, + "description": "Resolved country name." + }, + "ip_country_code": { + "type": "string", + "nullable": true, + "description": "Resolved country code. Compare countries through `country_mismatch` instead of comparing raw country strings yourself." + }, + "ip_state": { + "type": "string", + "nullable": true, + "description": "Resolved state or region." + }, + "ip_city": { + "type": "string", + "nullable": true, + "description": "Resolved city." + }, + "latitude": { + "type": "number", + "nullable": true, + "description": "Approximate network geolocation, not a precise device location." + }, + "longitude": { + "type": "number", + "nullable": true, + "description": "Approximate network geolocation, not a precise device location." + }, + "isp": { + "type": "string", + "nullable": true, + "description": "Internet service provider serving the address." + }, + "organization": { + "type": "string", + "nullable": true, + "description": "Organization owning the address block." + }, + "is_vpn_or_tor": { + "type": "boolean", + "nullable": true, + "description": "`true` when the address is associated with VPN or Tor infrastructure." + }, + "is_data_center": { + "type": "boolean", + "nullable": true, + "description": "`true` when the address belongs to a hosting or data-center network." + }, + "time_zone": { + "type": "string", + "nullable": true, + "description": "Time zone of the resolved location." + }, + "time_zone_offset": { + "type": "string", + "nullable": true, + "description": "UTC offset of the resolved location." + }, + "asn_number": { + "type": "number", + "nullable": true, + "description": "Autonomous system number." + }, + "asn_organization": { + "type": "string", + "nullable": true, + "description": "Organization operating the autonomous system." + }, + "connection_type": { + "type": "string", + "nullable": true, + "description": "Connection classification (e.g., `cellular`, `hosting`)." + }, + "carrier": { + "type": "string", + "nullable": true, + "description": "Mobile carrier when the address is on a mobile network." + }, + "proxy_type": { + "type": "string", + "nullable": true, + "description": "Proxy classification when the address is behind a proxy." + }, + "claimed_country": { + "type": "string", + "nullable": true, + "description": "Echo of the `claimed_country` you sent." + }, + "country_mismatch": { + "type": "boolean", + "nullable": true, + "description": "`true` when the resolved country differs from `claimed_country`. `null` when no enrichment data or no claimed country is available." + }, + "threat": { + "type": "object", + "additionalProperties": true, + "description": "Free-form network-threat evidence from the enrichment source (e.g., `is_proxy`, `is_tor`). Empty object when no enrichment data is available. Treat as diagnostic detail, not a stable contract." } - ], - "description": "Access one customer request using its request-specific token. No private case findings, original identity data or recipient address are returned. Actionable requests expire after seven days. Cancellation or case resolution invalidates the link. Responses are immutable and remain evidence for reviewer assessment; they never approve or update a source verification." + } }, - "post": { - "operationId": "case_requests_create", - "parameters": [ - { - "in": "path", - "name": "request_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Case Management" + "PhoneRiskResponse": { + "type": "object", + "required": [ + "request_id", + "status", + "created_at" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseCustomerResponseRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseCustomerResponseRequest" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseCustomerResponseResult" - } - } - }, - "description": "" + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Id of the API session created for this request. Store it with `vendor_data` if you need to correlate the result with later webhooks or console activity." + }, + "status": { + "type": "string", + "enum": [ + "Approved", + "In Review", + "Declined" + ], + "description": "Final decision for this API session. Normally `Approved`; the risk evaluation can produce `In Review` or `Declined`." }, - "404": { - "description": "The request or case is unavailable for this caller." + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, `null` when you did not send one." }, - "400": { - "description": "Invalid inputs, unsupported purpose, unavailable verified contact or an existing immutable response." + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` you sent, `null` when you did not send one." }, - "409": { - "description": "The preview changed or expired. Preview again before confirming." - } - }, - "security": [ - { - "CaseRequestToken": [] - } - ], - "description": "Access one customer request using its request-specific token. No private case findings, original identity data or recipient address are returned. Actionable requests expire after seven days. Cancellation or case resolution invalidates the link. Responses are immutable and remain evidence for reviewer assessment; they never approve or update a source verification." - } - }, - "/v3/fraud/checks/plan/": { - "post": { - "operationId": "plan_create", - "description": "Validate selected checks and estimate their price without executing, creating a session or billing.", - "summary": "Plan a Fraud Check", - "tags": [ - "Fraud Check" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/FraudCheckRequestRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudCheckRequestRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/FraudCheckRequestRequest" - } - } + "created_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp of this response." }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudCheckPlan" + "phone": { + "type": "object", + "description": "Normalized phone report, the same block used by Phone Verification responses.", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "In Review", + "Declined" + ], + "description": "Decision for this phone number. Mirrors the top-level `status`." + }, + "phone_number_prefix": { + "type": "string", + "description": "Country calling code with leading `+` (e.g., `+1`).", + "example": "+1" + }, + "phone_number": { + "type": "string", + "description": "National number without the country calling code.", + "example": "4155552671" + }, + "full_number": { + "type": "string", + "description": "Complete number in E.164 format.", + "example": "+14155552671" + }, + "country_code": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country of the number.", + "example": "US" + }, + "country_name": { + "type": "string", + "description": "Full country name derived from `country_code`.", + "example": "United States" + }, + "carrier": { + "type": "object", + "nullable": true, + "description": "Current network serving the number. `null` when the lookup could not resolve a carrier.", + "properties": { + "name": { + "type": "string", + "description": "Carrier name. Empty string when the lookup could not identify it." + }, + "type": { + "type": "string", + "description": "Line type reported by the lookup: `mobile`, `fixed_line`, `voip`, `isp`, `vpn`, `toll_free`, `premium_rate`, or similar." + } } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FraudBadRequestErrorMessage" + }, + "is_disposable": { + "type": "boolean", + "description": "`true` when the number belongs to a temporary or burner number service." + }, + "is_virtual": { + "type": "boolean", + "description": "`true` when the line type is virtual (`voip`, `isp`, or `vpn`)." + }, + "verification_method": { + "type": "string", + "nullable": true, + "description": "OTP channel of the underlying verification. `null` for risk lookups, which never send a code." + }, + "verification_attempts": { + "type": "integer", + "description": "OTP sends for this number. `0` for risk lookups." + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the number was last verified by an OTP. `null` for risk lookups." + }, + "warnings": { + "type": "array", + "description": "Risk warnings recorded for this request. Empty array when there are none.", + "items": { + "$ref": "#/components/schemas/RiskWarning" + } + }, + "lifecycle": { + "type": "array", + "description": "Chronological audit trail of the request. Empty array for risk lookups without send events.", + "items": { + "$ref": "#/components/schemas/RiskLifecycleEvent" } + }, + "matches": { + "type": "array", + "description": "Other sessions of your application where the same phone number was used by a different user (different `vendor_data`). Up to 5 entries. Empty array when there are no matches.", + "items": { + "$ref": "#/components/schemas/PhoneMatchItem" + } + }, + "phone_intelligence": { + "$ref": "#/components/schemas/PhoneIntelligence" + }, + "enrichment": { + "$ref": "#/components/schemas/PhoneIntelligence" + }, + "phone_social": { + "$ref": "#/components/schemas/PhoneSocialFootprint" } - }, - "description": "" - } - }, - "security": [ - { - "ApiKeyAuth": [] - }, - { - "BearerAuth": [] + } } - ] - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/": { - "get": { - "summary": "List routing rules", - "description": "Requires `list:cases` for the requested application.", - "operationId": "list_case_routing_rules", - "tags": [ - "Case Assignment" + } + }, + "EmailRiskResponse": { + "type": "object", + "required": [ + "request_id", + "status", + "created_at" ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Id of the API session created for this request. Store it with `vendor_data` if you need to correlate the result with later webhooks or console activity." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "status": { + "type": "string", + "enum": [ + "Approved", + "In Review", + "Declined" + ], + "description": "Final decision for this API session. Normally `Approved`; the risk evaluation can produce `In Review` or `Declined`." }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 50 - } + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, `null` when you did not send one." }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "integer", - "minimum": 0, - "maximum": 1000000, - "default": 0 - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` you sent, `null` when you did not send one." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp of this response." + }, + "email": { + "type": "object", + "description": "Normalized email report, the same block used by Email Verification responses.", + "required": [ + "status", + "email", + "is_breached", + "breaches", + "is_disposable", + "is_undeliverable" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "In Review", + "Declined" + ], + "description": "Decision for this email address. Mirrors the top-level `status`." + }, + "email": { + "type": "string", + "description": "The scored address.", + "example": "alice@example.com" + }, + "is_breached": { + "type": "boolean", + "description": "Whether the address appears in a known credential breach." + }, + "breaches": { + "type": "array", + "description": "Up to 5 most recent known breaches containing this address. Empty array when `is_breached` is `false`.", + "items": { "type": "object", "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseRoutingRule" - } + "name": { + "type": "string", + "description": "Breach identifier." }, - "count": { + "domain": { + "type": "string", + "description": "Domain of the breached service." + }, + "breach_date": { + "type": "string", + "format": "date", + "description": "Date the breach occurred." + }, + "breach_emails_count": { "type": "integer", - "description": "Matching count, capped at 100. Follow next to continue beyond the cap." + "description": "Total number of accounts exposed in the breach." }, - "next": { + "description": { "type": "string", - "nullable": true + "description": "Human-readable description of the breach (may contain HTML markup)." }, - "previous": { + "logo_path": { "type": "string", - "nullable": true + "description": "URL of the breached service's logo." + }, + "data_classes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Categories of data exposed, in snake_case (e.g., `email_addresses`, `passwords`)." + }, + "is_verified": { + "type": "boolean", + "description": "Whether the breach has been verified as legitimate." } } } - } - } - }, - "400": { - "description": "Invalid input." - }, - "403": { - "description": "Insufficient permission." - } - } - }, - "post": { - "summary": "Create routing rule", - "description": "Requires `write:cases` for the requested application. Set publish=false to create an unpublished draft; Publish creates its first live version. Discarding a never-published draft deletes it.", - "operationId": "create_case_routing_rules", - "tags": [ - "Case Assignment" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." - }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - } - ], - "responses": { - "201": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseRoutingRule" + }, + "is_disposable": { + "type": "boolean", + "description": "Whether the domain belongs to a throwaway provider." + }, + "is_undeliverable": { + "type": "boolean", + "description": "Whether the address was found undeliverable." + }, + "verification_attempts": { + "type": "integer", + "description": "OTP sends for this address. `0` for risk lookups." + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the address was last verified by an OTP. `null` for risk lookups." + }, + "warnings": { + "type": "array", + "description": "Risk warnings recorded for this request. Empty array when there are none.", + "items": { + "$ref": "#/components/schemas/RiskWarning" } + }, + "lifecycle": { + "type": "array", + "description": "Chronological audit trail of the request. Empty array for risk lookups without sends.", + "items": { + "$ref": "#/components/schemas/RiskLifecycleEvent" + } + }, + "matches": { + "type": "array", + "description": "Other sessions of your application where the same email address was used by a different user (different `vendor_data`). Up to 5 entries. Empty array when there are no matches.", + "items": { + "$ref": "#/components/schemas/EmailMatchItem" + } + }, + "email_intelligence": { + "$ref": "#/components/schemas/EmailIntelligence" + }, + "enrichment": { + "$ref": "#/components/schemas/EmailIntelligence" + }, + "email_social": { + "$ref": "#/components/schemas/EmailSocialFootprint" } } + } + } + }, + "IPRiskResponse": { + "type": "object", + "required": [ + "request_id", + "status", + "created_at" + ], + "properties": { + "request_id": { + "type": "string", + "format": "uuid", + "description": "Id of the API session created for this request. Store it with `vendor_data` if you need to correlate the result with later webhooks or console activity." }, - "400": { - "description": "Invalid input." - }, - "403": { - "description": "Insufficient permission." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseRoutingRuleCreate" - } - } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/publish/": { - "post": { - "operationId": "publish_case_routing_rule", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, - { - "in": "path", - "name": "record_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "publish" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraftPreconditionRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraftPreconditionRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraftPreconditionRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationCaseRoutingRule" - } - } - }, - "description": "" + "status": { + "type": "string", + "enum": [ + "Approved", + "In Review" + ], + "description": "Decision for this API session: `In Review` on network or country-mismatch risk, otherwise `Approved`." }, - "403": { - "description": "Case access is required." + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Echo of the `vendor_data` you sent, `null` when you did not send one." }, - "409": { - "description": "The published version or draft revision has changed. Reload before retrying." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/publish/": { - "post": { - "operationId": "publish_case_blueprint", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Echo of the `metadata` you sent, `null` when you did not send one." }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "created_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp of this response." }, - { - "in": "path", - "name": "blueprint_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Case Management" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPolicyDraftPreconditionRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPolicyDraftPreconditionRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPolicyDraftPreconditionRequest" + "ip": { + "type": "object", + "description": "IP risk result. Individual enrichment fields can be `null` when the lookup returned no usable data.", + "required": [ + "status", + "threat" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "Approved", + "In Review" + ], + "description": "`In Review` when the resolved country conflicts with `claimed_country`, or when the address is associated with VPN, Tor, or data-center infrastructure. Otherwise `Approved`." + }, + "ip_address": { + "type": "string", + "nullable": true, + "description": "The scored address. `null` when no enrichment source returned usable data." + }, + "ip_country": { + "type": "string", + "nullable": true, + "description": "Resolved country name." + }, + "ip_country_code": { + "type": "string", + "nullable": true, + "description": "Resolved country code. Compare countries through `country_mismatch` instead of comparing raw country strings yourself." + }, + "ip_state": { + "type": "string", + "nullable": true, + "description": "Resolved state or region." + }, + "ip_city": { + "type": "string", + "nullable": true, + "description": "Resolved city." + }, + "latitude": { + "type": "number", + "nullable": true, + "description": "Approximate network geolocation, not a precise device location." + }, + "longitude": { + "type": "number", + "nullable": true, + "description": "Approximate network geolocation, not a precise device location." + }, + "isp": { + "type": "string", + "nullable": true, + "description": "Internet service provider serving the address." + }, + "organization": { + "type": "string", + "nullable": true, + "description": "Organization owning the address block." + }, + "is_vpn_or_tor": { + "type": "boolean", + "nullable": true, + "description": "`true` when the address is associated with VPN or Tor infrastructure." + }, + "is_data_center": { + "type": "boolean", + "nullable": true, + "description": "`true` when the address belongs to a hosting or data-center network." + }, + "time_zone": { + "type": "string", + "nullable": true, + "description": "Time zone of the resolved location." + }, + "time_zone_offset": { + "type": "string", + "nullable": true, + "description": "UTC offset of the resolved location." + }, + "asn_number": { + "type": "number", + "nullable": true, + "description": "Autonomous system number." + }, + "asn_organization": { + "type": "string", + "nullable": true, + "description": "Organization operating the autonomous system." + }, + "connection_type": { + "type": "string", + "nullable": true, + "description": "Connection classification (e.g., `cellular`, `hosting`)." + }, + "carrier": { + "type": "string", + "nullable": true, + "description": "Mobile carrier when the address is on a mobile network." + }, + "proxy_type": { + "type": "string", + "nullable": true, + "description": "Proxy classification when the address is behind a proxy." + }, + "claimed_country": { + "type": "string", + "nullable": true, + "description": "Echo of the `claimed_country` you sent." + }, + "country_mismatch": { + "type": "boolean", + "nullable": true, + "description": "`true` when the resolved country differs from `claimed_country`. `null` when no enrichment data or no claimed country is available." + }, + "threat": { + "type": "object", + "additionalProperties": true, + "description": "Free-form network-threat evidence from the enrichment source (e.g., `is_proxy`, `is_tor`). Empty object when no enrichment data is available. Treat as diagnostic detail, not a stable contract." } } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CasePublicationCaseBlueprint" - } - } - }, - "description": "" - }, - "409": { - "description": "Published version or saved draft revision changed. Reload before retrying." - }, - "403": { - "description": "Required case permission is missing." - } - }, - "security": [ - { - "ApiKeyAuth": [] } - ] - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/preview/": { - "post": { - "operationId": "organization_application_cases_requests_preview_create", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + } + }, + "SocialFootprintPlatform": { + "type": "object", + "properties": { + "platform": { + "type": "string", + "description": "Platform identifier.", + "example": "whatsapp" }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "category": { + "type": "string", + "description": "Platform category, e.g. MESSAGING, SOCIAL_MEDIA, ECOMMERCE, PROFESSIONAL, EMAIL_PROVIDER.", + "example": "MESSAGING" }, - { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "scope": { + "type": "string", + "enum": [ + "global", + "local" + ], + "description": "`global` for worldwide platforms, `local` for region-specific ones.", + "example": "global" } - ], - "tags": [ - "Case Management" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestPreviewRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestPreviewRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestPreviewRequest" - } - } + } + }, + "SocialFootprintCategorySummary": { + "type": "object", + "properties": { + "registered": { + "type": "integer", + "nullable": true, + "description": "Platforms in this category where registration is confirmed.", + "example": 2 }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestPreviewResponse" - } - } - }, - "description": "" + "ratio": { + "type": "number", + "format": "float", + "nullable": true, + "description": "Share of the category's checked platforms with a registration, 0.0-1.0.", + "example": 0.6667 + } + } + }, + "SocialFootprintProfile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "description": "Profile name, when the platform exposes one." }, - "404": { - "description": "The request or case is unavailable for this caller." + "photo": { + "type": "string", + "nullable": true, + "description": "Profile photo URL, when the platform exposes one." }, - "403": { - "description": "The caller lacks the required cases permission." + "about": { + "type": "string", + "nullable": true, + "description": "Profile about/bio text, when the platform exposes one." }, - "400": { - "description": "Invalid inputs, unsupported purpose, unavailable verified contact or an existing immutable response." + "privacy_status": { + "type": "string", + "nullable": true, + "enum": [ + "PUBLIC", + "PRIVATE", + null + ], + "description": "Profile visibility, when the platform exposes it." }, - "409": { - "description": "The preview changed or expired. Preview again before confirming." - } - }, - "security": [ - { - "ApiKeyAuth": [] + "business_account": { + "type": "boolean", + "nullable": true, + "description": "Whether the profile is a business account, when the platform exposes it." } - ], - "description": "Render the production email without sending or changing the case. Returns the exact recipient, sender, subject, body and a signed preview_token. Send the same contact_id, purpose, language, request_id, expires_at and preview_token to confirm within 15 minutes." - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/action-receipts/": { - "get": { - "operationId": "action_receipts_retrieve", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, - { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + } + }, + "PhoneSocialFootprint": { + "type": "object", + "nullable": true, + "description": "Social footprint of the phone number: the online platforms it is registered on. Present only when `enable_social` was set and the check completed. Every count may be null when no data was returned for it. `profiles_registered: 0` means the platforms were checked and the phone number was found on none of them - a valid, billable answer and a common synthetic-identity signal.", + "properties": { + "profiles_registered": { + "type": "integer", + "nullable": true, + "description": "Platforms where the phone number is confirmed registered.", + "example": 5 }, - { - "in": "query", - "name": "offset", - "schema": { - "type": "integer", - "maximum": 1000000, - "minimum": 0, - "default": 0 - } - } - ], - "tags": [ - "Case Management" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseActionCaseActionReceiptPage" - } - } - }, - "description": "" - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "List subject-action receipts, newest first, with at most 20 results per page. Use next_offset for the next page. Input note text is not exposed in the history." - } - }, - "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/notes/{note_uuid}/": { - "delete": { - "operationId": "delete_network_note", - "tags": [ - "Networks" - ], - "summary": "Delete your network note", - "description": "Requires write:networks and the authenticated author of the note. Deletion is irreversible. Connected account and verification data are unaffected.", - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "profiles_checked": { + "type": "integer", + "nullable": true, + "description": "Platforms evaluated for registration.", + "example": 14 }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "names_found": { + "type": "integer", + "nullable": true, + "description": "Platforms that returned an associated name.", + "example": 0 }, - { - "name": "network_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Network UUID." + "photos_found": { + "type": "integer", + "nullable": true, + "description": "Platforms that returned a profile photo.", + "example": 0 }, - { - "name": "note_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } + "communication_breadth": { + "type": "integer", + "nullable": true, + "description": "Communication platforms with detected presence.", + "example": 2 }, - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - }, - "description": "Console user access token in the form Bearer ." - } - ], - "responses": { - "204": { - "description": "Note deleted." + "digital_footprint_diversity": { + "type": "integer", + "nullable": true, + "description": "Distinct platform categories with detected presence.", + "example": 5 }, - "401": { - "description": "Authentication is required." + "digital_sophistication_index": { + "type": "integer", + "nullable": true, + "description": "Composite digital-maturity indicator, 0-10.", + "example": 5 }, - "403": { - "description": "Missing permission or Networks access." + "engagement_depth_index": { + "type": "integer", + "nullable": true, + "description": "Profile-richness indicator, 0-10.", + "example": 3 }, - "404": { - "description": "Network or note is not visible to this application." - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/action-receipts/{receipt_uuid}/retry/": { - "post": { - "operationId": "action_receipts_retry_create", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "subscription_affordability_index": { + "type": "integer", + "nullable": true, + "description": "Free vs paid platform mix, 0-10.", + "example": 2 }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "free_platforms": { + "type": "integer", + "nullable": true, + "description": "Registrations on free platforms.", + "example": 4 }, - { - "in": "path", - "name": "case_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "full_paid_platforms": { + "type": "integer", + "nullable": true, + "description": "Registrations on fully paid platforms.", + "example": 0 }, - { - "in": "path", - "name": "receipt_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Case Management" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseActionCaseActionReceipt" - } - } - }, - "description": "" - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Retry only this failed subject action using its saved inputs. Current write permission is checked. Applied actions return their receipt without executing again. Case closure is independent." - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/draft/": { - "get": { - "operationId": "get_case_blueprint_draft", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "partial_paid_platforms": { + "type": "integer", + "nullable": true, + "description": "Registrations on freemium platforms.", + "example": 1 }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "whatsapp_profile_completeness": { + "type": "string", + "nullable": true, + "enum": [ + "Basic", + "Partial", + "Complete", + "Not Present", + null + ], + "description": "Completeness of the WhatsApp profile. Phone only; null for email.", + "example": "Partial" }, - { - "in": "path", - "name": "blueprint_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Case Management" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPolicyDraftResponse" - } - } - }, - "description": "" + "registered_platforms": { + "type": "array", + "description": "Platforms where registration is confirmed.", + "items": { + "$ref": "#/components/schemas/SocialFootprintPlatform" + } }, - "403": { - "description": "Required case permission is missing." - } - }, - "security": [ - { - "ApiKeyAuth": [] + "not_registered_platforms": { + "type": "array", + "description": "Platforms checked with a negative result.", + "items": { + "$ref": "#/components/schemas/SocialFootprintPlatform" + } + }, + "categories": { + "type": "object", + "description": "Registered count and ratio per platform category.", + "additionalProperties": { + "$ref": "#/components/schemas/SocialFootprintCategorySummary" + } + }, + "profiles": { + "type": "object", + "description": "Per-platform profile details, keyed by platform, when the platform exposes them.", + "additionalProperties": { + "$ref": "#/components/schemas/SocialFootprintProfile" + } } - ] + } }, - "put": { - "operationId": "save_case_blueprint_draft", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "EmailSocialFootprint": { + "type": "object", + "nullable": true, + "description": "Social footprint of the email address: the online platforms it is registered on. Present only when `enable_social` was set and the check completed. Every count may be null when no data was returned for it. `profiles_registered: 0` means the platforms were checked and the email address was found on none of them - a valid, billable answer and a common synthetic-identity signal.", + "properties": { + "profiles_registered": { + "type": "integer", + "nullable": true, + "description": "Platforms where the email address is confirmed registered.", + "example": 5 }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "profiles_checked": { + "type": "integer", + "nullable": true, + "description": "Platforms evaluated for registration.", + "example": 14 }, - { - "in": "path", - "name": "blueprint_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Case Management" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPolicyDraftWriteRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPolicyDraftWriteRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPolicyDraftWriteRequest" - } - } + "names_found": { + "type": "integer", + "nullable": true, + "description": "Platforms that returned an associated name.", + "example": 0 }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPolicyDraft" - } - } - }, - "description": "" + "photos_found": { + "type": "integer", + "nullable": true, + "description": "Platforms that returned a profile photo.", + "example": 0 }, - "409": { - "description": "Published version or saved draft revision changed. Reload before retrying." + "communication_breadth": { + "type": "integer", + "nullable": true, + "description": "Communication platforms with detected presence.", + "example": 2 }, - "403": { - "description": "Required case permission is missing." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ] - }, - "delete": { - "operationId": "discard_case_blueprint_draft", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "digital_footprint_diversity": { + "type": "integer", + "nullable": true, + "description": "Distinct platform categories with detected presence.", + "example": 5 }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "digital_sophistication_index": { + "type": "integer", + "nullable": true, + "description": "Composite digital-maturity indicator, 0-10.", + "example": 5 }, - { - "in": "path", - "name": "blueprint_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Case Management" - ], - "responses": { - "204": { - "description": "No response body" + "engagement_depth_index": { + "type": "integer", + "nullable": true, + "description": "Profile-richness indicator, 0-10.", + "example": 3 }, - "409": { - "description": "Published version or saved draft revision changed. Reload before retrying." + "subscription_affordability_index": { + "type": "integer", + "nullable": true, + "description": "Free vs paid platform mix, 0-10.", + "example": 2 }, - "403": { - "description": "Required case permission is missing." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPolicyDraftPreconditionRequest" - } - } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/teams/{record_uuid}/": { - "get": { - "summary": "Retrieve review team", - "description": "Requires `read:cases` for the requested application.", - "operationId": "retrieve_case_teams", - "tags": [ - "Case Assignment" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "free_platforms": { + "type": "integer", + "nullable": true, + "description": "Registrations on free platforms.", + "example": 4 }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "full_paid_platforms": { + "type": "integer", + "nullable": true, + "description": "Registrations on fully paid platforms.", + "example": 0 }, - { - "name": "record_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseReviewTeam" - } - } + "partial_paid_platforms": { + "type": "integer", + "nullable": true, + "description": "Registrations on freemium platforms.", + "example": 1 + }, + "whatsapp_profile_completeness": { + "type": "string", + "nullable": true, + "enum": [ + "Basic", + "Partial", + "Complete", + "Not Present", + null + ], + "description": "Completeness of the WhatsApp profile. Phone only; null for email.", + "example": "Partial" + }, + "registered_platforms": { + "type": "array", + "description": "Platforms where registration is confirmed.", + "items": { + "$ref": "#/components/schemas/SocialFootprintPlatform" } }, - "400": { - "description": "Invalid input." + "not_registered_platforms": { + "type": "array", + "description": "Platforms checked with a negative result.", + "items": { + "$ref": "#/components/schemas/SocialFootprintPlatform" + } }, - "403": { - "description": "Insufficient permission." + "categories": { + "type": "object", + "description": "Registered count and ratio per platform category.", + "additionalProperties": { + "$ref": "#/components/schemas/SocialFootprintCategorySummary" + } }, - "404": { - "description": "Record not found in this application." + "profiles": { + "type": "object", + "description": "Per-platform profile details, keyed by platform, when the platform exposes them.", + "additionalProperties": { + "$ref": "#/components/schemas/SocialFootprintProfile" + } } } }, - "patch": { - "summary": "Update review team", - "description": "Requires `write:cases` for the requested application.", - "operationId": "update_case_teams", - "tags": [ - "Case Assignment" - ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "KYBRegistryAddress": { + "type": "object", + "description": "An address the registry states, in the same shape for every country and registry source. Every key is always present; a part the registry does not publish is null.", + "properties": { + "type": { + "type": "string", + "nullable": true, + "description": "Address type in lower case: `registered` for the registered office; other types as the registry names them (for example `postal`, `office`). `null` when the registry does not say what the address is." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "address": { + "type": "string", + "description": "The full address as one line. Always present on every address." }, - { - "name": "record_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseReviewTeam" - } - } - } + "street": { + "type": "string", + "nullable": true, + "description": "Street and number." }, - "400": { - "description": "Invalid input." + "city": { + "type": "string", + "nullable": true, + "description": "City or locality." }, - "403": { - "description": "Insufficient permission." + "postal_code": { + "type": "string", + "nullable": true, + "description": "Postal code." }, - "404": { - "description": "Record not found in this application." + "region": { + "type": "string", + "nullable": true, + "description": "Region, state or province." + }, + "country_code": { + "type": "string", + "nullable": true, + "description": "ISO 3166-1 alpha-2 country code." + }, + "description": { + "type": "string", + "nullable": true, + "description": "The registry's own label for the address, when it gives one." } }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseReviewTeamUpdate" - } - } + "required": [ + "type", + "address", + "street", + "city", + "postal_code", + "region", + "country_code", + "description" + ] + }, + "KYBRegistryIndustry": { + "type": "object", + "description": "An activity the registry classifies the company under, in the same shape for every country and registry source.", + "properties": { + "code": { + "type": "string", + "nullable": true, + "description": "Activity code as the registry prints it (for example `73110`)." + }, + "description": { + "type": "string", + "nullable": true, + "description": "Activity label, when the registry publishes one." + }, + "scheme": { + "type": "string", + "nullable": true, + "description": "Classification system, one name per system whichever source reported it: `UK SIC 2007`, `US SIC`, `NACE Rev. 2`, `NACE Rev. 2.1`, `NAF Rev. 2`, `CNAE`, `SBI`, `NOGA 2008`, `CAE Rev. 4`, `Canada SIC 1977`; other national systems keep the registry's own name. Null when the registry does not name the system." + }, + "is_primary": { + "type": "boolean", + "description": "True for the company's main activity. Exactly one entry is primary when the list is not empty." } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/report/": { - "get": { - "operationId": "download_network_report", - "tags": [ - "Networks" - ], - "summary": "Download a network evidence report", - "description": "Download a PDF of the current organization network, its observed account connections, signal references and review status. Requires read:networks. Global intelligence and private analyst notes are not included. Each account and signal list is capped at 500 entries and the report states when it is incomplete. The request does not change network status. Errors are returned as JSON.", - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + }, + "required": [ + "code", + "description", + "scheme", + "is_primary" + ] + }, + "KYBRegistryShareholding": { + "type": "object", + "nullable": true, + "description": "What an owner holds, in the same shape for every country and registry source. Null when the registry states no holding. A band (for example a UK PSC's 25-50%) is not a stake: `percentage` is null and the band stays in `ownership_min_shares` / `ownership_max_shares`.", + "properties": { + "share_class": { + "type": "string", + "nullable": true, + "description": "Share class or type as the registry names it." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "number_of_shares": { + "type": "number", + "nullable": true, + "description": "Number of shares held." }, - { - "name": "network_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Network UUID." + "currency": { + "type": "string", + "nullable": true, + "description": "ISO 4217 currency of the values below." }, - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - }, - "description": "Console user access token in the form Bearer ." + "nominal_value": { + "type": "number", + "nullable": true, + "description": "Nominal value per share." + }, + "total_value": { + "type": "number", + "nullable": true, + "description": "Total value of the holding." + }, + "percentage": { + "type": "number", + "nullable": true, + "description": "Exact stake in percent, to four decimal places." } - ], - "responses": { - "200": { - "description": "Network evidence report. Cache-Control: no-store.", - "content": { - "application/pdf": { - "schema": { + }, + "required": [ + "share_class", + "number_of_shares", + "currency", + "nominal_value", + "total_value", + "percentage" + ] + }, + "KYBRegistryOwnership": { + "type": "object", + "description": "The outcome of the ownership (Shareholders / UBO) tier, in the same shape for every country and registry source.", + "properties": { + "status": { + "type": "string", + "nullable": true, + "enum": [ + "not_requested", + "pending", + "delivered", + "unavailable", + null + ], + "description": "`delivered` when the registry published ownership; `unavailable` when it did not (see `unavailable_reason`); `pending` while the registry is still fetching it; `not_requested` for the Basic tier." + }, + "tier_requested": { + "type": "string", + "nullable": true, + "enum": [ + "basic", + "shareholders", + "ubo", + null + ], + "description": "The tier the workflow ordered." + }, + "tier_delivered": { + "type": "string", + "nullable": true, + "enum": [ + "basic", + "shareholders", + "ubo", + null + ], + "description": "The tier the registry delivered (and that is billed). A UBO request answered with direct shareholders only is `shareholders`." + }, + "unavailable_reason": { + "type": "string", + "nullable": true, + "enum": [ + "no_owners_on_record", + "no_natural_person_owner", + "ownership_section_unavailable", + "company_not_found", + "ambiguous_company_match", + "missing_registration_number", + "entity_type_not_covered", + "provider_unavailable", + "other", + null + ], + "description": "Why a requested ownership tier was not delivered. Null unless `status` is `unavailable` or `pending`." + }, + "direct_ownership_complete": { + "type": "boolean", + "nullable": true, + "description": "True when the direct shareholders' stated stakes add up to 100% (within one percentage point of rounding); false when they do not; null when ownership was not delivered or a stake is not stated." + }, + "unresolved": { + "type": "array", + "description": "Parts of the ownership chain the registry could not resolve, for example no group structure on record above the direct owners.", + "items": { + "type": "object", + "properties": { + "reason": { + "type": "string" + }, + "section": { "type": "string", - "format": "binary" + "nullable": true } } } + } + }, + "required": [ + "status", + "tier_requested", + "tier_delivered", + "unavailable_reason", + "direct_ownership_complete", + "unresolved" + ] + }, + "DigitalIdWalletApiWalletVerificationError": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Stable machine-readable code, e.g. wallet_unavailable." }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "Missing permission or Networks access." + "detail": { + "type": "string" + } + }, + "required": [ + "detail", + "error" + ] + }, + "DigitalIdWalletApiWalletVerificationStartForbidden": { + "oneOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" }, - "404": { - "description": "Network or note is not visible to this application." + { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + } + ] + }, + "DigitalIdWalletApiClientCredentialsForbiddenErrorMessage": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "default": "Forbidden - User doesn't have permission" } } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/notes/": { - "get": { - "operationId": "list_network_notes", - "tags": [ - "Networks" - ], - "summary": "List private network notes", - "description": "Console users with read:networks can read notes on a network visible to the application. Notes stay within the organization and are not contributed to shared intelligence. Newest notes appear first.", - "parameters": [ + }, + "DigitalIdWalletApiWalletVerificationStartBadRequest": { + "oneOf": [ { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" }, { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationFieldErrors" + } + ] + }, + "DigitalIdWalletApiWalletVerificationFieldErrors": { + "type": "object", + "description": "Request validation errors: each rejected field with its messages.", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "DigitalIdWalletApiWalletVerificationResponse": { + "type": "object", + "description": "One standalone wallet verification, as every wallet endpoint answers it.", + "properties": { + "request_id": { + "type": "string", + "format": "uuid" }, - { - "name": "network_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Network UUID." + "wallet_verification": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResult" }, - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - }, - "description": "Console user access token in the form Bearer ." + "vendor_data": { + "type": "string", + "nullable": true }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 30 - } + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "The metadata object sent at start, or null." }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "integer", - "minimum": 0, - "maximum": 1000000, - "default": 0 - } + "created_at": { + "type": "string", + "format": "date-time" } - ], - "responses": { - "200": { - "description": "A page of notes. Count is capped at 100; use next to continue.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { - "type": "string", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NetworkNote" - } - } - } - } + }, + "required": [ + "created_at", + "metadata", + "request_id", + "vendor_data", + "wallet_verification" + ] + }, + "DigitalIdWalletApiWalletVerificationResult": { + "type": "object", + "properties": { + "wallet": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiWallet" } - } - }, - "401": { - "description": "Authentication is required." + ], + "readOnly": true }, - "403": { - "description": "Missing permission or Networks access." + "country": { + "type": "string" }, - "404": { - "description": "Network or note is not visible to this application." - } - } - }, - "post": { - "operationId": "create_network_note", - "tags": [ - "Networks" - ], - "summary": "Add a private network note", - "description": "Requires a console user with write:networks. The author is assigned from the authenticated user. Supply a new random UUID client_reference for each note; retry the same body and reference after an uncertain response to avoid duplicates. Reusing a reference for another body, author or network returns 409. Notes follow the surviving investigation when networks merge.", - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "status": { + "type": "string", + "description": "Not Finished while pending, then Approved (verified) or Declined." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." + "outcome": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResultOutcomeEnum" }, - { - "name": "network_uuid", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Network UUID." + "error": { + "type": "string", + "nullable": true, + "readOnly": true }, - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - }, - "description": "Console user access token in the form Bearer ." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "comment", - "client_reference" - ], - "properties": { - "comment": { - "type": "string", - "minLength": 1, - "maxLength": 10000 - }, - "client_reference": { - "type": "string", - "format": "uuid" - } - } + "interaction": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiInteractionEnum" } - } - } - }, - "responses": { - "201": { - "description": "Saved private note.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NetworkNote" - } + ], + "readOnly": true + }, + "simulated": { + "type": "boolean", + "readOnly": true + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "next_action": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletNextAction" } - } + ], + "nullable": true, + "readOnly": true }, - "200": { - "description": "Saved private note.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NetworkNote" - } + "identity": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletIdentity" } - } + ], + "nullable": true, + "readOnly": true }, - "400": { - "description": "Invalid or blank note." + "attributes": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "readOnly": true }, - "409": { - "description": "The reference was already used for another note." + "level_of_assurance": { + "type": "string", + "nullable": true }, - "401": { - "description": "Authentication is required." + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "403": { - "description": "Missing permission or Networks access." + "signature_valid": { + "type": "boolean", + "nullable": true, + "description": "True once the credential's signature was verified; null for a simulated one." }, - "404": { - "description": "Network or note is not visible to this application." + "credential_type": { + "type": "string", + "nullable": true } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/{record_uuid}/draft/": { - "get": { - "operationId": "get_case_routing_rule_draft", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + }, + "required": [ + "attributes", + "country", + "credential_type", + "error", + "expires_at", + "identity", + "interaction", + "level_of_assurance", + "next_action", + "outcome", + "signature_valid", + "simulated", + "status", + "verified_at", + "wallet" + ] + }, + "DigitalIdWalletApiWalletIdentity": { + "type": "object", + "properties": { + "full_name": { + "type": "string", + "nullable": true }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "first_name": { + "type": "string", + "nullable": true }, - { - "in": "path", - "name": "record_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "last_name": { + "type": "string", + "nullable": true + }, + "date_of_birth": { + "type": "string", + "format": "date", + "nullable": true + }, + "nationality": { + "type": "string", + "nullable": true, + "description": "ISO3 nationality, only when the wallet asserts one (never inferred)." + }, + "issuing_state": { + "type": "string", + "description": "ISO3 country whose scheme issued the identity." } - ], - "tags": [ - "draft" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraftResponse" - } + }, + "required": [ + "date_of_birth", + "first_name", + "full_name", + "issuing_state", + "last_name", + "nationality" + ] + }, + "DigitalIdWalletApiWalletNextAction": { + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletNextActionTypeEnum" } - }, - "description": "" + ], + "description": "redirect: send the user's browser to url. confirm_in_app: show verification_code to the user, who checks it matches the code in their wallet app and confirms there; then poll.\n\n* `redirect` - redirect\n* `confirm_in_app` - confirm_in_app" }, - "403": { - "description": "Case access is required." + "url": { + "type": "string", + "format": "uri", + "description": "redirect only: where the user signs in with the wallet." + }, + "verification_code": { + "type": "string", + "description": "confirm_in_app only: the 4-digit comparison code the wallet app shows too." + }, + "poll_interval_seconds": { + "type": "integer", + "description": "confirm_in_app only: how often to call POST .../verifications/{request_id}/poll/ for prompt feedback. Didit also polls pending sign-ins in the background, so the webhook reports the outcome even when you do not poll." } }, - "security": [ - { - "ApiKeyAuth": [] + "required": [ + "type" + ] + }, + "DigitalIdWalletApiWalletNextActionTypeEnum": { + "enum": [ + "redirect", + "confirm_in_app" + ], + "type": "string", + "description": "* `redirect` - redirect\n* `confirm_in_app` - confirm_in_app" + }, + "DigitalIdWalletApiInteractionEnum": { + "enum": [ + "redirect", + "in_app" + ], + "type": "string", + "description": "* `redirect` - redirect\n* `in_app` - in_app" + }, + "DigitalIdWalletApiWalletVerificationResultOutcomeEnum": { + "enum": [ + "pending", + "verified", + "cancelled", + "timeout", + "failed" + ], + "type": "string", + "description": "* `pending` - Pending\n* `verified` - Verified\n* `cancelled` - Cancelled by the user\n* `timeout` - Timed out\n* `failed` - Failed" + }, + "DigitalIdWalletApiWallet": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Catalog id of the wallet, e.g. bankid_se." + }, + "name": { + "type": "string" + }, + "issuing_authority": { + "type": "string", + "nullable": true } + }, + "required": [ + "id", + "issuing_authority", + "name" ] }, - "put": { - "operationId": "save_case_routing_rule_draft", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "DigitalIdWalletApiEudiWalletVerificationStartRequest": { + "type": "object", + "description": "Start a EUDI Wallet verification (AUT, BEL, BGR, HRV, CYP, CZE, DNK, EST, FIN, FRA, DEU, GRC, HUN, IRL, ITA, LVA, LTU, LUX, MLT, NLD, POL, PRT, ROU, SVK, SVN, ESP, SWE, ISL, LIE, NOR).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - { - "in": "path", - "name": "record_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + }, + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiEudiWalletVerificationStartCountryEnum" + } + ], + "description": "ISO 3166-1 alpha-3 country of the EUDI Wallet identity.\n\n* `AUT` - AUT\n* `BEL` - BEL\n* `BGR` - BGR\n* `HRV` - HRV\n* `CYP` - CYP\n* `CZE` - CZE\n* `DNK` - DNK\n* `EST` - EST\n* `FIN` - FIN\n* `FRA` - FRA\n* `DEU` - DEU\n* `GRC` - GRC\n* `HUN` - HUN\n* `IRL` - IRL\n* `ITA` - ITA\n* `LVA` - LVA\n* `LTU` - LTU\n* `LUX` - LUX\n* `MLT` - MLT\n* `NLD` - NLD\n* `POL` - POL\n* `PRT` - PRT\n* `ROU` - ROU\n* `SVK` - SVK\n* `SVN` - SVN\n* `ESP` - ESP\n* `SWE` - SWE\n* `ISL` - ISL\n* `LIE` - LIE\n* `NOR` - NOR" + }, + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } + }, + "required": [ + "country", + "return_url" + ] + }, + "DigitalIdWalletApiDeviceEnum": { + "enum": [ + "same_device", + "cross_device" ], - "tags": [ - "draft" + "type": "string", + "description": "* `same_device` - same_device\n* `cross_device` - cross_device" + }, + "DigitalIdWalletApiEudiWalletVerificationStartCountryEnum": { + "enum": [ + "AUT", + "BEL", + "BGR", + "HRV", + "CYP", + "CZE", + "DNK", + "EST", + "FIN", + "FRA", + "DEU", + "GRC", + "HUN", + "IRL", + "ITA", + "LVA", + "LTU", + "LUX", + "MLT", + "NLD", + "POL", + "PRT", + "ROU", + "SVK", + "SVN", + "ESP", + "SWE", + "ISL", + "LIE", + "NOR" ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraftWriteRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraftWriteRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraftWriteRequest" - } - } + "type": "string", + "description": "* `AUT` - AUT\n* `BEL` - BEL\n* `BGR` - BGR\n* `HRV` - HRV\n* `CYP` - CYP\n* `CZE` - CZE\n* `DNK` - DNK\n* `EST` - EST\n* `FIN` - FIN\n* `FRA` - FRA\n* `DEU` - DEU\n* `GRC` - GRC\n* `HUN` - HUN\n* `IRL` - IRL\n* `ITA` - ITA\n* `LVA` - LVA\n* `LTU` - LTU\n* `LUX` - LUX\n* `MLT` - MLT\n* `NLD` - NLD\n* `POL` - POL\n* `PRT` - PRT\n* `ROU` - ROU\n* `SVK` - SVK\n* `SVN` - SVN\n* `ESP` - ESP\n* `SWE` - SWE\n* `ISL` - ISL\n* `LIE` - LIE\n* `NOR` - NOR" + }, + "DigitalIdWalletApiConnectidWalletVerificationStartRequest": { + "type": "object", + "description": "Start a ConnectID verification (AUS).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraft" - } + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." + }, + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + }, + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiConnectidWalletVerificationStartCountryEnum" } - }, - "description": "" + ], + "default": "AUS", + "description": "ISO 3166-1 alpha-3 country of the ConnectID identity. Defaults to AUS, the only country it serves.\n\n* `AUS` - AUS" }, - "403": { - "description": "Case access is required." + "authorisation_server_id": { + "type": "string", + "minLength": 1, + "description": "Authorisation server ID selected with ConnectID's bank selector. Sandbox defaults to Mock Bank A.", + "maxLength": 36 }, - "409": { - "description": "The published version or draft revision has changed. Reload before retrying." + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } }, - "security": [ - { - "ApiKeyAuth": [] - } + "required": [ + "return_url" ] }, - "delete": { - "operationId": "discard_case_routing_rule_draft", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, - { - "in": "path", - "name": "record_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "draft" + "DigitalIdWalletApiConnectidWalletVerificationStartCountryEnum": { + "enum": [ + "AUS" ], - "responses": { - "204": { - "description": "No response body" + "type": "string", + "description": "* `AUS` - AUS" + }, + "DigitalIdWalletApiAudkenniWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Auðkenni verification (ISL).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "403": { - "description": "Case access is required." + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "409": { - "description": "The published version or draft revision has changed. Reload before retrying." - } - }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraftPreconditionRequest" - } - } - } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/duplicates/": { - "post": { - "operationId": "duplicates_create", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Case Management" - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CaseDuplicateCaseDuplicateQueryRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseDuplicateCaseDuplicateQueryRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CaseDuplicateCaseDuplicateQueryRequest" + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiAudkenniWalletVerificationStartCountryEnum" } - } - } - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseDuplicateCaseDuplicatePage" - } + ], + "default": "ISL", + "description": "ISO 3166-1 alpha-3 country of the Auðkenni identity. Defaults to ISL, the only country it serves.\n\n* `ISL` - ISL" + }, + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" } - }, - "description": "" + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } }, - "security": [ - { - "ApiKeyAuth": [] - } - ], - "description": "Review existing cases for a subject or owned source record before manual case creation. Both open and resolved investigations are returned. Each page contains at most 20 cases; use next_offset to request another page." - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/routing-rules/preview/": { - "post": { - "summary": "Test case routing", - "description": "Requires `read:cases` for the requested application. Evaluates current rules or a supplied draft against current reviewer access, availability, skills and capacity. It does not save the draft or reserve reviewer capacity.", - "operationId": "preview_case_routing", - "tags": [ - "Case Assignment" + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiAudkenniWalletVerificationStartCountryEnum": { + "enum": [ + "ISL" ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "type": "string", + "description": "* `ISL` - ISL" + }, + "DigitalIdWalletApiFranceconnectWalletVerificationStartRequest": { + "type": "object", + "description": "Start a FranceConnect verification (FRA).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseRoutingPreview" - } + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." + }, + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + }, + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiFranceconnectWalletVerificationStartCountryEnum" } - } + ], + "default": "FRA", + "description": "ISO 3166-1 alpha-3 country of the FranceConnect identity. Defaults to FRA, the only country it serves.\n\n* `FRA` - FRA" }, - "400": { - "description": "Invalid input." + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "403": { - "description": "Insufficient permission." - } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CaseRoutingPreviewRequest" + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" } - } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/reviewers/": { - "get": { - "summary": "List eligible case reviewers", - "description": "Requires `list:cases` for the requested application.", - "operationId": "list_case_reviewers", - "tags": [ - "Case Assignment" + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiFranceconnectWalletVerificationStartCountryEnum": { + "enum": [ + "FRA" ], - "parameters": [ - { - "name": "organization_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Organization UUID." + "type": "string", + "description": "* `FRA` - FRA" + }, + "DigitalIdWalletApiDiiaWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Diia verification (UKR).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - }, - "description": "Application UUID." - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "results": { - "type": "array", - "items": { - "type": "object", - "properties": { - "user_id": { - "type": "string", - "format": "uuid" - }, - "email": { - "type": "string", - "format": "email" - }, - "name": { - "type": "string" - } - } - } - } - } - } - } - } + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "400": { - "description": "Invalid input." + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "403": { - "description": "Insufficient permission." + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDiiaWalletVerificationStartCountryEnum" + } + ], + "default": "UKR", + "description": "ISO 3166-1 alpha-3 country of the Diia identity. Defaults to UKR, the only country it serves.\n\n* `UKR` - UKR" }, - "503": { - "description": "Reviewer access could not be verified. Retry later." + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } - } - } - }, - "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/history/": { - "get": { - "operationId": "list_case_blueprint_history", - "parameters": [ - { - "in": "path", - "name": "organization_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiDiiaWalletVerificationStartCountryEnum": { + "enum": [ + "UKR" + ], + "type": "string", + "description": "* `UKR` - UKR" + }, + "DigitalIdWalletApiMojeidWalletVerificationStartRequest": { + "type": "object", + "description": "Start a MojeID verification (CZE).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - { - "in": "path", - "name": "application_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - { - "in": "path", - "name": "blueprint_uuid", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - { - "name": "ordering", - "required": false, - "in": "query", - "description": "Which field to use when ordering the results.", - "schema": { - "type": "string" - } + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiCountryE2dEnum" + } + ], + "default": "CZE", + "description": "ISO 3166-1 alpha-3 country of the MojeID identity. Defaults to CZE, the only country it serves.\n\n* `CZE` - CZE" }, - { - "name": "limit", - "required": false, - "in": "query", - "description": "Number of results to return per page.", - "schema": { - "type": "integer" - } + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - { - "name": "offset", - "required": false, - "in": "query", - "description": "The initial index from which to return the results.", - "schema": { - "type": "integer" - } + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiCountryE2dEnum": { + "enum": [ + "CZE" ], - "tags": [ - "Case Management" - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CasePublicationPaginatedPolicyRevisionList" - } + "type": "string", + "description": "* `CZE` - CZE" + }, + "DigitalIdWalletApiBankidCzWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Bank iD verification (CZE).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + }, + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." + }, + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + }, + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiCountryE2dEnum" } - }, - "description": "" + ], + "default": "CZE", + "description": "ISO 3166-1 alpha-3 country of the Bank iD identity. Defaults to CZE, the only country it serves.\n\n* `CZE` - CZE" }, - "403": { - "description": "Required case permission is missing." + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } }, - "security": [ - { - "ApiKeyAuth": [] - } + "required": [ + "return_url" ] - } - } - }, - "components": { - "schemas": { - "DocumentAIDetectedCode": { + }, + "DigitalIdWalletApiEparakstsMobileWalletVerificationStartRequest": { "type": "object", - "required": [ - "type", - "page_number", - "position", - "x", - "y", - "width", - "height", - "page_width", - "page_height", - "raw_payload", - "parsed_payload" - ], + "description": "Start a eParaksts mobile verification (LVA).", "properties": { - "type": { + "vendor_data": { "type": "string", - "description": "Detected symbology, for example `QR_CODE`, `CODE_128`, `EAN_13`, `DATA_MATRIX`, or `PDF_417`." - }, - "page_number": { - "type": "integer", - "minimum": 1, - "description": "1-based page number." + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "position": { - "type": "array", + "metadata": { + "type": "object", + "additionalProperties": {}, "nullable": true, - "description": "Detected polygon in rendered-page pixel coordinates. `null` when no reliable location is available.", - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": { - "type": "integer" + "description": "Optional JSON object stored with the verification and returned in results and webhooks." + }, + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + }, + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiEparakstsMobileWalletVerificationStartCountryEnum" } - } + ], + "default": "LVA", + "description": "ISO 3166-1 alpha-3 country of the eParaksts mobile identity. Defaults to LVA, the only country it serves.\n\n* `LVA` - LVA" }, - "x": { - "type": "integer", + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiEparakstsMobileWalletVerificationStartCountryEnum": { + "enum": [ + "LVA" + ], + "type": "string", + "description": "* `LVA` - LVA" + }, + "DigitalIdWalletApiIdcardEeWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Estonian ID-card verification (EST).", + "properties": { + "vendor_data": { + "type": "string", "nullable": true, - "description": "Left edge of the bounding box in rendered-page pixels." + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "y": { - "type": "integer", + "metadata": { + "type": "object", + "additionalProperties": {}, "nullable": true, - "description": "Top edge of the bounding box in rendered-page pixels." + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "width": { - "type": "integer", + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + }, + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiIdcardEeWalletVerificationStartCountryEnum" + } + ], + "default": "EST", + "description": "ISO 3166-1 alpha-3 country of the Estonian ID-card identity. Defaults to EST, the only country it serves.\n\n* `EST` - EST" + }, + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiIdcardEeWalletVerificationStartCountryEnum": { + "enum": [ + "EST" + ], + "type": "string", + "description": "* `EST` - EST" + }, + "DigitalIdWalletApiMobileIdWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Mobile-ID verification (EST, LTU).", + "properties": { + "vendor_data": { + "type": "string", "nullable": true, - "description": "Bounding-box width in rendered-page pixels." + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "height": { - "type": "integer", + "metadata": { + "type": "object", + "additionalProperties": {}, "nullable": true, - "description": "Bounding-box height in rendered-page pixels." + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "page_width": { - "type": "integer", - "description": "Rendered page width in pixels." + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "page_height": { - "type": "integer", - "description": "Rendered page height in pixels." + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiMobileIdWalletVerificationStartCountryEnum" + } + ], + "description": "ISO 3166-1 alpha-3 country of the Mobile-ID identity.\n\n* `EST` - EST\n* `LTU` - LTU" }, - "raw_payload": { + "personal_code": { "type": "string", - "description": "Raw decoder payload as a base64 string when available; otherwise an empty string." + "minLength": 1, + "description": "The user's national personal identification code. It addresses the user's Mobile-ID app; the user confirms the comparison code and enters their PIN in the app. Never collect the PIN.", + "pattern": "^[0-9-]{6,20}$" }, - "parsed_payload": { - "nullable": true, - "description": "Decoded string or structured value. `null` when the code was located but could not be decoded." + "phone_number": { + "type": "string", + "minLength": 1, + "description": "The Mobile-ID phone number in E.164 format (+372... or +370...).", + "maxLength": 16 } - } - }, - "SessionImportJob": { - "type": "object", + }, "required": [ - "uuid", - "status", - "import_type", - "source_format", - "source_filename", - "total_rows", - "processed_rows", - "created_count", - "updated_count", - "skipped_count", - "failed_count" + "country", + "personal_code", + "phone_number" + ] + }, + "DigitalIdWalletApiMobileIdWalletVerificationStartCountryEnum": { + "enum": [ + "EST", + "LTU" ], + "type": "string", + "description": "* `EST` - EST\n* `LTU` - LTU" + }, + "DigitalIdWalletApiSmartIdWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Smart-ID verification (EST, LVA, LTU, BEL).", "properties": { - "uuid": { + "vendor_data": { "type": "string", - "format": "uuid", - "description": "Import job id. Use this value to poll progress and fetch row errors." + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "status": { - "type": "string", - "enum": [ - "PENDING", - "PROCESSING", - "COMPLETED", - "FAILED", - "PAUSED", - "CANCELLED" - ] + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "import_type": { + "sandbox_scenario": { "type": "string", - "enum": [ - "user_verification", - "business_verification", - "status_rules", - "transactions" - ] + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "source_format": { + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiSmartIdWalletVerificationStartCountryEnum" + } + ], + "description": "ISO 3166-1 alpha-3 country of the Smart-ID identity.\n\n* `EST` - EST\n* `LVA` - LVA\n* `LTU` - LTU\n* `BEL` - BEL" + }, + "personal_code": { "type": "string", - "enum": [ - "csv", - "ndjson" - ] + "minLength": 1, + "description": "The user's national personal identification code. It addresses the user's Smart-ID app; the user confirms the comparison code and enters their PIN in the app. Never collect the PIN.", + "pattern": "^[0-9-]{6,20}$" + } + }, + "required": [ + "country", + "personal_code" + ] + }, + "DigitalIdWalletApiSmartIdWalletVerificationStartCountryEnum": { + "enum": [ + "EST", + "LVA", + "LTU", + "BEL" + ], + "type": "string", + "description": "* `EST` - EST\n* `LVA` - LVA\n* `LTU` - LTU\n* `BEL` - BEL" + }, + "DigitalIdWalletApiGovukWalletWalletVerificationStartRequest": { + "type": "object", + "description": "Start a GOV.UK Wallet verification (GBR).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "source_filename": { - "type": "string" + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "total_rows": { - "type": "integer", - "format": "int64" + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "processed_rows": { - "type": "integer", - "format": "int64" + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiCountry520Enum" + } + ], + "default": "GBR", + "description": "ISO 3166-1 alpha-3 country of the GOV.UK Wallet identity. Defaults to GBR, the only country it serves.\n\n* `GBR` - GBR" }, - "checkpoint_row": { - "type": "integer", - "format": "int64" + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "created_count": { - "type": "integer", - "format": "int64" + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiCountry520Enum": { + "enum": [ + "GBR" + ], + "type": "string", + "description": "* `GBR` - GBR" + }, + "DigitalIdWalletApiOneidWalletVerificationStartRequest": { + "type": "object", + "description": "Start a OneID verification (GBR).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "updated_count": { - "type": "integer", - "format": "int64" + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "skipped_count": { - "type": "integer", - "format": "int64" + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "failed_count": { - "type": "integer", - "format": "int64" + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiCountry520Enum" + } + ], + "default": "GBR", + "description": "ISO 3166-1 alpha-3 country of the OneID identity. Defaults to GBR, the only country it serves.\n\n* `GBR` - GBR" }, - "total_media_bytes": { - "type": "integer", - "format": "int64" + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "last_error": { + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiGovbrWalletVerificationStartRequest": { + "type": "object", + "description": "Start a gov.br verification (BRA).", + "properties": { + "vendor_data": { "type": "string", - "nullable": true + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "created_at": { - "type": "string", - "format": "date-time" + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "updated_at": { + "sandbox_scenario": { "type": "string", - "format": "date-time" + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "started_at": { - "type": "string", - "format": "date-time", - "nullable": true + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiGovbrWalletVerificationStartCountryEnum" + } + ], + "default": "BRA", + "description": "ISO 3166-1 alpha-3 country of the gov.br identity. Defaults to BRA, the only country it serves.\n\n* `BRA` - BRA" }, - "completed_at": { + "return_url": { "type": "string", - "format": "date-time", - "nullable": true + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } - } - }, - "SessionImportError": { - "type": "object", + }, "required": [ - "uuid", - "row_number", - "error", - "raw_row" + "return_url" + ] + }, + "DigitalIdWalletApiGovbrWalletVerificationStartCountryEnum": { + "enum": [ + "BRA" ], + "type": "string", + "description": "* `BRA` - BRA" + }, + "DigitalIdWalletApiUaePassWalletVerificationStartRequest": { + "type": "object", + "description": "Start a UAE PASS verification (ARE).", "properties": { - "uuid": { + "vendor_data": { "type": "string", - "format": "uuid" + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "row_number": { - "type": "integer", - "format": "int64" + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "external_id": { + "sandbox_scenario": { "type": "string", - "nullable": true - }, - "error": { - "type": "string" + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "raw_row": { - "type": "object", - "additionalProperties": true + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiUaePassWalletVerificationStartCountryEnum" + } + ], + "default": "ARE", + "description": "ISO 3166-1 alpha-3 country of the UAE PASS identity. Defaults to ARE, the only country it serves.\n\n* `ARE` - ARE" }, - "created_at": { + "return_url": { "type": "string", - "format": "date-time" + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } - } + }, + "required": [ + "return_url" + ] }, - "BrandingCustomizationUpdate": { + "DigitalIdWalletApiUaePassWalletVerificationStartCountryEnum": { + "enum": [ + "ARE" + ], + "type": "string", + "description": "* `ARE` - ARE" + }, + "DigitalIdWalletApiFrejaidWalletVerificationStartRequest": { "type": "object", - "description": "Partial branding update body. Provide only the fields you want to change. Hex colors accept `#RRGGBB` or `#RRGGBBAA`. Custom domain and white-label email are not accepted here.", + "description": "Start a Freja eID verification (SWE).", "properties": { - "color_primary": { + "vendor_data": { "type": "string", - "description": "Headers / primary text color." + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "color_secondary": { - "type": "string", - "description": "Paragraph / secondary text color." + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "color_background": { + "sandbox_scenario": { "type": "string", - "description": "Page background color." + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "color_panel": { - "type": "string", - "description": "Panel / accent color." + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiCountryEe9Enum" + } + ], + "default": "SWE", + "description": "ISO 3166-1 alpha-3 country of the Freja eID identity. Defaults to SWE, the only country it serves.\n\n* `SWE` - SWE" }, - "color_panel_10": { + "return_url": { "type": "string", - "description": "Panel color at 10% opacity." + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "color_on_panel_1": { + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiCountryEe9Enum": { + "enum": [ + "SWE" + ], + "type": "string", + "description": "* `SWE` - SWE" + }, + "DigitalIdWalletApiPersonalausweisWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Personalausweis verification (DEU).", + "properties": { + "vendor_data": { "type": "string", - "description": "Support text color on panels." + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "color_on_panel_2": { - "type": "string", - "description": "Pill / badge color on panels." + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "color_on_background": { + "sandbox_scenario": { "type": "string", - "description": "Text color on background." + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "color_button_1": { - "type": "string", - "description": "Primary button color." + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiPersonalausweisWalletVerificationStartCountryEnum" + } + ], + "default": "DEU", + "description": "ISO 3166-1 alpha-3 country of the Personalausweis identity. Defaults to DEU, the only country it serves.\n\n* `DEU` - DEU" }, - "color_button_2": { + "return_url": { "type": "string", - "description": "Secondary button color." + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "color_button_text_1": { + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiPersonalausweisWalletVerificationStartCountryEnum": { + "enum": [ + "DEU" + ], + "type": "string", + "description": "* `DEU` - DEU" + }, + "DigitalIdWalletApiFtnWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Finnish Trust Network verification (FIN).", + "properties": { + "vendor_data": { "type": "string", - "description": "Primary button text color." + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "color_button_text_2": { - "type": "string", - "description": "Secondary button text color." + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "color_pill_text": { + "sandbox_scenario": { "type": "string", - "description": "Text color for pills, inputs and selectors." - }, - "border_radius_panel": { - "type": "integer", - "minimum": 0, - "description": "Panel corner radius in pixels." - }, - "border_radius_buttons": { - "type": "integer", - "minimum": 0, - "description": "Button corner radius in pixels." + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "font_family": { - "type": "string", - "description": "Google Font family name (e.g. `Inter`, `AR One Sans`)." + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiFtnWalletVerificationStartCountryEnum" + } + ], + "default": "FIN", + "description": "ISO 3166-1 alpha-3 country of the Finnish Trust Network identity. Defaults to FIN, the only country it serves.\n\n* `FIN` - FIN" }, - "font_weight": { + "return_url": { "type": "string", - "description": "Font weight available for the selected family (e.g. `regular`, `500`, `700`)." + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "app_public_name": { + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiFtnWalletVerificationStartCountryEnum": { + "enum": [ + "FIN" + ], + "type": "string", + "description": "* `FIN` - FIN" + }, + "DigitalIdWalletApiIdinWalletVerificationStartRequest": { + "type": "object", + "description": "Start a iDIN verification (NLD).", + "properties": { + "vendor_data": { "type": "string", "nullable": true, - "description": "Public name shown in the verification flow." + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "privacy_policy_url": { - "type": "string", + "metadata": { + "type": "object", + "additionalProperties": {}, "nullable": true, - "description": "Privacy policy URL shown to the end user." - }, - "skip_welcome_screen": { - "type": "boolean", - "description": "Skip the initial welcome screen." - }, - "disable_login_with_didit": { - "type": "boolean", - "description": "Continue as guest instead of showing the login-with-Didit screen. Cannot be set back to `false` while a custom domain is configured (400)." - }, - "hide_progress_bar": { - "type": "boolean", - "description": "Hide the progress bar during the flow." + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "callback_seconds": { - "type": "integer", - "minimum": 0, - "description": "Delay (seconds) before redirecting to the callback URL." + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "delete_logo_square": { - "type": "boolean", - "description": "When `true`, removes the current square logo." + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiIdinWalletVerificationStartCountryEnum" + } + ], + "default": "NLD", + "description": "ISO 3166-1 alpha-3 country of the iDIN identity. Defaults to NLD, the only country it serves.\n\n* `NLD` - NLD" }, - "delete_logo_rectangular": { - "type": "boolean", - "description": "When `true`, removes the current rectangular logo." + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "delete_favicon": { - "type": "boolean", - "description": "When `true`, removes the current favicon." + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } - } + }, + "required": [ + "return_url" + ] }, - "BrandingCustomization": { + "DigitalIdWalletApiIdinWalletVerificationStartCountryEnum": { + "enum": [ + "NLD" + ], + "type": "string", + "description": "* `NLD` - NLD" + }, + "DigitalIdWalletApiItsmeWalletVerificationStartRequest": { "type": "object", - "description": "White-label branding for an application: colors, fonts, radii, logo URLs and UI/UX options. Logo URLs are read-only (set them by uploading images). Custom domain and white-label email are not included.", + "description": "Start a itsme verification (BEL, LUX, NLD).", "properties": { - "color_primary": { - "type": "string" - }, - "color_secondary": { - "type": "string" - }, - "color_background": { - "type": "string" - }, - "color_panel": { - "type": "string" - }, - "color_panel_10": { - "type": "string" - }, - "color_on_panel_1": { - "type": "string" - }, - "color_on_panel_2": { - "type": "string" - }, - "color_on_background": { - "type": "string" - }, - "color_button_1": { - "type": "string" + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "color_button_2": { - "type": "string" + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "color_button_text_1": { - "type": "string" + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "color_button_text_2": { - "type": "string" + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiItsmeWalletVerificationStartCountryEnum" + } + ], + "description": "ISO 3166-1 alpha-3 country of the itsme identity.\n\n* `BEL` - BEL\n* `LUX` - LUX\n* `NLD` - NLD" }, - "color_pill_text": { - "type": "string" + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "border_radius_panel": { - "type": "integer" + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "country", + "return_url" + ] + }, + "DigitalIdWalletApiItsmeWalletVerificationStartCountryEnum": { + "enum": [ + "BEL", + "LUX", + "NLD" + ], + "type": "string", + "description": "* `BEL` - BEL\n* `LUX` - LUX\n* `NLD` - NLD" + }, + "DigitalIdWalletApiBuypassWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Buypass ID verification (NOR).", + "properties": { + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "border_radius_buttons": { - "type": "integer" + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "font_family": { - "type": "string" + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "font_weight": { - "type": "string" + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiCountryAfcEnum" + } + ], + "default": "NOR", + "description": "ISO 3166-1 alpha-3 country of the Buypass ID identity. Defaults to NOR, the only country it serves.\n\n* `NOR` - NOR" }, - "font_url": { + "return_url": { "type": "string", - "nullable": true, - "description": "Read-only. Resolved Google Fonts stylesheet URL for the selected family." + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "logo_square": { + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiCountryAfcEnum": { + "enum": [ + "NOR" + ], + "type": "string", + "description": "* `NOR` - NOR" + }, + "DigitalIdWalletApiVippsWalletVerificationStartRequest": { + "type": "object", + "description": "Start a Vipps verification (NOR).", + "properties": { + "vendor_data": { "type": "string", "nullable": true, - "description": "Read-only URL of the square logo. Upload via `image_square`." + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "logo_rectangular": { - "type": "string", + "metadata": { + "type": "object", + "additionalProperties": {}, "nullable": true, - "description": "Read-only URL of the rectangular logo. Upload via `image_rectangular`." + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "favicon": { + "sandbox_scenario": { "type": "string", - "nullable": true, - "description": "Read-only URL of the favicon. Upload via `image_favicon`." + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "app_public_name": { + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiCountryAfcEnum" + } + ], + "default": "NOR", + "description": "ISO 3166-1 alpha-3 country of the Vipps identity. Defaults to NOR, the only country it serves.\n\n* `NOR` - NOR" + }, + "return_url": { "type": "string", - "nullable": true + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, - "privacy_policy_url": { + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiBankidNoWalletVerificationStartRequest": { + "type": "object", + "description": "Start a BankID verification (NOR).", + "properties": { + "vendor_data": { "type": "string", - "nullable": true + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "skip_welcome_screen": { - "type": "boolean" + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." }, - "disable_login_with_didit": { - "type": "boolean" + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." }, - "hide_progress_bar": { - "type": "boolean" + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiCountryAfcEnum" + } + ], + "default": "NOR", + "description": "ISO 3166-1 alpha-3 country of the BankID identity. Defaults to NOR, the only country it serves.\n\n* `NOR` - NOR" }, - "callback_seconds": { - "type": "integer" + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } - } - }, - "WebhookEvent": { - "type": "string", - "description": "Supported webhook event name. Use these exact strings in `subscribed_events`; unsupported values are rejected.", - "enum": [ - "status.updated", - "data.updated", - "user.status.updated", - "user.data.updated", - "business.status.updated", - "business.data.updated", - "activity.created", - "transaction.created", - "transaction.status.updated", - "travel_rule.status.updated" - ] - }, - "WebhookSubscribedEvents": { - "type": "array", - "description": "Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit `[]` is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged.", - "minItems": 1, - "uniqueItems": true, - "items": { - "$ref": "#/components/schemas/WebhookEvent" }, - "example": [ - "status.updated", - "data.updated" + "required": [ + "return_url" ] }, - "PhoneVerificationSendResponse": { + "DigitalIdWalletApiBankidSeWalletVerificationStartRequest": { "type": "object", + "description": "Start a BankID verification (SWE).", "properties": { - "request_id": { + "vendor_data": { "type": "string", - "format": "uuid", - "description": "Session id of the verification. A `Retry` send returns the same `request_id` as the original send. This id appears in the Business Console, is returned again by a finalized `POST /v3/phone/check/`, and can be passed to `GET /v3/session/{sessionId}/decision/`." + "nullable": true, + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, - "status": { + "metadata": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "description": "Optional JSON object stored with the verification and returned in results and webhooks." + }, + "sandbox_scenario": { "type": "string", - "enum": [ - "Success", - "Retry", - "Blocked" + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + }, + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiCountryEe9Enum" + } ], - "description": "`Success` — OTP sent to a new verification. `Retry` — OTP re-sent for the pending verification created by a previous send. `Blocked` — the anti-fraud layer refused to send; the verification is immediately finalized as `Declined` and nothing is billed." + "default": "SWE", + "description": "ISO 3166-1 alpha-3 country of the BankID identity. Defaults to SWE, the only country it serves.\n\n* `SWE` - SWE" }, - "reason": { + "return_url": { "type": "string", - "nullable": true, - "description": "Why a send was `Blocked` (e.g., `repeated_attempts`, `suspicious`, `spam`). `null` on `Success` and `Retry`." + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "required": [ + "return_url" + ] + }, + "DigitalIdWalletApiMitidWalletVerificationStartRequest": { + "type": "object", + "description": "Start a MitID verification (DNK).", + "properties": { "vendor_data": { "type": "string", "nullable": true, - "description": "Echo of the `vendor_data` stored on the session (from the first send)." + "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." }, "metadata": { "type": "object", + "additionalProperties": {}, "nullable": true, - "description": "Echo of the `metadata` stored on the session (from the first send)." + "description": "Optional JSON object stored with the verification and returned in results and webhooks." + }, + "sandbox_scenario": { + "type": "string", + "minLength": 1, + "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + }, + "country": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiMitidWalletVerificationStartCountryEnum" + } + ], + "default": "DNK", + "description": "ISO 3166-1 alpha-3 country of the MitID identity. Defaults to DNK, the only country it serves.\n\n* `DNK` - DNK" + }, + "return_url": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", + "maxLength": 2048 + }, + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + } + ], + "default": "same_device", + "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" } - } + }, + "required": [ + "return_url" + ] }, - "PhoneVerificationCheckResponse": { + "DigitalIdWalletApiMitidWalletVerificationStartCountryEnum": { + "enum": [ + "DNK" + ], + "type": "string", + "description": "* `DNK` - DNK" + }, + "DigitalIdWalletApiWalletCatalogResponse": { "type": "object", "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "On `Approved`/`Declined`: the session id of the matched verification — identical to the `request_id` returned by `POST /v3/phone/send/`. On `Failed` and `Expired or Not Found`: a random one-off UUID that cannot be looked up later." + "wallets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DigitalIdWalletApiWalletCatalogEntry" + } + } + }, + "required": [ + "wallets" + ] + }, + "DigitalIdWalletApiWalletCatalogEntry": { + "type": "object", + "properties": { + "id": { + "type": "string" }, - "status": { + "name": { + "type": "string" + }, + "path": { "type": "string", - "enum": [ - "Approved", - "Declined", - "Failed", - "Expired or Not Found" - ], - "description": "`Approved` — correct code, no declining risk. `Declined` — terminal: a declining risk matched or the attempt budget (3) was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — no pending verification for this number in the last 5 minutes." + "description": "The wallet's own start endpoint." }, - "message": { + "countries": { + "type": "array", + "items": { + "type": "string" + } + }, + "issuing_authority": { + "type": "string" + }, + "level_of_assurance": { "type": "string", - "description": "Human-readable explanation of the outcome, including the number of attempts remaining after a wrong code." + "nullable": true }, - "phone": { - "type": "object", - "nullable": true, - "description": "Full phone report. Present (non-null) only on finalized outcomes (`Approved`/`Declined`); `null` on `Failed` and absent on `Expired or Not Found`.", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined", - "In Review", - "Not Finished", - "Expired" - ], - "description": "Final status of the phone verification. Matches the top-level `status` for finalized checks." - }, - "phone_number_prefix": { - "type": "string", - "description": "Country calling code with leading `+` (e.g., `+1`).", - "example": "+1" - }, - "phone_number": { - "type": "string", - "description": "National number without the country calling code.", - "example": "4155552671" - }, - "full_number": { - "type": "string", - "description": "Complete number in E.164 format.", - "example": "+14155552671" - }, - "country_code": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-2 country of the number.", - "example": "US" - }, - "country_name": { - "type": "string", - "description": "Full country name derived from `country_code`.", - "example": "United States" - }, - "carrier": { - "type": "object", - "description": "Current network serving the number, from the carrier lookup run at finalization.", - "properties": { - "name": { - "type": "string", - "description": "Carrier name. Empty string when the lookup could not identify it." - }, - "type": { - "type": "string", - "description": "Line type reported by the lookup: `mobile`, `fixed_line`, `voip`, `isp`, `vpn`, `toll_free`, `premium_rate`, `pager`, `payphone`, `satellite`, `service`, `shared_cost`, `short_codes_commercial`, `calling_cards`, `local_rate`, `universal_access`, `voice_mail`, `other`, or `unknown`." - } - } - }, - "is_disposable": { - "type": "boolean", - "description": "`true` when the number belongs to a temporary/burner number service." - }, - "is_virtual": { - "type": "boolean", - "description": "`true` when the line type is virtual (`voip`, `isp`, or `vpn`)." - }, - "verification_method": { - "type": "string", - "description": "Channel that actually delivered the OTP once delivery is confirmed (e.g., `whatsapp`, `sms`); until then, the requested channel." - }, - "verification_attempts": { - "type": "integer", - "description": "Number of OTP sends for this verification (1, or 2 after a retry)." - }, - "verified_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the correct code was entered. `null` if the verification was never approved." - }, - "warnings": { - "type": "array", - "description": "Risk warnings recorded during the verification (duplicate, disposable, VoIP, attempts exceeded, blocklist, etc.). Empty array when there are none.", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "description": "Feature that produced the warning. Always `PHONE` here." - }, - "risk": { - "type": "string", - "description": "Machine-readable risk code." - }, - "additional_data": { - "type": "object", - "nullable": true, - "description": "Risk-specific context (e.g., the duplicated session id for duplicate risks). `null` when the risk carries no extra data." - }, - "log_type": { - "type": "string", - "enum": [ - "error", - "warning", - "information" - ], - "description": "Severity. `error` risks decline the verification, `warning` flags it for review, `information` is recorded without affecting the status." - }, - "short_description": { - "type": "string", - "description": "Human-readable one-line summary of the risk." - }, - "long_description": { - "type": "string", - "description": "Human-readable explanation of the risk." - } - } - } - }, - "lifecycle": { - "type": "array", - "description": "Chronological audit trail of the verification. Event `type` is one of `PHONE_VERIFICATION_MESSAGE_SENT`, `PHONE_VERIFICATION_RETRY_MESSAGE_SENT`, `PHONE_VERIFICATION_BLOCKED`, `PHONE_DELIVERY_DELIVERED`, `PHONE_DELIVERY_UNDELIVERABLE`, `VALID_CODE_ENTERED`, `INVALID_CODE_ENTERED`, `PHONE_VERIFICATION_APPROVED`, `PHONE_VERIFICATION_DECLINED`, `PHONE_VERIFICATION_IN_REVIEW`, `PHONE_VERIFICATION_EXPIRED`.", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "Event type." - }, - "timestamp": { - "type": "string", - "format": "date-time", - "description": "When the event happened." - }, - "details": { - "type": "object", - "nullable": true, - "description": "Event-specific payload. Send events: `{status, reason, channel, actual_channel}` (`actual_channel` is filled once delivery is confirmed). Delivery events: `{channel, status}`. Code-entry events: `{code_tried, status}`. Final-status events: `null`, or `{reason}` when declined or in review." - }, - "fee": { - "type": "number", - "description": "USD amount billed for this event. Non-zero only on send events (the delivery price); 0 for code entries, delivery confirmations, and status events." - } - } - } - }, - "matches": { - "type": "array", - "description": "Other sessions of your application (KYC, KYB, or API) where the same phone number was used by a different user (different `vendor_data`). Up to 5 entries, a synthetic list-entry blocklist match first when present (session-derived matches keep query order). Empty array when there are no matches.", - "items": { - "type": "object", - "properties": { - "session_id": { - "type": "string", - "format": "uuid", - "description": "Session id of the other verification that used this phone number." - }, - "session_number": { - "type": "integer", - "description": "Human-friendly sequential number of that session." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "`vendor_data` of that session." - }, - "verification_date": { - "type": "string", - "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)." - }, - "phone_number": { - "type": "string", - "description": "The matched phone number." - }, - "status": { - "type": "string", - "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)." - }, - "is_blocklisted": { - "type": "boolean", - "description": "Whether the phone number is on your application's blocklist." - }, - "api_service": { - "type": "string", - "nullable": true, - "description": "For API-type sessions, the standalone API that created it (e.g., `PHONE_VERIFICATION`); `null` for regular verification sessions." - }, - "source": { - "type": "string", - "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry prepends a synthetic match with `source: \"list_entry\"` and null `session_id`/`session_number`." - } - } - } - }, - "phone_social": { - "$ref": "#/components/schemas/PhoneSocialFootprint" - } + "interaction": { + "$ref": "#/components/schemas/DigitalIdWalletApiInteractionEnum" + }, + "required_fields": { + "type": "array", + "items": { + "type": "string" } }, - "vendor_data": { + "available": { + "type": "boolean", + "description": "Whether this application can start it in this environment now. Listed does not mean live." + } + }, + "required": [ + "available", + "countries", + "id", + "interaction", + "issuing_authority", + "level_of_assurance", + "name", + "path", + "required_fields" + ] + }, + "CaseDuplicateCaseDuplicate": { + "type": "object", + "properties": { + "uuid": { "type": "string", - "nullable": true, - "description": "`vendor_data` of the matched verification's session. `null` on `Expired or Not Found`." + "format": "uuid", + "readOnly": true }, - "metadata": { - "type": "object", + "case_number": { + "type": "string", "nullable": true, - "description": "`metadata` of the matched verification's session. `null` on `Expired or Not Found`." + "maxLength": 32 + }, + "title": { + "type": "string", + "maxLength": 255 + }, + "status": { + "$ref": "#/components/schemas/CaseDuplicateStatusEnum" + }, + "priority": { + "$ref": "#/components/schemas/CaseDuplicatePriorityEnum" }, "created_at": { "type": "string", "format": "date-time", - "description": "Timestamp of this check response." + "readOnly": true } - } + }, + "required": [ + "created_at", + "title", + "uuid" + ] }, - "EmailVerificationSendResponse": { + "CaseDuplicateCaseDuplicatePage": { "type": "object", "properties": { - "request_id": { + "count": { + "type": "integer" + }, + "next_offset": { + "type": "integer", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseDuplicateCaseDuplicate" + } + } + }, + "required": [ + "count", + "next_offset", + "results" + ] + }, + "RoutingPublicationPolicyDraftResponse": { + "type": "object", + "properties": { + "draft": { + "allOf": [ + { + "$ref": "#/components/schemas/RoutingPublicationPolicyDraft" + } + ], + "nullable": true + } + }, + "required": [ + "draft" + ] + }, + "RoutingPublicationPolicyDraftWriteRequest": { + "type": "object", + "properties": { + "expected_version": { + "type": "integer", + "minimum": 0 + }, + "expected_revision": { "type": "string", "format": "uuid", - "description": "Session id of the verification. A `Retry` send returns the same `request_id` as the original send. This id appears in the Business Console, is returned again by a finalized `POST /v3/email/check/`, and can be passed to `GET /v3/session/{sessionId}/decision/`." + "nullable": true }, - "status": { + "changes": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "changes", + "expected_revision", + "expected_version" + ] + }, + "CasePublicationPolicyDraft": { + "type": "object", + "properties": { + "revision": { "type": "string", - "enum": [ - "Success", - "Retry", - "Undeliverable" - ], - "description": "`Success` — OTP emailed to a new verification (billed). `Retry` — fresh OTP emailed for the pending verification created by a previous send (free). `Undeliverable` — the address failed deliverability validation or the message could not be sent; the verification is immediately finalized as `Declined` and nothing is billed." + "format": "uuid" }, - "reason": { + "base_version": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "changes": {}, + "actor_email": { "type": "string", + "format": "email", "nullable": true, - "enum": [ - "email_can_not_be_delivered", - null - ], - "description": "`email_can_not_be_delivered` when `status` is `Undeliverable`; `null` otherwise." + "maxLength": 254 }, - "vendor_data": { + "updated_at": { "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` stored on the session (from the first send)." + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "base_version", + "updated_at" + ] + }, + "CasePublicationPolicyDraftPreconditionRequest": { + "type": "object", + "properties": { + "expected_version": { + "type": "integer", + "minimum": 0 }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Echo of the `metadata` stored on the session (from the first send)." + "expected_revision": { + "type": "string", + "format": "uuid", + "nullable": true } - } + }, + "required": [ + "expected_revision", + "expected_version" + ] }, - "EmailVerificationCheckResponse": { + "CaseCollaborationCaseNote": { "type": "object", "properties": { - "request_id": { + "uuid": { "type": "string", "format": "uuid", - "description": "On `Approved`/`Declined`: the session id of the matched verification — identical to the `request_id` returned by `POST /v3/email/send/`. On `Failed` and `Expired or Not Found`: a random one-off UUID that cannot be looked up later." + "readOnly": true }, - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined", - "Failed", - "Expired or Not Found" - ], - "description": "`Approved` — correct code, no declining risk. `Declined` — terminal: a declining risk matched or the attempt budget (3) was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — no pending verification for this address in the last 5 minutes." + "comment": { + "type": "string" }, - "message": { + "actor_email": { "type": "string", - "description": "Human-readable explanation of the outcome, including the number of attempts remaining after a wrong code." - }, - "email": { - "type": "object", + "format": "email", "nullable": true, - "description": "Full email report. Present (non-null) only on finalized outcomes (`Approved`/`Declined`); `null` on `Failed` and absent on `Expired or Not Found`.", - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "Declined", - "In Review", - "Not Finished", - "Expired" - ], - "description": "Final status of the email verification. Matches the top-level `status` for finalized checks." - }, - "email": { - "type": "string", - "format": "email", - "description": "The verified email address.", - "example": "alice@example.com" - }, - "is_breached": { - "type": "boolean", - "description": "`true` when the address appears in known data breaches (breach intelligence lookup run at finalization)." - }, - "breaches": { - "type": "array", - "description": "Up to 5 most recent known breaches containing this address. Empty array (or omitted) when `is_breached` is `false`.", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Breach identifier." - }, - "domain": { - "type": "string", - "description": "Domain of the breached service." - }, - "breach_date": { - "type": "string", - "format": "date", - "description": "Date the breach occurred." - }, - "breach_emails_count": { - "type": "integer", - "description": "Total number of accounts exposed in the breach." - }, - "description": { - "type": "string", - "description": "Human-readable description of the breach (may contain HTML markup)." - }, - "logo_path": { - "type": "string", - "description": "URL of the breached service's logo." - }, - "data_classes": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Categories of data exposed, in snake_case (e.g., `email_addresses`, `passwords`)." - }, - "is_verified": { - "type": "boolean", - "description": "Whether the breach has been verified as legitimate." - } - } - } - }, - "is_disposable": { - "type": "boolean", - "description": "`true` when the domain belongs to a known disposable/temporary-mail provider." - }, - "is_undeliverable": { - "type": "boolean", - "description": "`true` when the address cannot receive mail (failed syntax or DNS/MX validation, or the message bounced)." - }, - "verification_attempts": { - "type": "integer", - "description": "Number of OTP sends for this verification (1, or 2 after a retry)." - }, - "verified_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the correct code was entered. `null` if the verification was never approved." - }, - "warnings": { - "type": "array", - "description": "Risk warnings recorded during the verification (duplicate, breached, disposable, undeliverable, attempts exceeded, blocklist, etc.). Empty array when there are none.", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string", - "description": "Feature that produced the warning. Always `EMAIL` here." - }, - "risk": { - "type": "string", - "description": "Machine-readable risk code." - }, - "additional_data": { - "type": "object", - "nullable": true, - "description": "Risk-specific context (e.g., the duplicated session id for duplicate risks). `null` when the risk carries no extra data." - }, - "log_type": { - "type": "string", - "enum": [ - "error", - "warning", - "information" - ], - "description": "Severity. `error` risks decline the verification, `warning` flags it for review, `information` is recorded without affecting the status." - }, - "short_description": { - "type": "string", - "description": "Human-readable one-line summary of the risk." - }, - "long_description": { - "type": "string", - "description": "Human-readable explanation of the risk." - } - } - } - }, - "lifecycle": { - "type": "array", - "description": "Chronological audit trail of the verification. Event `type` is one of `EMAIL_VERIFICATION_MESSAGE_SENT`, `EMAIL_VERIFICATION_RETRY_MESSAGE_SENT`, `VALID_CODE_ENTERED`, `INVALID_CODE_ENTERED`, `EMAIL_VERIFICATION_APPROVED`, `EMAIL_VERIFICATION_DECLINED`, `EMAIL_VERIFICATION_IN_REVIEW`, `EMAIL_VERIFICATION_EXPIRED`.", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "Event type." - }, - "timestamp": { - "type": "string", - "format": "date-time", - "description": "When the event happened." - }, - "details": { - "type": "object", - "nullable": true, - "description": "Event-specific payload. Send events: `{status, reason}`. Code-entry events: `{code_tried, status}`. Final-status events: `null`, or `{reason}` when declined or in review." - }, - "fee": { - "type": "number", - "description": "USD amount billed for this event. Non-zero only on the first successful send; 0 for retries, code entries, and status events." - } - } - } - }, - "matches": { - "type": "array", - "description": "Other sessions of your application (KYC, KYB, or API) where the same email address was used by a different user (different `vendor_data`). Up to 5 entries; a synthetic list-entry blocklist match is prepended when present (and only when no session-derived blocklisted email exists) — session-derived matches keep created_at order. Empty array when there are no matches.", - "items": { - "type": "object", - "properties": { - "session_id": { - "type": "string", - "format": "uuid", - "description": "Session id of the other verification that used this email address." - }, - "session_number": { - "type": "integer", - "description": "Human-friendly sequential number of that session." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "`vendor_data` of that session." - }, - "verification_date": { - "type": "string", - "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)." - }, - "email": { - "type": "string", - "description": "The matched email address." - }, - "status": { - "type": "string", - "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)." - }, - "is_blocklisted": { - "type": "boolean", - "description": "Whether the email address is on your application's blocklist." - }, - "api_service": { - "type": "string", - "nullable": true, - "description": "For API-type sessions, the standalone API that created it (e.g., `PHONE_VERIFICATION`); `null` for regular verification sessions." - }, - "source": { - "type": "string", - "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry prepends a synthetic match with `source: \"list_entry\"` and null `session_id`/`session_number`." - } - } - } - }, - "email_social": { - "$ref": "#/components/schemas/EmailSocialFootprint" - } - } + "maxLength": 255 }, - "vendor_data": { + "actor_name": { "type": "string", "nullable": true, - "description": "`vendor_data` of the matched verification's session. `null` on `Expired or Not Found`." + "maxLength": 255 }, - "metadata": { - "type": "object", - "nullable": true, - "description": "`metadata` of the matched verification's session. `null` on `Expired or Not Found`." + "mentioned_emails": {}, + "is_internal": { + "type": "boolean" }, + "attachments": {}, + "tags": {}, "created_at": { "type": "string", "format": "date-time", - "description": "Timestamp of this check response." + "readOnly": true + }, + "reply_to": { + "allOf": [ + { + "$ref": "#/components/schemas/CaseCollaborationCaseNoteReply" + } + ], + "nullable": true, + "readOnly": true + }, + "is_pinned": { + "type": "boolean" + }, + "client_reference": { + "type": "string", + "format": "uuid", + "nullable": true } - } + }, + "required": [ + "comment", + "created_at", + "reply_to", + "uuid" + ] }, - "WorkflowListItem": { + "CustomerMessagingPaginatedCaseRequestList": { "type": "object", - "description": "One workflow version in the list view.", + "required": [ + "count", + "results" + ], "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Per-version UUID. Use as `{settings_uuid}` for get/update/delete, and as `workflow_id` when creating sessions." + "count": { + "type": "integer", + "example": 123 }, - "workflow_id": { + "next": { "type": "string", - "format": "uuid", - "description": "Stable group ID shared by every version of the same workflow. Equals `uuid` for version 1." + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=400&limit=100" }, - "workflow_label": { + "previous": { "type": "string", - "description": "Display name for the workflow." + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=200&limit=100" }, - "workflow_type": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequest" + } + } + } + }, + "FraudCheckResponse": { + "type": "object", + "properties": { + "check_id": { "type": "string", - "nullable": true, - "description": "Base type for simple workflows: `kyc`, `kyb`, `adaptive_age_verification`, `biometric_authentication`, etc. `null` for graph-based workflows without a KYB feature." + "format": "uuid" }, - "is_default": { - "type": "boolean", - "description": "Whether this is the default workflow for new sessions." + "request_id": { + "type": "string", + "format": "uuid", + "description": "Native verification session created for this check." }, - "is_archived": { - "type": "boolean" + "client_reference": { + "type": "string", + "nullable": true }, - "is_white_label_enabled": { - "type": "boolean", - "description": "Whether white-label customization applies to sessions created with this workflow." + "processing_status": { + "$ref": "#/components/schemas/FraudProcessingStatusEnum" }, - "total_price": { - "type": "number", - "description": "Fixed price per verification in USD (JSON number, excludes variable-priced features such as DATABASE_VALIDATION and PHONE_VERIFICATION)." + "profile_id": { + "type": "string" }, - "min_price": { - "type": "number", - "description": "Cheapest fixed price across workflow-graph paths, in USD." + "profile_version": { + "type": "string" }, - "max_price": { - "type": "number", - "description": "Most expensive fixed price across workflow-graph paths, in USD." + "created_at": { + "type": "string", + "format": "date-time" }, - "features": { + "completed_at": { "type": "string", - "description": "Enabled features as a single `\" + \"`-joined string, e.g. `\"OCR + LIVENESS + FACE_MATCH\"`. Split on `\" + \"` to get a list." + "format": "date-time", + "nullable": true }, - "is_simple_workflow": { - "type": "boolean", - "description": "True when the workflow has no custom workflow graph." + "inputs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FraudCheckInput" + } }, - "is_editable": { - "type": "boolean", - "description": "True when this version is a `draft` (drafts are editable in place; published versions are immutable in the Console)." + "modules": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FraudCheckModuleReport" + } }, - "workflow_url": { - "type": "string", - "format": "uri", - "description": "Public shareable verification URL for this workflow (`https://verify.didit.me/u/`)." + "attribute_evidence": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + } }, - "max_retry_attempts": { - "type": "integer", - "description": "Maximum retry attempts allowed after a declined verification (0-10)." + "compound_evidence": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + } }, - "retry_window_days": { - "type": "integer", - "nullable": true, - "description": "Rolling window in days used to count retries (1-365). `null` = all-time limit." + "risk": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FraudCheckRisk" + } }, - "session_expiration_time": { - "type": "integer", - "description": "Seconds before an unfinished session expires (3600-2419200)." + "network": { + "type": "object", + "additionalProperties": {} }, - "version": { - "type": "integer", - "description": "Version number within the `workflow_id` group, starting at 1." + "enrichment": { + "type": "object", + "additionalProperties": {} }, - "status": { - "type": "string", - "enum": [ - "draft", - "published" + "identity_relationship": { + "allOf": [ + { + "$ref": "#/components/schemas/FraudCheckIdentityRelationship" + } ], - "description": "Version status. New sessions use the latest `published` version of a group." + "nullable": true }, - "has_draft": { - "type": "boolean", - "description": "True when any version in this `workflow_id` group is currently a draft." + "recommendation": { + "$ref": "#/components/schemas/FraudRecommendationEnum" }, - "created_by": { + "next_action": { + "$ref": "#/components/schemas/FraudNextActionEnum" + }, + "decision_reasons": { + "type": "array", + "items": { + "type": "string" + } + }, + "coverage": { "type": "object", - "nullable": true, - "description": "Who created this version: `{name, email, type}` where `type` is `user` (Console user, `email` set) or `api_key` (`name` is the client id, `email` is null). `null` when no creator was recorded.", - "properties": { - "name": { - "type": "string" - }, - "email": { - "type": "string", - "nullable": true - }, - "type": { - "type": "string", - "enum": [ - "user", - "api_key" - ] - } + "additionalProperties": {} + }, + "usage": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} } }, - "updated_at": { + "vendor_data": { "type": "string", - "format": "date-time" + "nullable": true + }, + "metadata": { + "nullable": true } - } + }, + "required": [ + "attribute_evidence", + "check_id", + "client_reference", + "completed_at", + "compound_evidence", + "coverage", + "created_at", + "decision_reasons", + "inputs", + "modules", + "network", + "next_action", + "processing_status", + "profile_id", + "profile_version", + "recommendation", + "risk", + "usage" + ] }, - "SimpleWorkflowRequest": { + "CaseReviewTeam": { "type": "object", - "description": "Create a simple linear workflow. The API converts this feature list into a node-based workflow graph internally. STRICT field whitelist: any key not listed here (including `workflow_type`) is rejected with a 400.", - "required": [ - "features" - ], "properties": { - "workflow_label": { + "name": { "type": "string", - "maxLength": 50, - "default": "ID Verification", - "description": "Display name for the workflow (max 50 characters). Defaults to `\"ID Verification\"` when omitted — always set it so workflows stay distinguishable.", - "example": "Standard KYC" - }, - "is_default": { - "type": "boolean", - "description": "Set this workflow as the default for new sessions." + "maxLength": 100 }, - "status": { + "description": { "type": "string", - "enum": [ - "draft", - "published" - ], - "description": "Omit this field to create a published workflow ready for sessions. Use `draft` only when you want to save without publishing." + "maxLength": 500 }, - "features": { + "members": { "type": "array", - "minItems": 1, - "description": "Verification features in execution order. The API links each item to the next one and adds one final status node automatically.", "items": { - "$ref": "#/components/schemas/SimpleWorkflowFeature" - } - }, - "is_white_label_enabled": { - "type": "boolean", - "description": "Enable white-label customization for sessions created with this workflow.", - "default": false - }, - "is_desktop_allowed": { - "type": "boolean", - "description": "Allow the verification flow to run on desktop browsers.", - "default": false - }, - "max_retry_attempts": { - "type": "integer", - "minimum": 0, - "maximum": 10, - "default": 3, - "description": "Maximum retry attempts allowed after a declined verification. `0` blocks the user after the first decline." + "$ref": "#/components/schemas/CaseReviewTeamMember" + }, + "maxItems": 250 }, - "retry_window_days": { - "type": "integer", - "minimum": 1, - "maximum": 365, - "nullable": true, - "default": 7, - "description": "Rolling window in days used to count retries. `null` enforces an all-time limit." + "is_active": { + "type": "boolean" }, - "face_liveness_max_attempts": { - "type": "integer", - "minimum": 1, - "maximum": 3, - "default": 3, - "description": "Maximum liveness submissions per session. Default 3: one initial attempt plus two retries. This value is also copied into generated liveness and age-estimation workflow nodes unless the feature-level config overrides it." + "uuid": { + "type": "string", + "format": "uuid", + "readOnly": true }, - "face_match_max_attempts": { - "type": "integer", - "minimum": 1, - "maximum": 3, - "default": 3, - "description": "Maximum face-match submissions per session. Default 3: one initial attempt plus two retries. This value is also copied into generated face-match workflow nodes unless the feature-level config overrides it." + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "session_expiration_time": { - "type": "integer", - "minimum": 3600, - "maximum": 2419200, - "default": 604800, - "description": "Seconds before an unfinished session expires. Minimum 1 hour (3600), maximum 4 weeks (2419200)." + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } } }, - "SimpleWorkflowUpdateRequest": { + "CaseActionCaseEntityActionRequest": { "type": "object", - "description": "Update workflow metadata or replace the full linear feature list. STRICT field whitelist: any key not listed here is rejected with a 400.", "properties": { - "workflow_label": { + "client_reference": { "type": "string", - "maxLength": 50, - "description": "Display name for the workflow (max 50 characters)." + "format": "uuid" }, - "is_default": { + "retry": { "type": "boolean", - "description": "Set this workflow as the default for new sessions." + "default": false + }, + "action": { + "$ref": "#/components/schemas/CaseActionActionEnum" }, "status": { "type": "string", - "enum": [ - "draft", - "published" - ], - "description": "Controls versioning. Omit to update the current version in place (drafts auto-publish). `published` on an already-published version creates and returns a NEW version. `draft` is only valid while the version is still a draft." + "nullable": true }, - "features": { + "tags": { "type": "array", - "minItems": 1, - "description": "If provided, this replaces the full feature order and configuration.", "items": { - "$ref": "#/components/schemas/SimpleWorkflowFeature" + "type": "string", + "minLength": 1 } }, - "is_white_label_enabled": { - "type": "boolean", - "description": "Enable white-label customization for sessions created with this workflow." - }, - "is_desktop_allowed": { - "type": "boolean", - "description": "Allow the verification flow to run on desktop browsers." - }, - "max_retry_attempts": { - "type": "integer", - "minimum": 0, - "maximum": 10, - "description": "Maximum retry attempts allowed after a declined verification. `0` blocks the user after the first decline." - }, - "retry_window_days": { - "type": "integer", - "minimum": 1, - "maximum": 365, - "nullable": true, - "description": "Rolling window in days used to count retries. `null` enforces an all-time limit." - }, - "face_liveness_max_attempts": { - "type": "integer", - "minimum": 1, - "maximum": 3, - "description": "Maximum liveness submissions per session. This value is also copied into generated liveness and age-estimation workflow nodes unless the feature-level config overrides it." - }, - "face_match_max_attempts": { - "type": "integer", - "minimum": 1, - "maximum": 3, - "description": "Maximum face-match submissions per session. This value is also copied into generated face-match workflow nodes unless the feature-level config overrides it." - }, - "session_expiration_time": { - "type": "integer", - "minimum": 3600, - "maximum": 2419200, - "description": "Seconds before an unfinished session expires. Minimum 1 hour (3600), maximum 4 weeks (2419200)." + "note": { + "type": "string", + "nullable": true } - } + }, + "required": [ + "action" + ] }, - "SimpleWorkflowFeature": { + "RoutingPublicationPaginatedPolicyRevisionList": { "type": "object", "required": [ - "feature" + "count", + "results" ], "properties": { - "feature": { - "type": "string", - "description": "Feature to run. Use the uppercase values shown here.", - "enum": [ - "OCR", - "NFC", - "LIVENESS", - "FACE_MATCH", - "PROOF_OF_ADDRESS", - "QUESTIONNAIRE", - "DOCUMENT_AI", - "PHONE_VERIFICATION", - "EMAIL_VERIFICATION", - "DATABASE_VALIDATION", - "AML", - "IP_ANALYSIS", - "AGE_ESTIMATION", - "KYB_REGISTRY", - "KYB_DOCUMENTS", - "KYB_KEY_PEOPLE" - ] + "count": { + "type": "integer", + "example": 123 }, - "config": { - "type": "object", - "description": "Feature-specific settings. For `QUESTIONNAIRE`, include `questionnaire_uuid` with the `questionnaire_id` returned by the questionnaire create endpoint. For `OCR`, `methods` selects which ID Verification methods each country may use (document capture, non-doc lookup against a government or other authoritative source, and digital identity wallets); omit it and every country is document capture only.", - "additionalProperties": true + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=400&limit=100" }, - "label": { + "previous": { "type": "string", - "description": "Optional internal label for this workflow node." + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=200&limit=100" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RoutingPublicationPolicyRevision" + } } } }, - "ReviewItem": { + "FraudRecommendationEnum": { + "enum": [ + "approve", + "review", + "reject" + ], + "type": "string", + "description": "* `approve` - Approve\n* `review` - Review\n* `reject` - Reject" + }, + "CustomerMessagingCaseRequestPreviewResponse": { "type": "object", - "description": "One entry in a session's review / activity feed. Status changes carry `previous_status`, `new_status`, `previous_value`, `new_value`, and `changed_fields`; comments carry `comment` and (for Console comments) `mentioned_emails`; the remaining fields are `null` or empty when they do not apply to the activity type.", "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Unique identifier of this feed entry.", - "example": "de0561bc-5583-4409-9171-1a31a4c8a5f4" - }, - "activity_type": { - "type": "string", - "enum": [ - "COMMENT", - "STATUS_UPDATED", - "KYC_DATA_UPDATED", - "POA_DATA_UPDATED", - "FILE_ADDED", - "FILE_REMOVED", - "AML_ONGOING_MATCH", - "AML_STATUS_UPDATED", - "AML_HIT_STATUS_UPDATED", - "KYB_DATA_UPDATED", - "KYB_DOCUMENT_UPLOADED", - "TAG_ADDED", - "TAG_REMOVED" - ], - "description": "What kind of activity this entry records. `STATUS_UPDATED` rows come from manual decisions (`PATCH /v3/session/{session_id}/update-status/`) and from entries added via `POST …/reviews/` (which default to this type even when no status is recorded). `COMMENT` rows come from Console reviewers; Console detail-page views are also stored as `COMMENT` rows flagged with `metadata.system_event = \"DETAIL_VIEWED\"`. The remaining types track data edits, file changes, tags, and AML monitoring activity. STATUS_UPDATED rows are also written by system actors (e.g. AML ongoing monitoring with actor_type SYSTEM, and automatic feature-status updates), not only by the update-status endpoint and manual review posts.", - "example": "STATUS_UPDATED" - }, - "actor_display": { - "type": "string", - "description": "Human-readable author label, resolved in order: actor's full name → Console account identifier → local part of `actor_email` → `\"API Client\"` (for `API_KEY` actors) → `\"System\"` (for `SYSTEM` actors) → `\"Unknown\"`. Entries created via `POST …/reviews/` always display `\"Unknown\"` because that endpoint records no actor attribution.", - "example": "API Client" - }, - "actor_email": { - "type": "string", - "nullable": true, - "description": "Email of the Console user who performed the action. `null` for API-key and system actors.", - "example": null - }, - "actor_type": { + "request_id": { "type": "string", - "enum": [ - "API_KEY", - "CONSOLE_USER", - "SYSTEM" - ], - "description": "Who performed the action: an API key, a Console user, or Didit itself (`SYSTEM`, e.g. AML ongoing monitoring). Entries created via `POST …/reviews/` carry the default `CONSOLE_USER` regardless of the caller.", - "example": "API_KEY" + "format": "uuid" }, - "new_status": { + "expires_at": { "type": "string", - "nullable": true, - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "description": "Session status recorded by this entry (for status updates). `null` for comments and data-change entries.", - "example": "Approved" + "format": "date-time" }, - "previous_status": { + "recipient": { "type": "string", - "nullable": true, - "enum": [ - "Not Started", - "In Progress", - "Approved", - "Declined", - "In Review", - "Expired", - "Abandoned", - "Kyc Expired", - "Resubmitted", - "Awaiting User" - ], - "description": "Status the session had before the change. Populated on `STATUS_UPDATED` entries written by the update-status endpoint; `null` on entries added via `POST …/reviews/`. Also populated on system-written STATUS_UPDATED rows.", - "example": "In Review" + "format": "email" }, - "comment": { - "type": "string", - "nullable": true, - "description": "Free-text note attached to the entry, or `null`.", - "example": "Document re-checked manually; address matches." + "sender": { + "type": "string" }, - "mentioned_emails": { - "type": "array", - "items": { - "type": "string", - "format": "email" - }, - "description": "Console users `@email`-mentioned in the comment. Always empty for entries created via this API — mention processing is a Console-only feature.", - "example": [] + "subject": { + "type": "string" }, - "previous_value": { - "type": "object", - "nullable": true, - "description": "Snapshot of the changed data before the action, e.g. `{\"status\": \"In Review\"}` on a status change. `null` when nothing was changed.", - "example": { - "status": "In Review" - } + "html": { + "type": "string" }, - "new_value": { - "type": "object", - "nullable": true, - "description": "Snapshot of the changed data after the action, e.g. `{\"status\": \"Approved\"}`. `null` when nothing was changed.", - "example": { - "status": "Approved" - } + "requires_user_action": { + "type": "boolean" }, - "changed_fields": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Names of the fields modified by this activity (e.g. `[\"status\"]`). Empty for comments and for entries added via `POST …/reviews/`.", - "example": [ - "status" - ] + "preview_token": { + "type": "string" + } + }, + "required": [ + "expires_at", + "html", + "preview_token", + "recipient", + "request_id", + "requires_user_action", + "sender", + "subject" + ] + }, + "CustomerMessagingCaseCustomerResponseRequest": { + "type": "object", + "properties": { + "full_name": { + "type": "string", + "minLength": 1, + "maxLength": 512 }, - "metadata": { - "type": "object", - "nullable": true, - "description": "Extra context for system-generated entries. Console detail-page views carry `{\"system_event\": \"DETAIL_VIEWED\", \"detail_type\": \"verification\"}` so they can be filtered out of notification logic. `null` for ordinary entries.", - "example": null + "date_of_birth": { + "type": "string", + "format": "date" }, - "created_at": { + "file": { "type": "string", - "format": "date-time", - "description": "When the entry was written, ISO 8601 UTC.", - "example": "2026-06-12T00:19:31.918735Z" + "format": "binary", + "writeOnly": true } } }, - "QuestionnaireListItem": { + "CaseCollaborationCaseNoteReply": { "type": "object", - "description": "One questionnaire version in the list view.", "properties": { "uuid": { "type": "string", - "format": "uuid", - "description": "Per-version UUID. Use as `{questionnaire_uuid}` for get/update/delete and as `questionnaire_uuid` in a workflow's QUESTIONNAIRE feature config." + "format": "uuid" }, - "title": { - "type": "string" + "actor_name": { + "type": "string", + "nullable": true }, - "description": { + "actor_email": { "type": "string", + "format": "email", "nullable": true }, - "languages": { - "type": "array", - "items": { - "type": "string" - } + "comment": { + "type": "string", + "maxLength": 300 + } + }, + "required": [ + "actor_email", + "actor_name", + "comment", + "uuid" + ] + }, + "FraudCheckModuleReport": { + "type": "object", + "properties": { + "module": { + "$ref": "#/components/schemas/FraudExcludeEnum" }, - "default_language": { - "type": "string" + "status": { + "$ref": "#/components/schemas/FraudStatusEnum" }, - "is_active": { - "type": "boolean" + "reason": { + "type": "string", + "nullable": true }, - "is_simple_questionnaire": { - "type": "boolean", - "description": "True for linear questionnaires created through this API; false for graph-based questionnaires built in the Console." + "latency_ms": { + "type": "integer" }, - "created_at": { + "freshness": { "type": "string", - "format": "date-time" + "nullable": true }, - "updated_at": { + "billable_units": { + "type": "integer" + } + }, + "required": [ + "billable_units", + "freshness", + "latency_ms", + "module", + "reason", + "status" + ] + }, + "CaseDuplicateEntityTypeEnum": { + "enum": [ + "session", + "business_session", + "transaction" + ], + "type": "string", + "description": "* `session` - Session\n* `business_session` - Business Session\n* `transaction` - Transaction" + }, + "FraudClientCredentialsErrorMessage": { + "type": "object", + "properties": { + "detail": { "type": "string", - "format": "date-time" + "default": "Authentication failed: Invalid client access token." + } + } + }, + "FraudAvailabilityEnum": { + "enum": [ + "available", + "not_requested", + "no_reference", + "insufficient_history", + "unsupported_region", + "source_unavailable" + ], + "type": "string", + "description": "* `available` - Available\n* `not_requested` - Not Requested\n* `no_reference` - No Reference\n* `insufficient_history` - Insufficient History\n* `unsupported_region` - Unsupported Region\n* `source_unavailable` - Source Unavailable" + }, + "CasePublicationPolicyDraftResponse": { + "type": "object", + "properties": { + "draft": { + "allOf": [ + { + "$ref": "#/components/schemas/CasePublicationPolicyDraft" + } + ], + "nullable": true + } + }, + "required": [ + "draft" + ] + }, + "FraudCheckAttributesRequest": { + "type": "object", + "description": "Corroborating attributes. All optional; they widen module coverage.", + "properties": { + "first_name": { + "type": "string", + "maxLength": 128 }, - "question_types": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Uppercase element types of the answerable questions, in creation order (e.g. `SHORT_TEXT`, `MULTIPLE_CHOICE`, `FILE_UPLOAD`). Structural `SECTION_HEADER` and `SEPARATOR` elements are excluded. One entry per question, so values can repeat." + "last_name": { + "type": "string", + "maxLength": 128 }, - "workflow_names": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Names of workflows using this questionnaire. Currently always `[]` on this endpoint (populated only in the Console listing)." + "full_name": { + "type": "string", + "maxLength": 256 }, - "questionnaire_group_id": { + "date_of_birth": { "type": "string", - "format": "uuid", - "description": "Stable identifier that groups all versions of the same questionnaire." + "format": "date" }, - "version": { - "type": "integer", - "description": "Version number within the group, starting at 1." + "nationality": { + "type": "string", + "maxLength": 3 }, - "status": { + "gender": { "type": "string", - "enum": [ - "draft", - "published" - ] + "maxLength": 16 }, - "published_at": { + "address": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "When this version was published. `null` for drafts." + "maxLength": 512 }, - "is_editable": { - "type": "boolean", - "description": "True when this version is a `draft`." + "city": { + "type": "string", + "maxLength": 128 + }, + "state": { + "type": "string", + "maxLength": 128 + }, + "postal_code": { + "type": "string", + "maxLength": 32 } } }, - "SimpleQuestionnaireRequest": { + "NetworkNote": { "type": "object", - "description": "Create a simple linear questionnaire. The API converts `form_elements` into the internal questionnaire graph.", "required": [ - "title", - "languages", - "form_elements" + "uuid", + "actor_email", + "comment", + "client_reference", + "created_at" ], "properties": { - "title": { + "uuid": { "type": "string", - "description": "Internal name for the questionnaire." + "format": "uuid", + "readOnly": true }, - "description": { + "actor_email": { "type": "string", - "nullable": true - }, - "languages": { - "type": "array", - "description": "Languages included in every label. Must include `en`.", - "items": { - "type": "string" - }, - "example": [ - "en" - ] + "format": "email", + "readOnly": true, + "description": "Authenticated author, assigned by the server." }, - "default_language": { + "comment": { "type": "string", - "description": "Default language. Must be included in `languages`.", - "example": "en" - }, - "is_active": { - "type": "boolean", - "default": true + "minLength": 1, + "maxLength": 10000 }, - "status": { + "client_reference": { "type": "string", - "enum": [ - "draft", - "published" - ], - "description": "Omit this field to create a published questionnaire ready for workflows." + "format": "uuid" }, - "form_elements": { - "type": "array", - "minItems": 1, - "description": "Questions in the exact order users should see them. Branching and conditional fields are not supported.", - "items": { - "$ref": "#/components/schemas/SimpleQuestionnaireElement" - } + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true } } }, - "SimpleQuestionnaireUpdateRequest": { + "CaseActionEntityTypeEnum": { + "enum": [ + "user", + "business" + ], + "type": "string", + "description": "* `user` - user\n* `business` - business" + }, + "CaseRoutingCondition": { "type": "object", - "description": "Update questionnaire metadata or replace the full linear `form_elements` list.", "properties": { - "title": { - "type": "string" - }, - "description": { + "field": { "type": "string", - "nullable": true - }, - "languages": { - "type": "array", - "items": { - "type": "string" - } - }, - "default_language": { - "type": "string" - }, - "is_active": { - "type": "boolean" + "enum": [ + "source", + "priority", + "tags" + ] }, - "status": { + "operator": { "type": "string", "enum": [ - "draft", - "published" + "equals", + "in", + "contains" ] }, - "form_elements": { - "type": "array", - "minItems": 1, - "description": "If provided, this replaces the full linear question list.", - "items": { - "$ref": "#/components/schemas/SimpleQuestionnaireElement" - } + "value": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] } - } - }, - "SimpleQuestionnaireElement": { - "type": "object", + }, "required": [ - "element_type", - "label" + "field", + "operator", + "value" ], + "description": "Source and priority support equals or in with supported values. Tags support contains with one non-empty tag." + }, + "RoutingPublicationPolicyRevision": { + "type": "object", "properties": { - "id": { - "type": "string", - "description": "Optional stable question id. If omitted, the API generates `q1`, `q2`, etc. Use lowercase letters, numbers, and underscores for readability.", - "example": "source_of_funds" + "version": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "element_type": { + "snapshot": {}, + "actor_email": { "type": "string", - "description": "Question type. The API accepts these lowercase values and their uppercase equivalents.", - "enum": [ - "short_text", - "long_text", - "dropdown", - "single_choice", - "multiple_choice", - "number", - "image", - "file_upload", - "time", - "email", - "address", - "phone", - "country", - "date_picker", - "consent", - "section_header", - "separator", - "heading", - "paragraph" - ] + "format": "email", + "nullable": true, + "maxLength": 254 }, - "label": { - "type": "object", - "description": "Question label by language code, for example `{ \"en\": \"Source of funds\" }`.", - "additionalProperties": { - "type": "string" - } + "published_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "published_at", + "snapshot", + "version" + ] + }, + "CasePublicationPolicyRevision": { + "type": "object", + "properties": { + "version": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "description": { - "type": "object", + "snapshot": {}, + "actor_email": { + "type": "string", + "format": "email", "nullable": true, - "additionalProperties": { - "type": "string" - } + "maxLength": 254 }, - "placeholder": { - "type": "object", - "nullable": true, - "additionalProperties": { - "type": "string" - } + "published_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "published_at", + "snapshot", + "version" + ] + }, + "FraudCheckIdentityRelationship": { + "type": "object", + "properties": { + "result": { + "$ref": "#/components/schemas/FraudResultEnum" }, - "is_required": { - "type": "boolean", - "default": false + "reference_type": { + "type": "string" }, - "options": { + "country": { + "type": "string" + }, + "availability": { + "$ref": "#/components/schemas/FraudAvailabilityEnum" + }, + "reason_codes": { "type": "array", - "description": "Use for `dropdown`, `single_choice`, and `multiple_choice`. If `value` is omitted, the API generates one from the option label.", "items": { - "type": "object", - "required": [ - "label" - ], - "properties": { - "value": { - "type": "string" - }, - "label": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } + "type": "string" } - }, - "max_files": { - "type": "integer", - "minimum": 1, - "maximum": 5, - "description": "Use with `file_upload` or `image`." - }, - "validation": { - "type": "object", - "nullable": true, - "description": "Optional format validation for `short_text`, `long_text`, or `number`. Enforced while the user fills the field (they cannot proceed until it passes) and re-checked by the API on submission.", - "properties": { - "pattern": { - "type": "string", - "description": "Regular expression (ECMA-262 subset) the value must fully match. For `short_text` and `long_text`. Do not add ^/$ anchors; the whole value is matched." - }, - "min_length": { - "type": "integer", - "minimum": 0, - "description": "Minimum number of characters. For `short_text` and `long_text`." - }, - "max_length": { - "type": "integer", - "minimum": 0, - "description": "Maximum number of characters. For `short_text` and `long_text`." - }, - "min": { - "type": "number", - "description": "Minimum numeric value. For `number`." - }, - "max": { - "type": "number", - "description": "Maximum numeric value. For `number`." - }, - "error_message": { - "type": "object", - "description": "Translated message shown when validation fails, keyed by language code.", - "additionalProperties": { - "type": "string" - } - } + } + }, + "required": [ + "availability", + "country", + "reason_codes", + "reference_type", + "result" + ] + }, + "CasePublicationPaginatedPolicyRevisionList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { + "type": "integer", + "example": 123 + }, + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=400&limit=100" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=200&limit=100" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CasePublicationPolicyRevision" } } } }, - "QuestionnaireDetail": { + "CaseDuplicateBlankEnum": { + "enum": [ + "" + ] + }, + "FraudCheckPlan": { "type": "object", - "description": "Full questionnaire detail returned from GET, POST (create) and PATCH (update) endpoints.", "properties": { - "questionnaire_id": { - "type": "string", - "format": "uuid", - "description": "Unique identifier of the questionnaire. Use this id when referencing the questionnaire from a workflow or in subsequent update/delete requests." - }, - "title": { + "profile_id": { "type": "string" }, - "description": { - "type": "string", - "nullable": true + "profile_version": { + "type": "string" }, - "languages": { + "modules": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/FraudCheckPlannedModule" } }, - "default_language": { - "type": "string" + "estimated_price": { + "type": "string", + "nullable": true }, - "is_active": { + "currency": { + "type": "string", + "nullable": true + }, + "is_sandbox": { "type": "boolean" }, - "is_simple_questionnaire": { + "can_afford": { "type": "boolean" + } + }, + "required": [ + "can_afford", + "currency", + "estimated_price", + "is_sandbox", + "modules", + "profile_id", + "profile_version" + ] + }, + "CaseReviewTeamUpdate": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100 }, - "graph": { - "type": "object", - "description": "Graph structure with start_node and nodes map." + "description": { + "type": "string", + "maxLength": 500 }, - "sections": { + "members": { "type": "array", - "description": "Questionnaire content grouped into sections (derived from the graph).", "items": { - "type": "object" - } + "$ref": "#/components/schemas/CaseReviewTeamMember" + }, + "maxItems": 250 }, - "questionnaire_group_id": { + "is_active": { + "type": "boolean" + } + } + }, + "RoutingPublicationPolicyDraft": { + "type": "object", + "properties": { + "revision": { "type": "string", - "format": "uuid", - "description": "Stable identifier that groups all versions of the same questionnaire." + "format": "uuid" }, - "version": { - "type": "integer" + "base_version": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "status": { + "changes": {}, + "actor_email": { "type": "string", - "enum": [ - "draft", - "published" - ] + "format": "email", + "nullable": true, + "maxLength": 254 }, - "published_at": { + "updated_at": { "type": "string", "format": "date-time", - "nullable": true - }, - "is_editable": { - "type": "boolean", - "description": "True when the questionnaire can still be edited in place (draft versions)." + "readOnly": true } - } + }, + "required": [ + "base_version", + "updated_at" + ] }, - "UserListItem": { + "FraudCheckRequestRequest": { "type": "object", - "description": "A verified user.", "properties": { - "didit_internal_id": { + "client_reference": { "type": "string", - "format": "uuid", - "description": "Didit's stable internal UUID for this user." + "minLength": 1, + "description": "Your own reference for this check. Doubles as the idempotency key: reusing it with the same body returns the original check without running or billing the modules again; reusing it with a different body returns 409.", + "maxLength": 255 }, - "vendor_data": { + "profile": { "type": "string", - "nullable": true, - "description": "Your unique identifier for this user (passed when creating sessions). This can be null when no vendor identifier was supplied." + "minLength": 1, + "default": "standard", + "description": "Orchestration profile id. Defaults to the Didit `standard` profile.", + "maxLength": 64 }, - "display_name": { + "profile_version": { "type": "string", - "nullable": true, - "description": "Custom display name set by you" + "minLength": 1, + "description": "Pin a frozen profile version. Defaults to the current version of the profile.", + "maxLength": 32 }, - "full_name": { - "type": "string", - "nullable": true, - "description": "Full name extracted from verified documents" + "subject": { + "$ref": "#/components/schemas/FraudCheckSubjectRequest" }, - "date_of_birth": { + "context": { + "$ref": "#/components/schemas/FraudCheckContextRequest" + }, + "checks": { + "$ref": "#/components/schemas/FraudCheckModuleSelectionRequest" + }, + "vendor_data": { "type": "string", - "format": "date", - "nullable": true + "maxLength": 1000 }, - "effective_name": { + "metadata": {} + }, + "required": [ + "subject" + ] + }, + "CaseDuplicateCaseSubjectInputRequest": { + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/CaseDuplicateTypeEnum" + }, + "uuid": { "type": "string", - "nullable": true, - "description": "Best available name: display_name if set, otherwise full_name" + "format": "uuid" + } + }, + "required": [ + "type", + "uuid" + ] + }, + "CaseReviewTeamMember": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "format": "uuid" }, - "status": { + "availability": { "type": "string", "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED" + "available", + "unavailable" ], - "description": "Lifecycle status of the user record (NOT a session status). `ACTIVE` is the default, `FLAGGED` marks the user for manual attention, `BLOCKED` prevents new sessions for this `vendor_data`." - }, - "portrait_image_url": { - "type": "string", - "nullable": true, - "description": "Presigned URL of the user's portrait photo (expires after a few hours)" - }, - "session_count": { - "type": "integer", - "description": "Total number of verification sessions for this user" - }, - "approved_count": { - "type": "integer", - "description": "Number of approved sessions" - }, - "declined_count": { - "type": "integer", - "description": "Number of declined sessions" + "default": "available" }, - "in_review_count": { + "capacity": { "type": "integer", - "description": "Number of sessions in review" + "minimum": 1, + "maximum": 1000, + "default": 10 }, - "issuing_states": { + "skills": { "type": "array", "items": { - "type": "string" + "type": "string", + "maxLength": 64 }, - "description": "ISO 3166-1 alpha-3 codes of issuing countries seen on this user's approved ID documents, e.g. `[\"USA\", \"ESP\"]`. Empty array when none." + "maxItems": 25 }, - "approved_emails": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Verified email addresses collected from this user's approved sessions, e.g. `[\"john@example.com\"]`." + "available_from": { + "type": "string", + "format": "date-time", + "nullable": true }, - "approved_phones": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Verified phone numbers collected from this user's approved sessions, e.g. `[\"+14155551234\"]`." + "available_until": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "required": [ + "user_id" + ] + }, + "CaseRoutingPreviewRequest": { + "type": "object", + "properties": { + "source": { + "type": "string", + "enum": [ + "MANUAL", + "AML_SCREENING", + "TRANSACTION_RULE", + "WORKFLOW_BUILDER" + ] }, - "features": { - "type": "object", - "description": "Aggregated per-feature status across all of this user's sessions. Possible keys: `ID_VERIFICATION`, `NFC`, `LIVENESS`, `FACE_MATCH`, `POA`, `QUESTIONNAIRE`, `EMAIL_VERIFICATION`, `PHONE`, `AML`, `IP_ANALYSIS`, `AGE_ESTIMATION`, `DATABASE_VALIDATION`. Possible values: `Approved`, `Declined`, `In Review`, `Not Finished`, `Resub Requested`." + "priority": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "default": "MEDIUM" }, - "features_list": { + "tags": { "type": "array", "items": { - "type": "object", - "properties": { - "feature": { - "type": "string" - }, - "status": { - "type": "string" - } - } + "type": "string", + "maxLength": 64 }, - "description": "Same data as `features`, as an ordered array of `{feature, status}` objects." + "maxItems": 50 }, - "last_session_at": { + "rule_uuid": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the most recent session" + "format": "uuid" }, - "first_session_at": { + "draft": { + "$ref": "#/components/schemas/CaseRoutingRuleCreate" + } + }, + "required": [ + "source" + ] + }, + "FraudExcludeEnum": { + "enum": [ + "normalization", + "email_risk", + "email_social", + "phone_social", + "phone_risk", + "ip_risk", + "database_validation", + "networks_same_org", + "networks_cross_org", + "biometric_identity" + ], + "type": "string", + "description": "* `normalization` - Normalization\n* `email_risk` - Email Risk\n* `email_social` - Email Social\n* `phone_social` - Phone Social\n* `phone_risk` - Phone Risk\n* `ip_risk` - Ip Risk\n* `database_validation` - Database Validation\n* `networks_same_org` - Networks Same Org\n* `networks_cross_org` - Networks Cross Org\n* `biometric_identity` - Biometric Identity" + }, + "CaseDuplicateCaseLinkCreateRequest": { + "type": "object", + "properties": { + "entity_type": { + "$ref": "#/components/schemas/CaseDuplicateEntityTypeEnum" + }, + "entity_uuid": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the first session" + "format": "uuid" + } + }, + "required": [ + "entity_type", + "entity_uuid" + ] + }, + "CaseDuplicateStatusEnum": { + "enum": [ + "OPEN", + "AWAITING_USER", + "RESOLVED" + ], + "type": "string", + "description": "* `OPEN` - Open\n* `AWAITING_USER` - Awaiting user\n* `RESOLVED` - Resolved" + }, + "FraudBandEnum": { + "enum": [ + "low", + "medium", + "high", + "unknown" + ], + "type": "string", + "description": "* `low` - Low\n* `medium` - Medium\n* `high` - High\n* `unknown` - Unknown" + }, + "CaseDuplicatePriorityEnum": { + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "type": "string", + "description": "* `LOW` - Low\n* `MEDIUM` - Medium\n* `HIGH` - High" + }, + "FraudStatusEnum": { + "enum": [ + "planned", + "completed", + "skipped", + "failed" + ], + "type": "string", + "description": "* `planned` - Planned\n* `completed` - Completed\n* `skipped` - Skipped\n* `failed` - Failed" + }, + "FraudCheckInput": { + "type": "object", + "properties": { + "reference": { + "type": "string", + "description": "Redacted handle for the submitted identifier." }, - "last_activity_at": { + "type": { + "$ref": "#/components/schemas/FraudTypeEnum" + }, + "namespace": { + "type": "object", + "additionalProperties": {} + }, + "quality": { + "type": "string" + }, + "normalization": { + "type": "object", + "additionalProperties": {} + }, + "availability": { + "$ref": "#/components/schemas/FraudAvailabilityEnum" + } + }, + "required": [ + "availability", + "namespace", + "normalization", + "quality", + "reference", + "type" + ] + }, + "CustomerMessagingPurposesEnum": { + "enum": [ + "identity_document", + "proof_of_address", + "business_document", + "confirm_details", + "review_in_progress" + ], + "type": "string", + "description": "* `identity_document` - identity_document\n* `proof_of_address` - proof_of_address\n* `business_document` - business_document\n* `confirm_details` - confirm_details\n* `review_in_progress` - review_in_progress" + }, + "FraudTypeEnum": { + "enum": [ + "email", + "phone", + "national_id", + "tax_number", + "passport_number", + "driving_licence_number", + "residence_permit_number", + "voter_id", + "external_id" + ], + "type": "string", + "description": "* `email` - Email\n* `phone` - Phone\n* `national_id` - National Id\n* `tax_number` - Tax Number\n* `passport_number` - Passport Number\n* `driving_licence_number` - Driving Licence Number\n* `residence_permit_number` - Residence Permit Number\n* `voter_id` - Voter Id\n* `external_id` - External Id" + }, + "CaseDuplicateCaseSubjectInput": { + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/CaseDuplicateTypeEnum" + }, + "uuid": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the most recent activity on this user (session, transaction, status change, data edit, etc.)." + "format": "uuid" + } + }, + "required": [ + "type", + "uuid" + ] + }, + "CaseReviewTeamCreate": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100 }, - "tags": { + "description": { + "type": "string", + "maxLength": 500 + }, + "members": { "type": "array", "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "color": { - "type": "string" - } - } + "$ref": "#/components/schemas/CaseReviewTeamMember" }, - "description": "Tags assigned to this user" + "maxItems": 250 + }, + "is_active": { + "type": "boolean" + } + }, + "required": [ + "name", + "members" + ] + }, + "CaseDuplicateCaseDuplicateQueryRequest": { + "type": "object", + "properties": { + "subject": { + "$ref": "#/components/schemas/CaseDuplicateCaseSubjectInputRequest" }, - "created_at": { - "type": "string", - "format": "date-time" + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseDuplicateCaseLinkCreateRequest" + } + }, + "offset": { + "type": "integer", + "maximum": 1000000, + "minimum": 0, + "default": 0 } } }, - "BlocklistItem": { + "CaseDuplicateResolutionEnum": { + "enum": [ + "FALSE_POSITIVE", + "VALID_THREAT" + ], + "type": "string", + "description": "* `FALSE_POSITIVE` - False positive\n* `VALID_THREAT` - Valid threat" + }, + "CasePublicationPolicyDraftWriteRequest": { "type": "object", "properties": { - "type": { - "type": "string", - "enum": [ - "face", - "document", - "phone", - "email" - ] - }, - "session_id": { - "type": "string", - "format": "uuid" - }, - "session_number": { - "type": "integer" - }, - "blocklisted_at": { - "type": "string", - "format": "date-time" - }, - "face_image": { - "type": "string", - "nullable": true + "expected_version": { + "type": "integer", + "minimum": 0 }, - "document_type": { + "expected_revision": { "type": "string", + "format": "uuid", "nullable": true }, - "document_number": { + "changes": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "changes", + "expected_revision", + "expected_version" + ] + }, + "FraudCheckSubjectRequest": { + "type": "object", + "properties": { + "selfie": { "type": "string", - "nullable": true + "minLength": 1, + "description": "Base64 JPEG, PNG or WebP selfie, up to 6 MB. No reference image or URL.", + "maxLength": 8388672 }, - "phone_number": { - "type": "string", - "nullable": true + "identifiers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FraudCheckIdentifierRequest" + }, + "description": "One or more typed identifiers. One is enough.", + "maxItems": 10, + "minItems": 1 }, - "email": { - "type": "string", - "nullable": true + "attributes": { + "$ref": "#/components/schemas/FraudCheckAttributesRequest" } - } + }, + "required": [ + "identifiers" + ] }, - "UserDetailItem": { + "RoutingPublicationRoutingCondition": { "type": "object", - "description": "Full user detail. Extends UserListItem with metadata, comments, and updated_at.", - "allOf": [ - { - "$ref": "#/components/schemas/UserListItem" + "properties": { + "field": { + "$ref": "#/components/schemas/RoutingPublicationFieldEnum" }, - { - "type": "object", - "properties": { - "metadata": { - "type": "object", - "description": "Custom metadata JSON you attached to this user. Defaults to `{}`." - }, - "tags": { - "type": "array", - "description": "Tag assignments. NOTE: on detail responses each entry is a tag *link* object (`{uuid, tag: {...}, added_by_email, added_by_name, created_at}`), unlike the flat `{uuid, name, color}` shape used on list responses.", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "UUID of the tag assignment (not the tag itself)." - }, - "tag": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "color": { - "type": "string" - }, - "description": { - "type": "string", - "nullable": true - }, - "source": { - "type": "string", - "enum": [ - "didit", - "custom" - ] - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - } - } - }, - "added_by_email": { - "type": "string", - "nullable": true - }, - "added_by_name": { - "type": "string", - "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - } - }, - "comments": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "comment_type": { - "type": "string", - "enum": [ - "COMMENT", - "STATUS_CHANGED", - "METADATA_UPDATED", - "TAG_ADDED", - "TAG_REMOVED", - "PROFILE_UPDATED" - ] - }, - "comment": { - "type": "string", - "nullable": true - }, - "actor_email": { - "type": "string", - "nullable": true - }, - "actor_name": { - "type": "string", - "nullable": true - }, - "previous_status": { - "type": "string", - "nullable": true, - "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED", - null - ] - }, - "new_status": { - "type": "string", - "nullable": true, - "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED", - null - ] - }, - "previous_value": { - "type": "object", - "nullable": true, - "description": "Previous field values for PROFILE_UPDATED entries." - }, - "new_value": { - "type": "object", - "nullable": true, - "description": "New field values for PROFILE_UPDATED entries." - }, - "changed_fields": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Names of the fields changed by a PROFILE_UPDATED entry." - }, - "metadata": { - "type": "object", - "nullable": true - }, - "mentioned_emails": { - "type": "array", - "items": { - "type": "string" - } - }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - }, - "description": "Activity log and comments for this user (status changes, profile edits, manual notes)." - }, - "updated_at": { - "type": "string", - "format": "date-time" - } - } - } + "operator": { + "$ref": "#/components/schemas/RoutingPublicationOperatorEnum" + }, + "value": {} + }, + "required": [ + "field", + "operator", + "value" ] }, - "BusinessListItem": { + "CaseActionCaseActionReceipt": { "type": "object", - "description": "A verified business.", "properties": { - "didit_internal_id": { + "uuid": { "type": "string", "format": "uuid", - "description": "Didit's stable internal UUID for this business." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your unique identifier for this business (passed when creating sessions). This can be null when no vendor identifier was supplied." + "readOnly": true }, - "display_name": { + "client_reference": { "type": "string", - "nullable": true, - "description": "Custom display name set by you" + "format": "uuid" }, - "legal_name": { + "action": { "type": "string", - "nullable": true, - "description": "Official legal name from registry or manual entry" + "maxLength": 32 }, - "registration_number": { - "type": "string", - "nullable": true, - "description": "Company registration or incorporation number" + "status": { + "$ref": "#/components/schemas/CaseActionStatusEnum" }, - "country_code": { + "result": {}, + "error": { "type": "string", - "nullable": true, - "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `US`)." + "maxLength": 255 }, - "effective_name": { + "actor_email": { "type": "string", + "format": "email", "nullable": true, - "description": "Best available name: display_name if set, otherwise legal_name" - }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "FLAGGED", - "BLOCKED" - ], - "description": "Lifecycle status of the business record (NOT a session status). `ACTIVE` is the default, `FLAGGED` marks it for manual attention, `BLOCKED` prevents new sessions for this `vendor_data`." - }, - "session_count": { - "type": "integer", - "description": "Total number of verification sessions for this business" - }, - "approved_count": { - "type": "integer", - "description": "Number of approved sessions" + "maxLength": 254 }, - "declined_count": { + "attempts": { "type": "integer", - "description": "Number of declined sessions" + "maximum": 2147483647, + "minimum": 0 }, - "in_review_count": { - "type": "integer", - "description": "Number of sessions in review" + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "features": { - "type": "object", - "description": "Aggregated per-feature status across all of this business's KYB sessions. Currently the only key is `KYB` (the registry company check status). Possible values: `Approved`, `Declined`, `In Review`, `Not Finished`, `Resub Requested`." + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "action", + "client_reference", + "created_at", + "status", + "updated_at", + "uuid" + ] + }, + "FraudCheckContextRequest": { + "type": "object", + "properties": { + "ip_address": { + "type": "string", + "minLength": 1 }, - "features_list": { - "type": "array", - "items": { - "type": "object", - "properties": { - "feature": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "description": "Same data as `features`, as an ordered array of `{feature, status}` objects." + "user_agent": { + "type": "string", + "maxLength": 512 }, - "last_session_at": { + "country": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the most recent session" + "description": "Country the subject claims to be in. Contextual only - never proof of identity ownership.", + "maxLength": 3 }, - "first_session_at": { + "purpose": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the first session" + "maxLength": 128 }, - "last_activity_at": { + "consent_obtained": { + "type": "boolean" + } + } + }, + "CustomerMessagingCaseCustomerResponseResult": { + "type": "object", + "properties": { + "responded_at": { "type": "string", - "format": "date-time", - "nullable": true, - "description": "Timestamp of the most recent activity (status change, session update, etc.)" + "format": "date-time" + } + }, + "required": [ + "responded_at" + ] + }, + "CustomerMessagingCaseRequestContact": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" }, - "tags": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "color": { - "type": "string" - } - } - }, - "description": "Tags assigned to this business" + "email": { + "type": "string", + "format": "email" }, - "created_at": { + "verified_at": { "type": "string", "format": "date-time" } + }, + "required": [ + "email", + "uuid", + "verified_at" + ] + }, + "FraudClientCredentialsForbiddenErrorMessage": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "default": "Forbidden - User doesn't have permission" + } } }, - "BusinessDetailItem": { + "CasePublicationAssignmentMethodEnum": { + "enum": [ + "MANUAL", + "AUTOMATIC" + ], + "type": "string", + "description": "* `MANUAL` - Manual\n* `AUTOMATIC` - Automatic" + }, + "CaseDuplicateNullEnum": { + "enum": [ + null + ] + }, + "CustomerMessagingLanguageEnum": { + "enum": [ + "en", + "es", + "pt" + ], + "type": "string", + "description": "* `en` - en\n* `es` - es\n* `pt` - pt" + }, + "CaseDuplicateCaseFraudSourceContextRequest": { "type": "object", - "description": "Full business detail. Extends BusinessListItem with metadata and updated_at.", - "allOf": [ - { - "$ref": "#/components/schemas/BusinessListItem" + "properties": { + "check_id": { + "type": "string", + "format": "uuid" }, - { - "type": "object", - "properties": { - "metadata": { - "type": "object", - "description": "Custom metadata JSON you attached to this business. Defaults to `{}`." - }, - "updated_at": { - "type": "string", - "format": "date-time" - } - } + "request_id": { + "type": "string", + "format": "uuid" + }, + "source_section": { + "type": "string", + "minLength": 1, + "pattern": "^[A-Z][A-Z0-9_:-]{0,63}$" } + }, + "required": [ + "check_id", + "request_id", + "source_section" ] }, - "TransactionDetail": { + "FraudCheckPlannedModule": { "type": "object", - "description": "Full monitoring record for one transaction, as returned by `GET /v3/transactions/{transaction_id}/` and `POST /v3/transactions/`.", "properties": { - "uuid": { - "type": "string", - "format": "uuid", - "description": "Didit-stable transaction identifier. Use as `{transaction_id}` for follow-up calls." + "module": { + "$ref": "#/components/schemas/FraudExcludeEnum" }, - "environment": { + "available": { + "type": "boolean" + }, + "availability_reason": { "type": "string", - "enum": [ - "live", - "sandbox" - ], - "description": "Environment of the application that owns the transaction. A `sandbox` transaction is stored but never evaluated, screened, notified or billed, so its empty `rule_runs` means \"not evaluated\"." + "nullable": true }, - "transaction_number": { - "type": "integer", - "description": "Application-scoped sequential transaction number shown in Console (e.g. `4123`)." + "will_run": { + "type": "boolean" }, - "txn_id": { + "selection_reason": { "type": "string", - "description": "The `transaction_id` you supplied at create time (max 128 chars). Unique per application." + "nullable": true }, - "txn_date": { + "estimated_price": { "type": "string", - "format": "date-time", - "description": "When the transaction occurred (from `transaction_at`; defaults to submission time)." + "nullable": true + } + }, + "required": [ + "availability_reason", + "available", + "estimated_price", + "module", + "selection_reason", + "will_run" + ] + }, + "CustomerMessagingCaseRequestOptions": { + "type": "object", + "properties": { + "contacts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomerMessagingCaseRequestContact" + } }, - "zone_id": { - "type": "string", - "nullable": true, - "description": "IANA time zone identifier provided at create time." + "purposes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomerMessagingPurposesEnum" + } }, - "transaction_type": { - "type": "string", - "description": "Top-level category as stored: `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`, `gamblingLimitChange`, `gamblingBonusChange`, `auditTrailEvent`. Note multi-word values are echoed back in camelCase even when submitted in snake_case." + "languages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomerMessagingLanguagesEnum" + } + } + }, + "required": [ + "contacts", + "languages", + "purposes" + ] + }, + "CaseCollaborationPaginatedCaseNoteList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { + "type": "integer", + "example": 123 }, - "action_type": { + "next": { "type": "string", - "description": "Sub-type within the category (e.g. `deposit`, `withdrawal`, `transfer`). Defaults to the category when not supplied." + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=400&limit=100" }, - "direction": { + "previous": { "type": "string", - "enum": [ - "INBOUND", - "OUTBOUND" - ], - "description": "Direction relative to the subject. Stored uppercase regardless of the casing submitted (`in`/`out`/`inbound`/`outbound` are accepted on input)." + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?offset=200&limit=100" }, - "status": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseCollaborationCaseNote" + } + } + } + }, + "FraudProcessingStatusEnum": { + "enum": [ + "pending", + "running", + "completed", + "failed" + ], + "type": "string", + "description": "* `pending` - Pending\n* `running` - Running\n* `completed` - Completed\n* `failed` - Failed" + }, + "CasePublicationCaseBlueprint": { + "type": "object", + "properties": { + "uuid": { "type": "string", - "enum": [ - "APPROVED", - "IN_REVIEW", - "DECLINED", - "AWAITING_USER" - ], - "description": "Current monitoring verdict. Transactions are created `APPROVED`; rules may flip them to `IN_REVIEW`/`DECLINED` synchronously." + "format": "uuid", + "readOnly": true }, - "source": { + "name": { "type": "string", - "enum": [ - "LIVE", - "IMPORTED" - ], - "description": "How the record originated: `LIVE` for transactions submitted through the API or SDK, `IMPORTED` for transactions loaded through the console import." + "maxLength": 24 }, - "amount": { + "description": { "type": "string", - "description": "Transaction amount as a decimal string with trailing zeros stripped (e.g. `\"1500\"`, `\"0.5\"`, `\"0.123456789012345678\"`). Up to 18 decimal places." + "nullable": true }, - "currency": { - "type": "string", - "description": "Currency code of `amount`. Any ISO 4217 alphabetic code is accepted (e.g. `EUR`, `USD`, `NGN`), as is a crypto asset symbol (e.g. `BTC`). Prefer a currently-circulating code. Not every accepted code has an exchange rate: when none is available for the code (for example the ISO accounting unit `XSU`, or a crypto symbol Didit does not price), automatic conversion fails and `preferred_currency_amount` is returned null unless the request supplied a pre-converted `amount_in_default_currency`." + "assignment_method": { + "$ref": "#/components/schemas/CasePublicationAssignmentMethodEnum" }, - "currency_type": { - "type": "string", - "nullable": true, - "description": "`fiat` or `crypto`, as submitted in `currency_kind`." + "assignee_pool": {}, + "max_active_cases": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true }, - "amount_in_default_currency": { - "type": "string", - "nullable": true, - "description": "Pre-converted amount you supplied, as a decimal string with trailing zeros stripped." + "escalation_enabled": { + "type": "boolean" }, - "default_currency_code": { + "escalation_blueprint": { "type": "string", + "format": "uuid", "nullable": true }, - "preferred_currency_amount": { + "escalation_blueprint_name": { "type": "string", - "nullable": true, - "description": "`amount` converted to the application's preferred currency, when available." + "readOnly": true }, - "preferred_currency_code": { + "transfer_enabled": { + "type": "boolean" + }, + "four_eyes_checker_blueprint": { "type": "string", + "format": "uuid", "nullable": true }, - "payment_details": { + "four_eyes_checker_blueprint_name": { "type": "string", - "nullable": true, - "description": "Free-text payment reference or memo from the submission." + "readOnly": true }, - "payment_txn_id": { - "type": "string", - "nullable": true, - "description": "External payment system reference (e.g. blockchain transaction hash) from `payment_reference_id`." + "require_resolution_note": { + "type": "boolean" }, - "score": { + "content_config": {}, + "version": { "type": "integer", - "description": "Risk score accumulated by rules (typically 0–100). Higher = riskier." - }, - "severity": { - "type": "string", - "nullable": true, - "enum": [ - "UNKNOWN", - "LOW", - "MEDIUM", - "HIGH", - "CRITICAL" - ], - "description": "Categorical risk severity set by rules/providers (`UNKNOWN`, `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`). `null` until something sets it." + "maximum": 2147483647, + "minimum": 0 }, - "decision_reason_code": { - "type": "string", - "nullable": true, - "description": "Machine-readable reason for the current `status`." + "default_deadline_hours": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true }, - "decision_reason_label": { - "type": "string", - "nullable": true, - "description": "Human-readable label for `decision_reason_code`." + "is_active": { + "type": "boolean" }, - "vendor_data": { + "preset_key": { "type": "string", "nullable": true, - "description": "Convenience copy of the applicant's `vendor_data`." - }, - "metadata": { - "type": "object", - "description": "Snapshot of the `subject` and `counterparty` payloads as submitted at create time." - }, - "props": { - "type": "object", - "description": "The `custom_properties` you supplied at create time. Each key is addressable in rule conditions as `custom_values.`." - }, - "tags": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "color": { - "type": "string" - }, - "description": { - "type": "string", - "nullable": true - }, - "source": { - "type": "string", - "enum": [ - "didit", - "custom" - ], - "description": "Whether the tag is a Didit preset or a custom tag." - } - } - }, - "description": "Tags attached to the transaction (manually or by rules)." - }, - "parties": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "role": { - "type": "string", - "enum": [ - "APPLICANT", - "REMITTER", - "BENEFICIARY", - "COUNTERPARTY" - ], - "description": "Party role. The create endpoint assigns `APPLICANT` (the subject) and `COUNTERPARTY`." - }, - "entity_type": { - "type": "string", - "enum": [ - "individual", - "company", - "external", - "unhostedWallet" - ] - }, - "kind": { - "type": "string", - "enum": [ - "USER", - "BUSINESS", - "EXTERNAL" - ], - "description": "`USER`/`BUSINESS` parties link back to a Didit-owned entity via `vendor_data`. `EXTERNAL` parties have no Didit entity behind them." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your internal user/business identifier." - }, - "full_name": { - "type": "string", - "nullable": true - }, - "first_name": { - "type": "string", - "nullable": true - }, - "last_name": { - "type": "string", - "nullable": true - }, - "country_code": { - "type": "string", - "nullable": true, - "description": "Country code from the submitted `address.country` (typically ISO 3166-1 alpha-3)." - }, - "dob": { - "type": "string", - "format": "date", - "nullable": true, - "description": "Date of birth from the submitted `date_of_birth`." - }, - "address": { - "type": "object", - "description": "Address object as submitted. `{}` when omitted." - }, - "institution_info": { - "type": "object", - "description": "Institution details as submitted. `{}` when omitted." - }, - "device": { - "type": "object", - "description": "Normalized device context, including server-side IP enrichment under network_context (geolocation, VPN/data-center flags) when an IP was provided.", - "properties": { - "fingerprint": { - "type": "string", - "description": "Unique device fingerprint." - }, - "persistent_device_id": { - "type": "string", - "description": "Stable cross-session device identifier (v2 fingerprint schema)." - }, - "user_agent": { - "type": "string", - "description": "Browser or app user agent string." - }, - "browser_family": { - "type": "string", - "description": "Browser family (e.g. Chrome, Firefox, Safari)." - }, - "browser_version": { - "type": "string", - "description": "Browser version string." - }, - "platform": { - "type": "string", - "description": "Platform identifier (web, ios, android, macos, windows, linux)." - }, - "os_family": { - "type": "string", - "description": "Operating system family (e.g. iOS, Android, Windows)." - }, - "os_version": { - "type": "string", - "description": "Operating system version string." - }, - "accept_language": { - "type": "string", - "description": "Accept-Language header value." - }, - "session_id": { - "type": "string", - "description": "Session identifier (stored in metadata)." - }, - "session_age_ms": { - "type": "number", - "description": "Session age in milliseconds." - }, - "network_context": { - "type": "object", - "description": "Network context with IP address and geo-location.", - "properties": { - "ip_address": { - "type": "string", - "description": "Client IP address." - }, - "ip_country_code": { - "type": "string", - "description": "Geo-resolved country of the IP (ISO 3166-1 alpha-3)." - }, - "latitude": { - "type": "number", - "description": "Geographic latitude of the IP." - }, - "longitude": { - "type": "number", - "description": "Geographic longitude of the IP." - } - } - } - } - }, - "external_party_snapshot": { - "type": "object", - "nullable": true, - "description": "Frozen copy of the party data for `EXTERNAL` parties. `null` while the party is linked to a Didit user or business." - } - } - }, - "description": "Transaction parties (applicant, counterparty)." - }, - "payment_methods": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "payment_method_type": { - "type": "string", - "enum": [ - "bank_card", - "bank_account", - "e_wallet", - "crypto_wallet", - "unhosted_wallet", - "other" - ] - }, - "label": { - "type": "string", - "nullable": true - }, - "fingerprint": { - "type": "string", - "nullable": true, - "description": "SHA-256 over `type:account_id:issuing_country` — stable identifier to correlate reuse of the same instrument." - }, - "account_id": { - "type": "string", - "nullable": true, - "description": "Account identifier (IBAN, wallet address, card token, …)." - }, - "issuing_country": { - "type": "string", - "nullable": true - }, - "owner_kind": { - "type": "string", - "enum": [ - "USER", - "BUSINESS", - "EXTERNAL" - ] - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your identifier for the owning user/business." - }, - "owner_name": { - "type": "string", - "nullable": true - }, - "institution_info": { - "type": "object" - }, - "details": { - "type": "object", - "description": "Raw `payment_method` object as submitted." - }, - "external_owner_snapshot": { - "type": "object", - "nullable": true - }, - "role": { - "type": "string", - "enum": [ - "SOURCE", - "DESTINATION", - "FUNDING", - "BENEFICIARY" - ], - "description": "How this method was used in the transaction. The create endpoint assigns `SOURCE`/`DESTINATION` from the direction (outbound: subject = SOURCE; inbound: subject = DESTINATION)." - } - } - }, - "description": "Payment methods linked to the transaction." + "maxLength": 64 }, - "documents": { + "creation_sources": { "type": "array", - "description": "Files attached to the transaction from the console. `[]` when none.", "items": { "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "label": { - "type": "string", - "nullable": true - }, - "note": { - "type": "string", - "nullable": true - }, - "uploaded_by": { - "type": "string", - "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "files": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "file_url": { - "type": "string", - "format": "uri", - "nullable": true, - "description": "Short-lived signed download URL; fetch it when needed rather than storing it." - }, - "original_filename": { - "type": "string", - "nullable": true - }, - "file_size": { - "type": "integer", - "nullable": true - } - } - } - } - } - } + "additionalProperties": {} + }, + "readOnly": true }, - "activities": { + "receives_escalation_from": { "type": "array", "items": { "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "activity_type": { - "type": "string", - "description": "E.g. `TRANSACTION_CREATED`, `TRANSACTION_NOTE_ADDED`, `TRANSACTION_STATUS_CHANGED`." - }, - "source": { - "type": "string" - }, - "status": { - "type": "string", - "nullable": true - }, - "title": { - "type": "string", - "nullable": true - }, - "description": { - "type": "string", - "nullable": true - }, - "metadata": { - "type": "object" - }, - "occurred_at": { - "type": "string", - "format": "date-time" - } - } + "additionalProperties": {} }, - "description": "Activity log entries (creation, notes, manual reviews, status changes)." + "readOnly": true }, - "alerts": { + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "created_at", + "creation_sources", + "escalation_blueprint_name", + "four_eyes_checker_blueprint_name", + "name", + "receives_escalation_from", + "updated_at", + "uuid" + ] + }, + "CaseRoutingRuleUpdate": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 500 + }, + "priority": { + "type": "integer", + "minimum": 0 + }, + "conditions": { "type": "array", "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string", - "nullable": true - }, - "severity": { - "type": "string", - "enum": [ - "UNKNOWN", - "LOW", - "MEDIUM", - "HIGH", - "CRITICAL" - ] - }, - "status": { - "type": "string", - "enum": [ - "OPEN", - "INVESTIGATING", - "AWAITING_USER", - "PENDING_SAR", - "SAR_FILED", - "RESOLVED", - "DISMISSED" - ] - }, - "source": { - "type": "string", - "enum": [ - "RULE", - "PROVIDER", - "MANUAL" - ] - }, - "metadata": { - "type": "object" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "due_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } + "$ref": "#/components/schemas/CaseRoutingCondition" }, - "description": "Alerts raised by rules and providers." + "maxItems": 20 }, - "rule_runs": { + "required_skills": { "type": "array", "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "rule": { - "type": "string", - "format": "uuid", - "description": "UUID of the rule that ran." - }, - "rule_title": { - "type": "string" - }, - "rule_description": { - "type": "string", - "nullable": true - }, - "rule_category": { - "type": "string" - }, - "matched": { - "type": "boolean", - "description": "Whether the rule's conditions matched this transaction. A run is recorded only for matched rules, so live evaluations always carry `true`." - }, - "is_test": { - "type": "boolean", - "description": "True when the rule ran in TEST mode (no effect on status/score)." - }, - "mode": { - "type": "string", - "enum": [ - "ACTIVE", - "DISABLED", - "TEST" - ] - }, - "severity": { - "type": "string", - "nullable": true - }, - "score_delta": { - "type": "integer", - "description": "Score added by this rule's `add_score` actions." - }, - "status_target": { - "type": "string", - "nullable": true, - "description": "Status set by a `change_status` action, if any." - }, - "action_summary": { - "type": "string", - "nullable": true, - "description": "Summary of the actions the rule executed, joined with `; ` (e.g. `score +30; change_status:IN_REVIEW; add_tag:high_risk_country`). Parts: `score +N`, `change_status:`, `add_tag:`, `add_note`, `add_to_list`, `open_case`. `null` when the rule executed no action." - }, - "metadata": { - "type": "object", - "description": "Evaluation detail: `scope_match`, `conditions[]` and `aggregations[]`. Each condition entry carries `field`, `operator`, `expected_value`, the `current_value` read from the transaction, `matched`, `group_index` and `group_logic`. Each aggregation entry (velocity / count / sum) carries `metric`, `field`, `operator`, `expected_value`, the `computed_value` the aggregate evaluated to, `window`, `filters`, `matched` and `sample_references`; deferred async aggregations have `computed_value: null` and `deferred: true`." - }, - "created_at": { - "type": "string", - "format": "date-time" - } - } + "type": "string", + "maxLength": 64 }, - "description": "Per-rule execution results — which rule fired, with what score impact." + "maxItems": 25 }, - "provider_results": { + "blueprint": { + "type": "string", + "format": "uuid" + }, + "team": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "is_active": { + "type": "boolean" + }, + "expected_version": { + "type": "integer", + "minimum": 1, + "writeOnly": true, + "description": "Version opened by the editor. When supplied, stale writes return 409 without changing the rule." + } + } + }, + "CustomerMessagingCaseRequestPreviewRequest": { + "type": "object", + "properties": { + "contact_id": { + "type": "string", + "format": "uuid" + }, + "purpose": { + "$ref": "#/components/schemas/CustomerMessagingPurposeEnum" + }, + "language": { + "allOf": [ + { + "$ref": "#/components/schemas/CustomerMessagingLanguageEnum" + } + ], + "default": "en" + } + }, + "required": [ + "contact_id", + "purpose" + ] + }, + "CaseDuplicateCaseFraudSourceContext": { + "type": "object", + "properties": { + "check_id": { + "type": "string", + "format": "uuid" + }, + "request_id": { + "type": "string", + "format": "uuid" + }, + "source_section": { + "type": "string", + "pattern": "^[A-Z][A-Z0-9_:-]{0,63}$" + } + }, + "required": [ + "check_id", + "request_id", + "source_section" + ] + }, + "RoutingPublicationOperatorEnum": { + "enum": [ + "equals", + "in", + "contains" + ], + "type": "string", + "description": "* `equals` - equals\n* `in` - in\n* `contains` - contains" + }, + "CaseRoutingPreview": { + "type": "object", + "properties": { + "rule_uuid": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "rule_name": { + "type": "string", + "nullable": true + }, + "rule_version": { + "type": "integer", + "nullable": true + }, + "blueprint_uuid": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "team_uuid": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "assignee_user_id": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "reason": { + "type": "string" + }, + "candidates": { "type": "array", "items": { "type": "object", "properties": { - "uuid": { + "user_id": { "type": "string", "format": "uuid" }, - "provider": { - "type": "string" - }, - "result_type": { - "type": "string", - "enum": [ - "FIAT_MONITORING", - "WALLET_SCREENING", - "TRANSACTION_SCREENING", - "TRAVEL_RULE", - "NETWORK_GRAPH", - "CUSTOM" - ] - }, - "status": { - "type": "string" - }, - "severity": { - "type": "string", - "nullable": true - }, - "score": { - "type": "integer" - }, - "summary": { - "type": "string", - "nullable": true - }, - "payload": { - "type": "object", - "description": "Raw provider response payload." - }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - }, - "description": "Raw provider payloads (AML screening, blockchain analytics, etc.)." - }, - "travel_rule": { - "type": "object", - "nullable": true, - "description": "Travel Rule transfer or check. Populated when `travel_rule_details` was submitted, and also when Travel Rule is enabled and the transaction was created with `transaction_category: travelRule` (Didit initiates the exchange itself, even without `travel_rule_details`). `null` when no transfer or check exists.", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string" - }, - "protocol": { - "type": "string", - "nullable": true - }, - "required": { - "type": "boolean" - }, - "obligations_count": { - "type": "integer" - }, - "originator_data": { - "type": "object" - }, - "beneficiary_data": { - "type": "object" - }, - "metadata": { - "type": "object" - } - } - }, - "network_snapshot": { - "type": "object", - "nullable": true, - "description": "Graph snapshot of related transactions/parties used by the rules engine. Either the `network_snapshot` submitted with the transaction or the graph Didit builds from a successful AML wallet screening (which replaces a submitted one). `null` when neither exists.", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "nodes": { - "type": "array", - "items": { - "type": "object" - } - }, - "edges": { - "type": "array", - "items": { - "type": "object" - } - }, - "metrics": { - "type": "object" - } - } - }, - "remediation": { - "type": "object", - "nullable": true, - "description": "Remediation session offered to the user when re-verification is required. Superseded by `action_required`, which is the canonical block; kept for backward compatibility.", - "properties": { - "session_id": { - "type": "string" - }, - "session_token": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "status": { - "type": "string" - } - } - }, - "action_required": { - "type": "object", - "nullable": true, - "description": "Pending end-user action required to complete this transaction, or null when there is none. The Didit SDKs auto-launch the action by default and refresh the transaction when it finishes. Two variants, discriminated by `type`: `verification_session` (a hosted verification session created by a rule action) and `wallet_ownership` (a wallet-ownership widget session auto-minted for a Travel Rule transfer that needs proof of wallet control, gated by the `auto_wallet_verification` Travel Rule setting). When both could apply, `wallet_ownership` takes precedence because it blocks the Travel Rule exchange.", - "oneOf": [ - { - "title": "Verification session", - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "verification_session" - ] - }, - "url": { - "type": "string", - "format": "uri", - "description": "Hosted verification URL to open for the end user." - }, - "session_id": { - "type": "string", - "format": "uuid", - "description": "Verification session id." - }, - "session_token": { - "type": "string", - "description": "Session token, used by the mobile SDKs to launch the flow natively." - }, - "status": { - "type": "string", - "description": "Verification session status." - } + "eligible": { + "type": "boolean" }, - "required": [ - "type", - "url" - ] - }, - { - "title": "Wallet ownership", - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "wallet_ownership" - ] - }, - "url": { - "type": "string", - "format": "uri", - "description": "Hosted wallet-ownership widget URL. Open it in a real browser surface - on mobile use SFSafariViewController or a Chrome Custom Tab, never an embedded webview." - }, - "widget_session_id": { - "type": "string", - "format": "uuid", - "description": "Wallet-ownership widget session id." - }, - "expires_at": { - "type": "string", - "format": "date-time", - "description": "When the widget session expires." - } + "active_cases": { + "type": "integer", + "nullable": true }, - "required": [ - "type", - "url" - ] + "reason": { + "type": "string", + "nullable": true + } } - ] + } }, - "cost_breakdown": { - "type": "object", - "nullable": true, - "description": "Cost of this transaction: `total_price` and `items`, one per charge, each with `usage_type`, `price`, `details` and `created_at`. A re-screen of the same transaction is a new item." + "matched_rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "priority": { + "type": "integer" + } + } + } } } }, - "TravelRuleSettingsDetail": { + "FraudDimensionEnum": { + "enum": [ + "identity", + "contact", + "device_network", + "behavioral", + "fraud_network" + ], + "type": "string", + "description": "* `identity` - Identity\n* `contact` - Contact\n* `device_network` - Device Network\n* `behavioral` - Behavioral\n* `fraud_network` - Fraud Network" + }, + "FraudBadRequestErrorMessage": { "type": "object", "properties": { - "is_enabled": { - "type": "boolean", - "description": "Whether the managed Travel Rule negotiation flow is active for outbound `travelRule` transactions on this application." - }, - "jurisdiction": { + "detail": { "type": "string", - "maxLength": 8, - "description": "Your VASP's operating jurisdiction code. Default `\"EU\"`." + "default": "Bad Request - Invalid request data" + } + } + }, + "FraudNextActionEnum": { + "enum": [ + "none", + "manual_review", + "step_up", + "resubmit_capture" + ], + "type": "string", + "description": "* `none` - None\n* `manual_review` - Manual Review\n* `step_up` - Step Up\n* `resubmit_capture` - Resubmit Capture" + }, + "RoutingPublicationPolicyDraftPreconditionRequest": { + "type": "object", + "properties": { + "expected_version": { + "type": "integer", + "minimum": 0 }, - "name_matching_strictness": { + "expected_revision": { "type": "string", - "enum": [ - "NONE", - "STRICT", - "DEFAULT", - "FUZZY" - ], - "description": "How strictly beneficiary names must match before a transfer can reach COMPLETED." + "format": "uuid", + "nullable": true + } + }, + "required": [ + "expected_revision", + "expected_version" + ] + }, + "FraudCheckRisk": { + "type": "object", + "properties": { + "dimension": { + "$ref": "#/components/schemas/FraudDimensionEnum" }, - "confirmation_timeout_hours": { + "score": { "type": "integer", - "minimum": 1, - "description": "Hours an AWAITING_COUNTERPARTY transfer may remain unconfirmed before the periodic sweeper expires it. Default 48." + "nullable": true }, - "timeout_outcome": { - "type": "string", - "enum": [ - "HOLD", - "REJECT", - "PROCEED" - ], - "description": "What happens to the underlying transaction when a transfer expires. Default HOLD." + "band": { + "$ref": "#/components/schemas/FraudBandEnum" }, - "threshold_amount": { - "type": "string", - "description": "Decimal string. Minimum transfer amount that triggers a managed exchange; transfers below it resolve straight to NOT_APPLICABLE. Defaults to \"0.00\" (EU TFR applies no minimum amount)." + "availability": { + "$ref": "#/components/schemas/FraudAvailabilityEnum" }, - "legal_name": { + "requested_but_unavailable": { + "type": "boolean", + "description": "True when the request supplied everything this dimension needed and the platform still could not answer it. Such a check is never recommended for approval on the strength of the dimensions that did answer." + }, + "reason_codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "evidence_indexes": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "availability", + "band", + "dimension", + "evidence_indexes", + "reason_codes", + "requested_but_unavailable", + "score" + ] + }, + "CaseDuplicateSourceEnum": { + "enum": [ + "MANUAL", + "AML_SCREENING", + "TRANSACTION_RULE", + "WORKFLOW_BUILDER" + ], + "type": "string", + "description": "* `MANUAL` - Manual\n* `AML_SCREENING` - AML screening\n* `TRANSACTION_RULE` - Transaction rule\n* `WORKFLOW_BUILDER` - Workflow builder" + }, + "CaseCollaborationPatchedCaseNoteUpdateRequest": { + "type": "object", + "properties": { + "is_pinned": { + "type": "boolean" + } + } + }, + "CustomerMessagingPurposeEnum": { + "enum": [ + "identity_document", + "proof_of_address", + "business_document", + "confirm_details", + "review_in_progress" + ], + "type": "string", + "description": "* `identity_document` - identity_document\n* `proof_of_address` - proof_of_address\n* `business_document` - business_document\n* `confirm_details` - confirm_details\n* `review_in_progress` - review_in_progress" + }, + "CaseDuplicateTypeEnum": { + "enum": [ + "user", + "business" + ], + "type": "string", + "description": "* `user` - user\n* `business` - business" + }, + "CaseCollaborationCaseNoteCreateRequest": { + "type": "object", + "properties": { + "comment": { "type": "string", - "description": "Your VASP's legal name. Required (non-blank) to set is_enabled to true." + "minLength": 1, + "maxLength": 10000 }, - "lei": { + "reply_to": { "type": "string", - "description": "Your Legal Entity Identifier, surfaced in the VASP directory." + "format": "uuid", + "nullable": true }, - "compliance_email": { + "client_reference": { "type": "string", - "format": "email", - "description": "Compliance contact email for your VASP profile." + "format": "uuid", + "nullable": true }, - "is_discoverable": { - "type": "boolean", - "description": "Whether your VASP profile appears in GET /v3/travel-rule/vasps/ search results and can be reached on the INTERNAL rail. Default true." + "mentioned_emails": { + "type": "array", + "items": { + "type": "string", + "format": "email", + "minLength": 1 + } }, - "inbound_auto_accept": { + "is_internal": { "type": "boolean", - "description": "When true, inbound transfers whose beneficiary wallet is a verified address-book entry with a matching name are accepted automatically, without a manual review step. Default false." + "default": false }, - "allow_self_declaration": { - "type": "boolean", - "description": "When true, the wallet-ownership widget offers self-declaration as a fallback proof method. Default false." + "attachments": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + } }, - "allow_screenshot_proof": { - "type": "boolean", - "description": "When true, the wallet-ownership widget offers screenshot upload as an ownership evidence method. Default true." + "tags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + } + }, + "required": [ + "comment" + ] + }, + "CustomerMessagingLanguagesEnum": { + "enum": [ + "en", + "es", + "pt" + ], + "type": "string", + "description": "* `en` - en\n* `es` - es\n* `pt` - pt" + }, + "CustomerMessagingCaseRequestConfirmRequest": { + "type": "object", + "properties": { + "contact_id": { + "type": "string", + "format": "uuid" }, - "auto_wallet_verification": { - "type": "boolean", - "description": "When true, submitting an outbound Travel Rule transfer that needs end-user proof of wallet control auto-mints a wallet-ownership widget session bound to the transaction and exposes it in the transaction's action_required block. Default true." + "purpose": { + "$ref": "#/components/schemas/CustomerMessagingPurposeEnum" }, - "vasp_attribution_enabled": { - "type": "boolean", - "description": "When true, an outbound Travel Rule transfer that no rail can route is attributed through blockchain analytics: the destination wallet is resolved to its owning VASP, the entity is recorded on the transfer and in the VASP directory, and the rails are re-run once with the attributed name. Default true." + "language": { + "allOf": [ + { + "$ref": "#/components/schemas/CustomerMessagingLanguageEnum" + } + ], + "default": "en" }, - "travel_address": { + "request_id": { + "type": "string", + "format": "uuid" + }, + "expires_at": { "type": "string", - "description": "Read-only. Your VASP's own travel address - the encoded inbound TRP inquiry endpoint other VASPs use to reach you. Populated once your VASP profile is complete." + "format": "date-time" }, - "disabled_networks": { + "preview_token": { + "type": "string", + "minLength": 1, + "maxLength": 512 + } + }, + "required": [ + "contact_id", + "expires_at", + "preview_token", + "purpose", + "request_id" + ] + }, + "CaseRoutingRule": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 500 + }, + "priority": { + "type": "integer", + "minimum": 0 + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseRoutingCondition" + }, + "maxItems": 20 + }, + "required_skills": { "type": "array", "items": { "type": "string", - "enum": [ - "GTR", - "TRUST", - "VERIFYVASP", - "SYGNA" - ] + "maxLength": 64 }, - "description": "Networks opted out of platform membership. Does not disable your connected own memberships or provision unavailable networks.", - "default": [] + "maxItems": 25 }, - "networks": { - "type": "object", - "readOnly": true, - "description": "Effective membership configuration for each network. No network, including GTR, is included by default. This is not a live health or delivery check.", - "properties": { - "gtr": { - "type": "object", - "properties": { - "rail": { - "type": "string", - "enum": [ - "GTR" - ] - }, - "mode": { - "type": "string", - "enum": [ - "own", - "didit", - "off", - "unavailable" - ], - "description": "own: connected customer membership takes precedence; didit: configured platform membership allowed; off: platform routing disabled with no own membership; unavailable: neither membership available." - }, - "platform_available": { - "type": "boolean", - "description": "Whether platform membership is configured; not a live health check." - }, - "connected": { - "type": "boolean", - "description": "Whether a connected customer membership exists. Credentials are validated locally; network validation occurs on a live exchange." - } - } - }, - "trust": { - "type": "object", - "properties": { - "rail": { - "type": "string", - "enum": [ - "TRUST" - ] - }, - "mode": { - "type": "string", - "enum": [ - "own", - "didit", - "off", - "unavailable" - ], - "description": "own: connected customer membership takes precedence; didit: configured platform membership allowed; off: platform routing disabled with no own membership; unavailable: neither membership available." - }, - "platform_available": { - "type": "boolean", - "description": "Whether platform membership is configured; not a live health check." - }, - "connected": { - "type": "boolean", - "description": "Whether a connected customer membership exists. Credentials are validated locally; network validation occurs on a live exchange." - } - } - }, - "verifyvasp": { - "type": "object", - "properties": { - "rail": { - "type": "string", - "enum": [ - "VERIFYVASP" - ] - }, - "mode": { - "type": "string", - "enum": [ - "own", - "didit", - "off", - "unavailable" - ], - "description": "own: connected customer membership takes precedence; didit: configured platform membership allowed; off: platform routing disabled with no own membership; unavailable: neither membership available." - }, - "platform_available": { - "type": "boolean", - "description": "Whether platform membership is configured; not a live health check." - }, - "connected": { - "type": "boolean", - "description": "Whether a connected customer membership exists. Credentials are validated locally; network validation occurs on a live exchange." - } - } - }, - "sygna": { - "type": "object", - "properties": { - "rail": { - "type": "string", - "enum": [ - "SYGNA" - ] - }, - "mode": { - "type": "string", - "enum": [ - "own", - "didit", - "off", - "unavailable" - ], - "description": "own: connected customer membership takes precedence; didit: configured platform membership allowed; off: platform routing disabled with no own membership; unavailable: neither membership available." - }, - "platform_available": { - "type": "boolean", - "description": "Whether platform membership is configured; not a live health check." - }, - "connected": { - "type": "boolean", - "description": "Whether a connected customer membership exists. Credentials are validated locally; network validation occurs on a live exchange." - } - } - } - } + "blueprint": { + "type": "string", + "format": "uuid" + }, + "team": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "is_active": { + "type": "boolean" + }, + "uuid": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "version": { + "type": "integer", + "readOnly": true + }, + "blueprint_name": { + "type": "string", + "readOnly": true + }, + "team_name": { + "type": "string", + "nullable": true, + "readOnly": true } } }, - "TravelRuleSettingsUpdate": { + "FraudResultEnum": { + "enum": [ + "match", + "mismatch", + "inconclusive", + "no_reference" + ], + "type": "string", + "description": "* `match` - Match\n* `mismatch` - Mismatch\n* `inconclusive` - Inconclusive\n* `no_reference` - No Reference" + }, + "CaseRoutingRuleCreate": { "type": "object", "properties": { - "is_enabled": { - "type": "boolean" + "name": { + "type": "string", + "maxLength": 100 }, - "legal_name": { - "type": "string" + "description": { + "type": "string", + "maxLength": 500 }, - "lei": { - "type": "string" + "priority": { + "type": "integer", + "minimum": 0 }, - "jurisdiction": { + "conditions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseRoutingCondition" + }, + "maxItems": 20 + }, + "required_skills": { + "type": "array", + "items": { + "type": "string", + "maxLength": 64 + }, + "maxItems": 25 + }, + "blueprint": { "type": "string", - "maxLength": 8 + "format": "uuid" }, - "name_matching_strictness": { + "team": { "type": "string", - "enum": [ - "NONE", - "STRICT", - "DEFAULT", - "FUZZY" - ] + "format": "uuid", + "nullable": true }, - "confirmation_timeout_hours": { - "type": "integer", - "minimum": 1 + "is_active": { + "type": "boolean" }, - "timeout_outcome": { + "publish": { + "type": "boolean", + "writeOnly": true, + "description": "Set false to save a never-published draft. The record remains inactive at version 0 until the draft is explicitly published. Omitting this field preserves immediate creation for existing integrations." + } + }, + "required": [ + "name", + "conditions", + "blueprint" + ] + }, + "FraudCheckIdentifierRequest": { + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/FraudTypeEnum" + } + ], + "description": "Identifier type. Determines which namespace fields are required.\n\n* `email` - Email\n* `phone` - Phone\n* `national_id` - National Id\n* `tax_number` - Tax Number\n* `passport_number` - Passport Number\n* `driving_licence_number` - Driving Licence Number\n* `residence_permit_number` - Residence Permit Number\n* `voter_id` - Voter Id\n* `external_id` - External Id" + }, + "value": { "type": "string", - "enum": [ - "HOLD", - "REJECT", - "PROCEED" - ] + "minLength": 1, + "description": "The identifier value, in any customary format.", + "maxLength": 255 }, - "compliance_email": { + "country": { "type": "string", - "format": "email" + "description": "ISO-3166 alpha-2 or alpha-3 issuing country. Required for government identifier types: the same digits mean different people in different countries.", + "maxLength": 3 }, - "is_discoverable": { - "type": "boolean" + "issuer": { + "type": "string", + "description": "Optional issuing authority or sub-national issuer, when the country alone is not unique.", + "maxLength": 128 + } + }, + "required": [ + "type", + "value" + ] + }, + "CaseActionActionEnum": { + "enum": [ + "set_status", + "add_tags", + "add_note", + "add_to_blocklist" + ], + "type": "string", + "description": "* `set_status` - set_status\n* `add_tags` - add_tags\n* `add_note` - add_note\n* `add_to_blocklist` - add_to_blocklist" + }, + "CaseActionCaseActionReceiptPage": { + "type": "object", + "properties": { + "count": { + "type": "integer" }, - "threshold_amount": { + "next_offset": { + "type": "integer", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaseActionCaseActionReceipt" + } + } + }, + "required": [ + "count", + "next_offset", + "results" + ] + }, + "CaseActionCaseEntityActionResult": { + "type": "object", + "properties": { + "receipt": { + "$ref": "#/components/schemas/CaseActionCaseActionReceipt" + }, + "entity_type": { + "$ref": "#/components/schemas/CaseActionEntityTypeEnum" + }, + "entity_uuid": { "type": "string", - "description": "Decimal string. Minimum transfer amount that triggers a managed exchange." + "format": "uuid" }, - "inbound_auto_accept": { - "type": "boolean" + "action": { + "type": "string" }, - "allow_self_declaration": { - "type": "boolean" + "previous_status": { + "type": "string" }, - "allow_screenshot_proof": { - "type": "boolean", - "description": "When true, the wallet-ownership widget offers screenshot upload as an ownership evidence method. Default true." + "new_status": { + "type": "string" }, - "auto_wallet_verification": { - "type": "boolean" + "tags_added": { + "type": "array", + "items": { + "type": "string" + } }, - "vasp_attribution_enabled": { - "type": "boolean" + "comment_uuid": { + "type": "string", + "format": "uuid" }, - "disabled_networks": { + "blocklisted": { "type": "array", "items": { - "type": "string", - "enum": [ - "GTR", - "TRUST", - "VERIFYVASP", - "SYGNA" - ] - }, - "description": "Networks opted out of platform membership. Does not disable your connected own memberships or provision unavailable networks.", - "default": [] + "type": "string" + } } - }, - "description": "All fields optional; PUT applies a partial update." + } }, - "WalletAddressEntry": { + "CustomerMessagingCaseRequest": { "type": "object", "properties": { "uuid": { "type": "string", - "format": "uuid" + "format": "uuid", + "readOnly": true }, - "address": { + "purpose": { "type": "string", - "description": "The wallet address. Unique per application and chain among non-deleted entries." + "readOnly": true }, - "chain": { + "language": { "type": "string", - "nullable": true, - "description": "Free-text chain identifier, e.g. \"ethereum\", \"bitcoin\"." + "readOnly": true }, - "holder_vendor_data": { + "recipient": { "type": "string", - "nullable": true, - "description": "Your internal identifier for the holder." + "format": "email", + "readOnly": true }, - "holder_name": { + "expires_at": { "type": "string", - "nullable": true, - "description": "Name of the wallet holder, used for the beneficiary name match." + "format": "date-time", + "readOnly": true }, - "entity_type": { + "actor_email": { "type": "string", - "description": "Free text, defaults to \"individual\"." + "format": "email", + "readOnly": true, + "nullable": true }, - "is_ownership_verified": { - "type": "boolean", - "description": "Read-only. Whether ownership of this address has been proven." + "delivery_status": { + "type": "string", + "readOnly": true }, - "created_at": { + "delivered_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "readOnly": true, + "nullable": true }, - "self_declared": { + "cancelled_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "responded_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "response": { + "readOnly": true, + "nullable": true + }, + "has_attachment": { "type": "boolean", - "description": "Whether the entry was created as a self-declared ownership proof." + "readOnly": true }, - "travel_address": { + "created_at": { "type": "string", - "nullable": true, - "description": "The counterparty VASP's travel address for this wallet, if known. Lets Didit route this beneficiary's transfers over the TRP rail." + "format": "date-time", + "readOnly": true } - } + }, + "required": [ + "actor_email", + "cancelled_at", + "created_at", + "delivered_at", + "delivery_status", + "expires_at", + "has_attachment", + "language", + "purpose", + "recipient", + "responded_at", + "response", + "uuid" + ] }, - "WalletAddressEntryCreate": { + "CustomerMessagingCaseRequestPublic": { "type": "object", - "required": [ - "address" - ], "properties": { - "address": { - "type": "string" - }, - "chain": { - "type": "string" + "request_id": { + "type": "string", + "format": "uuid" }, - "holder_vendor_data": { - "type": "string" + "purpose": { + "$ref": "#/components/schemas/CustomerMessagingPurposeEnum" }, - "holder_name": { - "type": "string" + "language": { + "$ref": "#/components/schemas/CustomerMessagingLanguageEnum" }, - "entity_type": { + "expires_at": { "type": "string", - "default": "individual" - }, - "self_declared": { - "type": "boolean", - "default": false, - "description": "Write-only. When true, the entry is created already ownership-verified via a SELF_DECLARATION proof." + "format": "date-time" }, - "travel_address": { + "responded_at": { "type": "string", - "description": "Optional. The counterparty VASP's travel address for this wallet, enabling TRP routing." - } - } - }, - "WalletAddressEntryUpdate": { - "type": "object", - "properties": { - "holder_name": { - "type": "string" + "format": "date-time", + "nullable": true }, - "holder_vendor_data": { - "type": "string" + "copy": { + "type": "object", + "additionalProperties": {} }, - "entity_type": { + "organization_name": { "type": "string" }, - "travel_address": { - "type": "string" + "is_white_label": { + "type": "boolean" + }, + "customization": { + "type": "object", + "additionalProperties": {}, + "nullable": true } }, - "description": "PATCH is a partial update; the response echoes only holder_name, holder_vendor_data, entity_type, and travel_address." + "required": [ + "copy", + "customization", + "expires_at", + "is_white_label", + "language", + "organization_name", + "purpose", + "request_id", + "responded_at" + ] }, - "TravelRuleTransferDetail": { + "RoutingPublicationFieldEnum": { + "enum": [ + "source", + "priority", + "tags" + ], + "type": "string", + "description": "* `source` - source\n* `priority` - priority\n* `tags` - tags" + }, + "RoutingPublicationCaseRoutingRule": { "type": "object", - "description": "Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. originator_data/beneficiary_data are omitted while PII is masked (see description).", "properties": { "uuid": { "type": "string", - "format": "uuid" + "format": "uuid", + "readOnly": true }, - "status": { + "name": { "type": "string", - "enum": [ - "AWAITING_COUNTERPARTY", - "ON_HOLD", - "COMPLETED", - "FINISHED", - "CANCELLED", - "EXPIRED", - "COUNTERPARTY_MISMATCHED_DATA", - "COUNTERPARTY_VASP_NOT_FOUND", - "COUNTERPARTY_VASP_NOT_REACHABLE", - "UNCONFIRMED_OWNERSHIP", - "COUNTERPARTY_VASP_GENERAL_DECLINE", - "NOT_ENOUGH_COUNTERPARTY_DATA", - "NOT_APPLICABLE" - ] + "maxLength": 100 }, - "direction": { + "description": { "type": "string", - "enum": [ - "INBOUND", - "OUTBOUND" - ] + "maxLength": 500 }, - "rail": { + "priority": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RoutingPublicationRoutingCondition" + } + }, + "required_skills": { + "type": "array", + "items": { + "type": "string", + "maxLength": 64 + }, + "maxItems": 25 + }, + "blueprint": { "type": "string", - "nullable": true, - "enum": [ - "INTERNAL", - "TRP", - "GTR", - "EMAIL", - null - ] + "format": "uuid" }, - "protocol": { + "blueprint_name": { + "type": "string", + "readOnly": true + }, + "team": { "type": "string", + "format": "uuid", "nullable": true }, - "ivms_version": { + "team_name": { "type": "string", - "example": "ivms101.2023" + "readOnly": true }, - "required": { + "is_active": { "type": "boolean" }, - "ownership_confirmed": { - "type": "boolean", - "nullable": true + "version": { + "type": "integer", + "readOnly": true }, - "deadline_at": { + "created_at": { "type": "string", "format": "date-time", - "nullable": true - }, - "timeline": { - "type": "array", - "items": { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "at": { - "type": "string", - "format": "date-time" - }, - "detail": { - "type": "string" - } - } - } - }, - "counterparty_vasp": { - "type": "object", - "nullable": true, - "properties": { - "name": { - "type": "string" - }, - "lei": { - "type": "string" - }, - "dd_score": { - "type": "integer" - } - } - }, - "originator_data": { - "type": "object" - }, - "beneficiary_data": { - "type": "object" + "readOnly": true }, - "created_at": { + "updated_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "readOnly": true } - } - }, - "TravelRuleOwnershipConfirmRequest": { - "type": "object", + }, "required": [ - "confirmed" + "blueprint", + "blueprint_name", + "conditions", + "created_at", + "name", + "team_name", + "updated_at", + "uuid", + "version" + ] + }, + "CaseActionStatusEnum": { + "enum": [ + "APPLIED", + "FAILED" ], + "type": "string", + "description": "* `APPLIED` - Applied\n* `FAILED` - Failed" + }, + "FraudCheckModuleSelectionRequest": { + "type": "object", "properties": { - "confirmed": { - "type": "boolean" + "include": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FraudExcludeEnum" + } + }, + "exclude": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FraudExcludeEnum" + } } } }, - "TravelRuleTransferPatchRequest": { + "SignatureField": { "type": "object", - "description": "Provide exactly one of: payment_txn_id (finish), action=cancel, or action=resend.", + "description": "A field to place on a session document: which signer fills it, how, and where. Give either a position (`page`, `x`, `y`, `width` and `height`) or an `anchor`. Positions are fractions of the page as it's displayed, from its top-left corner, rounded to 4 decimals, and the field must fit inside the page. A document takes up to 200 fields and one signer up to 500, and every role with fields on a document needs a `signature` or `initials` field there. See [Fields on the document](/core-technology/electronic-signature/overview#fields-on-the-document).", + "required": [ + "key", + "type" + ], "properties": { - "payment_txn_id": { + "key": { "type": "string", - "description": "The on-chain transaction hash. Requires the transfer to be an outbound transfer in COMPLETED status." + "pattern": "^[a-z][a-z0-9_]{0,63}$", + "description": "Names the field in the results and in `sign.field_values`. Unique within the document." }, - "action": { + "type": { "type": "string", "enum": [ - "cancel", - "resend" + "signature", + "initials", + "full_name", + "date_signed", + "text", + "checkbox", + "email", + "data" ], - "description": "\"cancel\" cancels a non-terminal transfer. \"resend\" re-runs routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA." - } - } - }, - "VaspDirectoryEntry": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "lei": { - "type": "string" - }, - "jurisdiction": { - "type": "string" + "description": "What the field holds. The signer applies their adopted `signature` and `initials`, types `text`, ticks a `checkbox`, and types an `email` when the session has no verified email. Didit fills `full_name` (the verified name), `date_signed` (the date of signing, in the signer's time zone), `data` (from `prefill`) and a verified `email`, and shows them read-only. There is no date picker type: use `text`." }, - "dd_status": { + "role": { "type": "string", "enum": [ - "NOT_STARTED", - "IN_PROGRESS", - "COMPLETED", - "REJECTED" - ] + "verified_person", + "legal_representative", + "key_people" + ], + "description": "Who fills the field, with the `signature_signer` values. A step fills and stamps only the fields of its own signer. Defaults to the step's signer." }, - "dd_score": { - "type": "integer" + "page": { + "type": "integer", + "minimum": 1, + "description": "Page number, from 1 to the document's page count. For a Word document, a page of the converted PDF." }, - "reachable_rails": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "INTERNAL", - "TRP", - "GTR", - "EMAIL" - ] - } + "x": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Left edge, as a fraction of the page width." }, - "is_didit_customer": { + "y": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Top edge, as a fraction of the page height." + }, + "width": { + "type": "number", + "minimum": 0.02, + "maximum": 1, + "description": "Width, as a fraction of the page width. A signature is at least `0.12` wide." + }, + "height": { + "type": "number", + "minimum": 0.012, + "maximum": 1, + "description": "Height, as a fraction of the page height. A signature is at least `0.035` high." + }, + "anchor": { + "$ref": "#/components/schemas/SignatureFieldAnchor" + }, + "required": { "type": "boolean", - "description": "true for discoverable Didit applications (dd_status is always COMPLETED, dd_score always 0 for these); false for internal counterparty-registry rows, which carry a real due-diligence status and score." + "description": "`signature` and `initials` are required unless you set `false`. `full_name`, `date_signed` and `data` are always required. The signer can't finish a document until its required fields have a value, and a required checkbox must be ticked." }, - "dd_score_breakdown": { - "type": "object", - "additionalProperties": { - "type": "integer" - }, - "description": "Per-component contributions to dd_score (licensing/registry, sanctions/PEP, jurisdiction risk, on-chain analytics risk), all derived from checks Didit runs in-house. Empty for Didit-customer rows." + "label": { + "type": "string", + "minLength": 1, + "maxLength": 80, + "description": "Shown to the signer and in the results. Defaults from the type." }, - "dd_assessed_at": { + "placeholder": { "type": "string", - "format": "date-time", "nullable": true, - "description": "When the due-diligence assessment last ran. Null for Didit-customer rows." - } - } - }, - "TravelRulePickupInfo": { - "type": "object", - "properties": { - "originator_vasp_name": { - "type": "string" + "maxLength": 80, + "description": "`text` and `email` only." }, - "amount": { - "type": "string" + "validation": { + "$ref": "#/components/schemas/SignatureFieldValidation" }, - "currency": { - "type": "string" + "prefill": { + "type": "string", + "nullable": true, + "description": "Where the value comes from: `identity.full_name`, `identity.first_name`, `identity.last_name`, `identity.date_of_birth` (`DD/MM/YYYY`), `identity.nationality`, `identity.document_number`, `identity.document_type`, `identity.issuing_country`, `identity.address`, `contact.email`, `contact.phone`, `company.name`, `company.registration_number`, `company.address`, `company.country` (KYB), `session.vendor_data`, `metadata.` (a key of the session's `metadata`) or `api.` (a key of `sign.field_values`). Required for `data`. `full_name` always uses `identity.full_name` and `email` uses `contact.email`. The values of `data`, `full_name` and verified `email` fields are fixed when the signer starts signing." }, - "beneficiary_wallet": { - "type": "string" + "editable_when_prefilled": { + "type": "boolean", + "default": false, + "description": "`text` and `email` only: the signer can change a pre-filled value." }, - "status": { - "type": "string" + "checked_by_default": { + "type": "boolean", + "default": false, + "description": "`checkbox` only: the box starts ticked." + }, + "date_format": { + "type": "string", + "enum": [ + "DD/MM/YYYY", + "MM/DD/YYYY", + "YYYY-MM-DD", + "D MMMM YYYY" + ], + "default": "DD/MM/YYYY", + "description": "`date_signed` only." } } }, - "TravelRulePickupRespondRequest": { + "SessionSignDocument": { "type": "object", "required": [ - "decision" + "file_name", + "content_base64" ], "properties": { - "decision": { + "title": { "type": "string", - "enum": [ - "accept", - "decline" - ] + "minLength": 1, + "maxLength": 255, + "description": "Title shown to the signer. Defaults to `file_name`." }, - "beneficiary_name": { + "file_name": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "description": "File name, ending in `.pdf` or `.docx`." + }, + "content_base64": { "type": "string", - "description": "Required in practice for decision=accept; run through the originator's name_matching_strictness policy." + "format": "byte", + "description": "The file, base64-encoded. A PDF can have up to 20 MB and 100 pages, without a password, scripts or attached files. A Word file (`.docx`) can have up to 20 MB and is converted to PDF while the request runs, in at most 25 seconds; the PDF then has the same limits. Up to 5 Word files per session. A Word file with macros, tracked changes, links to external content or embedded objects is refused with `signature_document_not_supported`, and one that can't be converted returns `signature_document_conversion_failed`, each with a `reason`." + }, + "fields": { + "type": "array", + "description": "Optional fields to place on this document, up to 200. Pages and positions refer to the PDF, or to the converted PDF for a Word file. Without fields, the signer reads the document and agrees to it.", + "items": { + "$ref": "#/components/schemas/SignatureField" + }, + "maxItems": 200 } } }, - "TravelRuleInboundRequest": { + "SignatureResult": { "type": "object", - "required": [ - "chain", - "txid", - "wallet_address", - "amount", - "currency" - ], + "description": "One signer's result for one Electronic Signature step. See the [Electronic Signature report](/core-technology/electronic-signature/report-electronic-signature).", "properties": { - "chain": { + "id": { "type": "string", - "description": "Chain the deposit settled on, e.g. \"ethereum\"." + "format": "uuid", + "description": "Pass it as `signature_id` to download this signer's evidence or validate their signature." }, - "txid": { + "node_id": { "type": "string", - "description": "On-chain transaction hash. Deduped together with wallet_address." + "description": "The workflow step that produced it." }, - "wallet_address": { + "status": { "type": "string", - "description": "The destination wallet you received the deposit on." + "enum": [ + "intro", + "review", + "code", + "signing", + "signed", + "declined" + ], + "description": "Where the signer is in the step. Only `signed` means the documents are signed." }, - "amount": { + "verdict": { "type": "string", - "description": "Deposit amount as a decimal string." + "enum": [ + "Not Finished", + "Approved", + "In Review", + "Declined" + ], + "description": "The step's outcome. A warning sets it through its rule; custom status rules apply after." }, - "currency": { + "level": { "type": "string", - "description": "Deposit asset, e.g. \"USDC\"." + "enum": [ + "aes", + "qes" + ], + "description": "`aes`. `qes` (Qualified Electronic Signature, QES) is coming soon." }, - "originator_data": { + "format": { + "type": "string", + "enum": [ + "pades" + ] + }, + "profile": { + "type": "string", + "nullable": true, + "description": "`PAdES-B-LT` once signed." + }, + "signer": { "type": "object", - "description": "IVMS-101 originator payload from the sending VASP, if known.", - "additionalProperties": true + "properties": { + "name": { + "type": "string", + "nullable": true, + "description": "Verified name." + }, + "capacity": { + "type": "string", + "enum": [ + "self", + "company" + ] + }, + "company": { + "type": "string", + "nullable": true + }, + "role": { + "type": "string", + "nullable": true, + "description": "The key person's designation, as declared." + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The key person's structured roles, as declared for the company." + }, + "identity_session_id": { + "type": "string", + "format": "uuid", + "description": "The KYC session that verified the signer." + } + } }, - "beneficiary_data": { + "identity_used": { "type": "object", - "description": "IVMS-101 beneficiary payload (your customer).", - "additionalProperties": true + "properties": { + "session_id": { + "type": "string", + "format": "uuid" + }, + "kyc_id": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "verified": { + "type": "boolean" + }, + "expected_name": { + "type": "string", + "nullable": true, + "description": "The name the signer had to match." + } + } }, - "originating_vasp": { + "certificate": { "type": "object", - "description": "Optional description of the sending VASP.", + "nullable": true, + "description": "The certificate the documents were signed with. `null` until signed.", "properties": { - "name": { + "issuer": { "type": "string" }, - "lei": { + "issued_to": { "type": "string" }, - "travel_address": { + "serial": { + "type": "string", + "description": "Serial number, hexadecimal." + }, + "valid_from": { "type": "string", - "description": "Travel address that lets Didit reach the sender on the TRP rail." + "format": "date-time" + }, + "valid_until": { + "type": "string", + "format": "date-time" } } - } - } - }, - "TravelRuleInboundResponse": { - "type": "object", - "properties": { - "transaction_id": { - "type": "string", - "format": "uuid", - "description": "Didit transaction UUID for the inbound deposit." - }, - "txn_id": { - "type": "string", - "description": "Didit-generated txn_id for the sunrise transaction." - }, - "created": { - "type": "boolean", - "description": "true when a new transaction was minted (HTTP 201); false when an existing unclaimed deposit was reused (HTTP 200)." - }, - "travel_rule": { - "$ref": "#/components/schemas/TravelRuleTransferDetail" - } - } - }, - "TravelRuleWidgetSessionCreate": { - "type": "object", - "required": [ - "wallet_address", - "chain" - ], - "properties": { - "wallet_address": { - "type": "string", - "description": "The wallet address to verify." - }, - "chain": { - "type": "string", - "description": "Chain identifier for the address, e.g. \"ethereum\", \"bitcoin\", \"solana\"." - }, - "holder_name": { - "type": "string", - "description": "Name of the wallet holder, used for the beneficiary name match." - }, - "vendor_data": { - "type": "string", - "description": "Your internal identifier for the holder, carried onto the created address-book entry." - }, - "transaction_id": { - "type": "string", - "format": "uuid", - "description": "Optional. Didit transaction UUID of a Travel Rule transfer to advance when the proof verifies." - }, - "satoshi_deposit_address": { - "type": "string", - "description": "Optional. A deposit address you control; supplying it enables the SATOSHI_TEST proof method." - }, - "callback_url": { - "type": "string", - "description": "Optional. Where the widget returns the customer after completion." - }, - "expires_in_minutes": { - "type": "integer", - "description": "Optional. Lifetime of the minted link, in minutes. Automatically extended to cover the Satoshi test window when SATOSHI_TEST is offered." - } - } - }, - "TravelRuleWidgetSessionResponse": { - "type": "object", - "properties": { - "widget_session_id": { - "type": "string", - "format": "uuid" - }, - "token": { - "type": "string", - "description": "Opaque session token embedded in the widget URL." - }, - "url": { - "type": "string", - "description": "The hosted wallet-ownership widget URL. Open it verbatim in the system browser (never inside a mobile SDK webview)." - }, - "expires_at": { - "type": "string", - "format": "date-time" - } - } - }, - "TravelRuleWidgetContext": { - "type": "object", - "properties": { - "vasp_name": { - "type": "string", - "description": "Legal name from your VASP profile, shown to the customer in the widget. Empty when no profile exists." - }, - "wallet_address": { - "type": "string" }, - "chain": { - "type": "string" + "confirmation": { + "type": "object", + "properties": { + "channel": { + "type": "string", + "enum": [ + "email", + "sms" + ] + }, + "destination": { + "type": "string", + "nullable": true, + "description": "The masked contact the code went to." + }, + "confirmed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "attempts": { + "type": "integer", + "description": "Codes entered, the right one included." + } + } }, - "holder_name": { - "type": "string" + "timestamp": { + "type": "object", + "nullable": true, + "description": "The trusted timestamp in the signature. `null` until signed.", + "properties": { + "time": { + "type": "string", + "format": "date-time" + }, + "qualified": { + "type": "boolean", + "description": "Always `false` for AES." + }, + "valid": { + "type": "boolean" + } + } }, - "allowed_methods": { + "documents": { "type": "array", "items": { - "type": "string", - "enum": [ - "MESSAGE_SIGNING", - "SCREENSHOT", - "SATOSHI_TEST", - "SELF_DECLARATION" - ] - }, - "description": "Proof methods available for this session. MESSAGE_SIGNING requires a supported chain family, SATOSHI_TEST requires a satoshi_deposit_address on the session, SCREENSHOT requires the allow_screenshot_proof setting, and SELF_DECLARATION requires the allow_self_declaration setting." + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "The document on this signer's copy. Use it to download the signed file." + }, + "uuid": { + "type": "string", + "format": "uuid", + "description": "Same as `id`." + }, + "title": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "pages": { + "type": "integer", + "description": "Pages of the document signed. For Word, of the converted PDF." + }, + "size_bytes": { + "type": "integer" + }, + "sha256_original": { + "type": "string", + "description": "SHA-256 of the document as the signer was shown it." + }, + "sha256_signed": { + "type": "string", + "nullable": true, + "description": "SHA-256 of the signed PDF, once signed." + }, + "status": { + "type": "string", + "enum": [ + "signed", + "declined", + "not_signed", + "not_reached" + ], + "description": "`not_signed`: opened but not signed. `not_reached`: never opened." + }, + "opened_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "read_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the signer reached the end of a document without fields, when the step requires it." + }, + "read_to_end": { + "type": "boolean" + }, + "consented_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the signer agreed to a document without fields, or finished the fields of a document with fields." + }, + "consent": { + "type": "boolean" + }, + "signed_file": { + "type": "string", + "nullable": true, + "description": "Path of the signed PDF, once signed. See [Download a signed document](/sessions-api/electronic-signature/download-signed-document)." + }, + "mode": { + "type": "string", + "enum": [ + "fields", + "read_and_agree" + ], + "description": "`fields` for a document with fields, `read_and_agree` for a document without." + }, + "source_format": { + "type": "string", + "enum": [ + "pdf", + "docx" + ], + "description": "`pdf`, or `docx` for a Word document Didit converted to PDF." + }, + "conversion": { + "type": "object", + "nullable": true, + "description": "`null` for a PDF. For a Word document, the original file and the conversion.", + "properties": { + "from": { + "type": "string", + "enum": [ + "docx" + ] + }, + "file_name": { + "type": "string", + "description": "Your original Word file name." + }, + "sha256": { + "type": "string", + "description": "SHA-256 of your original Word file." + }, + "size_bytes": { + "type": "integer", + "description": "Size of your original Word file." + }, + "converter": { + "type": "string", + "description": "Version of the converter that produced the PDF." + } + } + }, + "fields": { + "type": "array", + "description": "Fields on the document, with their values. Empty for a document without fields.", + "items": { + "$ref": "#/components/schemas/SignatureFieldResult" + } + } + } + } }, - "transfer_status": { - "type": "string", + "adopted_signature": { + "type": "object", "nullable": true, - "description": "Status of the linked Travel Rule transfer. Null when the session was minted without a transaction_id." - }, - "callback_url": { - "type": "string", - "description": "Where the widget returns the customer after completion. Empty when not set." + "description": "The signature and initials the signer adopted for signature and initials fields. `null` until the signer adopts one.", + "properties": { + "method": { + "type": "string", + "enum": [ + "typed", + "drawn" + ], + "description": "`typed`: the verified name in a handwriting style. `drawn`: drawn with a finger or a mouse." + }, + "font": { + "type": "string", + "nullable": true, + "description": "The handwriting style of a typed signature. `null` when drawn." + }, + "adopted_at": { + "type": "string", + "format": "date-time" + } + } }, - "expires_at": { + "electronic_signature_consent_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "nullable": true, + "description": "When the signer agreed, on the review screen, to sign the documents electronically. Set when any document has fields." }, - "completed_at": { + "evidence_file": { "type": "string", - "format": "date-time", "nullable": true, - "description": "Non-null once a proof has been verified." + "description": "Path of the evidence PDF, once signed. See [Download the signature evidence](/sessions-api/electronic-signature/download-evidence)." }, - "proofs": { + "warnings": { "type": "array", + "description": "Warnings raised for this signer. See [Electronic Signature warnings](/core-technology/electronic-signature/warnings-electronic-signature).", "items": { - "$ref": "#/components/schemas/TravelRuleWidgetProofSummary" + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": [ + "SIGNATURE_DECLINED", + "SIGNATURE_OTP_FAILED", + "SIGNATURE_NAME_MISMATCH", + "SIGNATURE_IDENTITY_NOT_VERIFIED", + "SIGNATURE_DOCUMENT_CHANGED", + "SIGNATURE_FIELD_DATA_MISSING" + ] + }, + "node_id": { + "type": "string" + } + } + } + }, + "validation": { + "type": "object", + "nullable": true, + "description": "The last cryptographic check of the signed documents. `null` until the documents are signed. If Didit can't confirm the outcome of signing, it's `{\"valid\": null, \"reason\": \"signature_provider_outcome_unknown\", \"checked_at\": ...}` and `status` stays `signing`: treat it as not signed.", + "properties": { + "valid": { + "type": "boolean", + "nullable": true, + "description": "`true` when every document is valid. `null` when the outcome of signing couldn't be confirmed." + }, + "reason": { + "type": "string", + "description": "Set when `valid` is `null`: `signature_provider_outcome_unknown`." + }, + "checked_at": { + "type": "string", + "format": "date-time" + }, + "documents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "checked_at": { + "type": "string", + "format": "date-time" + }, + "valid": { + "type": "boolean", + "description": "`true` when the signature and the timestamp are both valid and trusted." + }, + "integrity": { + "type": "boolean", + "description": "`true` when the document is unchanged since signing." + }, + "trusted": { + "type": "boolean", + "description": "`true` when the signing certificate chains to a trusted root." + }, + "revoked": { + "type": "boolean" + }, + "trusted_list": { + "type": "object", + "description": "EU Trusted List check. For AES: `{\"status\": \"not_applicable\", \"reason\": \"aes_not_qualified\"}`.", + "properties": { + "status": { + "type": "string" + }, + "reason": { + "type": "string" + } + } + }, + "certificate": { + "type": "object", + "nullable": true, + "description": "The certificate the documents were signed with. `null` until signed.", + "properties": { + "issuer": { + "type": "string" + }, + "issued_to": { + "type": "string" + }, + "serial": { + "type": "string", + "description": "Serial number, hexadecimal." + }, + "valid_from": { + "type": "string", + "format": "date-time" + }, + "valid_until": { + "type": "string", + "format": "date-time" + } + } + }, + "timestamp": { + "type": "object", + "nullable": true, + "description": "The trusted timestamp in the signature. `null` until signed.", + "properties": { + "time": { + "type": "string", + "format": "date-time" + }, + "qualified": { + "type": "boolean", + "description": "Always `false` for AES." + }, + "valid": { + "type": "boolean" + } + } + } + } + } + } } }, - "customization": { - "type": "object", - "nullable": true, - "description": "The application's whitelabel customization (colors, logo, font family) when white label is enabled - the widget renders with the same branding as the verification flow. Null otherwise." - } - } - }, - "TravelRuleWidgetProofSummary": { - "type": "object", - "properties": { - "proof_id": { + "declined_at": { "type": "string", - "format": "uuid" + "format": "date-time", + "nullable": true }, - "method": { + "declined_action": { "type": "string", "enum": [ - "MESSAGE_SIGNING", - "SATOSHI_TEST", - "SCREENSHOT", - "SELF_DECLARATION" + "DECLINE", + "REVIEW", + "NO_ACTION" ] }, - "status": { + "signed_at": { "type": "string", - "enum": [ - "PENDING", - "VERIFIED", - "REJECTED", - "EXPIRED" - ], - "description": "PENDING on a SATOSHI_TEST proof means the deposit has been seen on-chain but has not yet reached enough confirmations; a background job automatically rechecks it and moves it to VERIFIED once it confirms, with no resubmission needed. REJECTED means a genuinely wrong deposit was submitted (absent, wrong sender, or wrong amount). EXPIRED means the proof's own test window closed before a valid deposit was seen." - }, - "wallet_address": { - "type": "string" + "format": "date-time", + "nullable": true }, - "chain": { - "type": "string" + "expires_at": { + "type": "string", + "format": "date-time", + "description": "When the signing link stops working." }, - "deposit_address": { + "email_sent_at": { "type": "string", - "nullable": true, - "description": "Deposit address the customer must send the Satoshi test amount to. Present only on a SATOSHI_TEST proof." + "format": "date-time", + "nullable": true }, - "expected_amount": { + "email_unavailable_reason": { "type": "string", - "nullable": true, - "description": "Exact on-chain amount, in the deposit's native currency, the Satoshi test deposit must match. Present only on a SATOSHI_TEST proof." + "nullable": true }, - "expires_at": { + "ip_address": { "type": "string", - "format": "date-time", "nullable": true, - "description": "Deadline for this proof's own Satoshi test window. Present only on a SATOSHI_TEST proof; the widget session's own expires_at is extended to cover it when SATOSHI_TEST is offered." + "description": "The signer's IP address when they asked for their code." }, - "challenge": { + "place": { "type": "string", "nullable": true, - "description": "Didit-issued message the customer signs with their wallet. Present only on a MESSAGE_SIGNING proof." + "description": "Not collected: always `null`." }, - "created_at": { + "place_unavailable_reason": { "type": "string", - "format": "date-time" + "nullable": true, + "enum": [ + "not_collected" + ] } } }, - "CaseSubject": { + "SignatureFieldValidation": { "type": "object", "nullable": true, - "description": "The single user or business the case is anchored to.", + "description": "Rules for a `text` field's value. The console preview, the signing flow and the API apply the same rules.", "properties": { - "type": { + "kind": { "type": "string", "enum": [ - "user", - "business" - ] + "any", + "letters", + "number", + "email", + "pattern" + ], + "description": "`letters`: letters in any script, spaces, hyphens and apostrophes. `number`: digits with an optional decimal separator. `pattern`: the value must match `pattern`." }, - "uuid": { - "type": "string", - "format": "uuid" + "min_length": { + "type": "integer", + "minimum": 0, + "maximum": 500, + "description": "From `0` up to `max_length`." }, - "name": { - "type": "string" + "max_length": { + "type": "integer", + "minimum": 1, + "maximum": 500, + "default": 120 }, - "external_id": { + "pattern": { "type": "string", "nullable": true, - "description": "The subject's vendor_data." - } - } - }, - "CaseAssignee": { - "type": "object", - "nullable": true, - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "full_name": { - "type": "string" + "minLength": 1, + "maxLength": 200, + "description": "Required with `kind: \"pattern\"`. A regular expression the whole value must match. Lookarounds and backreferences aren't supported." }, - "email": { + "message": { "type": "string", - "format": "email" + "nullable": true, + "maxLength": 120, + "description": "Shown to the signer instead of the default error." } } }, - "CaseBlueprintSummary": { + "SignatureFieldAnchor": { "type": "object", - "nullable": true, + "description": "Places a field by a piece of text in the document instead of a position. Didit doesn't remove the text from the PDF, so write it in the same color as the page background. When the text isn't found, the call returns `400` with `field_anchor_not_found`. See [Place fields by anchor text](/sessions-api/create-session#place-fields-by-anchor-text).", + "required": [ + "text" + ], "properties": { - "uuid": { + "text": { "type": "string", - "format": "uuid" + "minLength": 1, + "maxLength": 64, + "description": "Text searched in the document. For a Word file, in the converted PDF." }, - "name": { + "occurrence": { "type": "string", - "maxLength": 24 - } - } - }, - "CaseContentConfig": { - "type": "object", - "description": "The six togglable sections that drive which tabs and fields appear on the case page. Unknown sections or keys are rejected.", - "properties": { - "checklist": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "optional": { - "type": "boolean", - "description": "When true, a case can be resolved with incomplete checklist items." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "label": { - "type": "string", - "maxLength": 255 - }, - "order": { - "type": "integer" - } - } - } - } - } - }, - "aml_control": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "financial": { - "type": "boolean" - }, - "identity": { - "type": "boolean" - } - } + "enum": [ + "first", + "all" + ], + "default": "first", + "description": "`all` places one field per match; the keys after the first get `_2`, `_3` and so on." }, - "identity": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "personal_information": { - "type": "boolean" - }, - "applicant_tags": { - "type": "boolean" - }, - "contact_information": { - "type": "boolean" - }, - "risk_overview": { - "type": "boolean" - }, - "poi_documents": { - "type": "boolean" - }, - "poa_documents": { - "type": "boolean" - } - } + "offset_x": { + "type": "number", + "description": "Added to the left edge of the text, as a fraction of the page width." }, - "verifications": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "summary": { - "type": "boolean" - } - } + "offset_y": { + "type": "number", + "description": "Added to the top edge of the text, as a fraction of the page height." }, - "reporting": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "fiu_reports": { - "type": "boolean" - } - } + "width": { + "type": "number", + "minimum": 0.02, + "maximum": 1, + "description": "Width of the field, as a fraction of the page width." }, - "financial": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "payment_methods": { - "type": "boolean" - }, - "transactions": { - "type": "boolean" - } - } + "height": { + "type": "number", + "minimum": 0.012, + "maximum": 1, + "description": "Height of the field, as a fraction of the page height." } } }, - "CaseNote": { + "SignatureFieldResult": { "type": "object", + "description": "A field of a signed document, with its value on this signer's copy. See [Fields](/core-technology/electronic-signature/report-electronic-signature#fields).", "properties": { - "uuid": { + "key": { "type": "string", - "format": "uuid" + "description": "The field's key, as used in `sign.field_values`." }, - "comment": { - "type": "string" - }, - "actor_email": { + "type": { "type": "string", - "format": "email", - "nullable": true + "enum": [ + "signature", + "initials", + "full_name", + "date_signed", + "text", + "checkbox", + "email", + "data" + ] }, - "actor_name": { + "role": { "type": "string", - "nullable": true - }, - "mentioned_emails": { - "type": "array", - "items": { - "type": "string", - "format": "email" - } - }, - "is_internal": { - "type": "boolean" - }, - "attachments": { - "type": "array", - "items": { - "type": "object", - "properties": { - "s3_key": { - "type": "string" - }, - "filename": { - "type": "string" - }, - "file_size": { - "type": "integer" - }, - "url": { - "type": "string", - "format": "uri" - } - } - } + "enum": [ + "verified_person", + "legal_representative", + "key_people" + ] }, - "tags": { - "type": "array", - "items": { - "type": "string" - } + "label": { + "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - }, - "CaseEvent": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" + "page": { + "type": "integer", + "description": "Page number, from 1. For a Word document, a page of the converted PDF." }, - "event_type": { - "type": "string", - "enum": [ - "CREATED", - "STATUS_CHANGED", - "ASSIGNED", - "UNASSIGNED", - "RESOLVED", - "REOPENED", - "LINK_ADDED", - "LINK_REMOVED", - "NOTE_ADDED", - "PRIORITY_CHANGED", - "DUE_DATE_CHANGED", - "TAG_ADDED", - "TAG_REMOVED", - "ESCALATED", - "TRANSFERRED", - "CHECKLIST_COMPLETED", - "REPORT_CREATED", - "REPORT_FINALIZED", - "SUBMITTED_FOR_APPROVAL", - "APPROVAL_GRANTED", - "APPROVAL_REJECTED", - "ENTITY_ACTION", - "APPROVAL_INVALIDATED" - ] + "x": { + "type": "number", + "description": "Left edge, as a fraction of the page width." }, - "actor_email": { - "type": "string", - "format": "email", - "nullable": true + "y": { + "type": "number", + "description": "Top edge, as a fraction of the page height." }, - "actor_name": { - "type": "string", - "nullable": true + "width": { + "type": "number", + "description": "Width, as a fraction of the page width." }, - "previous_status": { - "type": "string", - "enum": [ - "OPEN", - "AWAITING_USER", - "RESOLVED" - ], - "nullable": true + "height": { + "type": "number", + "description": "Height, as a fraction of the page height." }, - "new_status": { - "type": "string", - "enum": [ - "OPEN", - "AWAITING_USER", - "RESOLVED" - ], - "nullable": true + "required": { + "type": "boolean" }, - "metadata": { - "type": "object" + "value": { + "nullable": true, + "description": "A string for `text`, `email`, `full_name`, `date_signed` and `data`, a boolean for a `checkbox`, and `true` for an applied signature or initials. `null` when empty." }, - "created_at": { - "type": "string", - "format": "date-time" - } - } - }, - "CaseLink": { - "type": "object", - "properties": { - "uuid": { + "display_value": { "type": "string", - "format": "uuid" + "nullable": true, + "description": "The value as stamped on the page: dates in the field's `date_format`, `Yes` or `No` for a checkbox, `Signed` for a signature or initials." }, - "linked_entity_type": { + "value_source": { "type": "string", + "nullable": true, "enum": [ + "signer", + "identity", + "contact", + "company", "session", - "business_session", - "transaction" + "metadata", + "api", + "signing_time", + null ], - "nullable": true + "description": "Where the value came from: `signer` (typed, ticked or applied by the signer), `identity` (the verified ID), `contact` (the verified email or phone), `company` (the verified company), `session` (`vendor_data`), `metadata`, `api` (`sign.field_values`) or `signing_time` (the date signed)." }, - "linked_entity_uuid": { + "filled_at": { "type": "string", - "format": "uuid", + "format": "date-time", "nullable": true }, - "linked_entity_summary": { - "type": "object", + "image_url": { + "type": "string", "nullable": true, - "description": "session_id/session_number/status for session and business_session links; uuid/txn_id/status for transaction links." + "description": "Signature and initials only: the path of the applied image. See [Download a signature image](/sessions-api/electronic-signature/download-field-image)." }, - "created_at": { + "value_unavailable_reason": { "type": "string", - "format": "date-time" + "nullable": true, + "enum": [ + "source_empty", + null + ], + "description": "`source_empty` when a pre-filled field had no value in the session and `signature_field_data_missing_action` let the signer sign anyway." } } }, - "CaseChecklistItem": { + "VaspDirectoryWidgetEntry": { "type": "object", + "description": "What the end user's VASP picker shows: who the VASP is and whether it can be reached.", "properties": { - "uuid": { + "id": { "type": "string", - "format": "uuid" + "format": "uuid", + "description": "The entry's id, for GET /v3/travel-rule/vasps/{vasp_id}/." }, - "label": { + "kind": { "type": "string", - "maxLength": 255 + "enum": [ + "directory", + "didit" + ], + "description": "directory for an entry of the shared directory; didit for a member of the Didit network." }, - "is_completed": { - "type": "boolean" + "name": { + "type": "string" }, - "completed_by": { - "type": "string", - "format": "email", - "nullable": true + "lei": { + "type": "string" }, - "completed_at": { + "jurisdiction": { "type": "string", - "format": "date-time", - "nullable": true + "description": "ISO 3166 country code, as the source gives it." }, - "order": { - "type": "integer" + "domains": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The VASP's web domains. Always empty for Didit network members." + }, + "reachable": { + "type": "boolean", + "description": "Whether your application has an available route to this VASP, or to one of its entities for a brand. Always true for a Didit network member." } } }, - "CaseBlueprintListItem": { + "TravelRuleDataSharingFields": { "type": "object", + "description": "The fields Didit sends to the counterparty VASP about your own users, per entity type. The `name` and the `wallet_address` are always sent, whatever the list says. An entity type you never chose shares every field. An empty list is a choice too: it shares the name and the wallet only.", "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string", - "maxLength": 24 - }, - "description": { - "type": "string", - "nullable": true - }, - "assignment_method": { - "type": "string", - "enum": [ - "MANUAL", - "AUTOMATIC" - ] + "individual": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "name", + "wallet_address", + "address", + "national_id", + "customer_identification", + "date_of_birth", + "place_of_birth" + ] + }, + "description": "Fields shared about a natural person." }, - "assignee_pool": { + "company": { "type": "array", "items": { "type": "string", - "format": "uuid" + "enum": [ + "name", + "wallet_address", + "address", + "national_id", + "customer_identification" + ] + }, + "description": "Fields shared about a legal person." + } + } + }, + "TravelRuleMatchingRules": { + "type": "object", + "description": "How strictly each field of the counterparty's data is compared with what you hold, per entity type. A field you leave out keeps its default. `NONE` skips the comparison, `STRICT` needs exactly the same words, `CONTAINS` accepts one value whose words all appear in the other, `DEFAULT` is the standard subset match, and `FUZZY` accepts close spellings (and, for dates of birth, a swapped day and month).", + "properties": { + "individual": { + "type": "object", + "description": "Rules for a natural person.", + "properties": { + "name": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ], + "description": "Default: follows `name_matching_strictness`." + }, + "date_of_birth": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ], + "description": "Default `NONE`." + }, + "address": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ], + "description": "Default `NONE`." + }, + "account": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ], + "description": "The wallet the exchange was routed by. Default `STRICT`." + } } }, - "max_active_cases": { - "type": "integer", - "nullable": true - }, - "escalation_enabled": { - "type": "boolean" - }, - "transfer_enabled": { - "type": "boolean" - }, - "require_resolution_note": { - "type": "boolean" - }, - "is_active": { - "type": "boolean" + "company": { + "type": "object", + "description": "Rules for a legal person.", + "properties": { + "name": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ], + "description": "Default: follows `name_matching_strictness`." + }, + "address": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ], + "description": "Default `NONE`." + }, + "account": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ], + "description": "The wallet the exchange was routed by. Default `STRICT`." + } + } + } + } + }, + "TravelRuleDataMatch": { + "type": "object", + "nullable": true, + "description": "Field-by-field comparison of the transfer's party data with what the other side knows about the wallet holder. It names fields and results, never values, so it stays readable while the counterparty's data is masked. Null until a comparison ran.", + "properties": { + "matched": { + "type": "boolean", + "description": "True when no compared field differs." }, - "preset_key": { - "type": "string", - "nullable": true + "mismatched_fields": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "name", + "date_of_birth", + "address", + "account" + ] + }, + "description": "The fields that differ." }, - "created_at": { + "fields": { + "type": "object", + "description": "One entry per field compared for the party's entity type (`name`, `date_of_birth`, `address`, `account`; a company has no `date_of_birth`).", + "additionalProperties": { + "type": "object", + "properties": { + "result": { + "type": "string", + "enum": [ + "matched", + "mismatched", + "not_compared" + ], + "description": "`not_compared` when the rule is `NONE` or either side has no value for the field." + }, + "strictness": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ] + } + } + } + } + } + }, + "TravelRuleDataUnmasked": { + "type": "object", + "nullable": true, + "description": "Audit record of the moment the counterparty's data was unmasked. Null while the data is still masked, and for outbound transfers.", + "properties": { + "at": { "type": "string", "format": "date-time" }, - "version": { - "type": "integer", - "readOnly": true + "reason": { + "type": "string", + "enum": [ + "ownership_confirmed", + "wallet_proof", + "address_book_match", + "own_verified_wallet", + "inquiry_approved", + "known_payment_method", + "travel_address" + ], + "description": "What confirmed ownership: ownership_confirmed (your confirmation through the console or the API), wallet_proof (a wallet proof in the widget, or an INTERNAL inbound transfer, set when it is created), address_book_match (a verified address-book wallet), own_verified_wallet (your user's own verified wallet), inquiry_approved (your approval of an inbound TRP inquiry or a GTR or Sygna message), known_payment_method (a wallet your user already paid with) or travel_address (the per-wallet travel address named the wallet)." }, - "default_deadline_hours": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 8760 + "actor": { + "type": "string", + "description": "Who confirmed it: the console user's email or the API key's label. Blank for an automatic decision." } } }, - "CaseBlueprintDetail": { + "TravelRuleMessageAttachment": { "type": "object", + "nullable": true, + "description": "The document attached to a message, stored like any other transaction document.", "properties": { "uuid": { "type": "string", "format": "uuid" }, - "name": { - "type": "string", - "maxLength": 24 - }, - "description": { + "label": { "type": "string", "nullable": true }, - "assignment_method": { + "note": { "type": "string", - "enum": [ - "MANUAL", - "AUTOMATIC" - ] - }, - "assignee_pool": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - }, - "max_active_cases": { - "type": "integer", "nullable": true }, - "escalation_enabled": { - "type": "boolean" - }, - "transfer_enabled": { - "type": "boolean" - }, - "require_resolution_note": { - "type": "boolean" - }, - "is_active": { - "type": "boolean" - }, - "preset_key": { + "uploaded_by": { "type": "string", - "nullable": true + "nullable": true, + "description": "Who attached it. In the pickup portal, a file you sent shows your VASP name, never the person or API key behind it." }, "created_at": { "type": "string", "format": "date-time" }, - "escalation_blueprint": { - "type": "string", - "format": "uuid", - "nullable": true - }, - "escalation_blueprint_name": { - "type": "string", - "nullable": true - }, - "four_eyes_checker_blueprint": { - "type": "string", - "format": "uuid", - "nullable": true - }, - "four_eyes_checker_blueprint_name": { - "type": "string", - "nullable": true - }, - "content_config": { - "type": "object", - "description": "The six togglable sections that drive which tabs and fields appear on the case page. Unknown sections or keys are rejected.", - "properties": { - "checklist": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "optional": { - "type": "boolean", - "description": "When true, a case can be resolved with incomplete checklist items." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "label": { - "type": "string", - "maxLength": 255 - }, - "order": { - "type": "integer" - } - } - } - } - } - }, - "aml_control": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "financial": { - "type": "boolean" - }, - "identity": { - "type": "boolean" - } - } - }, - "identity": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "personal_information": { - "type": "boolean" - }, - "applicant_tags": { - "type": "boolean" - }, - "contact_information": { - "type": "boolean" - }, - "risk_overview": { - "type": "boolean" - }, - "poi_documents": { - "type": "boolean" - }, - "poa_documents": { - "type": "boolean" - } - } - }, - "verifications": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "summary": { - "type": "boolean" - } - } - }, - "reporting": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "fiu_reports": { - "type": "boolean" - } - } - }, - "financial": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "payment_methods": { - "type": "boolean" - }, - "transactions": { - "type": "boolean" - } - } - } - } - }, - "creation_sources": { + "files": { "type": "array", - "description": "Connectors currently routing new cases into this blueprint.", "items": { "type": "object", "properties": { - "kind": { + "uuid": { "type": "string", - "enum": [ - "aml", - "rule", - "workflow" - ] + "format": "uuid" }, - "id": { - "type": "string" + "file_url": { + "type": "string", + "nullable": true, + "description": "Time-limited download URL." }, - "name": { - "type": "string" - } - } - } - }, - "receives_escalation_from": { - "type": "array", - "description": "Other blueprints whose Escalation target points at this one.", - "items": { - "type": "object", - "properties": { - "id": { + "original_filename": { "type": "string" }, - "name": { - "type": "string" + "file_size": { + "type": "integer" } } } - }, - "updated_at": { - "type": "string", - "format": "date-time" - }, - "version": { - "type": "integer", - "readOnly": true - }, - "default_deadline_hours": { - "type": "integer", - "nullable": true, - "minimum": 1, - "maximum": 8760 } } }, - "CaseListItem": { + "TravelRuleMessage": { "type": "object", "properties": { - "uuid": { + "id": { "type": "string", "format": "uuid" }, - "case_number": { - "type": "string", - "nullable": true, - "description": "Auto-assigned, e.g. CS-0001." - }, - "title": { - "type": "string" - }, - "status": { + "kind": { "type": "string", "enum": [ - "OPEN", - "AWAITING_USER", - "RESOLVED" + "MESSAGE", + "DOCUMENT_REQUEST" ] }, - "resolution": { + "direction": { "type": "string", "enum": [ - "FALSE_POSITIVE", - "VALID_THREAT" + "INBOUND", + "OUTBOUND" ], - "nullable": true + "description": "`OUTBOUND` is what the reader's own side sent. In the pickup portal the direction is from the counterparty's side." }, - "priority": { + "author": { "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ] + "description": "Who wrote it on its own side: a console user's email or an API key label, or the other VASP's name." }, - "source": { - "type": "string", - "enum": [ - "MANUAL", - "AML_SCREENING", - "TRANSACTION_RULE", - "WORKFLOW_BUILDER" - ] + "body": { + "type": "string" }, - "tags": { + "requested_documents": { "type": "array", "items": { "type": "string" - } - }, - "subject": { - "$ref": "#/components/schemas/CaseSubject" - }, - "assigned_to": { - "$ref": "#/components/schemas/CaseAssignee" + }, + "description": "What a DOCUMENT_REQUEST asks for, in the requester's words." }, - "assigned_at": { + "reply_to": { "type": "string", - "format": "date-time", + "format": "uuid", "nullable": true }, - "due_at": { + "fulfilled_at": { "type": "string", "format": "date-time", - "nullable": true + "nullable": true, + "description": "Set on a DOCUMENT_REQUEST once a reply to it carries a document." }, - "resolved_at": { - "type": "string", - "format": "date-time", - "nullable": true + "attachment": { + "$ref": "#/components/schemas/TravelRuleMessageAttachment" }, - "created_by_email": { + "created_at": { "type": "string", - "nullable": true - }, - "created_by_name": { + "format": "date-time" + } + } + }, + "TravelRuleMessageCreateRequest": { + "type": "object", + "description": "Send as JSON, or as multipart/form-data with the attachments in `files`.", + "properties": { + "kind": { "type": "string", - "nullable": true + "enum": [ + "MESSAGE", + "DOCUMENT_REQUEST" + ], + "default": "MESSAGE" }, - "blueprint": { - "$ref": "#/components/schemas/CaseBlueprintSummary" + "body": { + "type": "string", + "maxLength": 5000, + "description": "Required for a MESSAGE unless a file is attached." }, - "links_count": { - "type": "integer" + "requested_documents": { + "type": "array", + "items": { + "type": "string", + "maxLength": 255 + }, + "maxItems": 10, + "description": "Required (at least one) for a DOCUMENT_REQUEST, and only allowed there." }, - "notes_count": { - "type": "integer" + "reply_to": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "A message of the same transfer. A reply that carries a document fulfils the document request it answers." }, - "created_at": { + "files": { + "type": "array", + "items": { + "type": "string", + "format": "binary" + }, + "maxItems": 5, + "description": "Multipart only. Up to 5 files of 10 MB each." + } + } + }, + "SessionDeletionJob": { + "type": "object", + "description": "A bulk session deletion running in the background: progress and counts per outcome, never which sessions it deleted.", + "properties": { + "job_id": { "type": "string", - "format": "date-time" + "format": "uuid", + "description": "Poll `GET /v3/sessions/delete/{job_id}/` with this id." }, - "updated_at": { + "status": { "type": "string", - "format": "date-time" + "enum": [ + "PENDING", + "PROCESSING", + "COMPLETED", + "FAILED" + ], + "description": "`PENDING`: accepted, not started yet. `PROCESSING`: deleting; `processed` and `outcomes` are updating. `COMPLETED`: every selected session is accounted for. `FAILED`: the job stopped early; see `error`." }, - "application_id": { + "total": { + "type": "integer", + "description": "Sessions selected when the deletion was requested." + }, + "processed": { + "type": "integer", + "description": "Sessions the job has finished with so far." + }, + "outcomes": { + "type": "object", + "description": "How many sessions ended in each outcome. Every key is always present.", + "properties": { + "deleted": { + "type": "integer", + "description": "Sessions deleted together with their face embedding." + }, + "none": { + "type": "integer", + "description": "Sessions deleted that had no face embedding." + }, + "retained_with_user": { + "type": "integer", + "description": "Sessions deleted while one image-free biometric template was retained for their User, as the request or the application policy asked." + }, + "ineligible_no_vendor_user": { + "type": "integer", + "description": "Sessions deleted, with their face embedding, because retention was requested but they had no linked User." + }, + "failed_retryable": { + "type": "integer", + "description": "Sessions reported as not deleted. A job retries a group it could not delete by itself instead of counting it here." + }, + "already_deleted": { + "type": "integer", + "description": "Selected sessions that were already deleted, for example by another request, when the job reached them." + } + } + }, + "deleted_child_verifications": { + "type": "integer", + "description": "Verifications that the deleted business verifications created, deleted with them. Always `0` for a job started by `POST /v3/sessions/delete/`, which deletes KYC sessions only." + }, + "error": { "type": "string", - "format": "uuid" + "nullable": true, + "description": "`null` unless `status` is `FAILED`; then why the deletion stopped. The sessions the job had not reached are still in place: repeat the request to delete them." }, - "application_name": { - "type": "string" + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the deletion was requested." }, - "organization_id": { + "completed_at": { "type": "string", - "format": "uuid" + "format": "date-time", + "nullable": true, + "description": "When the job finished; `null` while it runs." + } + } + }, + "EntityStatusUpdateRequest": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "FLAGGED", + "BLOCKED" + ], + "description": "New lifecycle status. `BLOCKED` also adds the `vendor_data` to the system blocklist." + } + } + }, + "AMLMonitoringToggleResult": { + "oneOf": [ + { + "type": "object", + "required": [ + "users_total", + "already_enabled", + "with_existing_aml", + "needing_screening", + "skipped_no_identity_data", + "skipped_blocked", + "estimated_screening_cost_usd", + "estimated_annual_monitoring_cost_usd", + "balance_sufficient", + "unmatched" + ], + "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", + "properties": { + "users_total": { + "type": "integer", + "description": "Profiles matched by the selection." + }, + "already_enabled": { + "type": "integer", + "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." + }, + "with_existing_aml": { + "type": "integer", + "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." + }, + "needing_screening": { + "type": "integer", + "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." + }, + "skipped_no_identity_data": { + "type": "integer", + "description": "Profiles that cannot be screened because identity data is missing." + }, + "skipped_blocked": { + "type": "integer", + "description": "Blocked users that will be skipped (always 0 for businesses)." + }, + "estimated_screening_cost_usd": { + "type": "number", + "example": 0.2, + "description": "One-time cost of the new screenings." + }, + "estimated_annual_monitoring_cost_usd": { + "type": "number", + "example": 0.07, + "description": "Yearly monitoring cost of the profiles that will become monitored." + }, + "balance_sufficient": { + "type": "boolean", + "description": "Whether the organization balance covers the screening plus the first year of monitoring." + }, + "unmatched": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Identifiers of the request that matched no profile in this application, exactly as sent." + } + } }, - "organization_name": { - "type": "string" + { + "type": "object", + "required": [ + "job_id", + "status", + "is_enabled", + "target_type", + "total", + "processed", + "outcomes", + "results", + "error", + "created_at", + "completed_at", + "estimate", + "unmatched" + ], + "properties": { + "job_id": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "PENDING", + "PROCESSING", + "COMPLETED", + "FAILED" + ] + }, + "is_enabled": { + "type": "boolean", + "description": "true when the job enables monitoring, false when it disables it." + }, + "target_type": { + "type": "string", + "enum": [ + "user", + "business" + ], + "description": "Which profile family the job toggles." + }, + "total": { + "type": "integer", + "description": "Profiles the job processes." + }, + "processed": { + "type": "integer", + "description": "Profiles processed so far." + }, + "outcomes": { + "type": "object", + "description": "Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped.", + "properties": { + "enabled": { + "type": "integer", + "description": "Monitoring enabled on an existing AML screening." + }, + "screened_and_enabled": { + "type": "integer", + "description": "A new AML screening was created (billed) and monitoring enabled on it." + }, + "already_enabled": { + "type": "integer", + "description": "Monitoring was already active; nothing changed and nothing was billed." + }, + "disabled": { + "type": "integer", + "description": "Monitoring turned off across every AML screening of the profile." + }, + "skipped_no_identity_data": { + "type": "integer", + "description": "Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen." + }, + "skipped_blocked": { + "type": "integer", + "description": "Skipped: the user is blocked." + }, + "skipped_sandbox": { + "type": "integer", + "description": "Skipped: sandbox application, no screening is created." + }, + "failed_provider": { + "type": "integer", + "description": "The screening could not be completed; retry later." + }, + "failed_insufficient_balance": { + "type": "integer", + "description": "Balance ran out while the job was running." + } + } + }, + "results": { + "type": "array", + "description": "One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API.", + "items": { + "type": "object", + "properties": { + "didit_internal_id": { + "type": "string", + "format": "uuid", + "description": "Didit internal id of the profile." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own identifier for the profile." + }, + "outcome": { + "type": "string", + "enum": [ + "enabled", + "screened_and_enabled", + "already_enabled", + "disabled", + "skipped_no_identity_data", + "skipped_blocked", + "skipped_sandbox", + "failed_provider", + "failed_insufficient_balance" + ] + } + } + } + }, + "error": { + "type": "string", + "nullable": true, + "description": "Generic failure message when status is FAILED; the detailed cause is kept on the platform side." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "completed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "estimate": { + "type": "object", + "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", + "properties": { + "users_total": { + "type": "integer", + "description": "Profiles matched by the selection." + }, + "already_enabled": { + "type": "integer", + "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." + }, + "with_existing_aml": { + "type": "integer", + "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." + }, + "needing_screening": { + "type": "integer", + "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." + }, + "skipped_no_identity_data": { + "type": "integer", + "description": "Profiles that cannot be screened because identity data is missing." + }, + "skipped_blocked": { + "type": "integer", + "description": "Blocked users that will be skipped (always 0 for businesses)." + }, + "estimated_screening_cost_usd": { + "type": "number", + "example": 0.2, + "description": "One-time cost of the new screenings." + }, + "estimated_annual_monitoring_cost_usd": { + "type": "number", + "example": 0.07, + "description": "Yearly monitoring cost of the profiles that will become monitored." + }, + "balance_sufficient": { + "type": "boolean", + "description": "Whether the organization balance covers the screening plus the first year of monitoring." + } + } + }, + "unmatched": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job." + } + } } - } + ] }, - "CaseDetail": { + "AMLMonitoringJob": { "type": "object", "properties": { - "uuid": { + "job_id": { "type": "string", "format": "uuid" }, - "case_number": { - "type": "string", - "nullable": true - }, - "title": { - "type": "string" - }, "status": { "type": "string", "enum": [ - "OPEN", - "AWAITING_USER", - "RESOLVED" + "PENDING", + "PROCESSING", + "COMPLETED", + "FAILED" ] }, - "resolution": { + "is_enabled": { + "type": "boolean", + "description": "true when the job enables monitoring, false when it disables it." + }, + "target_type": { "type": "string", "enum": [ - "FALSE_POSITIVE", - "VALID_THREAT" + "user", + "business" ], - "nullable": true + "description": "Which profile family the job toggles." }, - "resolution_notes": { - "type": "string", - "nullable": true + "total": { + "type": "integer", + "description": "Profiles the job processes." }, - "priority": { - "type": "string", - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ] + "processed": { + "type": "integer", + "description": "Profiles processed so far." }, - "source": { - "type": "string", - "enum": [ - "MANUAL", - "AML_SCREENING", - "TRANSACTION_RULE", - "WORKFLOW_BUILDER" - ] + "outcomes": { + "type": "object", + "description": "Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped.", + "properties": { + "enabled": { + "type": "integer", + "description": "Monitoring enabled on an existing AML screening." + }, + "screened_and_enabled": { + "type": "integer", + "description": "A new AML screening was created (billed) and monitoring enabled on it." + }, + "already_enabled": { + "type": "integer", + "description": "Monitoring was already active; nothing changed and nothing was billed." + }, + "disabled": { + "type": "integer", + "description": "Monitoring turned off across every AML screening of the profile." + }, + "skipped_no_identity_data": { + "type": "integer", + "description": "Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen." + }, + "skipped_blocked": { + "type": "integer", + "description": "Skipped: the user is blocked." + }, + "skipped_sandbox": { + "type": "integer", + "description": "Skipped: sandbox application, no screening is created." + }, + "failed_provider": { + "type": "integer", + "description": "The screening could not be completed; retry later." + }, + "failed_insufficient_balance": { + "type": "integer", + "description": "Balance ran out while the job was running." + } + } }, - "tags": { + "results": { "type": "array", + "description": "One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API.", "items": { - "type": "string" + "type": "object", + "properties": { + "didit_internal_id": { + "type": "string", + "format": "uuid", + "description": "Didit internal id of the profile." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your own identifier for the profile." + }, + "outcome": { + "type": "string", + "enum": [ + "enabled", + "screened_and_enabled", + "already_enabled", + "disabled", + "skipped_no_identity_data", + "skipped_blocked", + "skipped_sandbox", + "failed_provider", + "failed_insufficient_balance" + ] + } + } } }, - "subject": { - "$ref": "#/components/schemas/CaseSubject" - }, - "assigned_to": { - "$ref": "#/components/schemas/CaseAssignee" - }, - "assigned_at": { + "error": { "type": "string", - "format": "date-time", - "nullable": true + "nullable": true, + "description": "Generic failure message when status is FAILED; the detailed cause is kept on the platform side." }, - "due_at": { + "created_at": { "type": "string", - "format": "date-time", - "nullable": true + "format": "date-time" }, - "resolved_at": { + "completed_at": { "type": "string", "format": "date-time", "nullable": true }, - "resolved_by_email": { - "type": "string", - "nullable": true - }, - "pending_resolution": { + "estimate": { "type": "object", - "nullable": true, - "description": "Set only while a 4-eyes resolution is staged on the checker blueprint, awaiting approve or reject-approval.", + "description": "Cost preview. Returned directly when estimate_only is true (nothing is written).", "properties": { - "resolution": { - "type": "string", - "enum": [ - "FALSE_POSITIVE", - "VALID_THREAT" - ] + "users_total": { + "type": "integer", + "description": "Profiles matched by the selection." }, - "resolution_notes": { - "type": "string", - "nullable": true + "already_enabled": { + "type": "integer", + "description": "Profiles already monitored (nothing to do when enabling; these are what disabling turns off)." }, - "maker_email": { - "type": "string", - "format": "email" + "with_existing_aml": { + "type": "integer", + "description": "Profiles whose existing AML screening will be switched to monitored (no new screening)." }, - "maker_name": { - "type": "string", - "nullable": true + "needing_screening": { + "type": "integer", + "description": "Profiles without any AML screening that will be screened first (billed as a regular AML screening)." }, - "maker_blueprint_id": { - "type": "string", - "format": "uuid" + "skipped_no_identity_data": { + "type": "integer", + "description": "Profiles that cannot be screened because identity data is missing." }, - "staged_at": { - "type": "string", - "format": "date-time" + "skipped_blocked": { + "type": "integer", + "description": "Blocked users that will be skipped (always 0 for businesses)." + }, + "estimated_screening_cost_usd": { + "type": "number", + "example": 0.2, + "description": "One-time cost of the new screenings." + }, + "estimated_annual_monitoring_cost_usd": { + "type": "number", + "example": 0.07, + "description": "Yearly monitoring cost of the profiles that will become monitored." + }, + "balance_sufficient": { + "type": "boolean", + "description": "Whether the organization balance covers the screening plus the first year of monitoring." } } }, - "metadata": { - "type": "object" - }, - "blueprint": { - "$ref": "#/components/schemas/CaseBlueprintDetail" - }, - "notes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseNote" - }, - "description": "Newest 50 notes." - }, - "events": { + "unmatched": { "type": "array", "items": { - "$ref": "#/components/schemas/CaseEvent" + "type": "string" }, - "description": "Newest 100 events." + "description": "Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job." + } + } + }, + "OngoingMonitoringToggleRequest": { + "type": "object", + "required": [ + "is_enabled" + ], + "properties": { + "is_enabled": { + "type": "boolean", + "description": "true to enable monitoring, false to disable it." }, - "links": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseLink" - } + "estimate_only": { + "type": "boolean", + "default": false, + "description": "When true, returns the cost estimate and performs no writes." }, - "links_count": { - "type": "integer" + "update_all": { + "type": "boolean", + "default": false, + "description": "Apply to every entity of the application (scoped by the filter_* params when present)." }, - "notes_count": { - "type": "integer" + "filter_search": { + "type": "string" }, - "checklist_items": { + "filter_status": { "type": "array", "items": { - "$ref": "#/components/schemas/CaseChecklistItem" + "type": "string" } }, - "created_at": { + "filter_country": { + "type": "string" + }, + "filter_date_from": { "type": "string", - "format": "date-time" + "format": "date" }, - "updated_at": { + "filter_date_to": { "type": "string", - "format": "date-time" + "format": "date" + }, + "didit_internal_id_list": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + }, + "description": "Entities to toggle. Required unless update_all is true." } } }, - "ReportTemplateDetail": { + "OngoingMonitoringJob": { "type": "object", "properties": { - "uuid": { + "job_id": { "type": "string", "format": "uuid" }, - "name": { - "type": "string" - }, - "jurisdiction": { - "type": "string", - "minLength": 2, - "maxLength": 2, - "description": "ISO 3166-1 alpha-2 country code." - }, - "report_type": { + "status": { "type": "string", "enum": [ - "SAR", - "STR", - "CTR", - "TTR", - "UTR", - "IFT", - "CBR", - "TFR" + "PENDING", + "PROCESSING", + "COMPLETED", + "FAILED" ] }, - "report_type_display": { - "type": "string" + "is_enabled": { + "type": "boolean" }, - "field_config": { + "total": { + "type": "integer" + }, + "processed": { + "type": "integer" + }, + "outcomes": { "type": "object", - "description": "Reusable goAML header defaults. Template-level obligatory fields: rentity_id, submission_code, and currency_code_local (3-letter). Optional: enabled_fields (which optional report blocks to include) plus reporting_person, location, reason, and action defaults. Per-filing header fields (action, submission_date, transactions, jurisdiction) are enforced when the report is validated or finalized, not on the template." + "description": "Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter.", + "properties": { + "enabled": { + "type": "integer", + "description": "Monitoring enabled on an existing AML screening." + }, + "screened_and_enabled": { + "type": "integer", + "description": "A new AML screening was created (billed) and monitoring enabled." + }, + "already_enabled": { + "type": "integer", + "description": "Monitoring was already active (legacy duplicate rows are consolidated)." + }, + "disabled": { + "type": "integer", + "description": "Monitoring disabled on every AML row of the entity." + }, + "skipped_no_identity_data": { + "type": "integer", + "description": "Skipped: no full name + date of birth + country available to screen." + }, + "skipped_blocked": { + "type": "integer", + "description": "Skipped: the user is blocked." + }, + "skipped_sandbox": { + "type": "integer", + "description": "Skipped: sandbox application." + }, + "failed_provider": { + "type": "integer", + "description": "The screening provider call failed." + }, + "failed_insufficient_balance": { + "type": "integer", + "description": "Balance ran out mid-job." + } + } }, - "created_at": { + "error": { "type": "string", - "format": "date-time" + "nullable": true }, - "updated_at": { - "type": "string", - "format": "date-time" + "estimate": { + "type": "object", + "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", + "properties": { + "users_total": { + "type": "integer", + "description": "Entities in the selection." + }, + "already_enabled": { + "type": "integer" + }, + "with_existing_aml": { + "type": "integer", + "description": "Entities whose existing AML screening will be switched to monitored." + }, + "needing_screening": { + "type": "integer", + "description": "Entities without any AML screening that will be screened first (billed)." + }, + "skipped_no_identity_data": { + "type": "integer" + }, + "skipped_blocked": { + "type": "integer" + }, + "estimated_screening_cost_usd": { + "type": "number", + "example": 0.2 + }, + "estimated_annual_monitoring_cost_usd": { + "type": "number", + "example": 0.07 + }, + "balance_sufficient": { + "type": "boolean" + } + } } } }, - "RegulatoryReportDetail": { + "OngoingMonitoringToggleResult": { + "oneOf": [ + { + "type": "object", + "description": "Cost preview. Returned directly when estimate_only is true (no writes are performed).", + "properties": { + "users_total": { + "type": "integer", + "description": "Entities in the selection." + }, + "already_enabled": { + "type": "integer" + }, + "with_existing_aml": { + "type": "integer", + "description": "Entities whose existing AML screening will be switched to monitored." + }, + "needing_screening": { + "type": "integer", + "description": "Entities without any AML screening that will be screened first (billed)." + }, + "skipped_no_identity_data": { + "type": "integer" + }, + "skipped_blocked": { + "type": "integer" + }, + "estimated_screening_cost_usd": { + "type": "number", + "example": 0.2 + }, + "estimated_annual_monitoring_cost_usd": { + "type": "number", + "example": 0.07 + }, + "balance_sufficient": { + "type": "boolean" + } + } + }, + { + "$ref": "#/components/schemas/OngoingMonitoringJob" + } + ] + }, + "CaseBlueprintPresetInstallResult": { "type": "object", "properties": { - "uuid": { - "type": "string", - "format": "uuid" + "created": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CaseBulkUpdateResult": { + "type": "object", + "properties": { + "updated": { + "type": "integer" }, - "case_uuid": { - "type": "string", - "format": "uuid" + "failures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "case_uuid": { + "type": "string", + "format": "uuid" + }, + "detail": { + "type": "string" + } + } + } + } + } + }, + "RegulatoryReportList": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Number of matching rows, capped at 100 for performance." }, - "case_number": { + "next": { "type": "string", "nullable": true }, - "report_type": { - "type": "string", - "enum": [ - "SAR", - "STR", - "CTR", - "TTR", - "UTR", - "IFT", - "CBR", - "TFR" - ] - }, - "report_type_display": { - "type": "string" - }, - "report_code": { - "type": "string", - "description": "goAML report code derived from report_type, e.g. STR, CTR, BCR." - }, - "report_number": { + "previous": { "type": "string", "nullable": true }, - "status": { - "type": "string", - "enum": [ - "DRAFT", - "VALIDATED", - "FINALIZED" - ] - }, - "status_display": { - "type": "string" - }, - "jurisdiction": { - "type": "string", - "minLength": 2, - "maxLength": 2 - }, - "rentity_id": { - "type": "string", - "nullable": true, - "description": "Reporting entity ID issued by the FIU." - }, - "rentity_branch": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RegulatoryReportDetail" + } + } + } + }, + "EntityNetworkMembershipList": { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityNetworkMembership" + } + } + } + }, + "SessionDecision": { + "type": "object", + "properties": { + "session_id": { "type": "string", - "nullable": true + "format": "uuid", + "description": "The unique identifier of the session." }, - "submission_code": { + "session_kind": { "type": "string", "enum": [ - "E", - "M" - ] + "user", + "business" + ], + "description": "Discriminator indicating whether this is a User Verification (KYC) or Business Verification (KYB) session. When `user`, the KYC feature arrays (`id_verifications`, `nfc_verifications`, `liveness_checks`, `face_matches`, `poa_verifications`, `document_ai_documents`, `database_validations`, ...) apply and the business arrays are absent. When `business`, the payload instead carries `registry_checks`, `document_verifications`, `key_people_checks` plus the shared arrays (`aml_screenings`, `phone_verifications`, `email_verifications`, `ip_analyses`, `questionnaire_responses`). **Omitted when `include=events` is passed** — the events shape only exists for user sessions, so the discriminator is dropped from that variant." }, - "entity_reference": { + "shared_from_session": { "type": "string", - "nullable": true + "format": "uuid", + "nullable": true, + "description": "For a session cloned with `POST /v3/session/import-shared/`: the `session_id` of the original session in the source application. `null` for every other session. Omitted when `include=events` is passed." }, - "fiu_ref_number": { - "type": "string", + "session_number": { + "type": "integer", + "description": "The number of the session.", "nullable": true }, - "currency_code_local": { + "session_url": { "type": "string", - "minLength": 3, - "maxLength": 3, + "format": "uri", + "description": "The URL of the session.", "nullable": true }, - "submission_date": { + "status": { "type": "string", - "format": "date-time", - "nullable": true - }, - "reporting_person": { - "type": "object" - }, - "location": { - "type": "object" + "enum": [ + "Not Started", + "In Progress", + "Awaiting User", + "In Review", + "Approved", + "Declined", + "Resubmitted", + "Expired", + "Kyc Expired", + "Abandoned" + ], + "description": "Overall lifecycle status of the session — the rolled-up workflow result. Terminal values are `Approved`, `Declined`, `In Review`, `Expired`, `Kyc Expired`, and `Abandoned`; `Not Started`, `In Progress`, `Awaiting User`, and `Resubmitted` indicate the session is still moving through the workflow." }, - "reason": { + "workflow_id": { "type": "string", + "format": "uuid", + "description": "Stable identifier of the workflow group that produced this session. Workflows are versioned — this value identifies the workflow itself, not the specific version.", "nullable": true }, - "action": { + "features": { + "type": "array", + "items": { + "type": "string", + "description": "The feature of the session." + }, + "description": "List of verification features configured for this session, as display names: `ID_VERIFICATION`, `NFC`, `LIVENESS`, `FACE_MATCH`, `POA`, `PHONE`, `EMAIL_VERIFICATION`, `AML`, `IP_ANALYSIS`, `DATABASE_VALIDATION`, `QUESTIONNAIRE`, `AGE_ESTIMATION`, `FACE_SEARCH` (Business Verification sessions use the KYB feature set: `KYB_REGISTRY`, `KYB_DOCUMENTS`, `KYB_KEY_PEOPLE`, `AML`, `PHONE`, `EMAIL_VERIFICATION`, `IP_ANALYSIS`, `QUESTIONNAIRE`). With `include=events` each entry becomes an object `{feature, node_id}` instead of a plain string, and the email feature is renamed: it appears as `EMAIL` in events mode instead of the `EMAIL_VERIFICATION` used here." + }, + "vendor_data": { "type": "string", + "description": "The vendor data of the session.", "nullable": true }, - "activity": { - "type": "string", + "metadata": { + "type": "object", + "description": "The metadata of the session.", "nullable": true }, - "narrative": { + "expected_details": { "type": "object", - "description": "Five Ws narrative sections.", + "description": "Expected details supplied at session creation — used for cross-validation against extracted data (KYC) or to pre-fill the company registry search (KYB). Echoes back exactly the keys that were provided; `null` when none were supplied. KYC sessions carry the person fields; Business (KYB) sessions carry `company_name`, `registry_country` and `registration_number`.", "properties": { - "who": { - "type": "string" + "first_name": { + "type": "string", + "description": "User's first name. For example, `John`." }, - "what": { - "type": "string" + "last_name": { + "type": "string", + "description": "User's last name. For example, `Doe`." }, - "when": { - "type": "string" + "date_of_birth": { + "type": "string", + "format": "date", + "description": "User's date of birth with format: YYYY-MM-DD. For example, `1990-05-15`." }, - "where": { - "type": "string" + "gender": { + "type": "string", + "nullable": true, + "enum": [ + "M", + "F" + ], + "default": null, + "description": "User's gender. Must be either 'M', 'F', or null." }, - "why": { - "type": "string" + "nationality": { + "type": "string", + "description": "ISO 3166-1 alpha-3 country code representing the applicant's country of origin. For example, `USA`." }, - "how": { - "type": "string" + "country": { + "type": "string", + "description": "ISO 3166-1 alpha-3 country code representing the country of the Proof of Address document, or the country of the applicant's ID document, which may differ from nationality. For example, `GBR`." + }, + "address": { + "type": "string", + "description": "The address in a human readable format, including as much information as possible. For example, `123 Main St, San Francisco, CA 94105, USA`." + }, + "identification_number": { + "type": "string", + "description": "The user's document number, personal number, or tax number. For example, `123456789`." + }, + "ip_address": { + "type": "string", + "format": "ipv4", + "description": "Expected IP address for the session. If the actual IP address differs from this value, a warning will be logged. For example, `192.168.1.100`." + }, + "expected_document_types": { + "type": "array", + "description": "Document types the ID verification step was restricted to at session creation. Empty/unset means no restriction. Codes: `P` (passport), `ID` (national ID), `DL` (driver's license), `RP` (residence permit), `HIC` (health insurance card), `TC` (tax card), `SSC` (social security card).", + "items": { + "type": "string", + "enum": [ + "P", + "ID", + "DL", + "RP", + "HIC", + "TC", + "SSC" + ] + } + }, + "id_country": { + "type": "string", + "nullable": true, + "description": "Expected ID-document issuing country (ISO 3166-1 alpha-3), when restricted at creation time.", + "example": "ESP" + }, + "poa_country": { + "type": "string", + "nullable": true, + "description": "Expected Proof of Address document country (ISO 3166-1 alpha-3), when restricted at creation time.", + "example": "ESP" + }, + "company_name": { + "type": "string", + "description": "Expected legal name of the business. Business (KYB) sessions only." + }, + "registry_country": { + "type": "string", + "description": "Expected registry country as `XX` or `XX-YY` (ISO 3166-1 alpha-2, optionally with an ISO 3166-2 subdivision for state-level registries, e.g. `US-CA`). Business (KYB) sessions only." + }, + "registration_number": { + "type": "string", + "description": "Expected company registration number. Business (KYB) sessions only." } - } - }, - "report_indicators": { - "type": "array", - "items": { - "type": "string" - } + }, + "nullable": true }, - "included_fields": { - "type": "array", - "items": { - "type": "string" - } + "contact_details": { + "type": "object", + "description": "Contact details supplied when the session was created (used for notification emails, prefilled phone/email verification, and communication language). `null` when neither an email nor a phone number was provided.", + "properties": { + "email": { + "type": "string", + "description": "Email address of the user (e.g., \"john.doe@example.com\") that will be used during the Email Verification step. If not provided, the user must provide it during the verification flow." + }, + "email_lang": { + "type": "string", + "description": "Language code (ISO 639-1) for email notifications. Controls the language of all email communications (e.g., \"en\", \"es\", \"fr\")." + }, + "send_notification_emails": { + "type": "boolean", + "default": false, + "description": "If true and an email is provided, Didit sends the initial \"Verify your identity\" email asynchronously when the User Verification (KYC) or Business Verification (KYB) session is created. Didit also sends verification status notifications for sessions requiring manual review to the provided email address." + }, + "phone": { + "type": "string", + "nullable": true, + "description": "Phone number of the user in E.164 format if available (e.g., +14155552671). `null` when only an email was supplied at session creation. Note: This field returns the original phone number provided during session creation. If the number was invalid, it may not be in E.164 format." + } + }, + "nullable": true }, - "transactions": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } + "callback": { + "type": "string", + "format": "uri", + "description": "The callback URL of the session.", + "nullable": true }, - "validation_errors": { + "id_verifications": { "type": "array", - "description": "Result of the most recent validation; each entry is a {field, message} object.", + "nullable": true, + "description": "**Array of ID verification (OCR) reports** produced by ID-document steps in the workflow graph. Always a JSON array — read `id_verifications[0]` and iterate if your workflow can run more than one ID check (for example a primary check plus a step-up). `null` until at least one ID step has produced data. Each item is the full report, including `node_id`, feature-level `status`, extracted document fields, image URLs, quality scores, MRZ, address parsing, cross-session matches, and `warnings[]`.", "items": { "type": "object", + "description": "One ID verification report - the full report for a single ID Verification step. `verification_method` says which of the three methods produced it (document capture, non-doc lookup or wallet sign-in) and the method-specific evidence block is populated accordingly.", "properties": { - "field": { - "type": "string" + "node_id": { + "type": "string", + "description": "Workflow graph node id of the OCR step that produced this report." + }, + "verification_method": { + "type": "string", + "enum": [ + "document", + "id_lookup", + "wallet" + ], + "description": "Which ID Verification method produced this report. `document` - the user photographed a physical ID (the default, and the value on every session created before the other methods existed). `id_lookup` - the user typed their national ID number and it was checked against a government register or another authoritative source for that country (`id_lookup.source` names which). `wallet` - the user signed in with a government or bank digital identity." + }, + "assurance": { + "type": "string", + "enum": [ + "documentary", + "data_match", + "cryptographic" + ], + "description": "Assurance tier derived from `verification_method`: `documentary` for a document that was captured and checked, `data_match` for details that matched a record held by the source queried, `cryptographic` for attributes an identity provider authenticated and signed." + }, + "wallet_provider": { + "type": "string", + "nullable": true, + "description": "Catalog wallet id when `verification_method` is `wallet` (`mitid`, `bankid_se`, `itsme`, ...). `null` otherwise." + }, + "fallback_from": { + "type": "object", + "nullable": true, + "description": "Why a non-document method ended the ID step. Set on the declined report when a lookup or a wallet sign-in failed and the country's fallback switch was `decline`. `null` otherwise.", + "properties": { + "method": { + "type": "string", + "enum": [ + "id_lookup", + "wallet" + ], + "description": "The method that failed." + }, + "reason": { + "type": "string", + "description": "The outcome that ended it, e.g. `no_match`, `provider_error`, `cancelled`, `timeout`." + }, + "action": { + "type": "string", + "description": "The configured action that was taken, e.g. `decline`." + } + } + }, + "id_lookup": { + "type": "object", + "nullable": true, + "description": "Registry evidence. Present only when `verification_method` is `id_lookup`; `null` otherwise.", + "properties": { + "source": { + "type": "string", + "description": "Customer-facing name of the source queried, e.g. `Department of Home Affairs` (a government register) or `UK credit bureau and financial services records` (not a government register). This is the only statement of provenance for the lookup; do not assume a government source." + }, + "checked_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the register answered." + }, + "attempts": { + "type": "integer", + "description": "1-based attempt number this report came from." + }, + "max_attempts": { + "type": "integer", + "description": "Attempt budget configured for the country (1 to 5, default 1)." + }, + "outcome": { + "type": "string", + "enum": [ + "match", + "partial_match", + "no_match", + "provider_error", + "format_rejected" + ], + "nullable": true, + "description": "What the register answered. Only `match`, `partial_match` and `no_match` count against the attempt budget and bill." + }, + "comparison": { + "type": "array", + "description": "One row per comparable field the source answered on, plus one row per source error. Comparison rows use the normalised field names below rather than the country's request field keys, because several request fields (first and last name, or any of the identifier aliases) collapse into a single comparison. A source that failed appends a row with `field: \"provider\"` and `result: \"error\"`, so read `result` before treating a row as a field verdict.", + "items": { + "type": "object", + "required": [ + "field", + "label", + "provided", + "record", + "result" + ], + "properties": { + "field": { + "type": "string", + "enum": [ + "identification_number", + "full_name", + "date_of_birth", + "address", + "provider" + ], + "description": "Normalised comparison field. `provider` is not a user field: it marks a source error row." + }, + "label": { + "type": "string", + "description": "Display label for the row, e.g. `ID number`, `Full name`, `Provider`." + }, + "provided": { + "type": "string", + "nullable": true, + "description": "What the user typed. `null` on a `provider` error row." + }, + "record": { + "type": "string", + "nullable": true, + "description": "What the source held. On a `provider` error row this carries the error message instead." + }, + "result": { + "type": "string", + "enum": [ + "match", + "partial", + "no_match", + "error" + ], + "description": "Per-field comparison result. `error` appears only on a `provider` row and means that source did not answer; treat it as evidence about the query, never as a field mismatch." + } + } + } + }, + "registry_portrait": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Short-lived presigned URL to the portrait the source returned. Only the four government registers that return one (South Africa, Nigeria, Argentina, Panama)." + }, + "face_match_score": { + "type": "number", + "nullable": true, + "description": "Selfie against the registry portrait, 0-100. Only where the register returns a portrait." + } + } + }, + "wallet_verification": { + "type": "object", + "nullable": true, + "description": "Credential evidence. Present only when `verification_method` is `wallet`; `null` otherwise.", + "properties": { + "provider": { + "type": "string", + "description": "Catalog wallet id, e.g. `mitid`." + }, + "provider_name": { + "type": "string", + "nullable": true, + "description": "Wallet name as the end user knows it, e.g. `MitID`." + }, + "issuing_authority": { + "type": "string", + "nullable": true, + "description": "The authority behind the wallet, e.g. `Danish Agency for Digital Government`." + }, + "issuing_country": { + "type": "string", + "description": "ISO 3166-1 alpha-3 country the wallet was offered in." + }, + "credential_type": { + "type": "string", + "nullable": true, + "description": "Credential the wallet presented, e.g. `person_identification`." + }, + "level_of_assurance": { + "type": "string", + "enum": [ + "low", + "substantial", + "high" + ], + "nullable": true, + "description": "The level of assurance the wallet asserted. A sign-in that asserts less than the level requested fails instead of downgrading." + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "The wallet's authentication instant." + }, + "signature_valid": { + "type": "boolean", + "nullable": true, + "description": "Whether the signed assertion the wallet returned validated." + }, + "attributes": { + "type": "object", + "additionalProperties": true, + "description": "The attributes the wallet shared, keyed as the wallet's catalog entry lists them." + }, + "portrait": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Short-lived presigned URL to the portrait the wallet shared, where it shares one." + }, + "face_match_score": { + "type": "number", + "nullable": true, + "description": "Selfie against the wallet portrait, 0-100, where the wallet shares one." + } + } + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Expired" + ], + "description": "Feature-level status of this ID verification step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`." + }, + "document_type": { + "type": "string", + "description": "Type of identity document" + }, + "document_subtype": { + "type": "string", + "nullable": true, + "description": "Accepted document subtype code from the workflow document settings, including the document region when applicable. Examples: `ID_CARD_GENERIC`, `QUEENSLAND_DRIVER_LICENSE_GENERIC`." + }, + "document_number": { + "type": "string", + "description": "Document identification number" + }, + "personal_number": { + "type": "string", + "description": "Personal identification number" + }, + "portrait_image": { + "type": "string", + "format": "uri", + "description": "URL of the portrait image" + }, + "front_image": { + "type": "string", + "format": "uri", + "description": "URL of the front document image" + }, + "front_video": { + "type": "string", + "format": "uri", + "description": "URL of the front document video" + }, + "back_image": { + "type": "string", + "format": "uri", + "description": "URL of the back document image" + }, + "back_video": { + "type": "string", + "format": "uri", + "description": "URL of the back document video" + }, + "full_front_image": { + "type": "string", + "format": "uri", + "description": "URL of the full front document image" + }, + "full_back_image": { + "type": "string", + "format": "uri", + "description": "URL of the full back document image" + }, + "front_image_camera_front": { + "type": "string", + "format": "uri", + "description": "URL of the front document image captured from the front-facing camera" + }, + "back_image_camera_front": { + "type": "string", + "format": "uri", + "description": "URL of the back document image captured from the front-facing camera" + }, + "date_of_birth": { + "type": "string", + "format": "date", + "description": "Date of birth from document" + }, + "age": { + "type": "integer", + "description": "Calculated age from date of birth" + }, + "expiration_date": { + "type": "string", + "format": "date", + "description": "Document expiration date" + }, + "date_of_issue": { + "type": "string", + "format": "date", + "description": "Document issue date" + }, + "issuing_state": { + "type": "string", + "description": "ISO code of issuing state" + }, + "issuing_state_name": { + "type": "string", + "description": "Full name of issuing state" + }, + "first_name": { + "type": "string", + "description": "First name from document" + }, + "last_name": { + "type": "string", + "description": "Last name from document" + }, + "full_name": { + "type": "string", + "description": "Full name from document" + }, + "gender": { + "type": "string", + "description": "Gender from document" + }, + "address": { + "type": "string", + "description": "Raw address from document" + }, + "formatted_address": { + "type": "string", + "description": "Formatted full address" + }, + "place_of_birth": { + "type": "string", + "description": "Place of birth from document" + }, + "marital_status": { + "type": "string", + "description": "Marital status from document" + }, + "nationality": { + "type": "string", + "description": "ISO nationality code" + }, + "extra_fields": { + "type": "object", + "description": "Additional document fields. Includes document-specific attributes (e.g. `dl_categories`, `blood_group`, `sponsor`, `employer`, `dl_class_code_b_from`, `dl_class_code_b_to`). Driver license class validity dates are returned as `dl_class_code__from` and `dl_class_code__to`, and per-class restriction/information codes (e.g. column 12 of UK licences) as `dl_class_code__notes`. When the document carries names, address, or place of birth in multiple scripts, this object also includes alternate-script versions of those fields. The workflow setting `preferred_characters` (`latin` or `non_latin`) controls which script populates the top-level fields (`first_name`, `last_name`, `full_name`, `address`, `place_of_birth`); the opposite script lands here as `first_name_non_latin` / `last_name_non_latin` / `full_name_non_latin` / `middle_name_non_latin` / `address_non_latin` / `place_of_birth_non_latin` (when `preferred_characters` is `latin`) or with a `_latin` suffix (when `preferred_characters` is `non_latin`). Keys appear only when the OCR actually extracts a real alternate-script value — a field whose alternate value is in the same script as the preferred set (for example, a Cyrillic-labelled field whose OCR value came back in Latin homoglyphs) is skipped. When `preferred_characters` is `latin` but the document only carries non-Latin text, Didit transliterates the extracted values and exposes `first_name_latin` / `last_name_latin` / etc. as a fallback.", + "additionalProperties": true, + "properties": { + "dl_categories": { + "type": "array", + "items": { + "type": "string" + } + }, + "blood_group": { + "type": "string", + "nullable": true + }, + "first_name_non_latin": { + "type": "string", + "description": "First name in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document (e.g. `Айна` for a Kyrgyz ID)." + }, + "last_name_non_latin": { + "type": "string", + "description": "Surname in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document." + }, + "middle_name_non_latin": { + "type": "string", + "description": "Middle name / patronymic in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document." + }, + "full_name_non_latin": { + "type": "string", + "description": "Composed full name in the non-Latin script. Reconstructed from the individual components when both first and last names are available." + }, + "address_non_latin": { + "type": "string", + "description": "Address in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document." + }, + "place_of_birth_non_latin": { + "type": "string", + "description": "Place of birth in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document." + }, + "first_name_latin": { + "type": "string", + "description": "First name in Latin script. Populated when `preferred_characters=non_latin` and a Latin version is present on the document, or — as a transliteration fallback — when `preferred_characters=latin` but the document only carries non-Latin text." + }, + "last_name_latin": { + "type": "string", + "description": "Surname in Latin script. Same rules as `first_name_latin`." + }, + "middle_name_latin": { + "type": "string", + "description": "Middle name / patronymic in Latin script. Same rules as `first_name_latin`." + }, + "full_name_latin": { + "type": "string", + "description": "Composed full name in Latin script. Always reconstructed from the individual components when both first and last names are available." + }, + "address_latin": { + "type": "string", + "description": "Address in Latin script. Same rules as `first_name_latin`." + }, + "place_of_birth_latin": { + "type": "string", + "description": "Place of birth in Latin script. Same rules as `first_name_latin`." + } + } + }, + "mrz": { + "type": "object", + "nullable": true, + "description": "Parsed Machine-Readable Zone (MRZ) fields extracted from the document. `null` if no MRZ was detected.", + "properties": { + "surname": { + "type": "string", + "example": "MARTINEZ for any key submitted in custom_properties on transaction create. Country-code fields accept ISO-2 or ISO-3; both sides are normalized to ISO-3 before comparison." - }, - "operator": { - "type": "string", - "enum": [ - "eq", - "ne", - "gt", - "gte", - "lt", - "lte", - "in", - "not_in", - "contains", - "not_contains", - "contains_any", - "regex", - "fuzzy_match", - "exists", - "is_not_empty", - "is_not_null", - "not_exists", - "is_empty", - "is_null" - ], - "description": "Comparison operator. in/not_in test membership (value may be a list or a single scalar). contains/not_contains are case-insensitive substring checks against a string value. contains_any takes a list of strings and matches when any is a case-insensitive substring of the field value. regex matches the value pattern against the field. fuzzy_match compares against the value string with the required `score` threshold (0-100). gt/gte/lt/lte never match when either side is missing. exists/is_not_empty/is_not_null (aliases) and not_exists/is_empty/is_null (aliases) are presence checks that ignore `value`." - }, - "value": { - "description": "Comparison value. Its shape depends on the operator and value_type; omit for the presence-check operators." - }, - "value_type": { - "type": "string", - "enum": [ - "list", - "field", - "relative_date" - ], - "description": "Optional value interpretation. `list`: value is a List UUID and the list's entries become the comparison value. `field`: value is another field path, compared field-to-field. `relative_date`: value is a relative-date object, e.g. {\"direction\": \"past\", \"unit\": \"days\", \"amount\": 30}." - }, - "score": { - "type": "number", - "minimum": 0, - "maximum": 100, - "description": "Required when operator is fuzzy_match: the 0-100 similarity threshold." - }, - "group_index": { - "type": "integer", - "description": "Optional grouped logic. When any condition carries group_index, conditions sharing an index form a group and grouped evaluation replaces evaluation_mode." - }, - "group_logic": { - "type": "string", - "enum": [ - "AND", - "OR" - ], - "default": "AND", - "description": "How conditions combine WITHIN a group." - }, - "groups_logic": { - "type": "string", - "enum": [ - "AND", - "OR" - ], - "default": "OR", - "description": "How group outcomes combine ACROSS groups. Read from the first condition." - } - }, - "description": "A condition reads only the transaction being evaluated - it never filters the aggregation window." - }, - "TransactionRuleAggregation": { - "type": "object", - "required": [ - "value" - ], - "properties": { - "metric": { - "type": "string", - "enum": [ - "count", - "sum", - "max", - "min", - "avg", - "distinct_count" - ], - "default": "count", - "description": "How the matching historical transactions are aggregated. unique_count is accepted as an alias of distinct_count." - }, - "field": { - "type": "string", - "default": "amount", - "description": "Field aggregated by sum/max/min/avg/distinct_count. Ignored for count." - }, - "operator": { - "type": "string", - "enum": [ - "eq", - "ne", - "gt", - "gte", - "lt", - "lte" - ], - "default": "eq", - "description": "Comparison applied to the computed metric." - }, - "value": { - "type": "number", - "description": "Threshold the computed metric is compared against." - }, - "window": { - "type": "string", - "pattern": "^\\d+(m|h|d)$", - "default": "1d", - "description": "Look-back window ending at the transaction's txn_date: m (MINUTES, not months), h (hours) or d (days), e.g. 30m, 24h, 7d, 30d." - }, - "filters": { - "type": "object", - "additionalProperties": true, - "description": "Narrows which historical transactions are aggregated. Keys are field paths from the condition field catalog; a value of \"__current__\" resolves to the same field's value on the transaction being evaluated (e.g. {\"subject_vendor_data\": \"__current__\"} scopes the window to the same subject), and a list value means membership. Filters compare by equality or membership ONLY - there is no lt/gte filter, so a range such as \"historical transactions under 10,000\" cannot be expressed here. Put the range on the metric instead (metric max with operator lt keeps every transaction in the window below the threshold)." - } - }, - "description": "Velocity check over the application's historical transactions inside the window. Every aggregation entry must match for the rule to match, regardless of evaluation_mode. An aggregation constrains only the window; the transaction being evaluated is constrained by scope and conditions, so repeat direction, currency or transaction_type on both halves when the rule promises them." - }, - "TransactionRuleScope": { - "type": "object", - "properties": { - "transaction_types": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Restrict to transaction types, e.g. finance, kyc, travel_rule, user_event. Empty or missing = no restriction." - }, - "directions": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "INBOUND", - "OUTBOUND" - ] - }, - "description": "Restrict to transaction directions (case-insensitive on write)." - }, - "action_types": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Restrict to your action_type values, e.g. withdrawal, deposit." - } - }, - "description": "Which transactions the rule applies to. Only these three keys are accepted." - }, - "TransactionRuleAction": { - "oneOf": [ - { - "type": "object", - "required": [ - "type", - "value" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "add_score" - ] - }, - "value": { - "type": "integer", - "description": "Points added to the transaction's risk score. May be negative." - } - }, - "description": "Add points to the transaction's cumulative risk score; the total decides the status via the application's review/decline thresholds." - }, - { - "type": "object", - "required": [ - "type", - "value" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "change_status" - ] - }, - "value": { - "type": "string", - "enum": [ - "APPROVED", - "IN_REVIEW", - "DECLINED", - "AWAITING_USER" - ], - "description": "Status to set immediately, regardless of score. Alias key: status." - }, - "workflow_id": { - "type": "string", - "format": "uuid", - "description": "REQUIRED when value is AWAITING_USER: the verification workflow the subject is asked to complete. Alias key: remediation_workflow_id. Didit creates a hosted remediation session for the subject when the rule fires." - } - }, - "description": "Directly set the transaction status. DECLINED always wins across matched rules." - }, - { - "type": "object", - "required": [ - "type" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "add_tags" - ] - }, - "tag_uuid": { - "type": "string", - "format": "uuid", - "description": "Existing application tag to attach." - }, - "tag_name": { - "type": "string", - "description": "Tag name to attach; created on first use (stored uppercased). Provide tag_uuid or tag_name." - }, - "tag_color": { - "type": "string", - "description": "Hex color used when tag_name creates a new tag, e.g. #007AFE." - } - }, - "description": "Attach an application tag to the matched transaction." - }, - { - "type": "object", - "required": [ - "type", - "note" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "add_note" - ] - }, - "note": { - "type": "string", - "description": "Note text attached to the transaction (author: Rule Engine). Alias key: value." - } - }, - "description": "Attach an automated analyst note to the matched transaction." - }, - { - "type": "object", - "required": [ - "type", - "list_id" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "add_to_list" - ] - }, - "list_id": { - "type": "string", - "format": "uuid", - "description": "List to add the subject's vendor_data to." - } - }, - "description": "Add the subject's vendor_data to a blocklist, allowlist or custom list." - }, - { - "type": "object", - "required": [ - "type" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "open_case" - ] - }, - "blueprint": { - "type": "string", - "format": "uuid", - "description": "Case blueprint UUID; defaults to the application's default blueprint." - }, - "grouping": { - "type": "string", - "enum": [ - "by_applicant", - "by_rule_and_applicant" - ], - "description": "Whether repeat matches join one case per applicant or one per rule+applicant." - }, - "attach_matched_transaction": { - "type": "boolean", - "description": "Attach the matched transaction to the case." + "extra_files": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + }, + "description": "Additional document verification files" + }, + "front_image_camera_front_face_match_score": { + "type": "number", + "nullable": true, + "description": "Face match score between the portrait extracted from the front document image and the front camera image" + }, + "back_image_camera_front_face_match_score": { + "type": "number", + "nullable": true, + "description": "Face match score between the portrait extracted from the back document image and the front camera image" + }, + "document_selfie_face_matches": { + "type": "array", + "description": "Previous sessions or face blocklist entries whose face matched the selfie the front camera took during document capture (Silent Capture Face Search). Same entry format as `liveness_checks[].matches`. `null` for sessions created before this field existed or when the search did not run; an empty array when it ran and found nothing. `match_image_url` is a presigned URL that expires.", + "items": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Session ID of the matching face. `null` when `source` is `imported`, `list_entry`, or `retained_template`." + }, + "session_number": { + "type": "integer", + "nullable": true + }, + "similarity_percentage": { + "type": "number", + "description": "Face similarity between the two images, clamped to 0–100." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of the matching session or linked User for imported and retained-template faces." + }, + "vendor_user_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Linked User id for `imported` and `retained_template` matches and for session matches linked to a User. `null` for list entries and sessions without a User." + }, + "biometric_template_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Id of the retained biometric template. Set only when `source` is `retained_template`; `null` otherwise." + }, + "verification_date": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the matching face was captured, or when a retained template was created. `null` for list entries." + }, + "user_details": { + "type": "object", + "nullable": true, + "description": "Identity data from the matching session or imported User profile. Always `null` for `retained_template` and `list_entry` matches.", + "properties": { + "full_name": { + "type": "string", + "nullable": true + }, + "document_type": { + "type": "string", + "nullable": true + }, + "document_number": { + "type": "string", + "nullable": true + } + } + }, + "match_image_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Reference image for image-backed matches. Always `null` for `retained_template` matches because retained templates contain no image." + }, + "status": { + "type": "string", + "nullable": true, + "description": "Lifecycle status of the matching session. `null` for non-session sources." + }, + "is_blocklisted": { + "type": "boolean", + "description": "Whether the matching face is on a blocklist." + }, + "is_allowlisted": { + "type": "boolean", + "description": "Whether the matching face is on an allowlist." + }, + "api_service": { + "type": "string", + "nullable": true, + "description": "Standalone API service of the matching session (e.g. `PASSIVE_LIVENESS`, `FACE_MATCH`); `null` for workflow sessions and non-session sources." + }, + "source": { + "type": "string", + "enum": [ + "session", + "imported", + "list_entry", + "retained_template" + ], + "description": "Origin of the matching face: `session`, `imported` (User face upload), `list_entry` (allow/block list), or `retained_template` (an image-free biometric template stored on a User, listed under `/v3/biometric-templates/`)." + } + } + }, + "nullable": true + }, + "front_image_quality_score": { + "type": "object", + "nullable": true, + "description": "Quality metrics for the front document image. Each sub-score ranges from 0 to 100.", + "properties": { + "focus_score": { + "type": "number", + "description": "Sharpness of the image measured via top-10% local Laplacian variance. Robust to documents with large uniform backgrounds. Higher values indicate a sharper, less blurry image." + }, + "brightness_score": { + "type": "number", + "description": "How well-lit the image is. Penalizes images that are too dark or overexposed, with ideal brightness around mid-range." + }, + "brightness_issue": { + "type": "string", + "enum": [ + "ok", + "too_dark", + "too_bright" + ], + "description": "Indicates the direction of the brightness problem when the brightness_score is low. 'ok' means brightness is acceptable, 'too_dark' means the image is underexposed, 'too_bright' means the image is overexposed." + }, + "is_document_fully_visible": { + "type": "boolean", + "nullable": true, + "description": "true if all four corners of the document are fully visible inside the image frame, false if any corner is cut off. Null when corner coordinates are unavailable." + }, + "resolution_score": { + "type": "number", + "description": "Effective resolution of the image based on total pixel count." + }, + "overall_score": { + "type": "number", + "description": "Weighted composite of sub-scores (focus 45%, brightness 30%, resolution 25%)." + } + } + }, + "back_image_quality_score": { + "type": "object", + "nullable": true, + "description": "Quality metrics for the back document image. Same structure as front_image_quality_score.", + "properties": { + "focus_score": { + "type": "number", + "description": "Sharpness of the image measured via top-10% local Laplacian variance. Robust to documents with large uniform backgrounds. Higher values indicate a sharper, less blurry image." + }, + "brightness_score": { + "type": "number", + "description": "How well-lit the image is. Penalizes images that are too dark or overexposed, with ideal brightness around mid-range." + }, + "brightness_issue": { + "type": "string", + "enum": [ + "ok", + "too_dark", + "too_bright" + ], + "description": "Indicates the direction of the brightness problem when the brightness_score is low. 'ok' means brightness is acceptable, 'too_dark' means the image is underexposed, 'too_bright' means the image is overexposed." + }, + "is_document_fully_visible": { + "type": "boolean", + "nullable": true, + "description": "true if all four corners of the document are fully visible inside the image frame, false if any corner is cut off. Null when corner coordinates are unavailable." + }, + "resolution_score": { + "type": "number", + "description": "Effective resolution of the image based on total pixel count." + }, + "overall_score": { + "type": "number", + "description": "Weighted composite of sub-scores (focus 45%, brightness 30%, resolution 25%)." + } + } + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning (e.g., `ID_VERIFICATION`, `NFC`, `LIVENESS`). The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module, but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true, + "description": "Additional contextual data about the warning" + }, + "log_type": { + "type": "string", + "description": "Type of log: information, warning, or error" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning (for graph-based workflows)" + } + } + }, + "description": "Document verification warnings" + }, + "matches": { + "type": "array", + "description": "Matching documents found during duplicate/blocklist detection (up to 5 matches)", + "items": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "description": "Session ID of the matching document" + }, + "session_number": { + "type": "integer", + "description": "Session number of the matching document" + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Vendor data associated with the matching session" + }, + "verification_date": { + "type": "string", + "format": "date-time", + "description": "Date when the matching session was verified" + }, + "user_details": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Full name from the matching document" + }, + "document_type": { + "type": "string", + "description": "Type of the matching document" + }, + "document_number": { + "type": "string", + "description": "Document number of the matching document" + } + } + }, + "status": { + "type": "string", + "description": "Status of the matching session" + }, + "is_blocklisted": { + "type": "boolean", + "description": "Whether the matching document is blocklisted" + }, + "api_service": { + "type": "string", + "nullable": true, + "description": "API service type of the matching session" + }, + "front_image_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the front document image from the matching document" + } + } + } + }, + "document_liveness": { + "type": "object", + "nullable": true, + "description": "Document-liveness scores per fraud type, reported whether or not a warning fired, so an approved document can still be audited. `null` when the check did not run.", + "properties": { + "portrait_replace": { + "type": "object", + "nullable": true, + "properties": { + "score": { + "type": "number", + "nullable": true, + "description": "Likelihood that the document is genuine for this fraud type, 0-100 (higher = more likely genuine). The lowest of the two sides - the value the decision used. `null` when neither side produced a usable score." + }, + "front": { + "type": "number", + "nullable": true, + "description": "Score for the front side; `null` when not captured or when the score was dropped." + }, + "back": { + "type": "number", + "nullable": true, + "description": "Score for the back side; `null` when not captured or when the score was dropped." + }, + "decline_threshold": { + "type": "number", + "nullable": true, + "description": "The decline threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." + }, + "review_threshold": { + "type": "number", + "nullable": true, + "description": "The review threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." + }, + "bucket": { + "type": "string", + "nullable": true, + "enum": [ + "approve", + "review", + "decline", + null + ], + "description": "The fraud-type bucket saved at evaluation time. Null when there is no usable score, the check was not applied, or historical evaluation evidence is unavailable. Later workflow edits do not change it." + } + } + }, + "printed_copy": { + "type": "object", + "nullable": true, + "properties": { + "score": { + "type": "number", + "nullable": true, + "description": "Likelihood that the document is genuine for this fraud type, 0-100 (higher = more likely genuine). The lowest of the two sides - the value the decision used. `null` when neither side produced a usable score." + }, + "front": { + "type": "number", + "nullable": true, + "description": "Score for the front side; `null` when not captured or when the score was dropped." + }, + "back": { + "type": "number", + "nullable": true, + "description": "Score for the back side; `null` when not captured or when the score was dropped." + }, + "decline_threshold": { + "type": "number", + "nullable": true, + "description": "The decline threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." + }, + "review_threshold": { + "type": "number", + "nullable": true, + "description": "The review threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." + }, + "bucket": { + "type": "string", + "nullable": true, + "enum": [ + "approve", + "review", + "decline", + null + ], + "description": "The fraud-type bucket saved at evaluation time. Null when there is no usable score, the check was not applied, or historical evaluation evidence is unavailable. Later workflow edits do not change it." + } + } + }, + "screen_replay": { + "type": "object", + "nullable": true, + "properties": { + "score": { + "type": "number", + "nullable": true, + "description": "Likelihood that the document is genuine for this fraud type, 0-100 (higher = more likely genuine). The lowest of the two sides - the value the decision used. `null` when neither side produced a usable score." + }, + "front": { + "type": "number", + "nullable": true, + "description": "Score for the front side; `null` when not captured or when the score was dropped." + }, + "back": { + "type": "number", + "nullable": true, + "description": "Score for the back side; `null` when not captured or when the score was dropped." + }, + "decline_threshold": { + "type": "number", + "nullable": true, + "description": "The decline threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." + }, + "review_threshold": { + "type": "number", + "nullable": true, + "description": "The review threshold saved when this document was evaluated. Later workflow edits do not change it. Null when historical evaluation evidence is unavailable." + }, + "bucket": { + "type": "string", + "nullable": true, + "enum": [ + "approve", + "review", + "decline", + null + ], + "description": "The fraud-type bucket saved at evaluation time. Null when there is no usable score, the check was not applied, or historical evaluation evidence is unavailable. Later workflow edits do not change it." + } + } + } + } + } } - }, - "description": "Open (or add to) an investigation case. Only the first open_case action on a rule is applied." - } - ], - "description": "One rule action. A rule can carry several; all are applied together when it matches. Rules in TEST mode record what they would have done without applying any action." - }, - "TransactionRuleCreateRequest": { - "type": "object", - "required": [ - "title", - "category", - "mode" - ], - "properties": { - "title": { - "type": "string", - "description": "Rule name. Unique per application among non-deleted rules; duplicates return 400." - }, - "description": { - "type": "string", - "nullable": true - }, - "category": { - "type": "string", - "description": "Risk theme used for grouping and reporting, e.g. finance, aml_ctf, anomaly_detection, fatf, device_intelligence, crypto_monitoring, travel_rule, responsible_gaming, e_commerce." - }, - "mode": { - "type": "string", - "enum": [ - "ACTIVE", - "DISABLED", - "TEST" - ], - "description": "ACTIVE evaluates live. TEST evaluates and records rule runs but never touches score or status - the recommended starting mode. DISABLED is skipped." - }, - "evaluation_mode": { - "type": "string", - "enum": [ - "ALL", - "ANY" - ], - "default": "ALL", - "description": "Whether every condition must match (ALL) or at least one (ANY). Ignored when conditions use group_index." - }, - "scope": { - "$ref": "#/components/schemas/TransactionRuleScope" - }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleCondition" - } - }, - "aggregation": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleAggregation" - } - }, - "actions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleAction" - } - }, - "metadata": { - "type": "object", - "description": "Free-form metadata stored on the rule." - } - }, - "description": "Creates a CUSTOM rule. Rules in ACTIVE mode take effect immediately for new transactions - create in TEST mode and backtest first." - }, - "TransactionRuleUpdateRequest": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "Rule name. Unique per application among non-deleted rules; duplicates return 400." - }, - "description": { - "type": "string", - "nullable": true - }, - "category": { - "type": "string", - "description": "Risk theme used for grouping and reporting, e.g. finance, aml_ctf, anomaly_detection, fatf, device_intelligence, crypto_monitoring, travel_rule, responsible_gaming, e_commerce." - }, - "mode": { - "type": "string", - "enum": [ - "ACTIVE", - "DISABLED", - "TEST" - ], - "description": "ACTIVE evaluates live. TEST evaluates and records rule runs but never touches score or status - the recommended starting mode. DISABLED is skipped." - }, - "evaluation_mode": { - "type": "string", - "enum": [ - "ALL", - "ANY" - ], - "default": "ALL", - "description": "Whether every condition must match (ALL) or at least one (ANY). Ignored when conditions use group_index." - }, - "scope": { - "$ref": "#/components/schemas/TransactionRuleScope" - }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleCondition" - } - }, - "aggregation": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleAggregation" - } - }, - "actions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleAction" - } - }, - "metadata": { - "type": "object", - "description": "Free-form metadata stored on the rule." - } - }, - "description": "Partial update. Custom rules accept every field; library (PRESET) rules accept only mode - anything else returns 400." - }, - "TransactionRuleDetail": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "title": { - "type": "string", - "description": "Rule name. Unique per application among non-deleted rules; duplicates return 400." - }, - "description": { - "type": "string", - "nullable": true - }, - "category": { - "type": "string", - "description": "Risk theme used for grouping and reporting, e.g. finance, aml_ctf, anomaly_detection, fatf, device_intelligence, crypto_monitoring, travel_rule, responsible_gaming, e_commerce." - }, - "mode": { - "type": "string", - "enum": [ - "ACTIVE", - "DISABLED", - "TEST" - ], - "description": "ACTIVE evaluates live. TEST evaluates and records rule runs but never touches score or status - the recommended starting mode. DISABLED is skipped." - }, - "evaluation_mode": { - "type": "string", - "enum": [ - "ALL", - "ANY" - ], - "default": "ALL", - "description": "Whether every condition must match (ALL) or at least one (ANY). Ignored when conditions use group_index." - }, - "scope": { - "$ref": "#/components/schemas/TransactionRuleScope" - }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleCondition" - } - }, - "aggregation": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleAggregation" - } - }, - "actions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleAction" } }, - "metadata": { - "type": "object", - "description": "Free-form metadata stored on the rule." - }, - "severity": { + "nfc_skip_reason": { "type": "string", - "enum": [ - "UNKNOWN", - "LOW", - "MEDIUM", - "HIGH", - "CRITICAL" - ], "nullable": true, - "description": "Informational severity carried by library rules; not settable on custom rules." - }, - "source": { - "type": "string", "enum": [ - "PRESET", - "CUSTOM" + "USER_SKIPPED", + "DOCUMENT_WITHOUT_CHIP", + "CHIP_CERTIFICATE_UNAVAILABLE", + "DEVICE_WITHOUT_NFC", + "INTEGRATION_WITHOUT_NFC_ACCESS", + "MRZ_KEY_UNAVAILABLE", + "JPKI_UNAVAILABLE" ], - "description": "PRESET rules come from the library (install/uninstall, mode-only edits); CUSTOM rules are fully yours." + "description": "Why NFC chip verification did not run, populated whenever the workflow includes an NFC step. `null` while NFC is pending or when the chip was actually read. Values: `USER_SKIPPED` - the user chose to skip on an NFC-capable device (allowed by `allow_nfc_skip`); `DOCUMENT_WITHOUT_CHIP` - the document has no readable NFC chip (not an ICAO 9303 electronic document); `CHIP_CERTIFICATE_UNAVAILABLE` - the issuing country's signing certificate (CSCA) covering the document's issue period is unavailable, so the chip signature cannot be verified; `DEVICE_WITHOUT_NFC` - the device reported no NFC reader; `INTEGRATION_WITHOUT_NFC_ACCESS` - the verification ran without a native SDK (e.g. in a web browser), which has no access to the phone's NFC reader; `MRZ_KEY_UNAVAILABLE` - the chip access key could not be derived from the document's MRZ; `JPKI_UNAVAILABLE` - the document is a Japan My Number Card, whose chip is read through JPKI, and JPKI was not available for this verification: it is not enabled, or the app's native SDK cannot read My Number Cards." }, - "library_key": { - "type": "string", + "nfc_verifications": { + "type": "array", "nullable": true, - "description": "Stable library identifier; null for custom rules." - }, - "latest_triggered_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "run_count": { - "type": "integer", - "description": "How many transactions matched this rule." - }, - "approved_count": { - "type": "integer" - }, - "reviewed_count": { - "type": "integer" - }, - "declined_count": { - "type": "integer" - }, - "awaiting_user_count": { - "type": "integer" - }, - "approved_pct": { - "type": "number" - }, - "reviewed_pct": { - "type": "number" - }, - "declined_pct": { - "type": "number" - }, - "created_at": { - "type": "string", - "format": "date-time" + "description": "**Array of NFC / ePassport reports** produced by NFC steps in the workflow graph. **This is always a JSON array, never a singular `nfc` object** — read `nfc_verifications[0]` and iterate if your workflow can run NFC more than once. `null` until at least one NFC step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `chip_protocol`, `chip_data`, `authenticity` (SOD + DG integrity), `certificate_summary`, and `warnings[]`. A Japan My Number Card is read through JPKI instead of ICAO 9303: its item has `chip_protocol: \"jpki\"`, carries its evidence under `jpki`, and leaves the ICAO fields `null`. When the NFC step was bypassed before it could ever be offered (chipless document, missing certificate, no NFC capability reported, unreadable MRZ key, JPKI unavailable for a My Number Card), this stays `null` and `nfc_skip_reason` explains why; a skip at the step itself (user tapped skip, or the reader turned out unavailable at scan time) still produces a report with `is_nfc_skipped: true`.", + "items": { + "type": "object", + "description": "One NFC report: the full report for a single chip read (an ICAO 9303 ePassport or eID chip, or a Japan My Number Card read through JPKI).", + "properties": { + "node_id": { + "type": "string", + "description": "Workflow graph node id of the NFC step that produced this report. Use it to correlate the report with the workflow definition." + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Feature-level status of this NFC step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`." + }, + "is_nfc_skipped": { + "type": "boolean", + "description": "`true` when this NFC step was explicitly skipped instead of read; `skip_reason` says why." + }, + "skip_reason": { + "type": "string", + "nullable": true, + "enum": [ + "USER_SKIPPED", + "DOCUMENT_WITHOUT_CHIP", + "CHIP_CERTIFICATE_UNAVAILABLE", + "DEVICE_WITHOUT_NFC", + "INTEGRATION_WITHOUT_NFC_ACCESS", + "MRZ_KEY_UNAVAILABLE", + "JPKI_UNAVAILABLE" + ], + "description": "Why this NFC step was skipped. Only set when `is_nfc_skipped` is `true`. Same taxonomy as the session-level `nfc_skip_reason`, which additionally covers the cases where NFC never produced a report at all." + }, + "chip_protocol": { + "type": "string", + "enum": [ + "icao", + "jpki" + ], + "description": "How the NFC step reads the chip. `icao`: ICAO 9303 ePassport or eID chip, opened with the access key from the MRZ. `jpki`: Japan My Number Card, where the card signs a one-time challenge after the holder enters the card PIN in a native SDK; the result is under `jpki`. Also set on skipped steps, so you can tell a skipped My Number Card read apart." + }, + "jpki": { + "type": "object", + "nullable": true, + "description": "What a Japan My Number Card read through JPKI proved. Present when `chip_protocol` is `jpki` and the card was read. `null` for ICAO chips, for skipped steps, and when the NFC step closed without any card read (that case raises `JPKI_SIGNATURE_INVALID`). Never carries the certificates, their serial numbers, the signatures or the 12-digit My Number.", + "properties": { + "certificate_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "user_authentication", + "signature" + ] + }, + "description": "The card certificates read. `user_authentication` (4-digit PIN) is always read; `signature` (signature PIN) is read when the NFC node's `jpki_request_attributes` setting is on." + }, + "chain_valid": { + "type": "boolean", + "description": "`true` when every certificate read chains to the JPKI certification authority, is inside its validity period and is a My Number Card certificate of its type." + }, + "signature_valid": { + "type": "boolean", + "description": "`true` when every signature the card made over the one-time challenge verifies. The card only signs after its PIN is entered, so this proves possession of the card and knowledge of the PIN." + }, + "revocation_status": { + "type": "string", + "enum": [ + "good", + "revoked", + "unchecked" + ], + "description": "`good` when the revocation check confirmed every certificate is valid, `revoked` when any certificate is revoked (for example a card reported lost), `unchecked` when no revocation answer was available. `unchecked` is never treated as `good`: the NFC node's `jpki_revocation_unchecked_action` decides the outcome (default `REVIEW`)." + }, + "ca_generation": { + "type": "string", + "nullable": true, + "description": "Generation of the JPKI certification authority that issued the certificates, for example `2023`. `null` when no certificate chains to one." + }, + "certificate_expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "The earliest expiry among the certificates read." + }, + "certificates": { + "type": "array", + "items": { + "type": "object", + "description": "What one certificate read from the card proved. Never the certificate itself or its serial number.", + "properties": { + "type": { + "type": "string", + "enum": [ + "user_authentication", + "signature" + ], + "description": "Which card certificate this entry describes." + }, + "chain_valid": { + "type": "boolean", + "description": "`true` when the certificate chains to the JPKI certification authority of its type, is inside its validity period and is a My Number Card certificate of that type." + }, + "signature_valid": { + "type": "boolean", + "description": "`true` when the card's signature over the one-time challenge verifies against this certificate." + }, + "ca_generation": { + "type": "string", + "nullable": true, + "description": "Generation of the JPKI certification authority that issued the certificate, for example `2023`. `null` when no trusted issuer was found." + }, + "not_valid_before": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Start of the certificate's validity period. `null` when the certificate could not be read." + }, + "not_valid_after": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "End of the certificate's validity period. `null` when the certificate could not be read." + }, + "failure": { + "type": "string", + "nullable": true, + "enum": [ + "unreadable_certificate", + "untrusted_issuer", + "expired", + "not_yet_valid", + "wrong_policy", + "key_usage", + null + ], + "description": "Why `chain_valid` is `false`: `unreadable_certificate`, `untrusted_issuer` (not issued by the JPKI certification authority of its type), `expired`, `not_yet_valid`, `wrong_policy` (not a My Number Card certificate of that type) or `key_usage` (the key may not sign). `null` when the chain is valid." + } + } + }, + "description": "Per-certificate results, one entry per certificate read." + }, + "attributes": { + "type": "object", + "nullable": true, + "description": "The holder's data from the signature certificate, once its chain is valid. `null` when the signature certificate was not requested or did not chain to the JPKI certification authority.", + "properties": { + "full_name": { + "type": "string", + "nullable": true, + "description": "Name as registered, usually in Japanese script." + }, + "date_of_birth": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Date of birth, `YYYY-MM-DD`. `null` when the registered date is incomplete." + }, + "gender": { + "type": "string", + "nullable": true, + "enum": [ + "M", + "F", + null + ], + "description": "`M`, `F`, or `null` when not registered." + }, + "address": { + "type": "string", + "nullable": true, + "description": "Registered address." + }, + "name_substitute_characters": { + "type": "string", + "nullable": true, + "description": "Position string marking the characters of `full_name` that were replaced by a substitute because the original character is outside the standard character set. `null` when none were replaced." + }, + "address_substitute_characters": { + "type": "string", + "nullable": true, + "description": "Position string marking substitute characters in `address`, as for `name_substitute_characters`." + } + } + }, + "attributes_match_ocr": { + "type": "boolean", + "nullable": true, + "description": "`true` when the attributes match the data read from the photo of the same card, `false` when any attribute differs (`JPKI_AND_OCR_DATA_NOT_SAME`), `null` when nothing could be compared, including whenever `attributes` is `null`." + }, + "mismatched_attributes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "full_name", + "date_of_birth", + "gender", + "address" + ] + }, + "description": "Names of the attributes that differ from the photo data, never their values. Empty when everything compared matches." + } + } + }, + "portrait_image": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Presigned URL to the portrait read from the chip (ICAO DG2). `null` for a JPKI read (`chip_protocol: \"jpki\"`) and for skipped steps." + }, + "signature_image": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Presigned URL to the holder's signature image read from the chip (ICAO DG7), when the chip stores one. `null` for a JPKI read (`chip_protocol: \"jpki\"`) and for skipped steps." + }, + "chip_data": { + "type": "object", + "nullable": true, + "description": "Personal data parsed from the ICAO chip's Data Groups. `null` for a JPKI read (`chip_protocol: \"jpki\"`) and for skipped steps.", + "properties": { + "document_type": { + "type": "string" + }, + "issuing_country": { + "type": "string" + }, + "document_number": { + "type": "string" + }, + "expiration_date": { + "type": "string", + "format": "date" + }, + "first_name": { + "type": "string" + }, + "last_name": { + "type": "string" + }, + "birth_date": { + "type": "string", + "format": "date" + }, + "gender": { + "type": "string" + }, + "nationality": { + "type": "string" + }, + "address": { + "type": "string" + }, + "place_of_birth": { + "type": "string" + } + } + }, + "authenticity": { + "type": "object", + "nullable": true, + "description": "ICAO passive authentication: SOD signature (`sod_integrity`) and Data Group hashes (`dg_integrity`). `null` for a JPKI read, which reports its proof under `jpki`.", + "properties": { + "sod_integrity": { + "type": "boolean" + }, + "dg_integrity": { + "type": "boolean" + } + } + }, + "certificate_summary": { + "type": "object", + "nullable": true, + "description": "Summary of the ICAO Document Signer Certificate. `null` for a JPKI read (`chip_protocol: \"jpki\"`) and for skipped steps.", + "properties": { + "issuer": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "serial_number": { + "type": "string" + }, + "not_valid_after": { + "type": "string", + "format": "date-time" + }, + "not_valid_before": { + "type": "string", + "format": "date-time" + } + } + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true + }, + "log_type": { + "type": "string" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning" + } + } + } + } + } + } }, - "updated_at": { - "type": "string", - "format": "date-time" - } - } - }, - "TransactionRuleBacktestRequest": { - "type": "object", - "properties": { - "conditions": { + "liveness_checks": { "type": "array", + "nullable": true, + "description": "**Array of liveness reports** produced by liveness steps in the workflow graph. Always a JSON array — never a singular `liveness` object. `null` until at least one liveness step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `method`, `score`, `reference_image`, `video_url`, optional `age_estimation`, cross-session `matches[]`, and `warnings[]`.", "items": { - "$ref": "#/components/schemas/TransactionRuleCondition" - }, - "default": [] + "type": "object", + "description": "One liveness report — the full report for a single liveness pass.", + "properties": { + "node_id": { + "type": "string", + "description": "Workflow graph node id of the liveness step that produced this report." + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Feature-level status of this liveness step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`." + }, + "method": { + "type": "string", + "description": "Method used for liveness detection" + }, + "score": { + "type": "number", + "description": "Confidence score of the liveness check" + }, + "reference_image": { + "type": "string", + "format": "uri", + "description": "URL of the reference image used" + }, + "video_url": { + "type": "string", + "format": "uri", + "description": "URL of the liveness check video" + }, + "age_estimation": { + "type": "number", + "description": "Estimated age of the person" + }, + "matches": { + "type": "array", + "description": "Similar faces found by the duplicate-face search (top matches, blocklisted/allowlisted entries first). Each entry's `source` tells you where the matching face came from: `session` (another verification session), `imported` (a face uploaded against a vendor user), `list_entry` (an org-managed allow/block list face), or `retained_template` (an image-free biometric template stored on a User, listed under `/v3/biometric-templates/`). `session_id`, `session_number`, `status` and `api_service` are `null` for non-session sources. `imported` matches still populate `vendor_data`, `verification_date` and — when the vendor user has a name on file — `user_details` (with `full_name` only) from the vendor-user record; `retained_template` matches populate `vendor_user_id`, `vendor_data`, `biometric_template_id` and `verification_date`, while their image and identity fields are `null`; `list_entry` matches return all of these as `null`.", + "items": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Session ID of the matching face. `null` when `source` is `imported`, `list_entry`, or `retained_template`." + }, + "session_number": { + "type": "integer", + "nullable": true + }, + "similarity_percentage": { + "type": "number", + "description": "Face similarity between the two images, clamped to 0–100." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of the matching session or linked User for imported and retained-template faces." + }, + "vendor_user_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Linked User id for `imported` and `retained_template` matches and for session matches linked to a User. `null` for list entries and sessions without a User." + }, + "biometric_template_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Id of the retained biometric template. Set only when `source` is `retained_template`; `null` otherwise." + }, + "verification_date": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the matching face was captured, or when a retained template was created. `null` for list entries." + }, + "user_details": { + "type": "object", + "nullable": true, + "description": "Identity data from the matching session or imported User profile. Always `null` for `retained_template` and `list_entry` matches.", + "properties": { + "full_name": { + "type": "string", + "nullable": true + }, + "document_type": { + "type": "string", + "nullable": true + }, + "document_number": { + "type": "string", + "nullable": true + } + } + }, + "match_image_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Reference image for image-backed matches. Always `null` for `retained_template` matches because retained templates contain no image." + }, + "status": { + "type": "string", + "nullable": true, + "description": "Lifecycle status of the matching session. `null` for non-session sources." + }, + "is_blocklisted": { + "type": "boolean", + "description": "Whether the matching face is on a blocklist." + }, + "is_allowlisted": { + "type": "boolean", + "description": "Whether the matching face is on an allowlist." + }, + "api_service": { + "type": "string", + "nullable": true, + "description": "Standalone API service of the matching session (e.g. `PASSIVE_LIVENESS`, `FACE_MATCH`); `null` for workflow sessions and non-session sources." + }, + "source": { + "type": "string", + "enum": [ + "session", + "imported", + "list_entry", + "retained_template" + ], + "description": "Origin of the matching face: `session`, `imported` (User face upload), `list_entry` (allow/block list), or `retained_template` (an image-free biometric template stored on a User, listed under `/v3/biometric-templates/`)." + } + } + } + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true + }, + "log_type": { + "type": "string" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning" + } + } + } + }, + "face_quality": { + "type": "number", + "nullable": true, + "description": "Face image quality score (0-100%). Only available for passive liveness." + }, + "face_luminance": { + "type": "number", + "nullable": true, + "description": "Face luminance/brightness score (0-100%). Only available for passive liveness." + } + } + } }, - "aggregation": { + "face_matches": { "type": "array", + "nullable": true, + "description": "**Array of face-match reports** produced by face-match steps in the workflow graph. Always a JSON array — never a singular `face_match` object. `null` until at least one face-match step has produced data. Each item is the full report, including `node_id`, feature-level `status`, similarity `score`, `source_image`, `target_image`, and `warnings[]`.", "items": { - "$ref": "#/components/schemas/TransactionRuleAggregation" - }, - "default": [] - }, - "evaluation_mode": { - "type": "string", - "enum": [ - "ALL", - "ANY" - ], - "default": "ALL" - }, - "scope": { - "$ref": "#/components/schemas/TransactionRuleScope" - }, - "period_days": { - "type": "integer", - "minimum": 1, - "maximum": 365, - "default": 90 - } - }, - "description": "Hypothetical rule configuration to evaluate against recent transactions. Read-only and free: nothing is created or modified. Send the same conditions, aggregation, evaluation_mode AND scope as the rule you intend to activate. Omitted conditions, aggregation and scope add no constraints of their own; dropping scope can include directions or transaction types the saved rule skips. Omitted evaluation_mode defaults to ALL (which can reduce matches for an ANY rule), and omitted period_days defaults to 90 days, not unlimited history." - }, - "TransactionRuleBacktestResult": { - "type": "object", - "properties": { - "evaluated": { - "type": "integer", - "description": "Transactions evaluated (most recent first, capped)." - }, - "matched": { - "type": "integer", - "description": "Transactions the configuration would have matched." - }, - "affected_entities": { - "type": "integer", - "description": "Distinct subjects (by vendor_data) among the matches." - }, - "period_days": { - "type": "integer" - } - } - }, - "TransactionRuleLibraryEntry": { - "type": "object", - "properties": { - "library_key": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string", - "nullable": true - }, - "category": { - "type": "string" - }, - "evaluation_mode": { - "type": "string", - "enum": [ - "ALL", - "ANY" - ] - }, - "scope": { - "$ref": "#/components/schemas/TransactionRuleScope" + "type": "object", + "description": "One face-match report — the full report.", + "properties": { + "node_id": { + "type": "string", + "description": "Workflow graph node id of the face-match step that produced this report." + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Feature-level status of this face-match step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`." + }, + "score": { + "type": "number", + "description": "Confidence score of the face match" + }, + "source_image_session_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Session id under which the source (reference) portrait is stored. In biometric-authentication flows this is the session's own id (the submitted portrait is stored under the new session). `null` when the source image is the same session's document portrait." + }, + "source_image": { + "type": "string", + "format": "uri", + "description": "URL of the source image used for comparison" + }, + "target_image": { + "type": "string", + "format": "uri", + "description": "URL of the target image used for comparison" + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true + }, + "log_type": { + "type": "string" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning" + } + } + }, + "description": "Face match verification warnings" + } + } + } }, - "conditions": { + "phone_verifications": { "type": "array", + "nullable": true, + "description": "**Array of phone verification reports** — always a JSON array, never a singular `phone` object. `null` until at least one phone step has produced data. Each item is the full report, including `node_id`, feature-level `status`, the phone number and country metadata, `lifecycle[]`, `matches[]`, and `warnings[]`.", "items": { - "$ref": "#/components/schemas/TransactionRuleCondition" + "type": "object", + "properties": { + "node_id": { + "type": "string", + "description": "Unique identifier for this phone step in the workflow graph." + }, + "status": { + "type": "string", + "description": "Feature-level status of this phone verification step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Expired" + ] + }, + "phone_number_prefix": { + "type": "string", + "description": "Country calling code prefix" + }, + "phone_number": { + "type": "string", + "description": "Phone number without country prefix" + }, + "full_number": { + "type": "string", + "description": "Complete phone number with country prefix" + }, + "country_code": { + "type": "string", + "description": "Two-letter country code (ISO 3166-1 alpha-2)" + }, + "country_name": { + "type": "string", + "description": "Full country name" + }, + "carrier": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the phone carrier" + }, + "type": { + "type": "string", + "description": "Type of phone line (mobile, landline, etc)" + } + } + }, + "is_disposable": { + "type": "boolean", + "description": "Whether the phone number is from a disposable service" + }, + "is_virtual": { + "type": "boolean", + "description": "Whether the phone number is virtual" + }, + "verification_method": { + "type": "string", + "description": "Method used to verify the phone number" + }, + "verification_attempts": { + "type": "integer", + "description": "Number of verification attempts made" + }, + "verified_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the phone was verified" + }, + "lifecycle": { + "type": "array", + "description": "Chronological list of events in the phone verification lifecycle.", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "details": { + "type": "object", + "nullable": true, + "description": "Event-specific payload. Send events carry `status`, `reason`, `channel`, `actual_channel`; delivery events carry `channel`, `status`; code-check events carry `code_tried`, `status`; final status events carry `reason` — or `null` when there is no reason to report (e.g. `PHONE_VERIFICATION_APPROVED`).", + "properties": { + "status": { + "type": "string" + }, + "reason": { + "type": "string", + "nullable": true + }, + "channel": { + "type": "string", + "nullable": true + }, + "actual_channel": { + "type": "string", + "nullable": true + }, + "code_tried": { + "type": "string" + } + } + }, + "fee": { + "type": "number" + } + } + } + }, + "warnings": { + "type": "array", + "description": "List of warnings related to phone verification", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true + }, + "log_type": { + "type": "string" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning" + } + } + } + }, + "matches": { + "type": "array", + "description": "Matches found for this phone number. Each match has a `source`: `session` (another verification session in your application matched on the same number, returned with `session_id`/`session_number`/`vendor_data`/`verification_date`) or `list_entry` (the number is on an org-managed allow/block list — see `/management-api/lists/overview`). Cross-session matches use different `vendor_data` and are capped at 5.", + "items": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "session_number": { + "type": "integer", + "nullable": true + }, + "vendor_data": { + "type": "string", + "nullable": true + }, + "verification_date": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "phone_number": { + "type": "string" + }, + "status": { + "type": "string", + "nullable": true + }, + "is_blocklisted": { + "type": "boolean" + }, + "api_service": { + "type": "string", + "nullable": true + }, + "source": { + "type": "string", + "enum": [ + "session", + "list_entry" + ], + "description": "Where this match came from. `session` means another verification session in your application matched on this value. `list_entry` means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at `/management-api/lists/overview` for how to manage those lists." + } + } + } + } + } } }, - "aggregation": { + "email_verifications": { "type": "array", + "nullable": true, + "description": "**Array of email verification reports** — always a JSON array, never a singular `email` object. `null` until at least one email step has produced data. Each item is the full report, including `node_id`, feature-level `status`, the email address, breach data, `lifecycle[]`, `matches[]`, and `warnings[]`.", "items": { - "$ref": "#/components/schemas/TransactionRuleAggregation" + "type": "object", + "properties": { + "node_id": { + "type": "string", + "description": "Unique identifier for this email step in the workflow graph." + }, + "status": { + "type": "string", + "description": "Feature-level status of this email verification step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Expired" + ] + }, + "email": { + "type": "string", + "description": "The email address being verified" + }, + "is_breached": { + "type": "boolean", + "description": "Indicates if the email was found in known data breaches" + }, + "breaches": { + "type": "array", + "description": "List of known breaches this email was found in", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the breach" + }, + "domain": { + "type": "string", + "description": "Domain affected by the breach" + }, + "logo_path": { + "type": "string", + "description": "URL to the logo of the breached service" + }, + "breach_date": { + "type": "string", + "format": "date", + "description": "Date when the breach occurred" + }, + "description": { + "type": "string", + "description": "Description of the breach" + }, + "is_verified": { + "type": "boolean", + "description": "Whether the breach is verified" + }, + "data_classes": { + "type": "array", + "description": "Types of data exposed in the breach", + "items": { + "type": "string" + } + }, + "breach_emails_count": { + "type": "integer", + "description": "Number of email addresses affected in the breach" + } + } + } + }, + "is_disposable": { + "type": "boolean", + "description": "Indicates if the email is from a disposable provider" + }, + "is_undeliverable": { + "type": "boolean", + "description": "Indicates if the email address is undeliverable" + }, + "verification_attempts": { + "type": "integer", + "description": "Number of verification attempts for this email" + }, + "verified_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the email was verified" + }, + "warnings": { + "type": "array", + "description": "List of warnings related to email verification", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true + }, + "log_type": { + "type": "string" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning" + } + } + } + }, + "lifecycle": { + "type": "array", + "description": "Chronological list of events in the email verification lifecycle.", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "details": { + "type": "object", + "nullable": true, + "description": "Event-specific payload. Send events carry `status`, `reason`; code-check events carry `code_tried`, `status`; final status events carry `reason` — or `null` when there is no reason to report (e.g. `EMAIL_VERIFICATION_APPROVED`).", + "properties": { + "status": { + "type": "string" + }, + "reason": { + "type": "string", + "nullable": true + }, + "code_tried": { + "type": "string" + } + } + }, + "fee": { + "type": "number" + } + } + } + }, + "matches": { + "type": "array", + "description": "Matches found for this email address. Each match has a `source`: `session` (another verification session in your application matched on the same email, returned with `session_id`/`session_number`/`vendor_data`/`verification_date`) or `list_entry` (the email is on an org-managed allow/block list — see `/management-api/lists/overview`). Cross-session matches use different `vendor_data` and are capped at 5.", + "items": { + "type": "object", + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "session_number": { + "type": "integer", + "nullable": true + }, + "vendor_data": { + "type": "string", + "nullable": true + }, + "verification_date": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "email": { + "type": "string", + "format": "email" + }, + "status": { + "type": "string", + "nullable": true + }, + "is_blocklisted": { + "type": "boolean" + }, + "api_service": { + "type": "string", + "nullable": true + }, + "source": { + "type": "string", + "enum": [ + "session", + "list_entry" + ], + "description": "Where this match came from. `session` means another verification session in your application matched on this value. `list_entry` means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at `/management-api/lists/overview` for how to manage those lists." + } + } + } + } + } } }, - "actions": { + "poa_verifications": { "type": "array", - "items": { - "$ref": "#/components/schemas/TransactionRuleAction" - } - }, - "metadata": { - "type": "object" - }, - "bundle": { - "type": "string", "nullable": true, - "description": "Functional family, e.g. finance, aml_ctf, crypto_monitoring, travel_rule, responsible_gaming." - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "industries": { - "type": "array", + "description": "**Array of Proof of Address (POA) reports** — always a JSON array, never a singular `poa` object. `null` until at least one POA step has produced data. Each item is the full report, including `node_id`, feature-level `status`, the document file URL, issuer, dates, parsed address, and `warnings[]`.", "items": { - "type": "string" + "type": "object", + "properties": { + "node_id": { + "type": "string", + "description": "Unique identifier for this POA step in the workflow graph." + }, + "status": { + "type": "string", + "description": "Feature-level status of this Proof of Address step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ] + }, + "document_file": { + "type": "string", + "format": "uri", + "description": "URL of the proof of address document" + }, + "issuing_state": { + "type": "string", + "description": "Two-letter country code of the issuing state" + }, + "document_type": { + "type": "string", + "description": "Type of proof of address document" + }, + "document_subtype": { + "type": "string", + "nullable": true, + "description": "More specific classification of the POA document within its `document_type`, when one was detected. `null` otherwise." + }, + "document_language": { + "type": "string", + "description": "Language code of the document" + }, + "document_metadata": { + "type": "object", + "properties": { + "file_size": { + "type": "integer", + "description": "Size of the document" + }, + "content_type": { + "type": "string", + "description": "Content type of the document" + }, + "creation_date": { + "type": "string", + "format": "date", + "description": "Date when the document was created" + }, + "modified_date": { + "type": "string", + "format": "date", + "description": "Date when the document was modified" + }, + "overlay_manipulation": { + "type": "object", + "nullable": true, + "description": "PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF.", + "properties": { + "detected": { + "type": "boolean", + "description": "Whether overlay-text manipulation evidence was detected." + }, + "analyzed": { + "type": "boolean", + "description": "Whether the PDF could be analyzed." + }, + "signals": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation." + }, + "manipulated_regions": { + "type": "array", + "description": "Suspected manipulated areas in PDF page coordinates.", + "items": { + "type": "object", + "properties": { + "page": { + "type": "integer" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "page_width": { + "type": "number" + }, + "page_height": { + "type": "number" + } + } + } + } + } + }, + "creator": { + "type": "string", + "nullable": true, + "description": "Creator application recorded in the document metadata." + }, + "producer": { + "type": "string", + "nullable": true, + "description": "Producer software recorded in the PDF metadata." + }, + "software": { + "type": "string", + "nullable": true, + "description": "Editing/processing software recorded in the file metadata." + }, + "encryption": { + "type": "string", + "nullable": true, + "description": "Encryption scheme of the document, when encrypted." + }, + "is_signed": { + "type": "boolean", + "nullable": true, + "description": "Whether the PDF carries a digital signature." + }, + "is_tampered": { + "type": "boolean", + "nullable": true, + "description": "Whether signature validation indicates the file was modified after signing." + }, + "signature_info": { + "type": "object", + "nullable": true, + "description": "Details of the digital signature(s), when present." + }, + "exif_original_date": { + "type": "string", + "nullable": true, + "description": "EXIF original capture date, for image uploads." + }, + "exif_digitized_date": { + "type": "string", + "nullable": true, + "description": "EXIF digitization date, for image uploads." + }, + "processed_by_known_editor": { + "type": "boolean", + "nullable": true, + "description": "Whether the file metadata indicates processing by a known document editor." + }, + "has_different_creation_mod_date": { + "type": "boolean", + "nullable": true, + "description": "Whether the creation and modification dates differ — a common manipulation signal." + } + }, + "description": "File-forensics metadata extracted from the uploaded POA document. Always contains the keys below (values are `null` when not applicable, e.g. EXIF dates for PDFs). `null` when no metadata could be extracted.", + "nullable": true + }, + "issuer": { + "type": "string", + "description": "Name of the document issuer" + }, + "issue_date": { + "type": "string", + "format": "date", + "description": "Date when the document was issued" + }, + "expiration_date": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Expiration date of the document, if available" + }, + "poa_address": { + "type": "string", + "description": "Raw address from the proof of address document" + }, + "poa_formatted_address": { + "type": "string", + "description": "Formatted address from the proof of address document" + }, + "poa_parsed_address": { + "type": "object", + "properties": { + "address_type": { + "type": "string", + "description": "Type of address (Street, Avenue, etc)" + }, + "street_1": { + "type": "string", + "description": "Primary street address" + }, + "street_2": { + "type": "string", + "nullable": true, + "description": "Secondary street address" + }, + "city": { + "type": "string", + "description": "City name" + }, + "region": { + "type": "string", + "description": "Region or state name" + }, + "country": { + "type": "string", + "description": "Two-letter country code" + }, + "postal_code": { + "type": "string", + "description": "Postal code" + }, + "document_location": { + "type": "object", + "properties": { + "latitude": { + "type": "number", + "description": "Latitude coordinate" + }, + "longitude": { + "type": "number", + "description": "Longitude coordinate" + } + } + } + } + }, + "name_on_document": { + "type": "string", + "nullable": true, + "description": "Account-holder / addressee name extracted from the POA document, used for name matching. `null` when no name could be extracted." + }, + "name_match_score_expected_details": { + "type": "number", + "nullable": true, + "description": "Similarity score (0-100) between `name_on_document` and the expected name supplied at session creation. `null` when either side is missing." + }, + "name_match_score_id_verification": { + "type": "number", + "nullable": true, + "description": "Similarity score (0-100) between `name_on_document` and the name extracted by the ID verification step. `null` when either side is missing." + }, + "expected_details_address": { + "type": "string", + "nullable": true, + "description": "Expected raw address for verification" + }, + "expected_details_formatted_address": { + "type": "string", + "nullable": true, + "description": "Expected formatted address for verification" + }, + "expected_details_parsed_address": { + "type": "object", + "description": "Expected parsed address details for verification" + }, + "extra_fields": { + "type": "object", + "description": "Additional banking and contact details extracted from the document.", + "properties": { + "bank_account_number": { + "type": "string", + "nullable": true + }, + "bank_iban": { + "type": "string", + "nullable": true + }, + "bank_sort_code": { + "type": "string", + "nullable": true + }, + "bank_routing_number": { + "type": "string", + "nullable": true + }, + "bank_swift_bic": { + "type": "string", + "nullable": true + }, + "bank_branch_name": { + "type": "string", + "nullable": true + }, + "bank_branch_address": { + "type": "string", + "nullable": true + }, + "document_phone_number": { + "type": "string", + "nullable": true + }, + "additional_names": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "extra_files": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + }, + "description": "Additional proof of address files" + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true + }, + "log_type": { + "type": "string" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning" + } + } + } + }, + "detected_codes": { + "type": "array", + "description": "QR codes and barcodes detected across every processed page of the document. Empty when none are found. A located but undecodable QR code has `parsed_payload: null`.", + "items": { + "$ref": "#/components/schemas/DocumentAIDetectedCode" + } + } + } } }, - "source": { - "type": "string" - }, - "is_installed": { - "type": "boolean", - "description": "Whether this preset is currently installed on the application." - } - } - }, - "TransactionRuleInstallRequest": { - "type": "object", - "properties": { - "library_keys": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Specific presets to install or uninstall." - }, - "bundle": { - "type": "string", - "description": "Install or uninstall every preset in this bundle instead." - } - }, - "description": "Provide library_keys OR bundle." - }, - "TransactionRuleInstallResult": { - "type": "object", - "properties": { - "installed_library_keys": { + "document_ai_documents": { "type": "array", - "items": { - "type": "string" - }, - "description": "POST (install) only: presets installed by this call." - }, - "installed_count": { - "type": "integer", - "description": "POST (install) only." - }, - "uninstalled_count": { - "type": "integer", - "description": "DELETE (uninstall) only: presets removed by this call." - } - } - }, - "WalletScreeningResult": { - "type": "object", - "properties": { - "provider": { - "type": "string", - "description": "Provider that performed the screening (e.g. `merklescience`, `crystal`)." - }, - "screening_type": { - "type": "string", - "enum": [ - "WALLET_SCREENING" - ], - "description": "Always `WALLET_SCREENING` for this endpoint." - }, - "risk_score": { - "type": "integer", - "description": "Normalised 0-100 risk score. Higher means greater exposure to risky entities. Merkle Science grades an address 0-5, which maps to 0, 15, 35, 55, 75 or 95." - }, - "severity": { - "type": "string", - "enum": [ - "UNKNOWN", - "LOW", - "MEDIUM", - "HIGH", - "CRITICAL" - ], - "description": "Risk bucket derived from `risk_score`: `0-9` UNKNOWN, `10-39` LOW, `40-69` MEDIUM, `70-89` HIGH, `90-100` CRITICAL. `UNKNOWN` is the lowest band, not a separate no-data state: `risk_score` 0 (the common clean-address case) means no adverse assessment, while a non-zero score in the 1-9 range is a real but sub-LOW signal - read the `risk_score`, not just the band. Never treat `UNKNOWN` as an affirmative low-risk or clear rating; do not display it as a pass." - }, - "status": { - "type": "string", - "enum": [ - "SCREENED", - "PENDING", - "ERROR" - ], - "description": "Screening outcome status." - }, - "summary": { - "type": "string", - "description": "Human-readable summary of the screening result." - }, - "wallet_address": { - "type": "string", - "description": "The screened address, echoed back." - }, - "transaction_hash": { - "type": "string", - "nullable": true, - "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." - }, - "blockchain": { - "type": "string", - "description": "The blockchain that was screened." - }, - "block_timestamp": { - "type": "string", - "nullable": true, - "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." - }, - "total_value_usd": { - "type": "number", "nullable": true, - "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." - }, - "fee_usd": { - "type": "number", - "nullable": true, - "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." - }, - "sender_rows": { - "type": "array", - "items": { - "type": "object" - }, - "description": "Transaction screening field, shared with the result shape. Always empty on a wallet screening result." - }, - "receiver_rows": { - "type": "array", + "description": "**Document AI reports grouped by workflow node** — always a JSON array, never a singular object. `null` until at least one Document AI step has produced data. Present only when `session_kind = user`. Each item is a node group with an aggregate `status`, the `node_id`, an `items[]` array of the per-document results (extracted data keyed by the configured field key), and `warnings`.", "items": { - "type": "object" - }, - "description": "Transaction screening field, shared with the result shape. Always empty on a wallet screening result." - }, - "transaction_flows": { - "type": "array", - "items": { - "type": "object" - }, - "description": "Transaction screening field, shared with the result shape. Always empty on a wallet screening result." - }, - "transaction_flows_status": { - "type": "string", - "nullable": true, - "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." - }, - "transaction_flows_error": { - "type": "string", - "nullable": true, - "description": "Transaction screening field, shared with the result shape. Always `null` on a wallet screening result." + "type": "object", + "description": "Document AI results grouped by workflow node.", + "properties": { + "status": { + "type": "string", + "description": "Aggregate Document AI status for this node, rolled up from its `items[]`.", + "enum": [ + "Approved", + "Declined", + "In Review", + "Not Finished" + ] + }, + "node_id": { + "type": "string", + "nullable": true, + "description": "Unique identifier for this Document AI step in the workflow graph." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "node_id": { + "type": "string", + "nullable": true, + "description": "Workflow graph node that produced this document." + }, + "document_key": { + "type": "string", + "description": "Key of the configured Document AI document this file corresponds to." + }, + "original_filename": { + "type": "string", + "nullable": true, + "description": "Filename as uploaded by the user." + }, + "file_size": { + "type": "integer", + "nullable": true, + "description": "File size in bytes." + }, + "file_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Short-lived presigned link to the uploaded file." + }, + "status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "In Review", + "Not Finished" + ], + "description": "Per-document Document AI status." + }, + "extracted_data": { + "type": "object", + "additionalProperties": true, + "nullable": true, + "description": "Values extracted by Document AI, keyed by the configured field `key`. Each value is a string, number, or `null`." + }, + "detected_codes": { + "type": "array", + "description": "QR codes and barcodes detected across every processed page. Empty when none are found. A located but undecodable QR code has `parsed_payload: null`.", + "items": { + "$ref": "#/components/schemas/DocumentAIDetectedCode" + } + }, + "fields": { + "type": "array", + "description": "The configured extraction fields for this document.", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Stable field key used as the key in `extracted_data`." + }, + "name": { + "type": "string", + "description": "Human-readable field name." + }, + "instruction": { + "type": "string", + "description": "Extraction instruction shown to the model." + }, + "type": { + "type": "string", + "enum": [ + "text", + "number", + "date" + ], + "description": "Expected value type of the extracted field." + }, + "required": { + "type": "boolean", + "description": "Whether this field is required." + } + } + } + }, + "document_metadata": { + "type": "object", + "nullable": true, + "description": "File-forensics metadata extracted from the uploaded document (e.g. `is_tampered`). `null` when nothing could be extracted." + }, + "cross_check_result": { + "type": "object", + "nullable": true, + "description": "Cross-reference of the extracted document fields against other session data. `null` when no cross-check was performed." + }, + "created_at": { + "type": "string", + "format": "date-time" + } + } + } + }, + "warnings": { + "type": "array", + "nullable": true, + "items": { + "type": "object" + }, + "description": "Warning logs raised for this Document AI node." + } + } + } }, - "source_of_funds": { + "questionnaire_responses": { "type": "array", - "description": "Where the address received funds from, attributed by entity. Each entry is an exposure breakdown.", + "nullable": true, + "description": "**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.", "items": { "type": "object", "properties": { - "category": { + "node_id": { "type": "string", - "description": "Normalised risk category, e.g. `exchange_licensed`, `dex`, `defi`, `payment_processor`, `mixer`, `darknet_market`, `sanctioned`, `stolen_funds`, `high_risk_exchange`." + "description": "Unique identifier for this questionnaire step in the workflow graph." }, - "percentage": { - "type": "number", - "description": "Share of the direction's total exposure, 0-100." + "questionnaire_id": { + "type": "string", + "description": "Unique identifier for the questionnaire" }, - "amount_usd": { - "type": "number", - "nullable": true, - "description": "USD value attributed to this entity." + "title": { + "type": "string", + "description": "Title of the questionnaire" }, - "entity_name": { + "description": { "type": "string", - "nullable": true, - "description": "Name of the attributed entity, for example an exchange, a mixer or a sanctioned group." + "description": "Description of the questionnaire" }, - "entity_type": { + "languages": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Supported languages" + }, + "default_language": { "type": "string", - "nullable": true, - "description": "Provider entity type (e.g. `Exchange`, `DeFi`, `Sanctions`, `Theft`)." + "description": "Default language" }, - "entity_subtype": { + "is_active": { + "type": "boolean", + "description": "Whether the questionnaire is active" + }, + "is_simple_questionnaire": { + "type": "boolean", + "description": "Whether the questionnaire is a simple linear form (no branching graph)." + }, + "questionnaire_group_id": { "type": "string", + "format": "uuid", "nullable": true, - "description": "More granular subtype (e.g. `Mandatory KYC and AML`, `OFAC`, `Yield Aggregator`)." + "description": "Stable identifier of the questionnaire group across versions." }, - "hops": { + "version": { "type": "integer", "nullable": true, - "description": "Number of hops to the entity (1 for a direct counterparty), when the provider reports one. Always `null` for Merkle Science, which reports only whether exposure is direct or indirect: use `exposure_type`." + "description": "Version number of the questionnaire that was answered." }, - "description": { + "published_at": { "type": "string", + "format": "date-time", "nullable": true, - "description": "`entity_type / entity_subtype`, as the provider labels the entity." + "description": "Timestamp at which this questionnaire version was published." }, - "is_direct": { - "type": "boolean", - "description": "True when `exposure_type` is `direct`." + "sections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "nullable": true, + "description": "Section title" + }, + "description": { + "type": "string", + "nullable": true, + "description": "Section description" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "description": "Unique identifier for the item" + }, + "value": { + "type": "string", + "nullable": true, + "description": "Stable node identifier of this form element within the questionnaire graph." + }, + "element_type": { + "type": "string", + "description": "Type of the questionnaire element" + }, + "is_required": { + "type": "boolean", + "description": "Whether the item is required" + }, + "title": { + "type": "string", + "description": "Title of the item" + }, + "description": { + "type": "string", + "nullable": true, + "description": "Description of the item" + }, + "placeholder": { + "type": "string", + "nullable": true, + "description": "Placeholder text" + }, + "choices": { + "type": "array", + "nullable": true, + "items": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "value": { + "type": "string" + }, + "requires_text_input": { + "type": "boolean", + "description": "Whether this choice requires additional text input", + "nullable": true + } + }, + "required": [ + "label", + "value" + ] + }, + "description": "Choices for dropdown or single choice elements" + }, + "max_files": { + "type": "integer", + "description": "Maximum number of files allowed", + "nullable": true + }, + "answer": { + "type": "object", + "description": "Answer provided for the item", + "properties": { + "value": { + "type": "string", + "nullable": true + }, + "text": { + "type": "string", + "nullable": true + }, + "files": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + } + } + }, + "required_if": { + "type": "object", + "nullable": true, + "description": "Conditional-requirement rule, when the element is only required for certain answers." + }, + "repeatable_config": { + "type": "object", + "nullable": true, + "description": "Configuration for repeatable element groups, when applicable." + } + }, + "required": [ + "uuid", + "value", + "element_type", + "is_required", + "title" + ] + } + } + } + } }, - "exposure_direction": { + "status": { "type": "string", - "enum": [ - "incoming", - "outgoing", - "connected" - ], - "description": "Whether the funds came from (`incoming`) or went to (`outgoing`) this entity." + "description": "Status of the questionnaire" + } + } + } + }, + "signatures": { + "type": "array", + "description": "Electronic Signature results, one item per signer and signature step, for User and Business Verification. Present only when the workflow has an Electronic Signature step; an empty array until a signer reaches it. See the [Electronic Signature report](/core-technology/electronic-signature/report-electronic-signature).", + "items": { + "$ref": "#/components/schemas/SignatureResult" + } + }, + "aml_screenings": { + "type": "array", + "nullable": true, + "description": "**Array of AML screening reports** — always a JSON array, never a singular `aml` object. `null` until at least one AML step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `total_hits`, `entity_type`, `hits[]`, `score`, `screened_data`, `is_ongoing_monitoring_enabled`, and `warnings[]`.", + "items": { + "$ref": "#/components/schemas/AMLScreening" + } + }, + "ip_analyses": { + "type": "array", + "nullable": true, + "description": "**Array of Device & IP Analysis reports** — always a JSON array, never a singular `ip_analysis` object. `null` until at least one IP-analysis step has produced data. Each item is the full report, including `node_id`, feature-level `status`, IP and geolocation data, device fingerprint, and `warnings[]`. Entries with duplicate `(node_id, ip_address, device_fingerprint)` are deduplicated server-side.", + "items": { + "type": "object", + "properties": { + "node_id": { + "type": "string", + "description": "Unique identifier for this Device & IP Analysis step in the workflow graph." }, - "exposure_type": { + "status": { "type": "string", + "description": "Feature-level status of this Device & IP Analysis entry. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", "enum": [ - "direct", - "indirect" - ], - "description": "`direct` (1 hop) or `indirect` (multi-hop) exposure." + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ] }, - "provider_entity_id": { + "device_brand": { "type": "string", - "nullable": true, - "description": "The provider's own identifier for the entity." + "description": "Brand of the device used" }, - "provider_entity_type_id": { + "device_model": { "type": "string", - "nullable": true, - "description": "The provider's own identifier for `entity_type`." + "description": "Model of the device used" }, - "provider_entity_subtype_id": { + "browser_family": { "type": "string", - "nullable": true, - "description": "The provider's own identifier for `entity_subtype`." + "description": "Browser family used" }, - "country": { + "os_family": { "type": "string", - "nullable": true, - "description": "Primary ISO 3166-1 alpha-2 country of the entity. Empty or `null` when the provider does not know it." + "description": "Operating system family" }, - "raw_amount": { - "type": "number", - "nullable": true, - "description": "Amount in the asset's own unit, when the provider reports it." + "platform": { + "type": "string", + "description": "Platform type (mobile, desktop, etc)" }, - "raw_currency": { + "device_fingerprint": { "type": "string", "nullable": true, - "description": "Unit of `raw_amount`, when the provider reports it." - } - } - } - }, - "destination_of_funds": { - "type": "array", - "description": "Where the address sent funds to, attributed by entity. Same item shape as `source_of_funds` with `exposure_direction` = `outgoing`.", - "items": { - "type": "object", - "properties": { - "category": { + "description": "Unique device fingerprint identifier" + }, + "ip_country": { "type": "string", - "description": "Normalised risk category, e.g. `exchange_licensed`, `dex`, `defi`, `payment_processor`, `mixer`, `darknet_market`, `sanctioned`, `stolen_funds`, `high_risk_exchange`." + "description": "Country detected from IP" }, - "percentage": { + "ip_country_code": { + "type": "string", + "description": "Two-letter country code from IP" + }, + "ip_state": { + "type": "string", + "description": "State/region detected from IP" + }, + "ip_city": { + "type": "string", + "description": "City detected from IP" + }, + "latitude": { "type": "number", - "description": "Share of the direction's total exposure, 0-100." + "description": "Latitude coordinate of IP location" }, - "amount_usd": { + "longitude": { "type": "number", - "nullable": true, - "description": "USD value attributed to this entity." + "description": "Longitude coordinate of IP location" }, - "entity_name": { + "ip_address": { "type": "string", - "nullable": true, - "description": "Name of the attributed entity, for example an exchange, a mixer or a sanctioned group." + "description": "IP address" }, - "entity_type": { + "isp": { "type": "string", "nullable": true, - "description": "Provider entity type (e.g. `Exchange`, `DeFi`, `Sanctions`, `Theft`)." + "description": "Internet Service Provider" }, - "entity_subtype": { + "organization": { "type": "string", "nullable": true, - "description": "More granular subtype (e.g. `Mandatory KYC and AML`, `OFAC`, `Yield Aggregator`)." + "description": "Organization associated with IP" }, - "hops": { - "type": "integer", - "nullable": true, - "description": "Number of hops to the entity (1 for a direct counterparty), when the provider reports one. Always `null` for Merkle Science, which reports only whether exposure is direct or indirect: use `exposure_type`." + "is_vpn_or_tor": { + "type": "boolean", + "description": "Whether IP is from VPN or Tor network" }, - "description": { + "is_data_center": { + "type": "boolean", + "description": "Whether IP is from a data center" + }, + "time_zone": { "type": "string", - "nullable": true, - "description": "`entity_type / entity_subtype`, as the provider labels the entity." + "description": "Time zone detected from IP" }, - "is_direct": { - "type": "boolean", - "description": "True when `exposure_type` is `direct`." + "time_zone_offset": { + "type": "string", + "description": "Time zone offset" }, - "exposure_direction": { + "ip": { + "type": "object", + "description": "IP geolocation of this entry plus great-circle distances (km) to the ID document address and the POA document address. Distances are `null` when either side has no resolvable location.", + "properties": { + "location": { + "type": "object", + "nullable": true, + "properties": { + "latitude": { + "type": "number", + "example": 41.2706327 + }, + "longitude": { + "type": "number", + "example": 1.9770097 + } + }, + "description": "Coordinates, or `null` when unavailable." + }, + "distance_from_id_document": { + "type": "number", + "nullable": true, + "example": 23.4 + }, + "distance_from_poa_document": { + "type": "number", + "nullable": true, + "example": 12.3 + } + } + }, + "id_document": { + "type": "object", + "description": "Location extracted from the ID document address plus distances (km) to the IP geolocation and the POA document address. `location` is `null` when no document address could be geocoded.", + "properties": { + "location": { + "type": "object", + "nullable": true, + "properties": { + "latitude": { + "type": "number", + "example": 41.2706327 + }, + "longitude": { + "type": "number", + "example": 1.9770097 + } + }, + "description": "Coordinates, or `null` when unavailable." + }, + "distance_from_ip": { + "type": "number", + "nullable": true, + "example": 23.4 + }, + "distance_from_poa_document": { + "type": "number", + "nullable": true, + "example": 18.7 + } + } + }, + "poa_document": { + "type": "object", + "description": "Location extracted from the Proof of Address document plus distances (km) to the IP geolocation and the ID document address. `location` is `null` when the session has no POA step or the address could not be geocoded.", + "properties": { + "location": { + "type": "object", + "nullable": true, + "properties": { + "latitude": { + "type": "number", + "example": 41.2706327 + }, + "longitude": { + "type": "number", + "example": 1.9770097 + } + }, + "description": "Coordinates, or `null` when unavailable." + }, + "distance_from_ip": { + "type": "number", + "nullable": true, + "example": 12.3 + }, + "distance_from_id_document": { + "type": "number", + "nullable": true, + "example": 18.7 + } + } + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true + }, + "log_type": { + "type": "string" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning" + } + } + } + }, + "matches": { + "type": "array", + "description": "Cross-session matches found for this entry's IP address or device identity. Every match is another verification session in your application (`source` is always `session`) that shares the same value, always belonging to a different vendor user / `vendor_data` than the current session, capped at 5 per collector (`device_fingerprint` sums two independently capped collectors, so up to 10 entries are possible). Every item carries `match_source`, `confidence`, `match_mode`, `device_info` and `location_info`; the keys `recovery_similarity`, `tls_ja4_corroborated` and `recovery_gate_reason` are present only when `match_source` is `recovered_high`.", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "session_id": { + "type": "string", + "format": "uuid", + "description": "Session ID of the matching session." + }, + "session_number": { + "type": "integer", + "description": "Sequential number of the matching session." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "`vendor_data` of the matching session — always different from the current session's vendor user." + }, + "verification_date": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Creation date of the matching session (`YYYY-MM-DDTHH:MM:SSZ`)." + }, + "match_type": { + "type": "string", + "enum": [ + "ip_address", + "device_fingerprint" + ], + "description": "Whether the match was on IP address or device fingerprint" + }, + "match_source": { + "type": "string", + "enum": [ + "ip_address", + "persistent_id", + "legacy_fp", + "recovered_high" + ], + "description": "How this match was reached: `ip_address` (shared IP address — network co-location, not a device-identity claim), `persistent_id` (exact stored persistent device identifier), `legacy_fp` (legacy `didit-fp-*` device-fingerprint hash), or `recovered_high` (high-confidence fuzzy fingerprint-recovery hit). Lower-confidence recovery candidates surface as risk warnings only, never in this array." + }, + "matched_value": { + "type": "string", + "nullable": true, + "description": "The value that matched: the shared IP address, the persistent device identifier or legacy fingerprint hash, or — for `recovered_high` — the UUID of the recovered device." + }, + "status": { + "type": "string", + "description": "Lifecycle status of the matching session." + }, + "is_blocklisted": { + "type": "boolean", + "description": "Whether the matched value is on a blocklist. Currently always `false` for device/IP matches." + }, + "api_service": { + "type": "string", + "nullable": true, + "description": "Standalone API service of the matching session; `null` for workflow sessions." + }, + "source": { + "type": "string", + "enum": [ + "session" + ], + "description": "Always `session` for device/IP matches — every match is another verification session in your application." + }, + "device_info": { + "type": "object", + "description": "Device details observed on the matching session.", + "properties": { + "device_brand": { + "type": "string", + "nullable": true + }, + "device_model": { + "type": "string", + "nullable": true + }, + "browser_family": { + "type": "string", + "nullable": true + }, + "os_family": { + "type": "string", + "nullable": true + }, + "platform": { + "type": "string", + "nullable": true + }, + "device_fingerprint": { + "type": "string", + "nullable": true + } + } + }, + "location_info": { + "type": "object", + "description": "Network and geolocation details observed on the matching session.", + "properties": { + "ip_address": { + "type": "string", + "nullable": true + }, + "ip_country": { + "type": "string", + "nullable": true + }, + "ip_country_code": { + "type": "string", + "nullable": true + }, + "ip_state": { + "type": "string", + "nullable": true + }, + "ip_city": { + "type": "string", + "nullable": true + }, + "is_vpn_or_tor": { + "type": "boolean" + }, + "is_data_center": { + "type": "boolean" + } + } + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Server-side identification confidence, computed as `1 - P(false positive)`: `1.0` for deterministic matches (`persistent_id`, or hardware-rooted `recovered_high`), `0.5` for `legacy_fp`, `0.0` for `ip_address` (co-location only), and a corroboration-adjusted value below `1.0` for probabilistic recovered matches (higher when the TLS JA4 fingerprint and IP country independently agree)." + }, + "match_mode": { + "type": "string", + "enum": [ + "deterministic", + "probabilistic", + "co_occurrence" + ], + "description": "Evidence class of the match: `deterministic` (exact stored or hardware-rooted identifier that cannot collide between physical devices), `probabilistic` (fuzzy fingerprint evidence), or `co_occurrence` (shared network only, no device-identity claim)." + }, + "recovery_similarity": { + "type": "number", + "description": "Cosine similarity of the fingerprint-recovery match, rounded to 4 decimals. Present only when `match_source` is `recovered_high`." + }, + "tls_ja4_corroborated": { + "type": "boolean", + "description": "Whether the TLS JA4 fingerprints of the two observations agree — a server-observed signal the browser cannot spoof. Present only when `match_source` is `recovered_high`." + }, + "recovery_gate_reason": { + "type": "string", + "description": "Why the recovery match passed the false-positive hard gate: `hardware_root_match` (a hardware-rooted device identifier matched exactly) or, on configurations that admit probe-only evidence, `exact_composite_plus__families` / `very_high_similarity_plus__families`. Present only when `match_source` is `recovered_high`." + } + } + } + } + } + } + }, + "database_validations": { + "type": "array", + "nullable": true, + "description": "**Array of Database Validation reports** — always a JSON array, never a singular `database_validation` object. `null` until at least one DB validation step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `screened_data`, the per-service `validations[]` (each with its registry `source_data`), and `warnings[]`. The `screened_data` and `source_data` fields vary by country:\n\n**ARG (Argentina)**: screened_data: {document_number, selfie, gender, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, date_of_death, tax_id, tax_id_type, face_match_score, sit_1_since, banks, last_position, highest_position, rejected_checks}. Uses RENAPER biometric face-match validation. Gender is auto-inferred from face analysis or given name when not present in the document (e.g., driver licenses)\n\n**BOL (Bolivia)**: screened_data: {document_number, date_of_birth, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, gender}\n\n**BRA (Brazil)**: screened_data: {tax_number, first_name, last_name, date_of_birth}. source_data always includes {identification_number, first_name, last_name, date_of_birth, lgpd_minor, minor_under_18, minor_under_16} for successful CPF matches. Successful adult lookups set those flags to false. The Receita Federal Consulta CPF service may return HTTP 422 (minor under 18) or 451 (under 16) with LGPD-withheld data; in those cases source_data sets the minor flags to true as appropriate, includes the upstream HTTP status, and field-level validation is no_match. See the Brazil section of the Database Validation Outcome Codes reference for the full code list.\n\n**CHL (Chile)**: screened_data: {personal_number, first_name, last_name, date_of_birth}. source_data: {identification_number, first_name, last_name, date_of_birth, gender}\n\n**COL (Colombia)**: screened_data: {document_number, document_type, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, document_type}\n\n**CRI (Costa Rica)**: screened_data: {personal_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**DOM (Dominican Republic)**: screened_data: {personal_number}. source_data: {identification_number} (returns valid/invalid only)\n\n**ECU (Ecuador)**: screened_data: {personal_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, street, formatted_address, gender, nationality, education, marital_status, spouse, mother_name, father_name, profession}\n\n**ESP (Spain)**: screened_data: {personal_number, document_type, expiration_date, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, document_type, expiration_date}\n\n**GTM (Guatemala)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**HND (Honduras)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**MEX (Mexico)**: screened_data: {personal_number (CURP), first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, gender, nationality, curp_status, state_of_birth, doc_probatorio, registration_year, registration_state, registration_municipality, num_acta, crip, issuing_state_code, foreign_registry_number, folio_carta, folio_certificado}. Uses the RENAPO civil registry. A separate INE validity-check service is also available for MEX as an additional one-by-one validation.\n\n**PAN (Panama)**: screened_data: {personal_number, selfie}. source_data: {identification_number}. Uses SIB biometric validation\n\n**PER (Peru)**: screened_data: {personal_number (DNI), first_name, last_name}. source_data: {identification_number, first_name, last_name, paternal_name, maternal_name, verification_number, verification_letter}\n\n**PRY (Paraguay)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**SLV (El Salvador)**: screened_data: {document_number, date_of_birth, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**URY (Uruguay)**: screened_data: {personal_number, date_of_birth, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**VEN (Venezuela)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, gender, date_of_birth, document_type}", + "items": { + "type": "object", + "properties": { + "node_id": { + "type": "string", + "description": "Unique identifier for this database validation step in the workflow graph." + }, + "issuing_state": { + "type": "string", + "description": "ISO 3166-1 alpha-3 code of the country whose government/registry sources were queried (e.g. `BRA`, `MEX`, `ESP`, `NGA`, `IDN`). Database Validation covers 60+ countries across the Americas, Europe, Africa, the Middle East and Asia-Pacific, plus global multi-country sources — coverage expands regularly, so treat the value as an open set rather than a fixed enum." + }, + "validation_type": { "type": "string", "enum": [ - "incoming", - "outgoing", - "connected" + "one_by_one", + "two_by_two", + "not_enabled" ], - "description": "Whether the funds came from (`incoming`) or went to (`outgoing`) this entity." + "description": "How the result was corroborated: `one_by_one` (single-service outcome), `two_by_two` (two or more distinct services independently full-matched the identification number), or `not_enabled` (persisted when no service produced a usable match). The value is never `null`. Dedicated biometric 2x2 validation modes are currently available for `ECU` and `PER`." + }, + "screened_data": { + "type": "object", + "description": "The input data used for validation. Fields vary by country — see the Database Validation docs (`/core-technology/database-validation/overview`) for country-specific formats.", + "properties": { + "last_name": { + "type": "string" + }, + "first_name": { + "type": "string" + }, + "tax_number": { + "type": "string", + "description": "Brazil: CPF (11 digits)" + }, + "personal_number": { + "type": "string", + "description": "Chile: RUT, DOM: Cédula (11 digits), ECU: Cédula (10 digits), ESP: DNI/NIE, MEX: CURP (18 chars), PER: DNI (8 digits), URY: CI" + }, + "document_number": { + "type": "string", + "description": "ARG: DNI, BOL: CI, COL: Cédula, GTM: DPI, HND: DNI, PAN: Cédula, PRY: CI, SLV: DUI, VEN: Cédula" + }, + "date_of_birth": { + "type": "string", + "format": "date" + }, + "document_type": { + "type": "string", + "description": "COL: CC/CE/TI/NIT, ESP: ID/PASSPORT" + }, + "expiration_date": { + "type": "string", + "format": "date", + "description": "Required for ESP" + } + } + }, + "validations": { + "type": "array", + "description": "One entry per registry service that returned a validation payload. Each entry identifies the service (`service_id`, `service_name`) and carries the per-field `validation` comparison, the service `outcome_code` / `outcome_detail` when reported, and the normalised registry record in `source_data`.", + "items": { + "type": "object", + "properties": { + "service_id": { + "type": "string", + "description": "Identifier of the registry service that produced this validation result.", + "example": "bra_cpf" + }, + "service_name": { + "type": "string", + "description": "Human-readable name of the registry service.", + "example": "Brazil - CPF status check" + }, + "validation": { + "type": "object", + "description": "The results of the field validations.", + "properties": { + "full_name": { + "type": "string", + "description": "Match result for full name (e.g., 'full_match', 'partial_match', 'no_match')." + }, + "date_of_birth": { + "type": "string", + "description": "Match result for date of birth." + }, + "identification_number": { + "type": "string", + "description": "Match result for identification number." + } + } + }, + "outcome_code": { + "type": "string", + "nullable": true, + "description": "Machine-readable outcome of the registry lookup (e.g. `MATCH`, `NO_MATCH`, `PARTIAL_MATCH`, `INCONCLUSIVE`, source-specific codes). Present when the service reports one." + }, + "outcome_detail": { + "type": "string", + "nullable": true, + "description": "Human-readable explanation of `outcome_code`, when provided by the service." + }, + "source_data": { + "type": "object", + "description": "The data returned from the government database. Common fields: identification_number, first_name, last_name, full_name, date_of_birth, gender. Additional fields by country: BRA always includes lgpd_minor, minor_under_18, and minor_under_16 on successful CPF matches; it also includes the upstream HTTP status when CPF data is withheld under LGPD (HTTP 422/451). ECU adds street, formatted_address, nationality, education, marital_status, spouse, mother_name, father_name, profession. PER adds paternal_name, maternal_name, verification_number, verification_letter. ESP adds document_type, expiration_date. VEN/COL add document_type.", + "additionalProperties": true + } + } + } + }, + "errors": { + "type": "array", + "description": "Errors returned by registry services while validating, each tagged with the `service_id` that produced it. **This key is omitted entirely when there are no errors.**", + "items": { + "type": "object", + "description": "The upstream service's error object spread as-is — its keys vary by service (commonly a message/code and any provider-specific details) — plus `service_id` identifying the service that errored.", + "properties": { + "service_id": { + "type": "string", + "nullable": true, + "description": "Identifier of the registry service that errored." + } + }, + "additionalProperties": true + } }, - "exposure_type": { + "match_type": { "type": "string", "enum": [ - "direct", - "indirect" + "no_match", + "partial_match", + "full_match" ], - "description": "`direct` (1 hop) or `indirect` (multi-hop) exposure." - }, - "provider_entity_id": { - "type": "string", "nullable": true, - "description": "The provider's own identifier for the entity." + "description": "The aggregate match outcome across the queried services. `null` until the validation has been evaluated." }, - "provider_entity_type_id": { + "status": { "type": "string", - "nullable": true, - "description": "The provider's own identifier for `entity_type`." + "description": "Feature-level status of this database validation. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ] }, - "provider_entity_subtype_id": { + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true + }, + "log_type": { + "type": "string" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning" + } + } + }, + "description": "Warnings related to the database validation." + } + } + } + }, + "reviews": { + "type": "array", + "items": { + "type": "object", + "properties": { + "user": { "type": "string", - "nullable": true, - "description": "The provider's own identifier for `entity_subtype`." + "description": "Who performed the review. Usually the console reviewer's identifier (their email), but the value is `\"API Client\"` for status updates made with an API key and `\"Unknown\"` when no actor could be resolved — so do not parse this field as an email address." }, - "country": { - "type": "string", - "nullable": true, - "description": "Primary ISO 3166-1 alpha-2 country of the entity. Empty or `null` when the provider does not know it." + "new_status": { + "type": "string" }, - "raw_amount": { - "type": "number", - "nullable": true, - "description": "Amount in the asset's own unit, when the provider reports it." + "comment": { + "type": "string" }, - "raw_currency": { + "created_at": { "type": "string", - "nullable": true, - "description": "Unit of `raw_amount`, when the provider reports it." + "format": "date-time" } } } }, - "counterparty_connections": { + "created_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp at which the session was created (ISO 8601, UTC)." + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp at which this session is scheduled to expire. Set on User Verification (KYC) sessions when an expiration was configured at creation time (or once `Approved` status triggers KYC expiry computation). `null` for sessions without a configured expiration, and for kinds that do not use expiry (e.g. Business Verification (KYB) sessions that never received an `expires_at`)." + }, + "registry_checks": { "type": "array", - "description": "Entities the provider attributes the screened address to. For Merkle Science these come from the address's own owner and user tags, so the same entity can appear twice, and they carry no fund flows: `received_usd`, `sent_usd`, `received_hops` and `sent_hops` are `null` and `percentage` is 0. Do not route on `risk_level` or `is_direct` here: use `sanctions_hit`, `dominant_risk_category` and `risk_factors[].is_high_risk` for the verdict, and `exposure_type` in `source_of_funds`/`destination_of_funds` for directness.", + "nullable": true, + "description": "Company registry checks. Present only when `session_kind = business`. Each item wraps the full company-registry payload.", "items": { "type": "object", + "description": "One company registry check — the full company-registry payload enriched with ownership structure and warnings. Present only when `session_kind = business`.", "properties": { - "entity_name": { - "type": "string", - "nullable": true, - "description": "Attributed entity name." - }, - "entity_type": { - "type": "string", - "nullable": true, - "description": "Provider entity type." - }, - "entity_subtype": { + "status": { "type": "string", - "nullable": true, - "description": "Provider entity subtype." + "description": "Feature lifecycle status." }, - "risk_level": { + "node_id": { "type": "string", - "description": "Lowercase and provider-dependent. For Merkle Science it is the address's own 0-5 risk grade (`no_risk`, `low`, `medium_low`, `medium`, `high`, `critical`), raised to `high` when the entity's category is high risk; it is not a separate grade of the entity." - }, - "categories": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Normalised risk categories of the entity." - }, - "received_usd": { - "type": "number", - "nullable": true, - "description": "USD received from the entity, when the provider reports it." - }, - "sent_usd": { - "type": "number", - "nullable": true, - "description": "USD sent to the entity, when the provider reports it." - }, - "received_hops": { - "type": "integer", - "nullable": true, - "description": "Hops to the entity on the receiving side, when the provider reports them." - }, - "sent_hops": { - "type": "integer", - "nullable": true, - "description": "Hops to the entity on the sending side, when the provider reports them." - }, - "percentage": { - "type": "number", - "description": "Share of the address's exposure, when the provider reports it; otherwise 0." + "nullable": true }, - "is_direct": { - "type": "boolean", - "description": "Always `true` for Merkle Science, whose connections are attributions rather than counterparties." + "data_resolved": { + "type": "boolean" }, - "country": { - "type": "string", - "nullable": true, - "description": "Country of the entity, when known." + "company": { + "type": "object", + "description": "Full registry payload for the company (registry record merged with any user-provided or console-edited data).", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "node_id": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string" + }, + "registry_status": { + "type": "string", + "nullable": true + }, + "data_resolved": { + "type": "boolean" + }, + "company_name": { + "type": "string" + }, + "registration_number": { + "type": "string", + "nullable": true + }, + "country_code": { + "type": "string", + "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `DE`, `US`)." + }, + "region": { + "type": "string", + "nullable": true, + "description": "ISO 3166-2 subdivision code when applicable (e.g. `CA`, `NY` for US states)." + }, + "company_type": { + "type": "string", + "nullable": true + }, + "incorporation_date": { + "type": "string", + "format": "date", + "nullable": true + }, + "registered_address": { + "type": "string", + "nullable": true + }, + "tax_number": { + "type": "string", + "nullable": true + }, + "risk_level": { + "type": "string", + "enum": [ + "LOW", + "MEDIUM", + "HIGH" + ], + "nullable": true + }, + "verification_status": { + "type": "string", + "nullable": true + }, + "is_from_registry": { + "type": "boolean" + }, + "fetch_status": { + "type": "string", + "nullable": true + }, + "alternative_names": { + "type": "string", + "nullable": true, + "description": "Alternative, trading, former or original-language company names as text, when reported." + }, + "nature_of_business": { + "type": "string", + "nullable": true + }, + "registered_capital": { + "type": "string", + "nullable": true + }, + "registered_capital_amount": { + "type": "string", + "format": "decimal", + "nullable": true, + "description": "Registered/share capital amount as a decimal string (e.g. `\"100.00\"`), parsed from the registry filing. `null` when not available." + }, + "registered_capital_currency": { + "type": "string", + "nullable": true, + "description": "ISO 4217 currency code of `registered_capital_amount` (e.g. `\"GBP\"`). `null` when not available." + }, + "website": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "format": "email", + "nullable": true + }, + "phone": { + "type": "string", + "nullable": true + }, + "legal_entity_identifier": { + "type": "string", + "nullable": true + }, + "location_of_registration": { + "type": "string", + "nullable": true + }, + "vat_number": { + "type": "string", + "nullable": true, + "description": "EU VAT number, when collected during the hosted registry step or edited afterwards." + }, + "vat_validation_status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "format_valid", + "invalid_format", + "could_not_validate", + "not_applicable" + ], + "description": "Result of validating the VAT number or, when there is none, the `tax_number`, at registry submit. `valid` / `invalid`: decided by the official registry of the country (EU VIES, Norway, Switzerland, Brazil, Colombia, Costa Rica, Ecuador, Paraguay; Poland also by its domestic VAT register). `format_valid` / `invalid_format`: decided by the country's format and check-digit rules where no registry can be queried. `could_not_validate`: the registry was unavailable. `not_applicable`: no number, a country without registry or rules, or a sandbox session." + }, + "vat_validated_number": { + "type": "string", + "nullable": true, + "description": "The number that was checked, as the source knows it (for example the VAT number derived from a French SIREN or a Dutch RSIN). `null` when no check ran." + }, + "vat_validation_source": { + "type": "string", + "nullable": true, + "enum": [ + "eu_vies", + "pl_vat_white_list", + "no_brreg", + "ch_uid_register", + "br_receita_federal", + "co_rues", + "cr_hacienda", + "ec_sri", + "py_dnit", + "format_rules", + null + ], + "description": "The source that decided `vat_validation_status`: an official registry, or `format_rules`. `null` when no check ran." + }, + "vat_validated_name": { + "type": "string", + "nullable": true, + "description": "Name registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_validated_address": { + "type": "string", + "nullable": true, + "description": "Address registered with the source that validated the number, when it discloses one. Only set when the status is `valid`." + }, + "vat_checked_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "Timestamp of the check. `null` when no check ran." + }, + "financial_summary": { + "type": "object", + "nullable": true + }, + "officers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "designation": { + "type": "string", + "nullable": true + }, + "role": { + "type": "string", + "nullable": true + }, + "nationality": { + "type": "string", + "nullable": true + }, + "is_active": { + "type": "boolean" + }, + "kyc_status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "Pending" + ], + "nullable": true + }, + "kyc_session_url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "first_name": { + "type": "string", + "nullable": true, + "description": "Given name when explicitly reported by the registry; not inferred from the full name." + }, + "last_name": { + "type": "string", + "nullable": true, + "description": "Family name when explicitly reported by the registry." + }, + "address": { + "type": "string", + "nullable": true, + "description": "Officer address reported by the registry." + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Date the officer was appointed.", + "format": "date" + }, + "termination_date": { + "type": "string", + "nullable": true, + "description": "Date the appointment ended, when reported.", + "format": "date" + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", + "format": "date" + }, + "date_of_birth_year": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 9999, + "description": "Registry-reported birth year, including when the full date is unavailable." + }, + "date_of_birth_month": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 12, + "description": "Registry-reported birth month. Null when only the year is known." + } + } + } + }, + "beneficial_owners": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "first_name": { + "type": "string", + "nullable": true + }, + "last_name": { + "type": "string", + "nullable": true + }, + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ] + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "ownership_min_shares": { + "type": "string", + "nullable": true, + "description": "Lower bound of the ownership percentage as reported by the registry. Free-form string (e.g. `\"25.00\"`, `\"50\"`); use `effective_ownership_percent` for a parsed numeric value." + }, + "ownership_max_shares": { + "type": "string", + "nullable": true, + "description": "Upper bound of the ownership percentage as reported by the registry. Free-form string; use `effective_ownership_percent` for a parsed numeric value." + }, + "is_active": { + "type": "boolean" + }, + "kyc_status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "Pending" + ], + "nullable": true + }, + "kyc_session_url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "effective_ownership_percent": { + "type": "number", + "nullable": true + }, + "company_name": { + "type": "string", + "nullable": true + }, + "registration_number": { + "type": "string", + "nullable": true + }, + "country_of_birth": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + }, + "nationality": { + "type": "string", + "nullable": true + }, + "designation": { + "type": "string", + "nullable": true + }, + "voting_min_shares": { + "type": "string", + "nullable": true + }, + "voting_max_shares": { + "type": "string", + "nullable": true + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete birth date when reported.", + "format": "date" + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Appointment date when reported.", + "format": "date" + }, + "shareholding": { + "$ref": "#/components/schemas/KYBRegistryShareholding" + } + } + } + }, + "addresses": { + "type": "array", + "nullable": true, + "description": "Every address the registry states, one canonical shape. Null when it states none.", + "items": { + "$ref": "#/components/schemas/KYBRegistryAddress" + } + }, + "industries": { + "type": "array", + "nullable": true, + "description": "The activities the registry classifies the company under, one canonical shape. Null when it states none.", + "items": { + "$ref": "#/components/schemas/KYBRegistryIndustry" + } + }, + "accounts": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Registry filing dates, when available. Annual returns and general meetings use last_annual_return_date and last_agm_date; they are distinct from financial accounts." + }, + "registry_data": { + "type": "object", + "description": "Raw record returned by the registry source, kept for reviewers. Its shape differs by country and source and is not a stable contract: read the canonical fields (`addresses`, `industries`, `ownership`, `beneficial_owners[].shareholding`) instead." + }, + "user_provided_data": { + "type": "object", + "description": "Data confirmed or edited by the end user." + }, + "confirmed_by_user_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_console_edit_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "is_editable": { + "type": "boolean" + }, + "ownership": { + "$ref": "#/components/schemas/KYBRegistryOwnership" + }, + "registry_tier": { + "type": "string", + "nullable": true, + "description": "The registry tier the workflow ordered: `basic`, `shareholders` or `ubo`. Also in `ownership.tier_requested`." + }, + "registry_tier_delivered": { + "type": "string", + "nullable": true, + "description": "The tier the registry delivered and that is billed. Also in `ownership.tier_delivered`." + }, + "registry_ownership_status": { + "type": "string", + "nullable": true, + "description": "`not_requested`, `pending`, `delivered` or `unavailable`. Also in `ownership.status`." + } + } }, - "provider_entity_id": { - "type": "string", - "nullable": true, - "description": "The provider's own identifier for the entity." + "ownership_structure": { + "type": "object", + "description": "Raw ownership snapshot kept for reviewers; its shape differs by registry source and is not a stable contract. Read `company.beneficial_owners` and `company.ownership` instead." + }, + "warnings": { + "type": "array", + "items": { + "type": "object" + } } } } }, - "network_nodes": { - "type": "array", - "items": { - "type": "object" - }, - "description": "Exposure graph nodes for transaction screening. Empty on a wallet screening result." - }, - "network_edges": { + "key_people_checks": { "type": "array", - "items": { - "type": "object" - }, - "description": "Exposure graph edges for transaction screening. Empty on a wallet screening result." - }, - "sanctions_hit": { - "type": "boolean", - "description": "True if the address has direct or indirect sanctions exposure." - }, - "pep_counterparty": { - "type": "boolean", - "description": "Politically exposed person exposure. `null` when no PEP finding is available, which is every result today because no provider reports one; `false` only for an explicit negative screening result. Do not read `null` as a completed PEP check.", - "nullable": true - }, - "dominant_risk_category": { - "type": "string", "nullable": true, - "description": "Highest-weighted high-risk category, or `null` when none is dominant (e.g. `sanctioned`, `mixer`, `stolen_funds`)." - }, - "risk_factors": { - "type": "array", - "description": "Ranked explanation of `risk_score`: what produced this verdict. Providers grade an address on their own model and return only a verdict (Merkle Science returns an integer 0-5), so the score alone is not actionable. Ordered high-risk first, then by the value that moved through each driver, capped at the five strongest; `source_of_funds` and `destination_of_funds` always carry the complete breakdown. A high `risk_score` with no `is_high_risk` entry means the provider reported no sanctions or attributed-entity match and graded the address purely on its own exposure model - `summary` states this explicitly.", + "description": "Aggregate key-people checks. Present only when `session_kind = business`. Each item uses the two-bucket shape (`registry`, `submitted`, `ubo_kyc_summary`).", "items": { "type": "object", + "description": "Aggregate check over the company's officers + UBOs. Uses the `registry` / `submitted` shape. Present only when session_kind = business.", "properties": { - "source": { - "type": "string", - "enum": [ - "attribution", - "exposure" - ], - "description": "`attribution` is what the provider says the address itself is (its own tag or cluster); `exposure` is where its funds actually moved." - }, - "category": { - "type": "string", - "description": "Normalised risk category, same vocabulary as the exposure breakdown." - }, - "label": { - "type": "string", - "description": "The provider's own wording for the driver (e.g. `Gambling / Crypto only`), which is what you would also see in the provider's console." + "status": { + "type": "string" }, - "entity_name": { + "node_id": { "type": "string", - "nullable": true, - "description": "Attributed entity behind the driver, for example a gambling site or an exchange." + "nullable": true }, - "direction": { - "type": "string", - "enum": [ - "incoming", - "outgoing", - "connected", - "unknown", - "attribution" - ], - "description": "Direction of the fund flow, `attribution` when the driver is the address's own tag, or `unknown` when the provider did not report a direction." + "registry": { + "type": "object", + "description": "Parties discovered via the registry check.", + "properties": { + "officers": { + "type": "array", + "description": "Officers discovered via the registry check, each enriched with the status of any child KYC session and AML screening spawned for them.", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "description": "Officer's full name as recorded in the registry." + }, + "designation": { + "type": "string", + "nullable": true, + "description": "Free-text designation from the registry (e.g. `Director`, `Company Secretary`)." + }, + "role": { + "type": "string", + "enum": [ + "director", + "non_executive_director", + "secretary", + "chairman", + "ubo", + "shareholder", + "representative", + "founder", + "legal_advisor", + "authorized_signatory", + "trustee", + "beneficiary", + "settlor", + "protector", + "company_officer", + "investor", + "other" + ], + "description": "Normalized officer role." + }, + "nationality": { + "type": "string", + "nullable": true + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Date the officer was appointed.", + "format": "date" + }, + "termination_date": { + "type": "string", + "nullable": true, + "description": "Date the appointment ended, when reported.", + "format": "date" + }, + "is_active": { + "type": "boolean" + }, + "address": { + "type": "string", + "nullable": true, + "description": "Officer address reported by the registry." + }, + "aml_status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "nullable": true, + "description": "Status of the AML screening run for this officer. `null` when no AML screening was run." + }, + "kyc_status": { + "type": "string", + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "nullable": true, + "description": "Raw lifecycle status of the child KYC session spawned for this officer. This is the full session status enum (not a reduced Approved/Declined/Pending set). `null` when no child KYC session exists." + }, + "kyc_session_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Shareable URL of the child KYC session. `null` when no child session exists." + }, + "kyc_session_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Session id of the child KYC session. Pass it to this same decision endpoint to fetch the person's full KYC report. `null` when no child session exists." + }, + "verification_workflow_type": { + "type": "string", + "nullable": true, + "description": "Base workflow type of the child session's workflow (e.g. `kyc`). `null` for graph-based workflows or when no child session exists." + }, + "verification_workflow_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Workflow id used by the child session. `null` when no child session exists." + }, + "verification_workflow_label": { + "type": "string", + "nullable": true, + "description": "Human-readable label of the child session's workflow. `null` when unset." + }, + "verification_workflow_features": { + "type": "array", + "nullable": true, + "description": "Per-feature progress of the child verification session, e.g. `[{\"feature\": \"ID_VERIFICATION\", \"status\": \"Approved\"}]`. `null` when the child session has no resolvable feature list.", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "description": "Feature display name (e.g. `ID_VERIFICATION`, `LIVENESS`, `AML`)." + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Feature-level status inside the child session." + } + } + } + }, + "didit_internal_id": { + "type": "string", + "nullable": true, + "description": "Stable internal identifier of the verified individual behind the child session — useful for cross-session linking. `null` when no child session exists or the user has not been resolved yet." + }, + "kyc_document_type": { + "type": "string", + "nullable": true, + "description": "Document type presented in the child KYC session (e.g. `P`, `ID`, `DL`). `null` until the child session has an ID verification result." + }, + "kyc_nationality": { + "type": "string", + "nullable": true, + "description": "Nationality extracted from the document in the child KYC session. `null` until available." + }, + "kyc_liveness_passed": { + "type": "boolean", + "nullable": true, + "description": "Whether the child KYC session's liveness check passed. `null` when no liveness check has run." + }, + "kyc_verified_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the child KYC session reached `Approved`. `null` while the child session has any other status." + }, + "kyc_aml_categories": { + "type": "object", + "nullable": true, + "description": "Per-category summary derived from the person's AML hits. `null` when there are no hits.", + "properties": { + "sanctions": { + "type": "string", + "enum": [ + "match", + "clear" + ] + }, + "pep": { + "type": "string", + "enum": [ + "match", + "clear" + ] + }, + "adverse_media": { + "type": "string", + "enum": [ + "match", + "clear" + ] + }, + "warnings": { + "type": "string", + "enum": [ + "match", + "clear" + ] + } + } + }, + "kyc_notification_sent_at": { + "type": "string", + "nullable": true, + "description": "ISO 8601 timestamp of the last verification-invite notification sent for this person's child session. `null` when none was sent." + }, + "first_name": { + "type": "string", + "nullable": true, + "description": "Given name when explicitly reported by the registry; not inferred from the full name." + }, + "last_name": { + "type": "string", + "nullable": true, + "description": "Family name when explicitly reported by the registry." + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete registry-reported birth date. Null when only a birth year or month is known.", + "format": "date" + }, + "date_of_birth_year": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 9999, + "description": "Registry-reported birth year, including when the full date is unavailable." + }, + "date_of_birth_month": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 12, + "description": "Registry-reported birth month. Null when only the year is known." + } + } + } + }, + "beneficial_owners": { + "type": "array", + "description": "Beneficial owners discovered via the registry check, each enriched with the status of any child KYC session and AML screening spawned for them.", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "description": "Beneficial owner's full name (or company name for corporate UBOs)." + }, + "first_name": { + "type": "string", + "nullable": true + }, + "last_name": { + "type": "string", + "nullable": true + }, + "date_of_birth": { + "type": "string", + "nullable": true, + "description": "Complete birth date when reported.", + "format": "date" + }, + "country_of_birth": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "nullable": true + }, + "company_name": { + "type": "string", + "nullable": true + }, + "registration_number": { + "type": "string", + "nullable": true + }, + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ] + }, + "nationality": { + "type": "string", + "nullable": true + }, + "designation": { + "type": "string", + "nullable": true + }, + "roles": { + "type": "array", + "description": "Role names held by this owner, e.g. `[\"ubo\", \"shareholder\"]`.", + "items": { + "type": "string" + } + }, + "ownership_min_shares": { + "type": "string", + "nullable": true, + "description": "Lower bound of the ownership percentage as reported by the registry. Free-form string (e.g. `\"25\"`, `\"25-50%\"`); use `effective_ownership_percent` for a parsed numeric value." + }, + "ownership_max_shares": { + "type": "string", + "nullable": true, + "description": "Upper bound of the ownership percentage as reported by the registry. Free-form string; use `effective_ownership_percent` for a parsed numeric value." + }, + "is_active": { + "type": "boolean" + }, + "aml_status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "nullable": true, + "description": "Status of the AML screening run for this beneficial owner. `null` when no AML screening was run." + }, + "kyc_status": { + "type": "string", + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "nullable": true, + "description": "Raw lifecycle status of the child KYC session spawned for this beneficial owner. This is the full session status enum (not a reduced Approved/Declined/Pending set). `null` when no child KYC session exists." + }, + "kyc_session_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Shareable URL of the child KYC session. `null` when no child session exists." + }, + "kyc_session_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Session id of the child KYC session. Pass it to this same decision endpoint to fetch the person's full KYC report. `null` when no child session exists." + }, + "verification_workflow_type": { + "type": "string", + "nullable": true, + "description": "Base workflow type of the child session's workflow (e.g. `kyc`). `null` for graph-based workflows or when no child session exists." + }, + "verification_workflow_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Workflow id used by the child session. `null` when no child session exists." + }, + "verification_workflow_label": { + "type": "string", + "nullable": true, + "description": "Human-readable label of the child session's workflow. `null` when unset." + }, + "verification_workflow_features": { + "type": "array", + "nullable": true, + "description": "Per-feature progress of the child verification session, e.g. `[{\"feature\": \"ID_VERIFICATION\", \"status\": \"Approved\"}]`. `null` when the child session has no resolvable feature list.", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "description": "Feature display name (e.g. `ID_VERIFICATION`, `LIVENESS`, `AML`)." + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Feature-level status inside the child session." + } + } + } + }, + "didit_internal_id": { + "type": "string", + "nullable": true, + "description": "Stable internal identifier of the verified individual behind the child session — useful for cross-session linking. `null` when no child session exists or the user has not been resolved yet." + }, + "kyc_document_type": { + "type": "string", + "nullable": true, + "description": "Document type presented in the child KYC session (e.g. `P`, `ID`, `DL`). `null` until the child session has an ID verification result." + }, + "kyc_nationality": { + "type": "string", + "nullable": true, + "description": "Nationality extracted from the document in the child KYC session. `null` until available." + }, + "kyc_liveness_passed": { + "type": "boolean", + "nullable": true, + "description": "Whether the child KYC session's liveness check passed. `null` when no liveness check has run." + }, + "kyc_verified_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the child KYC session reached `Approved`. `null` while the child session has any other status." + }, + "kyc_aml_categories": { + "type": "object", + "nullable": true, + "description": "Per-category summary derived from the person's AML hits. `null` when there are no hits.", + "properties": { + "sanctions": { + "type": "string", + "enum": [ + "match", + "clear" + ] + }, + "pep": { + "type": "string", + "enum": [ + "match", + "clear" + ] + }, + "adverse_media": { + "type": "string", + "enum": [ + "match", + "clear" + ] + }, + "warnings": { + "type": "string", + "enum": [ + "match", + "clear" + ] + } + } + }, + "kyc_notification_sent_at": { + "type": "string", + "nullable": true, + "description": "ISO 8601 timestamp of the last verification-invite notification sent for this person's child session. `null` when none was sent." + }, + "child_beneficial_owners": { + "type": "array", + "description": "For corporate UBOs: the owning entity's own beneficial owners, as raw objects extracted from the registry. Empty array when not applicable.", + "items": { + "type": "object" + } + }, + "effective_ownership_percent": { + "type": "number", + "nullable": true, + "description": "Parsed numeric ownership percentage — the max bound when available, otherwise the min bound. `null` when neither bound could be parsed." + }, + "voting_min_shares": { + "type": "string", + "nullable": true + }, + "voting_max_shares": { + "type": "string", + "nullable": true + }, + "appointment_date": { + "type": "string", + "nullable": true, + "description": "Appointment date when reported.", + "format": "date" + } + } + } + } + } }, - "exposure_type": { - "type": "string", - "enum": [ - "direct", - "indirect", - "unknown", - "attribution" - ], - "description": "`direct` (1 hop) or `indirect` (multi-hop), `attribution` for the address's own tag, or `unknown` when the provider did not state how many hops away the counterparty is. Treat `unknown` as unclassified, not as `direct`." + "submitted": { + "type": "object", + "description": "Parties submitted by the business admin during the Key People flow (BusinessParty rows where source=USER).", + "properties": { + "parties": { + "type": "array", + "items": { + "type": "object", + "description": "Party submitted by the business admin via the Key People flow (source=USER).", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ] + }, + "source": { + "type": "string", + "enum": [ + "USER" + ], + "description": "Always `USER` here — this bucket only contains parties entered or confirmed by the end user in the Key People flow (registry-discovered people appear under `registry`)." + }, + "name": { + "type": "string" + }, + "first_name": { + "type": "string", + "nullable": true + }, + "last_name": { + "type": "string", + "nullable": true + }, + "company_name": { + "type": "string", + "nullable": true + }, + "registration_number": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string", + "format": "email", + "nullable": true + }, + "nationality": { + "type": "string", + "nullable": true, + "description": "Nationality for persons; country of incorporation for company parties." + }, + "phone_number": { + "type": "string", + "nullable": true + }, + "date_of_birth": { + "type": "string", + "format": "date", + "nullable": true + }, + "position": { + "type": "string", + "nullable": true, + "description": "Free-text job title / position (persons only)." + }, + "custom_fields": { + "type": "object", + "description": "Workflow-defined custom fields collected for this party; keys are defined by the workflow's key-people field descriptors." + }, + "is_active": { + "type": "boolean" + }, + "is_skipped": { + "type": "boolean", + "description": "True when the end user skipped verification for this party (only possible when the role allows skipping)." + }, + "requires_verification": { + "type": "boolean", + "description": "True when a child KYC / KYB sub-session is required for this party based on role settings and ownership thresholds." + }, + "roles": { + "type": "array", + "description": "All roles held by this party, each with its own ownership and voting percentages. There is no scalar `role`/`ownership_percent` on the party itself.", + "items": { + "type": "object", + "properties": { + "role": { + "type": "string", + "enum": [ + "director", + "non_executive_director", + "secretary", + "chairman", + "ubo", + "shareholder", + "representative", + "founder", + "legal_advisor", + "authorized_signatory", + "trustee", + "beneficiary", + "settlor", + "protector", + "company_officer", + "investor", + "other" + ] + }, + "ownership_percent": { + "type": "number", + "nullable": true + }, + "voting_percent": { + "type": "number", + "nullable": true + } + } + } + }, + "kyc_session_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Child KYC session id for person parties requiring verification. `null` otherwise." + }, + "kyc_session_status": { + "type": "string", + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "nullable": true, + "description": "Lifecycle status of the child KYC session. `null` when no child KYC session exists." + }, + "kyc_session_url": { + "type": "string", + "format": "uri", + "nullable": true + }, + "kyb_sub_session_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Child KYB sub-session id for corporate parties requiring verification. `null` otherwise." + }, + "kyb_sub_session_status": { + "type": "string", + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "nullable": true, + "description": "Lifecycle status of the child KYB sub-session. `null` when no sub-session exists." + }, + "kyb_sub_session_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Shareable URL of the child KYB sub-session. `null` when no sub-session exists." + }, + "verification_workflow_type": { + "type": "string", + "nullable": true, + "description": "Base workflow type of the child session's workflow (KYC session preferred, falling back to the KYB sub-session). `null` for graph-based workflows or when no child session exists." + }, + "verification_workflow_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Workflow id used by the child session. `null` when no child session exists." + }, + "verification_workflow_label": { + "type": "string", + "nullable": true, + "description": "Human-readable label of the child session's workflow. `null` when unset." + }, + "verification_workflow_features": { + "type": "array", + "nullable": true, + "description": "Per-feature progress of the child verification session, e.g. `[{\"feature\": \"ID_VERIFICATION\", \"status\": \"Approved\"}]`. `null` when the child session has no resolvable feature list.", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "description": "Feature display name (e.g. `ID_VERIFICATION`, `LIVENESS`, `AML`)." + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Feature-level status inside the child session." + } + } + } + }, + "didit_internal_id": { + "type": "string", + "nullable": true, + "description": "Stable internal identifier of the verified individual behind the child session. `null` when not resolved." + } + } + } + } + } }, - "amount_usd": { - "type": "number", - "format": "float", + "ubo_kyc_summary": { + "type": "object", "nullable": true, - "description": "USD value attributed to this driver." - }, - "percentage": { - "type": "number", - "format": "float", - "description": "Share of the address's TOTAL screened volume, both directions combined - unlike `percentage` inside `source_of_funds`/`destination_of_funds`, which is normalised per direction. Comparable and summable across the whole list." - }, - "is_high_risk": { - "type": "boolean", - "description": "Whether the category is one Didit treats as high risk (`sanctioned`, `mixer`, `stolen_funds`, `ransomware`, `scam`, `darknet_market`, `terrorist_financing`, `child_exploitation`, `hacked_exchange`, `high_risk_exchange`, `seized_funds`)." + "description": "Aggregate UBO KYC progress, or null when no UBOs have linked KYC sessions.", + "properties": { + "total": { + "type": "integer" + }, + "approved": { + "type": "integer" + }, + "flagged": { + "type": "integer", + "description": "UBOs whose KYC is Declined or In Review." + }, + "pending": { + "type": "integer" + } + } }, - "description": { - "type": "string", - "description": "One-line rendering of the driver, e.g. `9.01% indirect outgoing exposure to Gambling / Crypto only (Example Gambling)`." + "warnings": { + "type": "array", + "items": { + "type": "object" + } } } } }, - "raw_response": { - "type": "object", - "additionalProperties": true, - "description": "The provider's own payload. Its shape is provider-specific and can change without notice: read the normalised fields instead of parsing it." - }, - "report_signature": { - "type": "string", - "description": "Opaque HMAC-SHA256 signature over this exact result, scoped to your application. Not meaningful on its own — repost the full response body, unmodified and including this field, to `POST /v3/wallet-screening/pdf/` to render it as a PDF. The PDF endpoint rejects the request with `400` if `report_signature` is missing or if any signed field (e.g. `risk_score`, `severity`, `sanctions_hit`) was edited before repost." - } - }, - "required": [ - "provider", - "screening_type", - "risk_score", - "severity", - "status", - "summary", - "wallet_address", - "blockchain", - "sanctions_hit", - "dominant_risk_category", - "source_of_funds", - "destination_of_funds", - "counterparty_connections", - "report_signature" - ] - }, - "NetworkRiskDisplay": { - "type": "object", - "properties": { - "score": { - "type": "integer", - "description": "Risk score, 0-100." - }, - "band": { - "type": "string", - "enum": [ - "low", - "medium", - "high" - ], - "description": "Band the score renders as. high >= 70, medium >= 40, otherwise low." - }, - "bar": { - "type": "number", - "format": "float", - "description": "Normalized 0-1 value for rendering a risk bar." - } - } - }, - "NetworkNodeTag": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "color": { - "type": "string", - "nullable": true, - "description": "Null for guard tags, whose color token the renderer owns." - }, - "source": { - "type": "string", - "enum": [ - "subject_tag", - "session_tag", - "transaction_tag", - "signal_guard" - ], - "description": "Where the tag came from: a tag on the user/business, a tag on a session in this network, a tag on a transaction a party belongs to, or a signal guard condition (e.g. hub, datacenter IP)." - }, - "category": { - "type": "string", - "enum": [ - "customer", - "quality" - ], - "nullable": true, - "description": "customer for customer-defined tags, quality for signal-quality guard tags. Null when not applicable." - } - } - }, - "NetworkListItem": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "network_id": { - "type": "string", - "description": "Human-readable per-organization sequential id, e.g. N-0001." - }, - "name": { - "type": "string", - "nullable": true - }, - "status": { - "type": "string", - "enum": [ - "active", - "in_review", - "resolved", - "dismissed" - ], - "description": "Network lifecycle status. Distinct from the per-applicant outcome_status inside a network (approved/in_review/declined/not_completed)." - }, - "risk": { - "$ref": "#/components/schemas/NetworkRiskDisplay" - }, - "pattern_types": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "exact_same_device", - "similar_device", - "same_ip_address", - "same_address", - "similar_selfie_backgrounds", - "similar_poa_documents", - "same_document_number", - "same_phone_number", - "same_email" - ] - }, - "description": "The clustering patterns detected in this network." - }, - "signal_types": { + "document_verifications": { "type": "array", - "items": { - "type": "string", - "enum": [ - "ip_address", - "device", - "face", - "document_number", - "phone", - "email", - "address" - ] - }, - "description": "The distinct signal types present in this network." - }, - "member_counts": { - "type": "object", - "description": "Counts of members by category (e.g. total, per outcome_status)." - }, - "member_status": { - "type": "object", - "description": "Breakdown of members by outcome_status." - }, - "signal_coverage": { - "type": "object", - "description": "Per signal_type coverage of this network's members." - }, - "first_activity_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "last_activity_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - }, - "NetworkDetail": { - "allOf": [ - { - "$ref": "#/components/schemas/NetworkListItem" - }, - { - "type": "object", - "properties": { - "stats": { - "type": "object", - "description": "Aggregate stats for the network (e.g. member totals)." - }, - "fraud_tags": { - "type": "array", - "items": { - "type": "object" - }, - "description": "Fraud-pattern tags surfaced on the network." - }, - "risk_signals": { - "type": "object", - "description": "Risk-signal summaries: residential_proxy_reuse, email_intelligence, phone_intelligence." - }, - "reviewed_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "reviewed_by": { - "type": "string", - "nullable": true - }, - "review_reason": { - "type": "string", - "nullable": true - } - } - } - ], - "properties": { - "notes_count": { - "type": "integer", - "minimum": 0, - "readOnly": true, - "description": "Number of private analyst notes in this investigation." - } - } - }, - "NetworkGraphNode": { - "type": "object", - "description": "A subject node or a signal node, distinguished by kind. Subject nodes (kind: vendor_user | vendor_business | transaction_party) carry id, kind, subject, node_status (approved | in_review | declined | not_completed), and tags. Signal nodes (kind: signal) carry id, kind, uuid, signal_type, subject_count, coverage, is_hub, is_datacenter_ip, is_high_collision_device, node_status (signal_active | signal_guarded), tags, risk_evidence, first_seen_at, and last_seen_at.", - "properties": { - "id": { - "type": "string" - }, - "kind": { - "type": "string", - "enum": [ - "vendor_user", - "vendor_business", - "transaction_party", - "signal" - ] - } - } - }, - "NetworkGraphEdge": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "source": { - "type": "string", - "description": "Subject node id." - }, - "target": { - "type": "string", - "description": "Signal node id." - }, - "subject_kind": { - "type": "string", - "enum": [ - "vendor_user", - "vendor_business", - "transaction_party" - ] - }, - "signal_type": { - "type": "string", - "enum": [ - "ip_address", - "device", - "face", - "document_number", - "phone", - "email", - "address" - ] - }, - "confidence": { - "type": "number", - "format": "float", - "description": "1.0 for an exact match." - }, - "observation_count": { - "type": "integer" - }, - "first_seen_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "last_seen_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - }, - "NetworkGraph": { - "type": "object", - "properties": { - "network": { - "$ref": "#/components/schemas/NetworkDetail" - }, - "depth": { - "type": "integer", "nullable": true, - "description": "Effective depth applied, or null when unbounded (depth=all)." - }, - "truncated": { - "type": "boolean", - "description": "True if the neighborhood was cut off by depth." - }, - "nodes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NetworkGraphNode" - } - }, - "edges": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NetworkGraphEdge" - } - }, - "metrics": { - "type": "object", - "properties": { - "node_count": { - "type": "integer" - }, - "edge_count": { - "type": "integer" - }, - "hub_signal_count": { - "type": "integer" - } - } - } - } - }, - "NetworkMember": { - "type": "object", - "properties": { - "subject": { - "type": "object", - "description": "The vendor_user, vendor_business, or transaction_party this member represents." - }, - "outcome_status": { - "type": "string", - "description": "The subject's verification/decision outcome." - }, - "node_status": { - "type": "string", - "enum": [ - "approved", - "in_review", - "declined", - "not_completed", - "signal_active", - "signal_guarded" - ], - "description": "For a member row this is always one of the subject-outcome values: approved, in_review, declined, or not_completed." - }, - "tags": { - "type": "array", + "description": "Document verification checks grouped by workflow node. Present only when `session_kind = business`.", "items": { - "$ref": "#/components/schemas/NetworkNodeTag" - } - }, - "risk": { - "$ref": "#/components/schemas/NetworkRiskDisplay" - }, - "signal_coverage": { - "type": "object" - }, - "shared_infrastructure": { - "type": "object" - }, - "joined_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "last_seen_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "provenance": { - "type": "object", - "properties": { - "session_ids": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - }, - "business_session_ids": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - } - }, - "transaction_ids": { - "type": "array", - "items": { + "type": "object", + "description": "Documents grouped by workflow node. Present only when session_kind = business.", + "properties": { + "status": { + "type": "string" + }, + "node_id": { "type": "string", - "format": "uuid" + "nullable": true + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "document_type": { + "type": "string", + "enum": [ + "LEGAL_PRESENCE", + "COMPANY_DETAILS", + "OWNERSHIP_STRUCTURE", + "REPRESENTATIVES_AUTHORIZATION", + "OTHER" + ], + "nullable": true, + "description": "High-level KYB document group the file was classified into. `null` while unclassified." + }, + "document_subtype": { + "type": "string", + "nullable": true, + "description": "Specific OCR-detected subtype (e.g. `CERTIFICATE_OF_INCORPORATION`, `SHAREHOLDER_REGISTRY`); `UNKNOWN` when the OCR could not confidently classify the document." + }, + "document_group": { + "type": "string", + "enum": [ + "LEGAL_PRESENCE", + "COMPANY_DETAILS", + "OWNERSHIP_STRUCTURE", + "REPRESENTATIVES_AUTHORIZATION", + "OTHER" + ], + "nullable": true, + "description": "Alias of `document_type` — the group name used by `groups` and `required_groups`." + }, + "file_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Short-lived presigned link to the uploaded file. `null` for analyst-created document request placeholders (`is_requested = true`) that have not been fulfilled yet." + }, + "original_filename": { + "type": "string", + "nullable": true, + "description": "Filename as uploaded by the user or analyst." + }, + "file_size": { + "type": "integer", + "nullable": true, + "description": "File size in bytes." + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Per-document verification status." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "cross_check_result": { + "type": "object", + "nullable": true, + "description": "Cross-reference of the OCR-extracted document fields against the registry company profile. An object with `critical`, `non_critical` and `people` sections, each mapping a field name (e.g. `company_name`, `registration_number`, `incorporation_date`, `registered_address`) to a comparison record like `{\"document\": \"...\", \"registry\": \"...\", \"match\": true, \"score\": 0.97}`. `null` when the document had no OCR data or there was no registry company to compare against." + }, + "document_metadata": { + "type": "object", + "nullable": true, + "description": "Metadata extracted from the uploaded corporate document. When metadata extraction succeeded, all 16 keys are always present (individual values are `null` when not applicable to the file type); the whole object is `null` when nothing could be extracted. For PDFs, `overlay_manipulation` carries forensic overlay-text analysis.", + "properties": { + "file_size": { + "type": "integer", + "nullable": true, + "description": "Size of the document." + }, + "content_type": { + "type": "string", + "nullable": true, + "description": "Content type of the document." + }, + "creation_date": { + "type": "string", + "nullable": true, + "description": "Date when the document was created." + }, + "modified_date": { + "type": "string", + "nullable": true, + "description": "Date when the document was modified." + }, + "creator": { + "type": "string", + "nullable": true, + "description": "Authoring tool/user recorded in the file metadata (PDF `Creator`)." + }, + "producer": { + "type": "string", + "nullable": true, + "description": "Software that produced the file (PDF `Producer`)." + }, + "software": { + "type": "string", + "nullable": true, + "description": "Editing software recorded in the file metadata." + }, + "encryption": { + "type": "string", + "nullable": true, + "description": "Encryption scheme of the file, when encrypted." + }, + "is_signed": { + "type": "boolean", + "nullable": true, + "description": "Whether the document carries a digital signature." + }, + "is_tampered": { + "type": "boolean", + "nullable": true, + "description": "Whether the digital signature failed validation (post-signing modification)." + }, + "signature_info": { + "type": "object", + "nullable": true, + "description": "Details of the digital signature(s), when present." + }, + "exif_original_date": { + "type": "string", + "nullable": true, + "description": "EXIF original capture date, for image uploads." + }, + "exif_digitized_date": { + "type": "string", + "nullable": true, + "description": "EXIF digitization date, for image uploads." + }, + "processed_by_known_editor": { + "type": "boolean", + "nullable": true, + "description": "Whether metadata indicates the file passed through a known image/PDF editor." + }, + "has_different_creation_mod_date": { + "type": "boolean", + "nullable": true, + "description": "Whether creation and modification dates differ — a possible (but not conclusive) editing signal." + }, + "overlay_manipulation": { + "type": "object", + "nullable": true, + "description": "PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF.", + "properties": { + "detected": { + "type": "boolean", + "description": "Whether overlay-text manipulation evidence was detected." + }, + "analyzed": { + "type": "boolean", + "description": "Whether the PDF could be analyzed." + }, + "signals": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation." + }, + "manipulated_regions": { + "type": "array", + "description": "Suspected manipulated areas in PDF page coordinates.", + "items": { + "type": "object", + "properties": { + "page": { + "type": "integer" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "page_width": { + "type": "number" + }, + "page_height": { + "type": "number" + } + } + } + } + } + } + } + }, + "description": { + "type": "string", + "nullable": true, + "description": "Analyst-provided label or reason for a document request. `null` for regular uploads." + }, + "is_requested": { + "type": "boolean", + "description": "True for analyst-created document request placeholders awaiting an upload." + }, + "ocr_data": { + "type": "object", + "nullable": true + }, + "is_console_upload": { + "type": "boolean", + "description": "True for documents uploaded from the console rather than the end-user flow. Console uploads never change the feature-level aggregate status." + }, + "is_confirmed": { + "type": "boolean", + "description": "Console uploads start `false` and flip `true` once the admin confirms them; end-user uploads are always `true`." + } + } + } + }, + "groups": { + "type": "object", + "description": "Per-group progress counters keyed by group name (e.g. legal_presence, ownership_structure).", + "additionalProperties": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "approved": { + "type": "integer" + }, + "pending": { + "type": "integer" + }, + "declined": { + "type": "integer" + }, + "in_review": { + "type": "integer" + }, + "other": { + "type": "integer" + }, + "missing": { + "type": "integer", + "description": "Requested placeholders with no file uploaded yet." + } + } + } + }, + "required_groups": { + "type": "array", + "items": { + "type": "string" + } + }, + "warnings": { + "type": "array", + "items": { + "type": "object" + } } } } - } - } - }, - "NetworkSignal": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "signal_type": { - "type": "string", - "enum": [ - "ip_address", - "device", - "face", - "document_number", - "phone", - "email", - "address" - ] - }, - "subject_count": { - "type": "integer" - }, - "coverage": { - "type": "number", - "format": "float" - }, - "is_hub": { - "type": "boolean", - "description": "True when this signal links an unusually large number of subjects." - }, - "is_datacenter_ip": { - "type": "boolean" - }, - "is_high_collision_device": { - "type": "boolean" }, - "node_status": { + "environment": { "type": "string", "enum": [ - "signal_active", - "signal_guarded" - ] - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NetworkNodeTag" - } - }, - "risk_evidence": { - "type": "object" - }, - "first_seen_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "last_seen_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - }, - "NetworkTimelineEvent": { - "type": "object", - "properties": { - "event_type": { - "type": "string", - "description": "e.g. network_detected, signal_observed, network_in_review, network_resolved, network_dismissed." - }, - "occurred_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "label": { - "type": "string" - }, - "metadata": { - "type": "object" - } - } - }, - "NetworkMap": { - "type": "object", - "properties": { - "network": { - "$ref": "#/components/schemas/NetworkDetail" - }, - "convergence_count": { - "type": "integer", - "description": "Number of distinct address/location points members converge on." - }, - "countries": { - "type": "array", - "items": { - "type": "string" - } + "live", + "sandbox" + ], + "description": "Whether the session was created by a live application (`live`) or a sandbox application (`sandbox`). Sandbox sessions are free, fully simulated, and never bill credits." }, - "points": { + "resubmissions": { "type": "array", + "description": "Returned only with `include=resubmissions`, for user and business sessions. One entry per resubmission request, oldest first. Each entry carries the results the resubmitted steps returned before the request, so earlier submissions stay readable. Deleting the session deletes every submission.", "items": { "type": "object", "properties": { - "uuid": { - "type": "string", - "format": "uuid" + "number": { + "type": "integer", + "description": "1 for the first resubmission of the session, 2 for the second, and so on." }, - "country": { + "created_at": { "type": "string", - "nullable": true + "format": "date-time", + "description": "When the resubmission was requested." }, - "country_code": { + "previous_status": { "type": "string", - "nullable": true + "description": "The session status just before the request." }, - "city": { - "type": "string", - "nullable": true + "nodes": { + "type": "array", + "description": "The workflow steps sent back to the user.", + "items": { + "type": "object", + "properties": { + "node_id": { + "type": "string" + }, + "feature": { + "type": "string" + } + } + } }, - "latitude": { - "type": "number", - "format": "float", - "nullable": true + "reasons": { + "type": "object", + "nullable": true, + "additionalProperties": { + "type": "string" + }, + "description": "The reason given for each step, keyed by `node_id`." }, - "longitude": { - "type": "number", - "format": "float", - "nullable": true + "requested_by": { + "type": "object", + "nullable": true, + "description": "Who requested the resubmission.", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "type": { + "type": "string" + } + } }, - "observed_at": { - "type": "string", - "format": "date-time", - "nullable": true + "results": { + "type": "object", + "additionalProperties": true, + "description": "What the resubmitted steps returned before the request, under the same keys and in the same shape as the current results of this response (`id_verifications`, `nfc_verifications`, `liveness_checks`, `face_matches`, `poa_verifications`, `phone_verifications`, `email_verifications`, `aml_screenings`, `ip_analyses`, `database_validations`, `questionnaire_responses`, `document_ai_documents`, `geolocation_checks`; for business sessions also `document_verifications`). Only the keys of the steps that were sent back appear." } } } + }, + "investigator": { + "$ref": "#/components/schemas/InvestigatorBlock" } } }, - "EntityNetworkMembership": { + "WorkflowDetail": { "type": "object", + "description": "The fields below are typed; the object also carries every other column of the workflow configuration (feature toggles, thresholds, warning actions, allowed documents, callbacks) as additional properties. Fields that do not apply to the `workflow_type` are `null`.", "properties": { - "network": { - "$ref": "#/components/schemas/NetworkListItem" - }, - "subject": { - "type": "object" - }, - "signal_types": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "ip_address", - "device", - "face", - "document_number", - "phone", - "email", - "address" - ] - } - }, - "signal_count": { - "type": "integer" - }, - "joined_at": { + "uuid": { "type": "string", - "format": "date-time", - "nullable": true + "format": "uuid", + "description": "Per-version identifier (`settings_uuid`). Every version of a workflow has its own." }, - "last_seen_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - }, - "RiskSignals": { - "type": "object", - "description": "Optional device and network context observed by your application. All fields are optional. Passing them feeds cross-session match detection in Networks.", - "properties": { - "ip": { + "workflow_id": { "type": "string", - "format": "ip", - "description": "IPv4 or IPv6 address observed for the end user.", - "example": "192.0.2.1" + "format": "uuid", + "description": "Stable identifier shared by every version of the workflow. Sessions created with it use the latest published version." }, - "device_id": { + "workflow_label": { "type": "string", - "maxLength": 255, - "description": "Stable device identifier collected by your application (Android `ANDROID_ID`, iOS `identifierForVendor`)." + "description": "Display name of the workflow." }, - "device_platform": { + "workflow_type": { "type": "string", + "nullable": true, "enum": [ - "android", - "ios", - "ipados", - "tvos", - "web" + "kyc", + "kyb", + "adaptive_age_verification", + "biometric_authentication", + "address_verification", + "questionnaire_verification", + "email_verification", + "phone_verification", + null ], - "description": "Platform of the end user's device." + "description": "Workflow template. `null` for graph workflows created without a template." }, - "device_model": { - "type": "string", - "maxLength": 255, - "description": "Device model reported by your application." + "is_default": { + "type": "boolean", + "description": "Whether this is the application's default workflow." }, - "os_version": { - "type": "string", - "maxLength": 64, - "description": "Operating-system version." + "is_archived": { + "type": "boolean", + "description": "Archived workflows cannot start new sessions." }, - "app_version": { - "type": "string", - "maxLength": 64, - "description": "Version of your application." + "version": { + "type": "integer", + "description": "Version number within the workflow." }, - "user_agent": { - "type": "string", - "maxLength": 512, - "description": "Browser or app user agent." - } - } - }, - "PhoneRiskRequest": { - "type": "object", - "required": [ - "phone_number" - ], - "properties": { - "phone_number": { + "status": { "type": "string", - "maxLength": 20, - "description": "Phone number to score. It is validated and normalized to E.164.", - "example": "+14155552671" + "enum": [ + "draft", + "published" + ], + "description": "Draft versions are editable; published versions are immutable and used for new sessions." }, - "country_code": { + "published_at": { "type": "string", - "maxLength": 3, - "description": "Optional ISO 3166-1 alpha-2 or alpha-3 country hint for intelligence coverage.", - "example": "US" + "format": "date-time", + "nullable": true, + "description": "When this version was published. `null` for drafts." }, - "signals": { - "$ref": "#/components/schemas/RiskSignals" + "is_simple_workflow": { + "type": "boolean", + "description": "`true` when the workflow has no custom `workflow_graph`." }, - "vendor_data": { + "is_editable": { + "type": "boolean", + "description": "`true` only for `draft` versions." + }, + "workflow_url": { "type": "string", - "description": "Your stable reference for this request. Stored on the API session and echoed in the response." + "format": "uri", + "description": "Public URL that starts a session with this workflow." }, - "metadata": { + "workflow_graph": { "type": "object", "nullable": true, "additionalProperties": true, - "description": "Your non-sensitive JSON metadata. Stored on the API session and echoed in the response." + "description": "Graph structure of the workflow (nodes and edges). `null` for simple workflows." }, - "enable_social": { - "type": "boolean", - "default": false, - "description": "When true, also look up the social footprint of the number - the messaging, social, e-commerce and professional platforms it is registered on. Billed as one `phone_social` unit per request in addition to this endpoint's own price.", - "example": true - } - } - }, - "EmailRiskRequest": { - "type": "object", - "required": [ - "email" - ], - "properties": { - "email": { + "features": { "type": "string", - "format": "email", - "description": "Email address to score.", - "example": "alice@example.com" + "description": "Enabled features joined with `\" + \"`, for example `\"OCR + LIVENESS\"`." }, - "country_code": { - "type": "string", - "maxLength": 3, - "description": "Optional ISO 3166-1 alpha-2 or alpha-3 country hint for intelligence coverage.", - "example": "US" + "total_price": { + "type": "number", + "nullable": true, + "description": "Price per session; for graph workflows the most expensive path." }, - "signals": { - "$ref": "#/components/schemas/RiskSignals" + "min_price": { + "type": "number", + "nullable": true, + "description": "Price of the cheapest path through the workflow." }, - "vendor_data": { - "type": "string", - "description": "Your stable reference for this request. Stored on the API session and echoed in the response." + "max_price": { + "type": "number", + "nullable": true, + "description": "Price of the most expensive path through the workflow." + }, + "special_add_on_prices": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Organization-specific add-on prices, or `null` when none are set." + }, + "max_retry_attempts": { + "type": "integer", + "description": "Retries allowed after a declined verification." + }, + "retry_window_days": { + "type": "integer", + "nullable": true, + "description": "Days during which the retries are allowed." }, - "metadata": { + "session_expiration_time": { + "type": "integer", + "description": "Seconds before an unfinished session expires." + }, + "questionnaire": { "type": "object", "nullable": true, "additionalProperties": true, - "description": "Your non-sensitive JSON metadata. Stored on the API session and echoed in the response." + "description": "The questionnaire attached to the workflow, or `null`." }, - "enable_social": { - "type": "boolean", - "default": false, - "description": "When true, also look up the social footprint of the address - the email, social, professional, e-commerce and entertainment platforms it is registered on. Billed as one `email_social` unit per request in addition to this endpoint's own price.", - "example": true + "kyb_role_settings": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + }, + "description": "Per-role verification settings for KYB key people." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" } - } + }, + "additionalProperties": true }, - "IPRiskRequest": { + "UserSessionListItem": { + "title": "KYC session row (session_kind: user)", "type": "object", + "description": "Summary row for a User Verification (KYC) session. Identity fields (`full_name`, `country`, `document_type`, `portrait_image`, contact fields) are populated progressively as the user completes verification steps, and are `null` until the relevant step finishes. Fields restricted by the workflow's response-attributes configuration are also returned as `null`.", "required": [ - "ip_address" + "session_id", + "session_kind", + "session_number", + "session_url", + "portrait_image", + "document_type", + "full_name", + "country", + "status", + "vendor_data", + "didit_internal_id", + "created_at", + "features", + "phone_number", + "email_address", + "additional_details" ], "properties": { - "ip_address": { + "session_id": { "type": "string", - "format": "ip", - "description": "IPv4 or IPv6 address to score.", - "example": "8.8.8.8" + "format": "uuid", + "description": "Unique identifier of the verification session. Use it with `GET /v3/session/{sessionId}/decision/` to fetch the full decision payload.", + "example": "5b3720ed-d429-42ef-b67f-37ea805f48ee" }, - "claimed_country": { + "session_kind": { "type": "string", - "maxLength": 3, - "description": "Optional ISO 3166-1 alpha-2 or alpha-3 country claimed by the user or session. Used to calculate `country_mismatch`.", - "example": "ESP" + "enum": [ + "user" + ], + "description": "Row discriminator. Always `\"user\"` for KYC rows; Business Verification (KYB) rows have `\"business\"` and a different shape.", + "example": "user" }, - "user_agent": { - "type": "string", - "maxLength": 512, - "description": "User agent observed by your backend. Stored as request context; it does not provide device fingerprinting." + "session_number": { + "type": "integer", + "description": "Human-friendly sequential number of the session within your application. Also matched by the `search` parameter.", + "example": 720 }, - "vendor_data": { + "session_url": { "type": "string", - "description": "Your stable reference for this request. Stored on the API session and echoed in the response." + "nullable": true, + "description": "Hosted verification URL the end user opens to complete the flow. `null` when the session has no hosted URL.", + "example": "https://verify.didit.me/session/CuXoBYCYVO5Y" }, - "metadata": { - "type": "object", + "portrait_image": { + "type": "string", "nullable": true, - "additionalProperties": true, - "description": "Your non-sensitive JSON metadata. Stored on the API session and echoed in the response." - } - } - }, - "RiskWarning": { - "type": "object", - "required": [ - "feature", - "risk", - "log_type", - "short_description", - "long_description" - ], - "properties": { - "feature": { + "description": "Expiring (presigned) URL of the best available portrait: the NFC chip photo when available, otherwise the ID document portrait, otherwise the liveness reference image. `null` until one of those exists. Download promptly; the link expires.", + "example": "https:///portraits/5b3720ed.jpg?X-Amz-Expires=3600&X-Amz-Signature=..." + }, + "document_type": { "type": "string", - "description": "Feature that produced the warning." + "nullable": true, + "enum": [ + "Passport", + "Identity Card", + "Driver's License", + "Residence Permit", + "Health Insurance Card", + "Tax Card" + ], + "description": "Human-readable type of the verified ID document. `null` before document verification completes, and also `null` after completion when the verified code has no display mapping (`SSC`, `WP`, `VISA`, `PSC`, `BC`, `OTHER`). Note: the `document_type` query parameter filters by short codes (`P`, `ID`, `DL`, `RP`, ...), not by these display values.", + "example": "Passport" }, - "risk": { + "full_name": { "type": "string", - "description": "Machine-readable risk code. New values can be added over time." + "nullable": true, + "description": "Best available full name for the session, resolved in order: ID document OCR, AML screened data, database-validation screened data, proof-of-address document, then the expected details provided at session creation. `null` when none is available yet.", + "example": "Jane Marie Doe" }, - "additional_data": { - "type": "object", + "country": { + "type": "string", "nullable": true, - "additionalProperties": true, - "description": "Risk-specific context, or `null`." + "description": "ISO 3166-1 alpha-3 country associated with the session, resolved in order: ID document issuing state, database-validation issuing state, proof-of-address issuing state, expected details, then the verified phone's country. May differ from nationality.", + "example": "ESP" }, - "log_type": { + "status": { "type": "string", "enum": [ - "error", - "warning", - "information" + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" ], - "description": "Severity used by the decision rules." + "description": "Overall session status. See the [Verification Statuses](/integration/verification-statuses) page for the lifecycle.", + "example": "Approved" }, - "short_description": { + "vendor_data": { "type": "string", - "description": "Human-readable one-line summary." + "nullable": true, + "description": "Your own identifier for the end user, echoed back exactly as sent at session creation.", + "example": "your-user-id-123" }, - "long_description": { - "type": "string", - "description": "Human-readable explanation." - } - } - }, - "RiskLifecycleEvent": { - "type": "object", - "required": [ - "type", - "timestamp", - "details", - "fee" - ], - "properties": { - "type": { + "didit_internal_id": { "type": "string", - "description": "Machine-readable event type. New values can be added over time." + "format": "uuid", + "nullable": true, + "description": "Didit's internal identifier for the verified user profile behind this session. Stable across multiple sessions of the same user; filterable via the `didit_internal_id` query parameter.", + "example": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147" }, - "timestamp": { + "created_at": { "type": "string", "format": "date-time", - "description": "When the event happened." + "description": "UTC timestamp when the session was created.", + "example": "2026-06-09T12:26:54.328364Z" }, - "details": { + "features": { + "type": "array", + "description": "Per-feature status for every verification step configured in the session's workflow, in workflow order.", + "items": { + "type": "object", + "required": [ + "feature", + "status" + ], + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACE_MATCH", + "POA", + "QUESTIONNAIRE", + "EMAIL_VERIFICATION", + "PHONE", + "AML", + "IP_ANALYSIS", + "AGE_ESTIMATION", + "DATABASE_VALIDATION" + ], + "description": "Verification feature identifier.", + "example": "ID_VERIFICATION" + }, + "status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "Status of this feature. `Not Finished` until the step completes.", + "example": "Approved" + } + } + } + }, + "phone_number": { "type": "object", "nullable": true, - "additionalProperties": true, - "description": "Event-specific payload, or `null`." + "description": "Phone associated with the session: the verified phone when phone verification ran, otherwise the contact phone provided at session creation. `null` when neither exists.", + "required": [ + "number", + "is_verified" + ], + "properties": { + "number": { + "type": "string", + "description": "Phone number in E.164 format.", + "example": "+14155552671" + }, + "is_verified": { + "type": "boolean", + "description": "`true` only when the phone-verification feature approved this number.", + "example": true + } + } }, - "fee": { - "type": "number", - "description": "USD amount billed for this event." + "email_address": { + "type": "object", + "nullable": true, + "description": "Email associated with the session: the verified email when email verification ran, otherwise the contact email provided at session creation. `null` when neither exists.", + "required": [ + "email", + "is_verified" + ], + "properties": { + "email": { + "type": "string", + "format": "email", + "example": "jane@example.com" + }, + "is_verified": { + "type": "boolean", + "description": "`true` only when the email-verification feature approved this address.", + "example": true + } + } + }, + "additional_details": { + "type": "object", + "nullable": true, + "description": "Compact per-feature extras. Only features that produced data appear as keys; `null` when no feature produced extras. Fields excluded by the workflow's response-attributes configuration are omitted.", + "properties": { + "aml": { + "type": "object", + "description": "Present when AML screening ran.", + "properties": { + "total_hits": { + "type": "integer", + "description": "Number of AML screening hits.", + "example": 0 + }, + "entity_type": { + "type": "string", + "description": "Screened entity type.", + "example": "person" + }, + "is_adverse_media_fetched": { + "type": "boolean", + "example": false + }, + "screened_data": { + "type": "object", + "description": "Subset of the data that was screened. Only non-empty keys are included.", + "properties": { + "nationality": { + "type": "string", + "example": "ESP" + }, + "document_number": { + "type": "string", + "example": "XX1234567" + }, + "date_of_birth": { + "type": "string", + "example": "1990-05-21" + } + } + } + } + }, + "database_validation": { + "type": "object", + "description": "Present when database validation ran.", + "properties": { + "validation_type": { + "type": "string", + "example": "DOCUMENT_VALIDATION" + }, + "match_type": { + "type": "string", + "example": "FULL_MATCH" + }, + "identification_number": { + "type": "string", + "description": "Tax, personal, or document number that was validated (first available).", + "example": "12345678Z" + } + } + }, + "poa": { + "type": "object", + "description": "Present when proof of address ran.", + "properties": { + "document_type": { + "type": "string", + "example": "UTILITY_BILL" + }, + "issuer": { + "type": "string", + "example": "Energy Co" + }, + "address": { + "type": "string", + "example": "Calle Mayor 1, 28013 Madrid" + }, + "name_on_document": { + "type": "string", + "example": "Jane Marie Doe" + } + } + }, + "liveness": { + "type": "object", + "description": "Present when a liveness check ran.", + "properties": { + "method": { + "type": "string", + "example": "PASSIVE" + }, + "score": { + "type": "number", + "description": "Liveness confidence score, rounded to 2 decimals.", + "example": 0.98 + } + } + }, + "face_match": { + "type": "object", + "description": "Present when face match ran.", + "properties": { + "score": { + "type": "number", + "description": "Face-match similarity score, rounded to 2 decimals.", + "example": 0.95 + }, + "source_image": { + "type": "string", + "description": "Expiring (presigned) URL of the source face image.", + "example": "https:///faces/source.jpg?X-Amz-Expires=3600&..." + }, + "target_image": { + "type": "string", + "description": "Expiring (presigned) URL of the target face image.", + "example": "https:///faces/target.jpg?X-Amz-Expires=3600&..." + } + } + }, + "phone_verification": { + "type": "object", + "description": "Present when phone verification ran.", + "properties": { + "country_code": { + "type": "string", + "description": "ISO 3166-1 alpha-2 country of the phone number.", + "example": "US" + }, + "is_temporary": { + "type": "boolean", + "example": false + }, + "is_ported": { + "type": "boolean", + "example": false + }, + "is_blocked": { + "type": "boolean", + "example": false + } + } + }, + "email_verification": { + "type": "object", + "description": "Present when email verification ran.", + "properties": { + "is_breached": { + "type": "boolean", + "example": false + }, + "is_disposable": { + "type": "boolean", + "example": false + }, + "is_undeliverable": { + "type": "boolean", + "example": false + }, + "is_blocklisted": { + "type": "boolean", + "example": false + } + } + } + } } } }, - "PhoneMatchItem": { + "BusinessSessionListItem": { + "title": "KYB session row (session_kind: business)", "type": "object", - "description": "Another session of your application that used the same phone number with a different `vendor_data`.", + "description": "Summary row for a Business Verification (KYB) session. Returned only when `session_kind` is `business` or `all`.", + "required": [ + "session_id", + "session_kind", + "session_number", + "status", + "vendor_data", + "created_at", + "workflow_id", + "workflow_type", + "workflow_label", + "company_name", + "registration_number", + "country" + ], "properties": { "session_id": { "type": "string", "format": "uuid", - "description": "Session id of the other verification that used this phone number." + "description": "Unique identifier of the business verification session.", + "example": "8a1f63c2-1b9e-4f3a-9c41-2f6a1f0b7d52" + }, + "session_kind": { + "type": "string", + "enum": [ + "business" + ], + "description": "Row discriminator. Always `\"business\"` for KYB rows.", + "example": "business" }, "session_number": { "type": "integer", - "description": "Human-friendly sequential number of that session." + "description": "Human-friendly sequential number of the business session within your application. Numbered independently from KYC sessions.", + "example": 42 + }, + "status": { + "type": "string", + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "description": "Overall session status. See the [Verification Statuses](/integration/verification-statuses) page.", + "example": "Approved" }, "vendor_data": { "type": "string", "nullable": true, - "description": "`vendor_data` of that session." + "description": "Your own identifier for the business, echoed back exactly as sent at session creation.", + "example": "your-company-id-77" }, - "verification_date": { + "created_at": { "type": "string", - "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)." + "format": "date-time", + "description": "UTC timestamp when the session was created.", + "example": "2026-06-08T09:14:02.118450Z" }, - "phone_number": { + "workflow_id": { "type": "string", - "description": "The matched phone number." + "format": "uuid", + "nullable": true, + "description": "Identifier of the workflow this session was created with.", + "example": "9f9b1234-aaaa-bbbb-cccc-1234567890ab" }, - "status": { + "workflow_type": { "type": "string", - "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)." + "nullable": true, + "description": "Workflow type. `kyb` for business workflows.", + "example": "kyb" }, - "is_blocklisted": { - "type": "boolean", - "description": "Whether the phone number is on your application's blocklist." + "workflow_label": { + "type": "string", + "nullable": true, + "description": "Display name of the workflow.", + "example": "Standard KYB" }, - "api_service": { + "company_name": { "type": "string", "nullable": true, - "description": "For API-type sessions, the standalone API that created it; `null` for regular verification sessions." + "description": "Name of the company being verified, from the registry company linked to the session. `null` until a company is selected.", + "example": "Acme Holdings S.L." }, - "source": { + "registration_number": { "type": "string", - "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry uses `list_entry`." + "nullable": true, + "description": "Company registration number from the registry company linked to the session.", + "example": "B12345678" + }, + "country": { + "type": "string", + "nullable": true, + "description": "Country code of the registry company linked to the session.", + "example": "ES" } } }, - "EmailMatchItem": { + "TransactionVerificationSessionAction": { + "title": "Verification session", "type": "object", - "description": "Another session of your application that used the same email address with a different `vendor_data`.", "properties": { - "session_id": { + "type": { "type": "string", - "format": "uuid", - "description": "Session id of the other verification that used this email address." - }, - "session_number": { - "type": "integer", - "description": "Human-friendly sequential number of that session." + "enum": [ + "verification_session" + ] }, - "vendor_data": { + "url": { "type": "string", - "nullable": true, - "description": "`vendor_data` of that session." + "format": "uri", + "description": "Hosted verification URL to open for the end user." }, - "verification_date": { + "session_id": { "type": "string", - "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)." + "format": "uuid", + "description": "Verification session id." }, - "email": { + "session_token": { "type": "string", - "description": "The matched email address." + "description": "Session token, used by the mobile SDKs to launch the flow natively." }, "status": { "type": "string", - "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)." - }, - "is_blocklisted": { - "type": "boolean", - "description": "Whether the email address is on your application's blocklist." - }, - "api_service": { - "type": "string", - "nullable": true, - "description": "For API-type sessions, the standalone API that created it; `null` for regular verification sessions." - }, - "source": { - "type": "string", - "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry uses `list_entry`." + "description": "Verification session status." } - } + }, + "required": [ + "type", + "url" + ] }, - "PhoneIntelligence": { + "TransactionWalletOwnershipAction": { + "title": "Wallet ownership", "type": "object", - "description": "Normalized phone-intelligence block. Present only when the intelligence add-on ran for this lookup; when present it is also exposed under the `enrichment` alias with identical values.", "properties": { - "score": { - "type": "number", - "nullable": true, - "description": "Normalized 0-100 risk score. Higher is riskier." - }, - "phone_type_risk": { + "type": { "type": "string", - "nullable": true, - "description": "`Low`, `Medium` or `High`, derived from the score." - }, - "phone_trust_index": { - "type": "number", - "nullable": true, - "description": "Inverse of the score, for dashboards that prefer higher is better." - }, - "network_stability_index": { - "type": "number", - "nullable": true, - "description": "Stability of the line's network." - }, - "is_disposable": { - "type": "boolean", - "nullable": true, - "description": "`true` when the number looks disposable or virtual." - }, - "is_ported": { - "type": "boolean", - "nullable": true, - "description": "`true` when the number has moved carrier." + "enum": [ + "wallet_ownership" + ] }, - "is_recently_ported": { - "type": "boolean", - "nullable": true, - "description": "`true` when the port happened recently." + "url": { + "type": "string", + "format": "uri", + "description": "Hosted wallet-ownership widget URL. Open it in a real browser surface - on mobile use SFSafariViewController or a Chrome Custom Tab, never an embedded webview." }, - "carrier_type": { + "widget_session_id": { "type": "string", - "nullable": true, - "description": "Line classification reported by the lookup (e.g., `mobile`)." + "format": "uuid", + "description": "Wallet-ownership widget session id." }, - "reason_codes": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Machine-readable codes explaining the score. Values can grow over time, so do not reject unknown codes." + "expires_at": { + "type": "string", + "format": "date-time", + "description": "When the widget session expires." } - } + }, + "required": [ + "type", + "url" + ] }, - "EmailIntelligence": { + "TransactionRuleActionAddScore": { "type": "object", - "description": "Normalized email-intelligence block. Present only when the intelligence add-on ran for this email; when present it is also exposed as the `enrichment` alias with identical values.", + "required": [ + "type", + "value" + ], "properties": { - "score": { - "type": "number", - "nullable": true, - "description": "Normalized 0-100 risk score. Higher is riskier." - }, - "deliverable": { - "type": "string", - "nullable": true, - "description": "Deliverability band (e.g., `high`)." - }, - "breach_risk_level": { + "type": { "type": "string", - "nullable": true, - "description": "`Low`, `Medium` or `High`." - }, - "breach_risk_index": { - "type": "number", - "nullable": true, - "description": "Numeric breach-risk index." - }, - "is_recent_breach": { - "type": "boolean", - "nullable": true, - "description": "`true` when the address appears in a recent breach." - }, - "is_frequent_breached": { - "type": "boolean", - "nullable": true, - "description": "`true` when the address appears in many breaches." - }, - "is_disposable": { - "type": "boolean", - "nullable": true, - "description": "`true` when the domain is a throwaway provider." - }, - "is_free_domain": { - "type": "boolean", - "nullable": true, - "description": "`true` when the domain is a free mail provider." - }, - "is_breached": { - "type": "boolean", - "nullable": true, - "description": "`true` when the address appears in known breaches." - }, - "no_of_breaches": { - "type": "number", - "nullable": true, - "description": "Number of known breaches containing the address." + "enum": [ + "add_score" + ] }, - "reason_codes": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Machine-readable codes explaining the score. Values can grow over time, so do not reject unknown codes." + "value": { + "type": "integer", + "description": "Points added to the transaction's risk score. May be negative." } - } + }, + "description": "Add points to the transaction's cumulative risk score; the total decides the status via the application's review/decline thresholds." }, - "IPRiskResult": { + "TransactionRuleActionChangeStatus": { "type": "object", - "description": "IP risk result. Individual enrichment fields can be `null` when the lookup returned no usable data.", "required": [ - "status", - "threat" + "type", + "value" ], "properties": { - "status": { + "type": { "type": "string", "enum": [ - "Approved", - "In Review" - ], - "description": "`In Review` when the resolved country conflicts with `claimed_country`, or when the address is associated with VPN, Tor, or data-center infrastructure. Otherwise `Approved`." + "change_status" + ] }, - "ip_address": { + "value": { "type": "string", - "nullable": true, - "description": "The scored address. `null` when no enrichment source returned usable data." + "enum": [ + "APPROVED", + "IN_REVIEW", + "DECLINED", + "AWAITING_USER" + ], + "description": "Status to set immediately, regardless of score. Alias key: status." }, - "ip_country": { + "workflow_id": { "type": "string", - "nullable": true, - "description": "Resolved country name." + "format": "uuid", + "description": "REQUIRED when value is AWAITING_USER: the verification workflow the subject is asked to complete. Alias key: remediation_workflow_id. Didit creates a hosted remediation session for the subject when the rule fires." + } + }, + "description": "Directly set the transaction status. DECLINED always wins across matched rules." + }, + "TransactionRuleActionAddTags": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "add_tags" + ] }, - "ip_country_code": { + "tag_uuid": { "type": "string", - "nullable": true, - "description": "Resolved country code. Compare countries through `country_mismatch` instead of comparing raw country strings yourself." + "format": "uuid", + "description": "Existing application tag to attach." }, - "ip_state": { + "tag_name": { "type": "string", - "nullable": true, - "description": "Resolved state or region." + "description": "Tag name to attach; created on first use (stored uppercased). Provide tag_uuid or tag_name." }, - "ip_city": { + "tag_color": { "type": "string", - "nullable": true, - "description": "Resolved city." + "description": "Hex color used when tag_name creates a new tag, e.g. #007AFE." + } + }, + "description": "Attach an application tag to the matched transaction." + }, + "TransactionRuleActionAddNote": { + "type": "object", + "required": [ + "type", + "note" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "add_note" + ] }, - "latitude": { - "type": "number", - "nullable": true, - "description": "Approximate network geolocation, not a precise device location." + "note": { + "type": "string", + "description": "Note text attached to the transaction (author: Rule Engine). Alias key: value." + } + }, + "description": "Attach an automated analyst note to the matched transaction." + }, + "TransactionRuleActionAddToList": { + "type": "object", + "required": [ + "type", + "list_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "add_to_list" + ] }, - "longitude": { - "type": "number", - "nullable": true, - "description": "Approximate network geolocation, not a precise device location." + "list_id": { + "type": "string", + "format": "uuid", + "description": "List to add the subject's vendor_data to." + } + }, + "description": "Add the subject's vendor_data to a blocklist, allowlist or custom list." + }, + "TransactionRuleActionOpenCase": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "open_case" + ] }, - "isp": { + "blueprint": { "type": "string", - "nullable": true, - "description": "Internet service provider serving the address." + "format": "uuid", + "description": "Case blueprint UUID; defaults to the application's default blueprint." }, - "organization": { + "grouping": { "type": "string", - "nullable": true, - "description": "Organization owning the address block." + "enum": [ + "by_applicant", + "by_rule_and_applicant" + ], + "description": "Whether repeat matches join one case per applicant or one per rule+applicant." }, - "is_vpn_or_tor": { + "attach_matched_transaction": { "type": "boolean", - "nullable": true, - "description": "`true` when the address is associated with VPN or Tor infrastructure." + "description": "Attach the matched transaction to the case." + } + }, + "description": "Open (or add to) an investigation case. Only the first open_case action on a rule is applied." + }, + "KYBRegistrySearchResults": { + "type": "object", + "properties": { + "companies": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kyb_response_id": { + "type": "string", + "nullable": true, + "description": "Handle for this candidate — pass it to `POST /v3/kyb/select/` to retrieve the full profile. Ephemeral and scoped to this search: do not store it long-term; re-search to get a fresh one.", + "example": "69aeeb95febb0f1704042259" + }, + "name": { + "type": "string", + "nullable": true, + "description": "Registered company name.", + "example": "Tesco PLC" + }, + "registration_number": { + "type": "string", + "nullable": true, + "description": "Registry registration number.", + "example": "00445790" + }, + "status": { + "type": "string", + "nullable": true, + "description": "Normalized registry status of the company (e.g. `active`, `dissolved`, `inactive`, `struck off`).", + "example": "active" + }, + "type": { + "type": "string", + "nullable": true, + "description": "Legal form as reported by the registry.", + "example": "Public Limited Company" + }, + "registered_address": { + "type": "string", + "nullable": true, + "description": "Registered address of the candidate, when the registry returns one. Use it, with the registration number, to tell same-named companies apart: in the United States a candidate can be listed under the state where the company operates rather than the state you searched.", + "example": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, AL7 1GA" + }, + "risk_level": { + "type": "string", + "nullable": true, + "description": "Registry-reported risk level, when available. Usually `null` at search time." + }, + "fetch_status": { + "type": "string", + "nullable": true, + "description": "Always `pending` for search candidates — the full company profile is only fetched (and billed) when you select the candidate.", + "example": "pending" + } + } + }, + "description": "Deduplicated candidate list. Empty while `search_resolved=false` (webhook mode) or when nothing matched." }, - "is_data_center": { - "type": "boolean", - "nullable": true, - "description": "`true` when the address belongs to a hosting or data-center network." + "pagination": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "description": "Number of candidates returned." + }, + "page": { + "type": "integer" + }, + "per_page": { + "type": "integer", + "example": 25 + } + } }, - "time_zone": { + "search_status": { "type": "string", "nullable": true, - "description": "Time zone of the resolved location." + "enum": [ + "pending", + "resolved" + ], + "description": "Registry search lifecycle status." }, - "time_zone_offset": { + "search_resolved": { + "type": "boolean", + "description": "`true` once the candidate list is final." + } + } + }, + "KYBRegistryCandidateRequest": { + "type": "object", + "required": [ + "kyb_response_id" + ], + "properties": { + "kyb_response_id": { "type": "string", - "nullable": true, - "description": "UTC offset of the resolved location." - }, - "asn_number": { - "type": "number", - "nullable": true, - "description": "Autonomous system number." + "maxLength": 255, + "description": "Candidate handle from `POST /v3/kyb/search/` (`kyb_registry.companies[].kyb_response_id`). Ephemeral — use it shortly after the search that produced it.", + "example": "69aeeb95febb0f1704042259" }, - "asn_organization": { + "vendor_data": { "type": "string", - "nullable": true, - "description": "Organization operating the autonomous system." + "description": "Your identifier for this company; echoed back and stored with the business session." }, - "connection_type": { - "type": "string", + "metadata": { + "type": "object", "nullable": true, - "description": "Connection classification (e.g., `cellular`, `hosting`)." + "description": "Free-form JSON stored with the business session and echoed back." }, - "carrier": { + "save_api_request": { + "type": "boolean", + "default": true, + "description": "Must be `true` (the default). KYB registry selects are always persisted so billing and retrieval can be tracked; `false` returns `400`." + } + } + }, + "WebhookEventEnvelope": { + "type": "object", + "description": "Fields Didit adds to every signed webhook delivery, whatever the event type. Read `webhook_type` to know which payload you received.", + "required": [ + "event_id", + "webhook_type", + "timestamp", + "created_at", + "application_id", + "environment" + ], + "properties": { + "event_id": { "type": "string", - "nullable": true, - "description": "Mobile carrier when the address is on a mobile network." + "format": "uuid", + "description": "Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate." }, - "proxy_type": { - "type": "string", - "nullable": true, - "description": "Proxy classification when the address is behind a proxy." + "webhook_type": { + "$ref": "#/components/schemas/WebhookEvent" }, - "claimed_country": { - "type": "string", - "nullable": true, - "description": "Echo of the `claimed_country` you sent." + "timestamp": { + "type": "integer", + "description": "Unix time in seconds at which this delivery was signed. Equal to the `X-Timestamp` header; a redelivery carries a new value." }, - "country_mismatch": { - "type": "boolean", - "nullable": true, - "description": "`true` when the resolved country differs from `claimed_country`. `null` when no enrichment data or no claimed country is available." + "created_at": { + "type": "integer", + "description": "Unix time in seconds of the change that produced the event." }, - "threat": { - "type": "object", - "additionalProperties": true, - "description": "Free-form network-threat evidence from the enrichment source (e.g., `is_proxy`, `is_tor`). Empty object when no enrichment data is available. Treat as diagnostic detail, not a stable contract." + "application_id": { + "type": "string", + "format": "uuid", + "description": "The application the event belongs to." + }, + "environment": { + "type": "string", + "enum": [ + "live", + "sandbox" + ], + "description": "`sandbox` for sandbox sessions and sandbox applications, otherwise `live`." } } }, - "PhoneRiskResponse": { + "SessionWebhookEventData": { "type": "object", + "description": "Session fields of `status.updated` and `data.updated`. Optional fields are omitted, never sent as `null`, when they do not apply. Business (KYB) sessions add `business_session_id`, `session_kind` and `vendor_business_id`.", "required": [ - "request_id", + "session_id", "status", - "created_at" + "sandbox_scenario" ], "properties": { - "request_id": { + "session_id": { "type": "string", "format": "uuid", - "description": "Id of the API session created for this request. Store it with `vendor_data` if you need to correlate the result with later webhooks or console activity." + "description": "The verification session." + }, + "business_session_id": { + "type": "string", + "format": "uuid", + "description": "Business (KYB) sessions only: the same value as `session_id`." + }, + "session_kind": { + "type": "string", + "enum": [ + "business" + ], + "description": "Business (KYB) sessions only. User (KYC) session events carry no top-level `session_kind`; read `decision.session_kind` instead." }, "status": { "type": "string", "enum": [ + "Not Started", + "In Progress", "Approved", + "Declined", "In Review", - "Declined" + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" ], - "description": "Final decision for this API session. Normally `Approved`; the risk evaluation can produce `In Review` or `Declined`." + "description": "Session status after the change." + }, + "trigger": { + "type": "string", + "description": "Present when the event was not produced by the verification flow itself, for example `manual_review`, `manual_step_update`, `ongoing_monitoring`, `webhook_reconciliation`, `manual_webhook_recovery`, `kyb_registry_change` or a `console_*` edit." + }, + "workflow_id": { + "type": "string", + "format": "uuid", + "description": "The session's workflow. Omitted when the session has none." + }, + "workflow_version": { + "type": "integer", + "description": "Version of the workflow the session ran. Sent together with `workflow_id`." }, "vendor_data": { "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent, `null` when you did not send one." + "description": "Your `vendor_data` for the session. Omitted when empty." + }, + "vendor_business_id": { + "type": "string", + "description": "Business (KYB) sessions only: the business the session belongs to. Omitted when none." }, "metadata": { "type": "object", - "nullable": true, "additionalProperties": true, - "description": "Echo of the `metadata` you sent, `null` when you did not send one." + "description": "Your session `metadata`. Omitted when empty." }, - "created_at": { + "decision": { + "$ref": "#/components/schemas/SessionDecision" + }, + "resubmit_info": { + "type": "object", + "description": "Only when `status` is `Resubmitted`: the steps the user must redo and why.", + "required": [ + "nodes_to_resubmit", + "reasons" + ], + "properties": { + "nodes_to_resubmit": { + "type": "array", + "items": { + "type": "object", + "required": [ + "node_id", + "feature" + ], + "properties": { + "node_id": { + "type": "string", + "description": "Node identifier as it appears in the session's workflow definition (for example `feature_ocr`, `feature_liveness`).", + "example": "feature_poa" + }, + "feature": { + "type": "string", + "description": "Feature type of the node, for example `OCR`, `LIVENESS` or `PROOF_OF_ADDRESS`.", + "example": "PROOF_OF_ADDRESS" + } + } + }, + "description": "Workflow steps the user must redo. The same `{node_id, feature}` entries as `nodes_to_resubmit` in [Update Status](#patch-/v3/session/-sessionId-/update-status/)." + }, + "reasons": { + "type": "object", + "additionalProperties": true, + "description": "Reason per `node_id` in `nodes_to_resubmit`." + } + } + }, + "sandbox_scenario": { "type": "string", - "format": "date-time", - "description": "Timestamp of this response." + "nullable": true, + "description": "The simulated outcome armed on a sandbox session, or `null`. Always `null` for live sessions." + } + } + }, + "SessionStatusUpdatedEvent": { + "description": "A verification session changed status. `decision` is sent only for `Approved`, `Declined`, `In Review` and `Abandoned`; it has the shape of `GET /v3/session/{sessionId}/decision/` for destinations on webhook version 3 (the default), and the older reduced shape for version 1 or 2 destinations. Business sessions always receive the version 3 business decision.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" + }, + { + "$ref": "#/components/schemas/SessionWebhookEventData" + }, + { + "type": "object", + "properties": { + "webhook_type": { + "type": "string", + "enum": [ + "status.updated" + ], + "description": "Always `status.updated`." + } + }, + "required": [ + "webhook_type" + ] + } + ] + }, + "SessionDataUpdatedEvent": { + "description": "Data of a verification session changed without a status change, for example after a manual review edit, a step override or an ongoing-monitoring rescreen. `decision` is sent only for `Approved`, `Declined`, `In Review` and `Abandoned`; it has the shape of `GET /v3/session/{sessionId}/decision/` for destinations on webhook version 3 (the default), and the older reduced shape for version 1 or 2 destinations. Business sessions always receive the version 3 business decision.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" + }, + { + "$ref": "#/components/schemas/SessionWebhookEventData" + }, + { + "type": "object", + "properties": { + "webhook_type": { + "type": "string", + "enum": [ + "data.updated" + ], + "description": "Always `data.updated`." + } + }, + "required": [ + "webhook_type" + ] + } + ] + }, + "UserStatusUpdatedEvent": { + "description": "A user changed status. Not sent when the user is created.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" + }, + { + "type": "object", + "properties": { + "webhook_type": { + "type": "string", + "enum": [ + "user.status.updated" + ], + "description": "Always `user.status.updated`." + }, + "vendor_user_id": { + "type": "string", + "format": "uuid", + "description": "The user the event is about." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your `vendor_data` for the user, or `null`." + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "IN_REVIEW", + "FLAGGED", + "BLOCKED" + ], + "description": "User status after the change." + }, + "previous_status": { + "type": "string", + "enum": [ + "ACTIVE", + "IN_REVIEW", + "FLAGGED", + "BLOCKED" + ], + "description": "Status before the change." + } + }, + "required": [ + "webhook_type", + "vendor_user_id", + "vendor_data", + "status", + "previous_status" + ] + } + ] + }, + "UserDataUpdatedEvent": { + "description": "Fields of a user changed. Not sent when the user is created. One save can produce both a status and a data event.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" + }, + { + "type": "object", + "properties": { + "webhook_type": { + "type": "string", + "enum": [ + "user.data.updated" + ], + "description": "Always `user.data.updated`." + }, + "vendor_user_id": { + "type": "string", + "format": "uuid", + "description": "The user the event is about." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your `vendor_data` for the user, or `null`." + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "IN_REVIEW", + "FLAGGED", + "BLOCKED" + ], + "description": "User status after the change." + }, + "changed_fields": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "vendor_data", + "display_name", + "full_name", + "date_of_birth", + "metadata", + "portrait_image", + "issuing_states", + "approved_emails", + "approved_phones", + "features", + "session_count", + "approved_count", + "declined_count", + "in_review_count", + "first_session_at", + "last_session_at" + ] + }, + "description": "Names of the fields that changed." + }, + "changes": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "previous": { + "nullable": true, + "description": "Value before the change. Dates are ISO 8601 strings." + }, + "current": { + "nullable": true, + "description": "Value after the change. Dates are ISO 8601 strings." + } + } + }, + "description": "Previous and current value of each changed field, keyed by field name. Values can be personal data (names, dates of birth, portrait links, emails, phone numbers); handle them accordingly." + } + }, + "required": [ + "webhook_type", + "vendor_user_id", + "vendor_data", + "status", + "changed_fields", + "changes" + ] + } + ] + }, + "BusinessStatusUpdatedEvent": { + "description": "A business changed status. Not sent when the business is created.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" + }, + { + "type": "object", + "properties": { + "webhook_type": { + "type": "string", + "enum": [ + "business.status.updated" + ], + "description": "Always `business.status.updated`." + }, + "vendor_business_id": { + "type": "string", + "format": "uuid", + "description": "The business the event is about." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your `vendor_data` for the business, or `null`." + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "IN_REVIEW", + "FLAGGED", + "BLOCKED" + ], + "description": "Business status after the change." + }, + "previous_status": { + "type": "string", + "enum": [ + "ACTIVE", + "IN_REVIEW", + "FLAGGED", + "BLOCKED" + ], + "description": "Status before the change." + } + }, + "required": [ + "webhook_type", + "vendor_business_id", + "vendor_data", + "status", + "previous_status" + ] + } + ] + }, + "BusinessDataUpdatedEvent": { + "description": "Fields of a business changed. Not sent when the business is created. One save can produce both a status and a data event.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" + }, + { + "type": "object", + "properties": { + "webhook_type": { + "type": "string", + "enum": [ + "business.data.updated" + ], + "description": "Always `business.data.updated`." + }, + "vendor_business_id": { + "type": "string", + "format": "uuid", + "description": "The business the event is about." + }, + "vendor_data": { + "type": "string", + "nullable": true, + "description": "Your `vendor_data` for the business, or `null`." + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "IN_REVIEW", + "FLAGGED", + "BLOCKED" + ], + "description": "Business status after the change." + }, + "changed_fields": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "vendor_data", + "display_name", + "metadata", + "legal_name", + "registration_number", + "country_code", + "session_count", + "approved_count", + "declined_count", + "in_review_count", + "features", + "first_session_at", + "last_session_at" + ] + }, + "description": "Names of the fields that changed." + }, + "changes": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "previous": { + "nullable": true, + "description": "Value before the change. Dates are ISO 8601 strings." + }, + "current": { + "nullable": true, + "description": "Value after the change. Dates are ISO 8601 strings." + } + } + }, + "description": "Previous and current value of each changed field, keyed by field name." + } + }, + "required": [ + "webhook_type", + "vendor_business_id", + "vendor_data", + "status", + "changed_fields", + "changes" + ] + } + ] + }, + "TransactionCreatedEvent": { + "description": "A transaction was created and screened. Sent for live applications only.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" }, - "phone": { + { "type": "object", - "description": "Normalized phone report, the same block used by Phone Verification responses.", - "required": [ - "status" - ], "properties": { - "status": { + "webhook_type": { "type": "string", "enum": [ - "Approved", - "In Review", - "Declined" + "transaction.created" ], - "description": "Decision for this phone number. Mirrors the top-level `status`." + "description": "Always `transaction.created`." }, - "phone_number_prefix": { + "transaction_id": { "type": "string", - "description": "Country calling code with leading `+` (e.g., `+1`).", - "example": "+1" + "format": "uuid", + "description": "Didit's identifier of the transaction." }, - "phone_number": { + "txn_id": { "type": "string", - "description": "National number without the country calling code.", - "example": "4155552671" + "nullable": true, + "description": "The `transaction_id` you supplied at create time, or `null`." }, - "full_number": { + "status": { "type": "string", - "description": "Complete number in E.164 format.", - "example": "+14155552671" + "enum": [ + "APPROVED", + "IN_REVIEW", + "DECLINED", + "AWAITING_USER" + ], + "description": "Monitoring status after the change." }, - "country_code": { + "score": { + "type": "integer", + "description": "Risk score accumulated by rules." + }, + "severity": { "type": "string", "nullable": true, - "description": "ISO 3166-1 alpha-2 country of the number.", - "example": "US" + "enum": [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL", + null + ], + "description": "Risk severity, or `null` until a rule or provider sets one." }, - "country_name": { + "amount": { "type": "string", - "description": "Full country name derived from `country_code`.", - "example": "United States" + "description": "Transaction amount as a decimal string. Unlike the API it is not normalized, so it can carry trailing zeros (up to 18 decimal places)." }, - "carrier": { + "currency": { + "type": "string", + "description": "Currency code or crypto asset symbol of `amount`." + }, + "direction": { + "type": "string", + "enum": [ + "INBOUND", + "OUTBOUND" + ], + "description": "Direction relative to the subject." + }, + "action_required": { "type": "object", "nullable": true, - "description": "Current network serving the number. `null` when the lookup could not resolve a carrier.", - "properties": { - "name": { - "type": "string", - "description": "Carrier name. Empty string when the lookup could not identify it." + "oneOf": [ + { + "$ref": "#/components/schemas/TransactionVerificationSessionAction" }, - "type": { - "type": "string", - "description": "Line type reported by the lookup: `mobile`, `fixed_line`, `voip`, `isp`, `vpn`, `toll_free`, `premium_rate`, or similar." + { + "$ref": "#/components/schemas/TransactionWalletOwnershipAction" } - } - }, - "is_disposable": { - "type": "boolean", - "description": "`true` when the number belongs to a temporary or burner number service." + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "verification_session": "#/components/schemas/TransactionVerificationSessionAction", + "wallet_ownership": "#/components/schemas/TransactionWalletOwnershipAction" + } + }, + "description": "Pending end-user action, or `null`. Same object as `action_required` on the transaction; `session_token` is a credential, so never log it." + } + }, + "required": [ + "webhook_type", + "transaction_id", + "txn_id", + "status", + "score", + "severity", + "amount", + "currency", + "direction", + "action_required" + ] + } + ] + }, + "TransactionStatusUpdatedEvent": { + "description": "A transaction changed status, from a rule, a review or an end-user action. Sent for live applications only.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" + }, + { + "type": "object", + "properties": { + "webhook_type": { + "type": "string", + "enum": [ + "transaction.status.updated" + ], + "description": "Always `transaction.status.updated`." }, - "is_virtual": { - "type": "boolean", - "description": "`true` when the line type is virtual (`voip`, `isp`, or `vpn`)." + "transaction_id": { + "type": "string", + "format": "uuid", + "description": "Didit's identifier of the transaction." }, - "verification_method": { + "txn_id": { "type": "string", "nullable": true, - "description": "OTP channel of the underlying verification. `null` for risk lookups, which never send a code." + "description": "The `transaction_id` you supplied at create time, or `null`." }, - "verification_attempts": { + "status": { + "type": "string", + "enum": [ + "APPROVED", + "IN_REVIEW", + "DECLINED", + "AWAITING_USER" + ], + "description": "Monitoring status after the change." + }, + "score": { "type": "integer", - "description": "OTP sends for this number. `0` for risk lookups." + "description": "Risk score accumulated by rules." }, - "verified_at": { + "severity": { "type": "string", - "format": "date-time", "nullable": true, - "description": "When the number was last verified by an OTP. `null` for risk lookups." + "enum": [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL", + null + ], + "description": "Risk severity, or `null` until a rule or provider sets one." }, - "warnings": { - "type": "array", - "description": "Risk warnings recorded for this request. Empty array when there are none.", - "items": { - "$ref": "#/components/schemas/RiskWarning" - } + "amount": { + "type": "string", + "description": "Transaction amount as a decimal string. Unlike the API it is not normalized, so it can carry trailing zeros (up to 18 decimal places)." }, - "lifecycle": { - "type": "array", - "description": "Chronological audit trail of the request. Empty array for risk lookups without send events.", - "items": { - "$ref": "#/components/schemas/RiskLifecycleEvent" - } + "currency": { + "type": "string", + "description": "Currency code or crypto asset symbol of `amount`." }, - "matches": { - "type": "array", - "description": "Other sessions of your application where the same phone number was used by a different user (different `vendor_data`). Up to 5 entries. Empty array when there are no matches.", - "items": { - "$ref": "#/components/schemas/PhoneMatchItem" - } + "direction": { + "type": "string", + "enum": [ + "INBOUND", + "OUTBOUND" + ], + "description": "Direction relative to the subject." }, - "phone_intelligence": { - "$ref": "#/components/schemas/PhoneIntelligence" + "action_required": { + "type": "object", + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/TransactionVerificationSessionAction" + }, + { + "$ref": "#/components/schemas/TransactionWalletOwnershipAction" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "verification_session": "#/components/schemas/TransactionVerificationSessionAction", + "wallet_ownership": "#/components/schemas/TransactionWalletOwnershipAction" + } + }, + "description": "Pending end-user action, or `null`. Same object as `action_required` on the transaction; `session_token` is a credential, so never log it." + } + }, + "required": [ + "webhook_type", + "transaction_id", + "txn_id", + "status", + "score", + "severity", + "amount", + "currency", + "direction", + "action_required" + ] + } + ] + }, + "TravelRuleStatusUpdatedEvent": { + "description": "A Travel Rule exchange changed status. Sent on every transition up to and including `FINISHED`, `CANCELLED` or `EXPIRED`, never after. Sent for live applications only. There is no top-level `status`.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" + }, + { + "type": "object", + "properties": { + "webhook_type": { + "type": "string", + "enum": [ + "travel_rule.status.updated" + ], + "description": "Always `travel_rule.status.updated`." }, - "enrichment": { - "$ref": "#/components/schemas/PhoneIntelligence" + "transaction_id": { + "type": "string", + "format": "uuid", + "description": "The transaction the exchange belongs to." }, - "phone_social": { - "$ref": "#/components/schemas/PhoneSocialFootprint" + "txn_id": { + "type": "string", + "nullable": true, + "description": "The `transaction_id` you supplied at create time, or `null`." + }, + "travel_rule_status": { + "type": "string", + "enum": [ + "AWAITING_COUNTERPARTY", + "ON_HOLD", + "COMPLETED", + "FINISHED", + "CANCELLED", + "EXPIRED", + "COUNTERPARTY_MISMATCHED_DATA", + "COUNTERPARTY_VASP_NOT_FOUND", + "COUNTERPARTY_VASP_NOT_REACHABLE", + "UNCONFIRMED_OWNERSHIP", + "COUNTERPARTY_VASP_GENERAL_DECLINE", + "NOT_ENOUGH_COUNTERPARTY_DATA", + "NOT_APPLICABLE" + ], + "description": "Exchange status after the change." + }, + "rail": { + "type": "string", + "enum": [ + "", + "INTERNAL", + "TRP", + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA", + "EMAIL" + ], + "description": "Network the exchange runs on, or an empty string when none could be resolved." + }, + "direction": { + "type": "string", + "enum": [ + "INBOUND", + "OUTBOUND" + ], + "description": "Direction of the transfer." } - } + }, + "required": [ + "webhook_type", + "transaction_id", + "txn_id", + "travel_rule_status", + "rail", + "direction" + ] + } + ] + }, + "WorkflowComplianceChangedEvent": { + "description": "The compliance status of a published workflow changed after a re-evaluation. One event per workflow. There is no top-level `status`.", + "allOf": [ + { + "$ref": "#/components/schemas/WebhookEventEnvelope" + }, + { + "type": "object", + "properties": { + "webhook_type": { + "type": "string", + "enum": [ + "workflow.compliance_changed" + ], + "description": "Always `workflow.compliance_changed`." + }, + "workflow_id": { + "type": "string", + "format": "uuid", + "description": "The workflow that changed." + }, + "workflow_version": { + "type": "integer", + "description": "Version that was evaluated." + }, + "workflow_label": { + "type": "string", + "nullable": true, + "description": "Display name of the workflow." + }, + "transition": { + "type": "object", + "required": [ + "from", + "to" + ], + "properties": { + "from": { + "type": "string", + "enum": [ + "compliant", + "gaps_detected", + "pending_change", + "unknown_coverage", + "not_evaluated" + ], + "description": "Status before the re-evaluation." + }, + "to": { + "type": "string", + "enum": [ + "compliant", + "gaps_detected", + "pending_change", + "unknown_coverage", + "not_evaluated" + ], + "description": "Status after the re-evaluation." + } + } + }, + "severity": { + "type": "string", + "enum": [ + "actionable", + "indicative", + "informative" + ], + "description": "`actionable` when gaps were detected, `indicative` for a pending change, `informative` when gaps were resolved." + }, + "kb_version": { + "type": "string", + "description": "Version of the regulatory knowledge base used." + } + }, + "required": [ + "webhook_type", + "workflow_id", + "workflow_version", + "workflow_label", + "transition", + "severity", + "kb_version" + ] + } + ] + }, + "WebhookEventPayload": { + "description": "Body of a signed webhook delivery, discriminated by `webhook_type`. `activity.created` can be subscribed to but is not sent today. Treat any other `webhook_type` as a new event type and keep the raw body.", + "oneOf": [ + { + "$ref": "#/components/schemas/SessionStatusUpdatedEvent" + }, + { + "$ref": "#/components/schemas/SessionDataUpdatedEvent" + }, + { + "$ref": "#/components/schemas/UserStatusUpdatedEvent" + }, + { + "$ref": "#/components/schemas/UserDataUpdatedEvent" + }, + { + "$ref": "#/components/schemas/BusinessStatusUpdatedEvent" + }, + { + "$ref": "#/components/schemas/BusinessDataUpdatedEvent" + }, + { + "$ref": "#/components/schemas/TransactionCreatedEvent" + }, + { + "$ref": "#/components/schemas/TransactionStatusUpdatedEvent" + }, + { + "$ref": "#/components/schemas/TravelRuleStatusUpdatedEvent" + }, + { + "$ref": "#/components/schemas/WorkflowComplianceChangedEvent" + } + ], + "discriminator": { + "propertyName": "webhook_type", + "mapping": { + "status.updated": "#/components/schemas/SessionStatusUpdatedEvent", + "data.updated": "#/components/schemas/SessionDataUpdatedEvent", + "user.status.updated": "#/components/schemas/UserStatusUpdatedEvent", + "user.data.updated": "#/components/schemas/UserDataUpdatedEvent", + "business.status.updated": "#/components/schemas/BusinessStatusUpdatedEvent", + "business.data.updated": "#/components/schemas/BusinessDataUpdatedEvent", + "transaction.created": "#/components/schemas/TransactionCreatedEvent", + "transaction.status.updated": "#/components/schemas/TransactionStatusUpdatedEvent", + "travel_rule.status.updated": "#/components/schemas/TravelRuleStatusUpdatedEvent", + "workflow.compliance_changed": "#/components/schemas/WorkflowComplianceChangedEvent" } } }, - "EmailRiskResponse": { + "KYBRegistrySearchResolvedCallback": { "type": "object", + "description": "Body of the callback sent to the `webhook_url` of `POST /v3/kyb/search/` once the registry search resolves. It is not signed: the request carries `X-Didit-Unsigned-Callback: true` and none of the signature headers, so confirm it by matching `request_id` against a search you started. Uses `event_type`, not `webhook_type`.", "required": [ + "event_id", + "event_type", "request_id", - "status", + "vendor_data", + "metadata", + "search_status", + "search_resolved", + "kyb_registry", + "timestamp", "created_at" ], "properties": { - "request_id": { + "event_id": { "type": "string", "format": "uuid", - "description": "Id of the API session created for this request. Store it with `vendor_data` if you need to correlate the result with later webhooks or console activity." + "description": "Identifier of the callback." }, - "status": { + "event_type": { "type": "string", "enum": [ - "Approved", - "In Review", - "Declined" + "kyb.registry_search.resolved" ], - "description": "Final decision for this API session. Normally `Approved`; the risk evaluation can produce `In Review` or `Declined`." + "description": "Always `kyb.registry_search.resolved`." + }, + "request_id": { + "type": "string", + "format": "uuid", + "description": "The `request_id` returned by `POST /v3/kyb/search/`." }, "vendor_data": { "type": "string", "nullable": true, - "description": "Echo of the `vendor_data` you sent, `null` when you did not send one." + "description": "Echo of the `vendor_data` you sent." }, "metadata": { "type": "object", "nullable": true, "additionalProperties": true, - "description": "Echo of the `metadata` you sent, `null` when you did not send one." + "description": "Echo of the `metadata` you sent." + }, + "search_status": { + "type": "string", + "description": "Always `resolved` today." + }, + "search_resolved": { + "type": "boolean", + "description": "`true` when `search_status` is `resolved`." + }, + "kyb_registry": { + "$ref": "#/components/schemas/KYBRegistrySearchResults" + }, + "timestamp": { + "type": "integer", + "description": "Unix time in seconds at which the callback was queued." }, "created_at": { + "type": "integer", + "description": "Unix time in seconds at which the callback was queued." + } + } + }, + "AMLScreening": { + "type": "object", + "properties": { + "node_id": { "type": "string", - "format": "date-time", - "description": "Timestamp of this response." + "description": "Unique identifier for this AML step in the workflow graph." }, - "email": { - "type": "object", - "description": "Normalized email report, the same block used by Email Verification responses.", - "required": [ - "status", - "email", - "is_breached", - "breaches", - "is_disposable", - "is_undeliverable" - ], - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "In Review", - "Declined" - ], - "description": "Decision for this email address. Mirrors the top-level `status`." - }, - "email": { - "type": "string", - "description": "The scored address.", - "example": "alice@example.com" - }, - "is_breached": { - "type": "boolean", - "description": "Whether the address appears in a known credential breach." - }, - "breaches": { - "type": "array", - "description": "Up to 5 most recent known breaches containing this address. Empty array when `is_breached` is `false`.", - "items": { + "status": { + "type": "string", + "description": "Feature-level status of this AML screening. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ] + }, + "total_hits": { + "type": "integer", + "description": "Total number of matches found" + }, + "entity_type": { + "type": "string", + "description": "Type of entity screened: person or company" + }, + "hits": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier of the match" + }, + "url": { + "type": "string", + "format": "uri", + "description": "Source URL for the match" + }, + "match": { + "type": "boolean", + "description": "Whether this is a confirmed match" + }, + "score": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 1, + "description": "Deprecated weighted match score in the 0-1 range (e.g. `0.73`). Use `match_score` (0-100) instead.", + "deprecated": true + }, + "target": { + "type": "boolean", + "nullable": true, + "description": "Whether the matched entity is the direct target of the listing (as opposed to a related party). `null` when the source does not specify it." + }, + "caption": { + "type": "string", + "description": "Caption or title of the match" + }, + "datasets": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "PEP", + "PEP Level 1", + "PEP Level 2", + "PEP Level 3", + "PEP Level 4", + "Sanctions", + "Adverse Media", + "Warnings and Regulatory Enforcement", + "Fitness and Probity", + "SIP", + "SIE", + "Insolvency" + ] + }, + "description": "List of dataset categories where the match was found. Indicates whether the entity appeared in PEP lists (with level), sanctions databases, adverse media, warnings/regulatory enforcement, or other compliance datasets." + }, + "features": { + "type": "object", + "nullable": true, + "description": "Additional features of the match" + }, + "rca_name": { + "type": "string", + "description": "RCA name if applicable" + }, + "last_seen": { + "type": "string", + "format": "date-time", + "description": "Date when match was last seen" + }, + "risk_view": { + "type": "object", + "properties": { + "crimes": { + "type": "object", + "properties": { + "score": { + "type": "integer" + }, + "weightage": { + "type": "integer" + }, + "risk_level": { + "type": "string" + }, + "risk_scores": { + "type": "object" + } + } + }, + "countries": { + "type": "object", + "properties": { + "score": { + "type": "integer" + }, + "weightage": { + "type": "integer" + }, + "risk_level": { + "type": "string" + }, + "risk_scores": { + "type": "object" + } + } + }, + "categories": { + "type": "object", + "properties": { + "score": { + "type": "integer" + }, + "weightage": { + "type": "integer" + }, + "risk_level": { + "type": "string" + }, + "risk_scores": { + "type": "object" + } + } + }, + "custom_list": { + "type": "object" + } + } + }, + "first_seen": { + "type": "string", + "format": "date-time", + "description": "Date when match was first seen" + }, + "properties": { "type": "object", "properties": { "name": { + "type": "array", + "items": { + "type": "string" + } + }, + "alias": { + "type": "array", + "items": { + "type": "string" + } + }, + "notes": { + "type": "array", + "items": { + "type": "string" + } + }, + "title": { + "type": "string", + "nullable": true + }, + "gender": { + "type": "array", + "items": { + "type": "string" + } + }, + "height": { + "type": "string", + "nullable": true + }, + "topics": { + "type": "string", + "nullable": true + }, + "weight": { + "type": "string", + "nullable": true + }, + "address": { + "type": "string", + "nullable": true + }, + "country": { + "type": "string", + "nullable": true + }, + "website": { + "type": "string", + "nullable": true + }, + "eyeColor": { + "type": "string", + "nullable": true + }, + "keywords": { + "type": "string", + "nullable": true + }, + "lastName": { + "type": "array", + "items": { + "type": "string" + } + }, + "position": { + "type": "string", + "nullable": true + }, + "religion": { + "type": "string", + "nullable": true + }, + "birthDate": { + "type": "array", + "items": { + "type": "string" + } + }, + "education": { + "type": "array", + "items": { + "type": "string" + } + }, + "ethnicity": { + "type": "string", + "nullable": true + }, + "firstName": { + "type": "array", + "items": { + "type": "string" + } + }, + "hairColor": { "type": "string", - "description": "Breach identifier." + "nullable": true }, - "domain": { + "weakAlias": { "type": "string", - "description": "Domain of the breached service." + "nullable": true }, - "breach_date": { + "birthPlace": { + "type": "array", + "items": { + "type": "string" + } + }, + "modifiedAt": { "type": "string", - "format": "date", - "description": "Date the breach occurred." + "nullable": true }, - "breach_emails_count": { + "wikidataId": { + "type": "string", + "nullable": true + }, + "citizenship": { + "type": "string", + "nullable": true + }, + "nationality": { + "type": "string", + "nullable": true + } + } + }, + "match_score": { + "type": "number", + "description": "Weighted identity confidence score (0-100) calculated from name similarity, date of birth, and country matching. Used to determine if hit is a False Positive or Possible Match.", + "format": "float", + "nullable": true + }, + "risk_score": { + "type": "number", + "description": "Entity risk score (0-100). Used to determine final AML status (Approved/In Review/Declined) based on the highest risk_score among non-False-Positive hits." + }, + "review_status": { + "type": "string", + "description": "Review status for this hit. Can be 'Unreviewed', 'Confirmed Match', 'False Positive', or 'Inconclusive'.", + "enum": [ + "Unreviewed", + "Confirmed Match", + "False Positive", + "Inconclusive" + ] + }, + "score_breakdown": { + "type": "object", + "description": "Detailed breakdown of how the match_score was calculated.", + "properties": { + "name_score": { "type": "integer", - "description": "Total number of accounts exposed in the breach." + "description": "Raw name similarity percentage (0-100)." }, - "description": { + "name_weight": { + "type": "integer", + "description": "Original weight for name from config (0-100)." + }, + "name_weight_normalized": { + "type": "number", + "description": "Normalized weight for name after re-weighting for missing data (0-100)." + }, + "name_contribution": { + "type": "number", + "description": "Points contributed by name to the total score." + }, + "dob_score": { + "type": "integer", + "description": "DOB match score (-100 to 100)." + }, + "dob_weight": { + "type": "integer", + "description": "Original weight for DOB from config (0-100)." + }, + "dob_weight_normalized": { + "type": "number", + "description": "Normalized weight for DOB after re-weighting for missing data (0-100)." + }, + "dob_contribution": { + "type": "number", + "description": "Points contributed by DOB to the total score." + }, + "country_score": { + "type": "integer", + "description": "Country match score (-100 to 100)." + }, + "country_weight": { + "type": "integer", + "description": "Original weight for country from config (0-100)." + }, + "country_weight_normalized": { + "type": "number", + "description": "Normalized weight for country after re-weighting for missing data (0-100)." + }, + "country_contribution": { + "type": "number", + "description": "Points contributed by country to the total score." + }, + "document_number_match_type": { "type": "string", - "description": "Human-readable description of the breach (may contain HTML markup)." + "description": "Result of document number 'Golden Key' evaluation.", + "enum": [ + "MATCH", + "NEUTRAL", + "HARD_MISMATCH" + ] }, - "logo_path": { + "document_number_effect": { "type": "string", - "description": "URL of the breached service's logo." + "description": "Human-readable description of the document number effect on the score." }, - "data_classes": { - "type": "array", - "items": { + "total_score": { + "type": "integer", + "description": "Final weighted match score (0-100)." + } + } + }, + "pep_matches": { + "type": "array", + "items": { + "type": "object", + "properties": { + "aliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "education": { + "type": "array", + "items": { + "type": "string" + } + }, + "list_name": { "type": "string" }, - "description": "Categories of data exposed, in snake_case (e.g., `email_addresses`, `passwords`)." + "publisher": { + "type": "string" + }, + "source_url": { + "type": "string", + "format": "uri" + }, + "description": { + "type": "string" + }, + "matched_name": { + "type": "string" + }, + "pep_position": { + "type": "string" + }, + "date_of_birth": { + "type": "string" + }, + "other_sources": { + "type": "array", + "items": { + "type": "string" + } + }, + "place_of_birth": { + "type": "string" + } + } + } + }, + "linked_entities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "array", + "items": { + "type": "string" + } + }, + "active": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "array", + "items": { + "type": "string" + } + }, + "details": { + "type": "array", + "items": { + "type": "string" + } + }, + "relation": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "warning_matches": { + "type": "array", + "description": "List of warning and regulatory enforcement matches. Includes entries from categories: Warning, SIP, Warnings and Regulatory Enforcement, Fitness and Probity, SIE, and Insolvency.", + "items": { + "type": "object", + "properties": { + "list_name": { + "type": "string", + "description": "Name of the warning list publisher" + }, + "matched_name": { + "type": "string", + "description": "Name matched in the warning list" + }, + "description": { + "type": "string", + "description": "Description of the warning entry" + }, + "publisher": { + "type": "string", + "description": "Publisher or source of the warning" + }, + "source_url": { + "type": "string", + "format": "uri", + "description": "URL to the source of the warning" + }, + "other_sources": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Additional source URLs" + }, + "countries": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "description": "Countries associated with the warning" + }, + "created_at": { + "type": "string", + "nullable": true, + "description": "Date the warning entry was created" + }, + "updated_at": { + "type": "string", + "nullable": true, + "description": "Date the warning entry was last updated" + }, + "additional_data": { + "type": "object", + "description": "Additional data from the warning source. May contain a `data` object with `case_details` array.", + "properties": { + "data": { + "type": "object", + "properties": { + "case_details": { + "type": "array", + "description": "Details of warning cases associated with this match", + "items": { + "type": "object", + "properties": { + "case_type": { + "type": "string", + "description": "Type of the warning case", + "enum": [ + "Accusation", + "Arrest", + "Bribery", + "Criminal", + "Fraud", + "Illegal Fundraising", + "Non-conviction Terror", + "Proclaimed Offender", + "Regulatory Enforcement", + "Terrorism", + "Terrorism-related", + "Terrorism-related listing" + ] + } + } + } + } + } + } + } + } + } + } + }, + "sanction_matches": { + "type": "array", + "description": "List of sanction list matches found for this entity.", + "items": { + "type": "object", + "properties": { + "list_name": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "description": "Name(s) of the sanction list(s)" + }, + "matched_name": { + "type": "string", + "description": "Name matched in the sanction list" + }, + "description": { + "type": "string", + "description": "Description of the sanction entry" + }, + "reason": { + "type": "string", + "nullable": true, + "description": "Free-text reason why the entity was sanctioned. Joined from the sanction list's reason entries. Examples: 'Executive Order 13224 (Terrorism)', 'Support for actions undermining Ukraine's sovereignty', 'Listed due to national security concerns.'" + }, + "source_url": { + "type": "string", + "format": "uri", + "description": "URL to the source of the sanction" + }, + "other_sources": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Additional source URLs" + }, + "details": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Additional details about the sanction" + }, + "legal_basis": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Legal basis for the sanction" + }, + "listed_on": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "description": "Date(s) when the entity was listed" + }, + "sanction_list": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "description": "Sanction list identifier(s)" + }, + "sanction_program": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Sanction program(s) the entity is listed under" + }, + "sanctioning_authority": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Authority/authorities that imposed the sanction" + }, + "updated_on": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Date(s) when the sanction entry was last updated" + }, + "additional_data": { + "type": "object", + "description": "Additional data from the sanction source not captured in the main fields" + } + } + } + }, + "adverse_media_details": { + "type": "object", + "properties": { + "sentiment": { + "type": "string" }, - "is_verified": { - "type": "boolean", - "description": "Whether the breach has been verified as legitimate." + "entity_type": { + "type": "string" + }, + "sentiment_score": { + "type": "integer" + }, + "adverse_keywords": { + "type": "object", + "additionalProperties": { + "type": "integer" + } } } + }, + "adverse_media_matches": { + "type": "array", + "items": { + "type": "object", + "properties": { + "country": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "headline": { + "type": "string" + }, + "sentiment": { + "type": "string" + }, + "thumbnail": { + "type": "string", + "format": "uri" + }, + "source_url": { + "type": "string", + "format": "uri" + }, + "author_name": { + "type": "string", + "nullable": true + }, + "other_sources": { + "type": "array", + "items": { + "type": "string" + } + }, + "adverse_keywords": { + "type": "array", + "items": { + "type": "string" + } + }, + "sentiment_score": { + "type": "integer" + }, + "publication_date": { + "type": "string", + "format": "date-time" + } + } + } + }, + "additional_information": { + "type": "object" } - }, - "is_disposable": { - "type": "boolean", - "description": "Whether the domain belongs to a throwaway provider." - }, - "is_undeliverable": { - "type": "boolean", - "description": "Whether the address was found undeliverable." - }, - "verification_attempts": { - "type": "integer", - "description": "OTP sends for this address. `0` for risk lookups." - }, - "verified_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "description": "When the address was last verified by an OTP. `null` for risk lookups." - }, - "warnings": { - "type": "array", - "description": "Risk warnings recorded for this request. Empty array when there are none.", - "items": { - "$ref": "#/components/schemas/RiskWarning" - } - }, - "lifecycle": { - "type": "array", - "description": "Chronological audit trail of the request. Empty array for risk lookups without sends.", - "items": { - "$ref": "#/components/schemas/RiskLifecycleEvent" - } - }, - "matches": { - "type": "array", - "description": "Other sessions of your application where the same email address was used by a different user (different `vendor_data`). Up to 5 entries. Empty array when there are no matches.", - "items": { - "$ref": "#/components/schemas/EmailMatchItem" - } - }, - "email_intelligence": { - "$ref": "#/components/schemas/EmailIntelligence" - }, - "enrichment": { - "$ref": "#/components/schemas/EmailIntelligence" - }, - "email_social": { - "$ref": "#/components/schemas/EmailSocialFootprint" } } - } - } - }, - "IPRiskResponse": { - "type": "object", - "required": [ - "request_id", - "status", - "created_at" - ], - "properties": { - "request_id": { - "type": "string", - "format": "uuid", - "description": "Id of the API session created for this request. Store it with `vendor_data` if you need to correlate the result with later webhooks or console activity." - }, - "status": { - "type": "string", - "enum": [ - "Approved", - "In Review" - ], - "description": "Decision for this API session: `In Review` on network or country-mismatch risk, otherwise `Approved`." - }, - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Echo of the `vendor_data` you sent, `null` when you did not send one." }, - "metadata": { - "type": "object", + "score": { + "type": "number", + "format": "float", "nullable": true, - "additionalProperties": true, - "description": "Echo of the `metadata` you sent, `null` when you did not send one." - }, - "created_at": { - "type": "string", - "format": "date-time", - "description": "Timestamp of this response." - }, - "ip": { - "type": "object", - "description": "IP risk result. Individual enrichment fields can be `null` when the lookup returned no usable data.", - "required": [ - "status", - "threat" - ], - "properties": { - "status": { - "type": "string", - "enum": [ - "Approved", - "In Review" - ], - "description": "`In Review` when the resolved country conflicts with `claimed_country`, or when the address is associated with VPN, Tor, or data-center infrastructure. Otherwise `Approved`." - }, - "ip_address": { - "type": "string", - "nullable": true, - "description": "The scored address. `null` when no enrichment source returned usable data." - }, - "ip_country": { - "type": "string", - "nullable": true, - "description": "Resolved country name." - }, - "ip_country_code": { - "type": "string", - "nullable": true, - "description": "Resolved country code. Compare countries through `country_mismatch` instead of comparing raw country strings yourself." - }, - "ip_state": { - "type": "string", - "nullable": true, - "description": "Resolved state or region." - }, - "ip_city": { - "type": "string", - "nullable": true, - "description": "Resolved city." - }, - "latitude": { - "type": "number", - "nullable": true, - "description": "Approximate network geolocation, not a precise device location." - }, - "longitude": { - "type": "number", - "nullable": true, - "description": "Approximate network geolocation, not a precise device location." - }, - "isp": { - "type": "string", - "nullable": true, - "description": "Internet service provider serving the address." - }, - "organization": { - "type": "string", - "nullable": true, - "description": "Organization owning the address block." - }, - "is_vpn_or_tor": { - "type": "boolean", - "nullable": true, - "description": "`true` when the address is associated with VPN or Tor infrastructure." - }, - "is_data_center": { - "type": "boolean", - "nullable": true, - "description": "`true` when the address belongs to a hosting or data-center network." - }, - "time_zone": { - "type": "string", - "nullable": true, - "description": "Time zone of the resolved location." - }, - "time_zone_offset": { - "type": "string", - "nullable": true, - "description": "UTC offset of the resolved location." - }, - "asn_number": { - "type": "number", - "nullable": true, - "description": "Autonomous system number." - }, - "asn_organization": { - "type": "string", - "nullable": true, - "description": "Organization operating the autonomous system." - }, - "connection_type": { - "type": "string", - "nullable": true, - "description": "Connection classification (e.g., `cellular`, `hosting`)." + "minimum": 0, + "maximum": 100, + "description": "Overall AML screening score — the highest risk score across all hits (float, 0-100). `null` until computed." + }, + "screened_data": { + "type": "object", + "properties": { + "full_name": { + "type": "string" }, - "carrier": { - "type": "string", - "nullable": true, - "description": "Mobile carrier when the address is on a mobile network." + "nationality": { + "type": "string" }, - "proxy_type": { + "date_of_birth": { "type": "string", - "nullable": true, - "description": "Proxy classification when the address is behind a proxy." + "format": "date" }, - "claimed_country": { + "document_number": { "type": "string", - "nullable": true, - "description": "Echo of the `claimed_country` you sent." - }, - "country_mismatch": { - "type": "boolean", - "nullable": true, - "description": "`true` when the resolved country differs from `claimed_country`. `null` when no enrichment data or no claimed country is available." - }, - "threat": { - "type": "object", - "additionalProperties": true, - "description": "Free-form network-threat evidence from the enrichment source (e.g., `is_proxy`, `is_tor`). Empty object when no enrichment data is available. Treat as diagnostic detail, not a stable contract." + "nullable": true } } - } - } - }, - "SocialFootprintPlatform": { - "type": "object", - "properties": { - "platform": { - "type": "string", - "description": "Platform identifier.", - "example": "whatsapp" }, - "category": { + "is_ongoing_monitoring_enabled": { + "type": "boolean", + "description": "Whether AML ongoing monitoring is enabled for this screening. When `true`, the entity is re-screened periodically and any new hits update the report." + }, + "next_ongoing_monitoring_bill_date": { "type": "string", - "description": "Platform category, e.g. MESSAGING, SOCIAL_MEDIA, ECOMMERCE, PROFESSIONAL, EMAIL_PROVIDER.", - "example": "MESSAGING" + "format": "date", + "nullable": true, + "description": "Date (YYYY-MM-DD) when this ongoing-monitoring subscription will next be billed. Computed as the date of the last ongoing-monitoring billing plus 365 days when `is_ongoing_monitoring_enabled = true`; otherwise `null`. This is a billing date, not a screening date - use `last_ongoing_screening_at` for that." }, - "scope": { + "last_ongoing_screening_at": { "type": "string", - "enum": [ - "global", - "local" - ], - "description": "`global` for worldwide platforms, `local` for region-specific ones.", - "example": "global" - } - } - }, - "SocialFootprintCategorySummary": { - "type": "object", - "properties": { - "registered": { - "type": "integer", + "format": "date-time", "nullable": true, - "description": "Platforms in this category where registration is confirmed.", - "example": 2 + "description": "When ongoing monitoring last completed a re-screening of this record, whether or not the result changed. `null` when the record has never been re-screened; it is never back-filled from the enablement or billing date." }, - "ratio": { - "type": "number", - "format": "float", + "last_ongoing_screening_datasets_updated_at": { + "type": "string", + "format": "date-time", "nullable": true, - "description": "Share of the category's checked platforms with a registration, 0.0-1.0.", - "example": 0.6667 + "description": "The most recent AML dataset update date the provider reported at the time of that screening. `null` when the provider reported no dataset dates; never estimated." + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "feature": { + "type": "string", + "enum": [ + "ID_VERIFICATION", + "NFC", + "LIVENESS", + "FACEMATCH", + "PROOF_OF_ADDRESS", + "PHONE", + "EMAIL", + "AML", + "LOCATION", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "DOCUMENT_AI", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE" + ], + "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Within an AML screening this is typically `AML`, but the enum above is authoritative across all feature warnings on the decision payload." + }, + "risk": { + "type": "string" + }, + "additional_data": { + "type": "object", + "nullable": true + }, + "log_type": { + "type": "string" + }, + "short_description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "node_id": { + "type": "string", + "description": "The workflow graph node ID that generated this warning" + } + } + } } } }, - "SocialFootprintProfile": { + "MonitoringEvent": { "type": "object", + "description": "One stored monitoring event as returned by the list and subject-timeline endpoints. Restricted evidence (payload, device context, rule evidence) is only returned by `GET /v3/events/{event_uuid}/`.", + "required": [ + "event_uuid", + "event_id", + "event_category", + "action_type", + "occurred_at", + "received_at", + "status", + "score", + "subject_vendor_data", + "subject_entity_type", + "subject_uuid", + "counterparty_vendor_data", + "session_id", + "device_fingerprint", + "ip_address", + "ip_country", + "amount", + "currency", + "game_id", + "round_id", + "bonus_id", + "affiliate_code", + "payment_reference_id", + "retention_class", + "rules_matched_count", + "correlation_pending", + "is_droppable" + ], "properties": { - "name": { - "type": "string", - "nullable": true, - "description": "Profile name, when the platform exposes one." - }, - "photo": { + "event_uuid": { "type": "string", - "nullable": true, - "description": "Profile photo URL, when the platform exposes one." + "format": "uuid", + "description": "Didit identifier of the event. Use it as `{event_uuid}` in the retrieve and delete calls." }, - "about": { + "event_id": { "type": "string", - "nullable": true, - "description": "Profile about/bio text, when the platform exposes one." + "maxLength": 128, + "description": "Your own identifier, the idempotency key you sent." }, - "privacy_status": { + "event_category": { "type": "string", - "nullable": true, "enum": [ - "PUBLIC", - "PRIVATE", - null + "auth", + "session", + "gameplay", + "bonus", + "affiliate", + "payment", + "dispute", + "security", + "responsible_gambling", + "custom" ], - "description": "Profile visibility, when the platform exposes it." - }, - "business_account": { - "type": "boolean", - "nullable": true, - "description": "Whether the profile is a business account, when the platform exposes it." - } - } - }, - "PhoneSocialFootprint": { - "type": "object", - "nullable": true, - "description": "Social footprint of the phone number: the online platforms it is registered on. Present only when `enable_social` was set and the check completed. Every count may be null when no data was returned for it. `profiles_registered: 0` means the platforms were checked and the phone number was found on none of them - a valid, billable answer and a common synthetic-identity signal.", - "properties": { - "profiles_registered": { - "type": "integer", - "nullable": true, - "description": "Platforms where the phone number is confirmed registered.", - "example": 5 - }, - "profiles_checked": { - "type": "integer", - "nullable": true, - "description": "Platforms evaluated for registration.", - "example": 14 - }, - "names_found": { - "type": "integer", - "nullable": true, - "description": "Platforms that returned an associated name.", - "example": 0 - }, - "photos_found": { - "type": "integer", - "nullable": true, - "description": "Platforms that returned a profile photo.", - "example": 0 - }, - "communication_breadth": { - "type": "integer", - "nullable": true, - "description": "Communication platforms with detected presence.", - "example": 2 + "description": "Event category." }, - "digital_footprint_diversity": { - "type": "integer", - "nullable": true, - "description": "Distinct platform categories with detected presence.", - "example": 5 - }, - "digital_sophistication_index": { - "type": "integer", - "nullable": true, - "description": "Composite digital-maturity indicator, 0-10.", - "example": 5 - }, - "engagement_depth_index": { - "type": "integer", - "nullable": true, - "description": "Profile-richness indicator, 0-10.", - "example": 3 - }, - "subscription_affordability_index": { - "type": "integer", - "nullable": true, - "description": "Free vs paid platform mix, 0-10.", - "example": 2 - }, - "free_platforms": { - "type": "integer", - "nullable": true, - "description": "Registrations on free platforms.", - "example": 4 + "action_type": { + "type": "string", + "maxLength": 128, + "description": "What happened within the category, as you sent it." }, - "full_paid_platforms": { - "type": "integer", - "nullable": true, - "description": "Registrations on fully paid platforms.", - "example": 0 + "occurred_at": { + "type": "string", + "format": "date-time", + "description": "When the event happened on your side." }, - "partial_paid_platforms": { - "type": "integer", - "nullable": true, - "description": "Registrations on freemium platforms.", - "example": 1 + "received_at": { + "type": "string", + "format": "date-time", + "description": "When Didit accepted the event." }, - "whatsapp_profile_completeness": { + "status": { "type": "string", - "nullable": true, "enum": [ - "Basic", - "Partial", - "Complete", - "Not Present", - null + "ACCEPTED", + "APPROVED", + "IN_REVIEW", + "DECLINED" ], - "description": "Completeness of the WhatsApp profile. Phone only; null for email.", - "example": "Partial" + "description": "`ACCEPTED`: stored, no matched rule changed its standing (the normal answer for most events, and always the answer in a sandbox application, where no rules run). `APPROVED`: a rule matched and the score stayed under your review threshold. `IN_REVIEW`: a rule pushed the score over the review threshold or set the status. `DECLINED`: a rule declined it or the score crossed your decline threshold." }, - "registered_platforms": { - "type": "array", - "description": "Platforms where registration is confirmed.", - "items": { - "$ref": "#/components/schemas/SocialFootprintPlatform" - } + "score": { + "type": "integer", + "description": "Accumulated rule score. Starts at 0; rule `add_score` actions move it (negative deltas are allowed)." }, - "not_registered_platforms": { - "type": "array", - "description": "Platforms checked with a negative result.", - "items": { - "$ref": "#/components/schemas/SocialFootprintPlatform" - } + "subject_vendor_data": { + "type": "string", + "maxLength": 255, + "description": "`subject.vendor_data` of the event: your id for the user or company." }, - "categories": { - "type": "object", - "description": "Registered count and ratio per platform category.", - "additionalProperties": { - "$ref": "#/components/schemas/SocialFootprintCategorySummary" - } + "subject_entity_type": { + "type": "string", + "description": "`individual` or `company`: which kind of subject `subject_vendor_data` names.", + "example": "individual" }, - "profiles": { - "type": "object", - "description": "Per-platform profile details, keyed by platform, when the platform exposes them.", - "additionalProperties": { - "$ref": "#/components/schemas/SocialFootprintProfile" - } - } - } - }, - "EmailSocialFootprint": { - "type": "object", - "nullable": true, - "description": "Social footprint of the email address: the online platforms it is registered on. Present only when `enable_social` was set and the check completed. Every count may be null when no data was returned for it. `profiles_registered: 0` means the platforms were checked and the email address was found on none of them - a valid, billable answer and a common synthetic-identity signal.", - "properties": { - "profiles_registered": { - "type": "integer", + "subject_uuid": { + "type": "string", + "format": "uuid", "nullable": true, - "description": "Platforms where the email address is confirmed registered.", - "example": 5 + "description": "UUID of the user (individual) or company the event resolved to, or null when that record no longer exists." }, - "profiles_checked": { - "type": "integer", + "counterparty_vendor_data": { + "type": "string", "nullable": true, - "description": "Platforms evaluated for registration.", - "example": 14 + "description": "The other player of a P2P event (promoted from `payload.counterparty_vendor_data`).", + "maxLength": 255 }, - "names_found": { - "type": "integer", + "session_id": { + "type": "string", "nullable": true, - "description": "Platforms that returned an associated name.", - "example": 0 + "description": "`session.session_id` sent with the event.", + "maxLength": 255 }, - "photos_found": { - "type": "integer", + "device_fingerprint": { + "type": "string", "nullable": true, - "description": "Platforms that returned a profile photo.", - "example": 0 + "description": "`device_context.fingerprint` sent with the event.", + "maxLength": 255 }, - "communication_breadth": { - "type": "integer", + "ip_address": { + "type": "string", "nullable": true, - "description": "Communication platforms with detected presence.", - "example": 2 + "description": "IP address of the event. On the server lane this is `device_context.network_context.ip_address` as you sent it; on the device lane it is the IP of the request.", + "maxLength": 64 }, - "digital_footprint_diversity": { - "type": "integer", + "ip_country": { + "type": "string", "nullable": true, - "description": "Distinct platform categories with detected presence.", - "example": 5 + "description": "Country code of `ip_address` from IP enrichment, or null when the address was not enriched (missing, private, or low-value telemetry whose address was not already known).", + "example": "ES" }, - "digital_sophistication_index": { - "type": "integer", + "amount": { + "type": "string", "nullable": true, - "description": "Composite digital-maturity indicator, 0-10.", - "example": 5 + "description": "Amount as a decimal string with 18 decimal places, from `transaction_context.amount`, `payload.amount` or `payload.stake_amount`, in that order.", + "example": "250.000000000000000000" }, - "engagement_depth_index": { - "type": "integer", + "currency": { + "type": "string", "nullable": true, - "description": "Profile-richness indicator, 0-10.", - "example": 3 + "description": "Currency from `transaction_context.currency` or `payload.currency`.", + "maxLength": 10 }, - "subscription_affordability_index": { - "type": "integer", + "game_id": { + "type": "string", "nullable": true, - "description": "Free vs paid platform mix, 0-10.", - "example": 2 + "description": "Promoted from `payload.game_id`.", + "maxLength": 128 }, - "free_platforms": { - "type": "integer", + "round_id": { + "type": "string", "nullable": true, - "description": "Registrations on free platforms.", - "example": 4 + "description": "Promoted from `payload.round_id`.", + "maxLength": 128 }, - "full_paid_platforms": { - "type": "integer", + "bonus_id": { + "type": "string", "nullable": true, - "description": "Registrations on fully paid platforms.", - "example": 0 + "description": "Promoted from `payload.bonus_id`.", + "maxLength": 128 }, - "partial_paid_platforms": { - "type": "integer", + "affiliate_code": { + "type": "string", "nullable": true, - "description": "Registrations on freemium platforms.", - "example": 1 + "description": "Promoted from `payload.affiliate_code`.", + "maxLength": 128 }, - "whatsapp_profile_completeness": { + "payment_reference_id": { "type": "string", "nullable": true, + "description": "Payment reference from `transaction_context.payment_reference_id` or `payload.payment_reference_id`.", + "maxLength": 255 + }, + "retention_class": { + "type": "string", "enum": [ - "Basic", - "Partial", - "Complete", - "Not Present", - null + "low_value", + "standard", + "risk_relevant", + "monetary_or_compliance" ], - "description": "Completeness of the WhatsApp profile. Phone only; null for email.", - "example": "Partial" + "description": "Retention class. Payment and dispute events are `monetary_or_compliance`; security and responsible-gambling events are `risk_relevant`; `session.page_view`, `session.sdk_heartbeat`, `session.sdk_loaded`, `session.device_seen`, `session.bot_signal_observed`, `bonus.promotion_viewed` and `affiliate.referral_clicked` are `low_value`; everything else is `standard`. An event an active rule matched is promoted to `risk_relevant` (never demoted)." }, - "registered_platforms": { - "type": "array", - "description": "Platforms where registration is confirmed.", - "items": { - "$ref": "#/components/schemas/SocialFootprintPlatform" - } + "rules_matched_count": { + "type": "integer", + "minimum": 0, + "description": "Number of rules that matched the event, including rules in `TEST` mode (which record evidence but apply no actions)." }, - "not_registered_platforms": { - "type": "array", - "description": "Platforms checked with a negative result.", - "items": { - "$ref": "#/components/schemas/SocialFootprintPlatform" - } + "correlation_pending": { + "type": "boolean", + "description": "`true` while an asynchronous rule (cross-subject correlation, or a sequence longer than 24 hours) still has to run for this event. It resolves within minutes and `status`, `score` and `rules_matched_count` update in place." }, - "categories": { - "type": "object", - "description": "Registered count and ratio per platform category.", - "additionalProperties": { - "$ref": "#/components/schemas/SocialFootprintCategorySummary" - } + "is_droppable": { + "type": "boolean", + "description": "Whether an SDK under backpressure may drop events of this kind. `false` for every `security`, `payment`, `dispute` and `responsible_gambling` event and for `gameplay.bet_placed`, `gameplay.bet_settled` and `gameplay.p2p_settlement`." + } + } + }, + "MonitoringEventDetail": { + "description": "One monitoring event with its full envelope, enrichment and rule evidence. Every field of `MonitoringEvent` plus the fields below.", + "allOf": [ + { + "$ref": "#/components/schemas/MonitoringEvent" }, - "profiles": { + { "type": "object", - "description": "Per-platform profile details, keyed by platform, when the platform exposes them.", - "additionalProperties": { - "$ref": "#/components/schemas/SocialFootprintProfile" + "required": [ + "payload", + "custom_values", + "device_context", + "field_sources", + "session_sequence", + "table_id", + "hand_id", + "match_id", + "market_id", + "side", + "role", + "net_result_amount", + "payment_fingerprint", + "instrument_reference_hash", + "withdrawal_address_hash", + "dispute_id", + "chargeback_id", + "changed_field", + "identity_match_ref", + "schema_version", + "expires_at", + "rules_evaluated_count", + "rule_runs", + "ip_enrichment", + "subject_profile", + "tags" + ], + "properties": { + "payload": { + "type": "object", + "additionalProperties": true, + "description": "The category fields you sent in `payload`." + }, + "custom_values": { + "type": "object", + "additionalProperties": true, + "description": "The scalar `custom_values` you sent. Didit adds `vpn_or_proxy_detected: true` or `is_data_center: true` when IP enrichment flags the address and you did not set them." + }, + "device_context": { + "type": "object", + "additionalProperties": true, + "description": "The device context as stored: your `device_context` (including `fingerprint`, `persistent_device_id` and `bot_score` when sent) with `network_context` completed by IP enrichment (`ip_country`, `ip_country_code`, `ip_city`, `asn_number`, `asn_organization`, `is_vpn_or_tor`, `is_data_center`, `proxy_type`, `connection_type`) for keys you left empty." + }, + "field_sources": { + "type": "object", + "additionalProperties": { + "type": "string", + "enum": [ + "customer", + "sdk", + "server" + ] + }, + "description": "Field path to provenance: `customer` (sent by your backend), `sdk` (sent through the device lane) or `server` (derived by Didit, such as `received_at` and enriched network fields). Check it before acting on a match." + }, + "session_sequence": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "`session.sequence` sent with the event." + }, + "table_id": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.table_id`.", + "maxLength": 128 + }, + "hand_id": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.hand_id`.", + "maxLength": 128 + }, + "match_id": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.match_id`.", + "maxLength": 128 + }, + "market_id": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.market_id`.", + "maxLength": 128 + }, + "side": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.side`.", + "maxLength": 64 + }, + "role": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.role`.", + "maxLength": 64 + }, + "net_result_amount": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.net_result_amount`, as a decimal string with 18 decimal places.", + "example": "-120.000000000000000000" + }, + "payment_fingerprint": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.payment_fingerprint`.", + "maxLength": 255 + }, + "instrument_reference_hash": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.instrument_reference_hash`.", + "maxLength": 255 + }, + "withdrawal_address_hash": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.withdrawal_address_hash` (or `payload.destination_reference_hash`).", + "maxLength": 255 + }, + "dispute_id": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.dispute_id`.", + "maxLength": 255 + }, + "chargeback_id": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.chargeback_id`.", + "maxLength": 255 + }, + "changed_field": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.changed_field` (security change events).", + "maxLength": 128 + }, + "identity_match_ref": { + "type": "string", + "nullable": true, + "description": "Promoted from `payload.identity_match_ref` (responsible-gambling registry matches).", + "maxLength": 255 + }, + "schema_version": { + "type": "integer", + "description": "Version of the event envelope schema. Currently `1`.", + "example": 1 + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the event expires under its retention class and is deleted: `occurred_at` plus the class period (14, 30 or 90 days by default, adjustable per application). Null for `monetary_or_compliance` events, which follow transaction retention." + }, + "rules_evaluated_count": { + "type": "integer", + "minimum": 0, + "description": "Number of event rules evaluated for this event." + }, + "rule_runs": { + "type": "array", + "description": "Every rule that matched the event, newest first.", + "items": { + "$ref": "#/components/schemas/MonitoringEventRuleRun" + } + }, + "ip_enrichment": { + "type": "object", + "nullable": true, + "description": "Enrichment of the event's IP address, or null when the address was not enriched. See [IP enrichment](https://docs.didit.me/transaction-monitoring/ip-enrichment).", + "properties": { + "ip_country_code": { + "type": "string", + "nullable": true, + "description": "Country code of the address." + }, + "ip_city": { + "type": "string", + "nullable": true, + "description": "City of the address." + }, + "asn_number": { + "type": "integer", + "nullable": true, + "description": "Autonomous system number." + }, + "asn_organization": { + "type": "string", + "nullable": true, + "description": "Organization that operates the autonomous system." + }, + "is_vpn_or_tor": { + "type": "boolean", + "description": "Whether the address is a known VPN or Tor exit." + }, + "is_data_center": { + "type": "boolean", + "description": "Whether the address belongs to a hosting or data-centre network." + }, + "proxy_type": { + "type": "string", + "nullable": true, + "description": "Proxy classification of the address as an opaque string, or null." + }, + "last_enriched_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the address was last enriched." + } + }, + "required": [ + "ip_country_code", + "ip_city", + "asn_number", + "asn_organization", + "is_vpn_or_tor", + "is_data_center", + "proxy_type", + "last_enriched_at" + ] + }, + "subject_profile": { + "type": "object", + "nullable": true, + "description": "Cached email and phone risk signals of the subject, or null when no profile exists for the subject.", + "properties": { + "email_risk_score": { + "type": "integer", + "nullable": true, + "description": "Email risk score." + }, + "email_deliverability": { + "type": "string", + "nullable": true, + "description": "Email deliverability verdict." + }, + "email_is_disposable": { + "type": "boolean", + "nullable": true, + "description": "Whether the email address is disposable." + }, + "email_is_breached": { + "type": "boolean", + "nullable": true, + "description": "Whether the email address appears in known data breaches." + }, + "email_social_count": { + "type": "integer", + "nullable": true, + "description": "Number of online accounts linked to the email address." + }, + "email_social_providers": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Names of the online services where an account linked to the email address was found." + }, + "phone_risk_score": { + "type": "integer", + "nullable": true, + "description": "Phone risk score." + }, + "phone_line_type": { + "type": "string", + "nullable": true, + "description": "Phone line type." + }, + "phone_carrier": { + "type": "string", + "nullable": true, + "description": "Phone carrier name." + }, + "phone_social_count": { + "type": "integer", + "nullable": true, + "description": "Number of online accounts linked to the phone number." + }, + "phone_social_providers": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Names of the online services where an account linked to the phone number was found." + }, + "source_provider": { + "type": "string", + "nullable": true, + "description": "Opaque identifier of the source of the profile." + }, + "last_enriched_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the profile was last refreshed." + } + }, + "required": [ + "email_risk_score", + "email_deliverability", + "email_is_disposable", + "email_is_breached", + "email_social_count", + "email_social_providers", + "phone_risk_score", + "phone_line_type", + "phone_carrier", + "phone_social_count", + "phone_social_providers", + "source_provider", + "last_enriched_at" + ] + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Names of the tags attached to the event (rule `add_tags` actions)." + } } } - } - }, - "KYBRegistryAddress": { - "type": "object", - "description": "An address the registry states, in the same shape for every country and registry source. Every key is always present; a part the registry does not publish is null.", - "properties": { - "type": { - "type": "string", - "nullable": true, - "description": "Address type in lower case: `registered` for the registered office; other types as the registry names them (for example `postal`, `office`). `null` when the registry does not say what the address is." - }, - "address": { - "type": "string", - "description": "The full address as one line. Always present on every address." - }, - "street": { - "type": "string", - "nullable": true, - "description": "Street and number." - }, - "city": { - "type": "string", - "nullable": true, - "description": "City or locality." - }, - "postal_code": { - "type": "string", - "nullable": true, - "description": "Postal code." - }, - "region": { - "type": "string", - "nullable": true, - "description": "Region, state or province." - }, - "country_code": { - "type": "string", - "nullable": true, - "description": "ISO 3166-1 alpha-2 country code." - }, - "description": { - "type": "string", - "nullable": true, - "description": "The registry's own label for the address, when it gives one." - } - }, - "required": [ - "type", - "address", - "street", - "city", - "postal_code", - "region", - "country_code", - "description" ] }, - "KYBRegistryIndustry": { + "MonitoringEventRuleRun": { "type": "object", - "description": "An activity the registry classifies the company under, in the same shape for every country and registry source.", "properties": { - "code": { + "rule_uuid": { "type": "string", - "nullable": true, - "description": "Activity code as the registry prints it (for example `73110`)." + "format": "uuid", + "description": "UUID of the transaction monitoring rule." }, - "description": { + "rule_title": { "type": "string", - "nullable": true, - "description": "Activity label, when the registry publishes one." + "description": "Title of the rule." }, - "scheme": { + "library_key": { "type": "string", "nullable": true, - "description": "Classification system, one name per system whichever source reported it: `UK SIC 2007`, `US SIC`, `NACE Rev. 2`, `NACE Rev. 2.1`, `NAF Rev. 2`, `CNAE`, `SBI`, `NOGA 2008`, `CAE Rev. 4`, `Canada SIC 1977`; other national systems keep the registry's own name. Null when the registry does not name the system." + "description": "Library key when the rule was installed from the rules library, otherwise null." }, - "is_primary": { + "matched": { "type": "boolean", - "description": "True for the company's main activity. Exactly one entry is primary when the list is not empty." - } - }, - "required": [ - "code", - "description", - "scheme", - "is_primary" - ] - }, - "KYBRegistryShareholding": { - "type": "object", - "nullable": true, - "description": "What an owner holds, in the same shape for every country and registry source. Null when the registry states no holding. A band (for example a UK PSC's 25-50%) is not a stake: `percentage` is null and the band stays in `ownership_min_shares` / `ownership_max_shares`.", - "properties": { - "share_class": { - "type": "string", - "nullable": true, - "description": "Share class or type as the registry names it." - }, - "number_of_shares": { - "type": "number", - "nullable": true, - "description": "Number of shares held." - }, - "currency": { - "type": "string", - "nullable": true, - "description": "ISO 4217 currency of the values below." - }, - "nominal_value": { - "type": "number", - "nullable": true, - "description": "Nominal value per share." - }, - "total_value": { - "type": "number", - "nullable": true, - "description": "Total value of the holding." + "description": "Always `true`: only matching runs are recorded." }, - "percentage": { - "type": "number", - "nullable": true, - "description": "Exact stake in percent, to four decimal places." - } - }, - "required": [ - "share_class", - "number_of_shares", - "currency", - "nominal_value", - "total_value", - "percentage" - ] - }, - "KYBRegistryOwnership": { - "type": "object", - "description": "The outcome of the ownership (Shareholders / UBO) tier, in the same shape for every country and registry source.", - "properties": { - "status": { + "mode": { "type": "string", - "nullable": true, "enum": [ - "not_requested", - "pending", - "delivered", - "unavailable", - null + "ACTIVE", + "TEST" ], - "description": "`delivered` when the registry published ownership; `unavailable` when it did not (see `unavailable_reason`); `pending` while the registry is still fetching it; `not_requested` for the Basic tier." + "description": "Mode of the rule when it ran. A `TEST` run records evidence but applied no actions." }, - "tier_requested": { - "type": "string", - "nullable": true, - "enum": [ - "basic", - "shareholders", - "ubo", - null - ], - "description": "The tier the workflow ordered." + "is_async": { + "type": "boolean", + "description": "`true` when the run came from the asynchronous correlation pass rather than the ingest request." }, - "tier_delivered": { + "severity": { "type": "string", "nullable": true, "enum": [ - "basic", - "shareholders", - "ubo", + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL", null ], - "description": "The tier the registry delivered (and that is billed). A UBO request answered with direct shareholders only is `shareholders`." + "description": "Severity of the rule. Null for `TEST` runs and for rules without a severity." }, - "unavailable_reason": { - "type": "string", - "nullable": true, - "enum": [ - "no_owners_on_record", - "no_natural_person_owner", - "ownership_section_unavailable", - "company_not_found", - "ambiguous_company_match", - "missing_registration_number", - "entity_type_not_covered", - "provider_unavailable", - "other", - null - ], - "description": "Why a requested ownership tier was not delivered. Null unless `status` is `unavailable` or `pending`." + "score_delta": { + "type": "integer", + "description": "How much this run changed the event's score (0 for `TEST` runs)." }, - "direct_ownership_complete": { - "type": "boolean", + "action_summary": { + "type": "string", "nullable": true, - "description": "True when the direct shareholders' stated stakes add up to 100% (within one percentage point of rounding); false when they do not; null when ownership was not delivered or a stake is not stated." + "description": "Short summary of the actions applied, for example `score +40; add_tag:VPN_LOGIN`. Null when nothing was applied." }, - "unresolved": { + "matched_payload": { "type": "array", - "description": "Parts of the ownership chain the registry could not resolve, for example no group structure on record above the direct owners.", + "description": "The conditions and aggregations that matched. Condition entries carry `field`, `operator`, `expected_value`, `current_value`, `matched`, `group_index`, `group_logic` and `field_source` (`server`, `sdk` or `customer`); aggregation entries carry `metric`, `field`, `operator`, `expected_value`, `computed_value`, `window`, `filters`, `matched` and `sample_references`.", "items": { "type": "object", - "properties": { - "reason": { - "type": "string" - }, - "section": { - "type": "string", - "nullable": true - } - } + "additionalProperties": true } + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the rule run was recorded." } }, "required": [ - "status", - "tier_requested", - "tier_delivered", - "unavailable_reason", - "direct_ownership_complete", - "unresolved" + "rule_uuid", + "rule_title", + "library_key", + "matched", + "mode", + "is_async", + "severity", + "score_delta", + "action_summary", + "matched_payload", + "created_at" ] }, - "DigitalIdWalletApiWalletVerificationError": { + "MonitoringEventPage": { "type": "object", + "description": "One page of monitoring events, newest `occurred_at` first.", + "required": [ + "count", + "next", + "previous", + "results" + ], "properties": { - "error": { - "type": "string", - "description": "Stable machine-readable code, e.g. wallet_unavailable." + "count": { + "type": "integer", + "description": "Number of matching events, exact up to 100 and capped at 100 above that. Do not compute page counts from it; follow `next` until it is null." }, - "detail": { - "type": "string" - } - }, - "required": [ - "detail", - "error" - ] - }, - "DigitalIdWalletApiWalletVerificationStartForbidden": { - "oneOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiClientCredentialsForbiddenErrorMessage" + "next": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL of the next page, or null on the last page." }, - { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" - } - ] - }, - "DigitalIdWalletApiClientCredentialsForbiddenErrorMessage": { - "type": "object", - "properties": { - "detail": { + "previous": { "type": "string", - "default": "Forbidden - User doesn't have permission" - } - } - }, - "DigitalIdWalletApiWalletVerificationStartBadRequest": { - "oneOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationError" + "format": "uri", + "nullable": true, + "description": "URL of the previous page, or null on the first page." }, - { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationFieldErrors" - } - ] - }, - "DigitalIdWalletApiWalletVerificationFieldErrors": { - "type": "object", - "description": "Request validation errors: each rejected field with its messages.", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MonitoringEvent" + } } } }, - "DigitalIdWalletApiWalletVerificationResponse": { + "MonitoringEventCreateRequest": { "type": "object", - "description": "One standalone wallet verification, as every wallet endpoint answers it.", + "description": "The event envelope, the same for every category. camelCase aliases are accepted: `eventId`, `eventCategory` (or `category`), `actionType`, `occurredAt`, `timeZone` (or `zoneId`), `deviceContext` (or `device`), `transactionContext`, `customValues` (or `props`); inside `subject`: `vendorData`, `entityType`, `fullName`; inside `session`: `sessionId`, `pageUrl`; inside `transaction_context`: `paymentReferenceId`.", + "required": [ + "event_id", + "event_category", + "action_type", + "occurred_at", + "subject" + ], "properties": { - "request_id": { - "type": "string", - "format": "uuid" - }, - "wallet_verification": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResult" - }, - "vendor_data": { + "event_id": { "type": "string", - "nullable": true - }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "The metadata object sent at start, or null." + "maxLength": 128, + "description": "Your identifier for the event, unique per application. It is the idempotency key: resending the same `event_category`, `action_type`, `occurred_at`, `subject.vendor_data` and `payload` under it is a replay, anything different is a conflict.", + "example": "login-2026-10-17-000001" }, - "created_at": { + "event_category": { "type": "string", - "format": "date-time" - } - }, - "required": [ - "created_at", - "metadata", - "request_id", - "vendor_data", - "wallet_verification" - ] - }, - "DigitalIdWalletApiWalletVerificationResult": { - "type": "object", - "properties": { - "wallet": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiWallet" - } + "enum": [ + "auth", + "session", + "gameplay", + "bonus", + "affiliate", + "payment", + "dispute", + "security", + "responsible_gambling", + "custom" ], - "readOnly": true - }, - "country": { - "type": "string" + "description": "Event category.", + "example": "auth" }, - "status": { + "action_type": { "type": "string", - "description": "Not Finished while pending, then Approved (verified) or Declined." + "minLength": 1, + "maxLength": 128, + "description": "What happened within the category. Open vocabulary; the managed actions listed in [Event ingestion](https://docs.didit.me/transaction-monitoring/events) (for example `login`, `deposit_completed`, `chargeback_opened`) unlock typed validation and managed rule fields. Leading and trailing whitespace is trimmed.", + "example": "login" }, - "outcome": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletVerificationResultOutcomeEnum" + "occurred_at": { + "type": "string", + "format": "date-time", + "description": "When the event happened on your side, ISO 8601 (a value without an offset is read as UTC). Must be no more than 7 days in the past and no more than 5 minutes in the future.", + "example": "2026-10-17T09:15:02.481Z" }, - "error": { + "time_zone": { "type": "string", "nullable": true, - "readOnly": true + "maxLength": 64, + "description": "Time zone of the occurrence on your side, for example `Europe/Madrid`. Stored with the event; not returned by the read endpoints." }, - "interaction": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiInteractionEnum" - } + "subject": { + "type": "object", + "description": "The user or company the event belongs to. It resolves to the user (or company) with this `vendor_data` in your application; one is created when none exists.", + "required": [ + "vendor_data" ], - "readOnly": true - }, - "simulated": { - "type": "boolean", - "readOnly": true - }, - "expires_at": { - "type": "string", - "format": "date-time" - }, - "next_action": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletNextAction" + "properties": { + "vendor_data": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "description": "Your id for the subject.", + "example": "user-42" + }, + "entity_type": { + "type": "string", + "maxLength": 32, + "default": "individual", + "description": "`company` resolves the subject to a company; any other value (default `individual`) resolves it to an individual. The same `vendor_data` can name one of each.", + "example": "individual" + }, + "full_name": { + "type": "string", + "nullable": true, + "maxLength": 512, + "description": "Display name, used only when this event creates the user or company." + }, + "email": { + "type": "string", + "format": "email", + "nullable": true, + "description": "Validated as an email address; not stored on the event." + }, + "phone": { + "type": "string", + "nullable": true, + "maxLength": 32, + "description": "Validated; not stored on the event." + }, + "country": { + "type": "string", + "nullable": true, + "maxLength": 3, + "description": "Validated; not stored on the event." } - ], - "nullable": true, - "readOnly": true + } }, - "identity": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletIdentity" + "business": { + "type": "object", + "description": "The business the event belongs to, when it differs from the subject. Only `vendor_data` is read.", + "additionalProperties": true, + "properties": { + "vendor_data": { + "type": "string", + "maxLength": 255, + "description": "Your id for the business. Filterable as `business_vendor_data`.", + "example": "merchant-7" } - ], - "nullable": true, - "readOnly": true + } }, - "attributes": { + "session": { "type": "object", - "additionalProperties": {}, - "nullable": true, - "readOnly": true - }, - "level_of_assurance": { - "type": "string", - "nullable": true + "description": "Your session context for the event.", + "properties": { + "session_id": { + "type": "string", + "nullable": true, + "maxLength": 255, + "description": "Your session identifier. Filterable and returned as `session_id`." + }, + "sequence": { + "type": "integer", + "nullable": true, + "description": "Position of the event within the session. Returned as `session_sequence`." + }, + "page_url": { + "type": "string", + "nullable": true, + "maxLength": 2048, + "description": "Validated; not stored on the event." + }, + "referrer": { + "type": "string", + "nullable": true, + "maxLength": 2048, + "description": "Validated; not stored on the event." + } + } }, - "verified_at": { - "type": "string", - "format": "date-time", - "nullable": true + "device_context": { + "type": "object", + "additionalProperties": true, + "description": "Device and network context, stored as sent and returned by `GET /v3/events/{event_uuid}/`. On this server lane Didit does not see the end user's connection, so the IP enriched and used by rules is `network_context.ip_address` as you send it.", + "properties": { + "fingerprint": { + "type": "string", + "maxLength": 255, + "description": "Device fingerprint. Filterable as `device_fingerprint`." + }, + "persistent_device_id": { + "type": "string", + "maxLength": 255, + "description": "Your persistent device identifier." + }, + "bot_score": { + "type": "integer", + "description": "Bot score from your own detection, as an integer." + }, + "network_context": { + "type": "object", + "additionalProperties": true, + "properties": { + "ip_address": { + "type": "string", + "maxLength": 64, + "description": "End user's IP address. Public addresses are enriched once per unique address.", + "example": "203.0.113.42" + } + } + } + } }, - "signature_valid": { - "type": "boolean", - "nullable": true, - "description": "True once the credential's signature was verified; null for a simulated one." + "transaction_context": { + "type": "object", + "description": "Money context. Non-empty values here also satisfy the required payload keys of the action.", + "properties": { + "amount": { + "type": "string", + "nullable": true, + "description": "Amount as a decimal string (a number is also accepted).", + "example": "1000.00" + }, + "currency": { + "type": "string", + "nullable": true, + "maxLength": 10, + "description": "Currency code." + }, + "direction": { + "type": "string", + "nullable": true, + "maxLength": 16, + "description": "Direction of the money movement, for example `inbound` or `outbound`." + }, + "payment_reference_id": { + "type": "string", + "nullable": true, + "maxLength": 255, + "description": "Your payment reference. Filterable as `payment_reference_id`." + } + } }, - "credential_type": { - "type": "string", - "nullable": true - } - }, - "required": [ - "attributes", - "country", - "credential_type", - "error", - "expires_at", - "identity", - "interaction", - "level_of_assurance", - "next_action", - "outcome", - "signature_valid", - "simulated", - "status", - "verified_at", - "wallet" - ] - }, - "DigitalIdWalletApiWalletIdentity": { - "type": "object", - "properties": { - "full_name": { - "type": "string", - "nullable": true + "payload": { + "type": "object", + "additionalProperties": true, + "description": "The category's domain fields, at most 16384 bytes once JSON-encoded. Some actions require keys (in `payload` or `transaction_context`): `gameplay.p2p_settlement` needs `game_id`, `counterparty_vendor_data`, `stake_amount`, `net_result_amount`, `role` and one of `hand_id`, `round_id`, `match_id`; `gameplay.bet_placed` and `gameplay.bet_settled` need `game_id`; `payment.deposit_initiated`, `payment.deposit_completed`, `payment.withdrawal_requested` and `payment.withdrawal_completed` need `payment_reference_id`, `amount`, `currency`, `payment_method`, `status`; `payment.withdrawal_address_changed` needs `withdrawal_address_hash`; `dispute.chargeback_opened`, `dispute.chargeback_won` and `dispute.chargeback_lost` need `payment_reference_id` and one of `dispute_id`, `chargeback_id`; `session.bot_signal_observed` needs `telemetry_schema_version` and one of `bot_score`, `actions_per_minute`, `timing_variance_ms`, `pointer_entropy`, `keystroke_entropy`, `reaction_time_ms_p50`; `responsible_gambling.self_exclusion_match` needs `registry_source`, `exclusion_status`, `identity_match_ref`. Every `security` action ending in `_changed` needs `changed_field`, plus `previous_reference_hash` or `new_reference_hash` when the field is `password`, `mfa_method`, `mfa`, `email`, `phone`, `withdrawal_address`, `payout_method` or `api_key`. Send hashes and references, never a card number, a full withdrawal address, a password hash or an MFA secret." }, - "first_name": { - "type": "string", - "nullable": true + "custom_values": { + "type": "object", + "description": "Up to 100 keys of your own, addressable from rules as `custom_values.`. Values must be scalars (string, number, boolean or null); nested objects and arrays are rejected.", + "additionalProperties": { + "nullable": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + }, + "example": { + "event_id": "login-2026-10-17-000001", + "event_category": "auth", + "action_type": "login", + "occurred_at": "2026-10-17T09:15:02.481Z", + "time_zone": "Europe/Madrid", + "subject": { + "vendor_data": "user-42", + "entity_type": "individual" }, - "last_name": { - "type": "string", - "nullable": true + "session": { + "session_id": "web-session-abc", + "sequence": 142 }, - "date_of_birth": { - "type": "string", - "format": "date", - "nullable": true + "device_context": { + "fingerprint": "fp-a8c1", + "network_context": { + "ip_address": "203.0.113.42" + } }, - "nationality": { - "type": "string", - "nullable": true, - "description": "ISO3 nationality, only when the wallet asserts one (never inferred)." + "payload": { + "method": "password" }, - "issuing_state": { - "type": "string", - "description": "ISO3 country whose scheme issued the identity." + "custom_values": { + "vip_tier": "gold" } - }, - "required": [ - "date_of_birth", - "first_name", - "full_name", - "issuing_state", - "last_name", - "nationality" - ] + } }, - "DigitalIdWalletApiWalletNextAction": { + "MonitoringEventIngestResult": { "type": "object", + "description": "Outcome of ingesting one event. The same shape is returned for a new event and for an idempotent replay.", + "required": [ + "event_uuid", + "event_id", + "status", + "score", + "rules_evaluated_count", + "rules_matched_count", + "correlation_pending", + "retention_class", + "expires_at", + "idempotent_replay", + "rule_matches" + ], "properties": { - "type": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletNextActionTypeEnum" - } - ], - "description": "redirect: send the user's browser to url. confirm_in_app: show verification_code to the user, who checks it matches the code in their wallet app and confirms there; then poll.\n\n* `redirect` - redirect\n* `confirm_in_app` - confirm_in_app" + "event_uuid": { + "type": "string", + "format": "uuid", + "description": "Didit identifier of the stored event." }, - "url": { + "event_id": { "type": "string", - "format": "uri", - "description": "redirect only: where the user signs in with the wallet." + "description": "Your `event_id`." }, - "verification_code": { + "status": { "type": "string", - "description": "confirm_in_app only: the 4-digit comparison code the wallet app shows too." + "enum": [ + "ACCEPTED", + "APPROVED", + "IN_REVIEW", + "DECLINED" + ], + "description": "Status after the real-time rules ran. See `MonitoringEvent.status`." }, - "poll_interval_seconds": { + "score": { "type": "integer", - "description": "confirm_in_app only: how often to call POST .../verifications/{request_id}/poll/ for prompt feedback. Didit also polls pending sign-ins in the background, so the webhook reports the outcome even when you do not poll." - } - }, - "required": [ - "type" - ] - }, - "DigitalIdWalletApiWalletNextActionTypeEnum": { - "enum": [ - "redirect", - "confirm_in_app" - ], - "type": "string", - "description": "* `redirect` - redirect\n* `confirm_in_app` - confirm_in_app" - }, - "DigitalIdWalletApiInteractionEnum": { - "enum": [ - "redirect", - "in_app" - ], - "type": "string", - "description": "* `redirect` - redirect\n* `in_app` - in_app" - }, - "DigitalIdWalletApiWalletVerificationResultOutcomeEnum": { - "enum": [ - "pending", - "verified", - "cancelled", - "timeout", - "failed" - ], - "type": "string", - "description": "* `pending` - Pending\n* `verified` - Verified\n* `cancelled` - Cancelled by the user\n* `timeout` - Timed out\n* `failed` - Failed" - }, - "DigitalIdWalletApiWallet": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Catalog id of the wallet, e.g. bankid_se." + "description": "Score after the real-time rules ran." }, - "name": { - "type": "string" + "rules_evaluated_count": { + "type": "integer", + "minimum": 0, + "description": "Number of event rules evaluated in the request. 0 in a sandbox application, where no rules run." }, - "issuing_authority": { - "type": "string", - "nullable": true - } - }, - "required": [ - "id", - "issuing_authority", - "name" - ] - }, - "DigitalIdWalletApiEudiWalletVerificationStartRequest": { - "type": "object", - "description": "Start a EUDI Wallet verification (AUT, BEL, BGR, HRV, CYP, CZE, DNK, EST, FIN, FRA, DEU, GRC, HUN, IRL, ITA, LVA, LTU, LUX, MLT, NLD, POL, PRT, ROU, SVK, SVN, ESP, SWE, ISL, LIE, NOR).", - "properties": { - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "rules_matched_count": { + "type": "integer", + "minimum": 0, + "description": "Number of rules that matched, including `TEST` mode rules." }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "correlation_pending": { + "type": "boolean", + "description": "`true` when an asynchronous rule still has to run. Read `GET /v3/events/{event_uuid}/` a few minutes later for the final `status` and `score`." }, - "sandbox_scenario": { + "retention_class": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." - }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiEudiWalletVerificationStartCountryEnum" - } + "enum": [ + "low_value", + "standard", + "risk_relevant", + "monetary_or_compliance" ], - "description": "ISO 3166-1 alpha-3 country of the EUDI Wallet identity.\n\n* `AUT` - AUT\n* `BEL` - BEL\n* `BGR` - BGR\n* `HRV` - HRV\n* `CYP` - CYP\n* `CZE` - CZE\n* `DNK` - DNK\n* `EST` - EST\n* `FIN` - FIN\n* `FRA` - FRA\n* `DEU` - DEU\n* `GRC` - GRC\n* `HUN` - HUN\n* `IRL` - IRL\n* `ITA` - ITA\n* `LVA` - LVA\n* `LTU` - LTU\n* `LUX` - LUX\n* `MLT` - MLT\n* `NLD` - NLD\n* `POL` - POL\n* `PRT` - PRT\n* `ROU` - ROU\n* `SVK` - SVK\n* `SVN` - SVN\n* `ESP` - ESP\n* `SWE` - SWE\n* `ISL` - ISL\n* `LIE` - LIE\n* `NOR` - NOR" + "description": "Retention class, after any promotion by a matched rule." }, - "return_url": { + "expires_at": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "format": "date-time", + "nullable": true, + "description": "When the event expires under its retention class and is deleted. Null for `monetary_or_compliance` (payment and dispute) events." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + "idempotent_replay": { + "type": "boolean", + "description": "`true` when an event with this `event_id` and the same content was already stored; the values are those of the stored event and no rule ran again." + }, + "rule_matches": { + "type": "array", + "description": "Rules that matched the event.", + "items": { + "type": "object", + "required": [ + "rule_id", + "library_key", + "mode", + "is_async" + ], + "properties": { + "rule_id": { + "type": "string", + "format": "uuid", + "description": "UUID of the rule." + }, + "library_key": { + "type": "string", + "nullable": true, + "description": "Library key when the rule was installed from the rules library, otherwise null." + }, + "mode": { + "type": "string", + "enum": [ + "ACTIVE", + "TEST" + ], + "description": "`TEST` rules record the match but apply no actions." + }, + "is_async": { + "type": "boolean", + "description": "`true` when the match came from the asynchronous pass." + } } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } } - }, - "required": [ - "country", - "return_url" - ] - }, - "DigitalIdWalletApiDeviceEnum": { - "enum": [ - "same_device", - "cross_device" - ], - "type": "string", - "description": "* `same_device` - same_device\n* `cross_device` - cross_device" - }, - "DigitalIdWalletApiEudiWalletVerificationStartCountryEnum": { - "enum": [ - "AUT", - "BEL", - "BGR", - "HRV", - "CYP", - "CZE", - "DNK", - "EST", - "FIN", - "FRA", - "DEU", - "GRC", - "HUN", - "IRL", - "ITA", - "LVA", - "LTU", - "LUX", - "MLT", - "NLD", - "POL", - "PRT", - "ROU", - "SVK", - "SVN", - "ESP", - "SWE", - "ISL", - "LIE", - "NOR" - ], - "type": "string", - "description": "* `AUT` - AUT\n* `BEL` - BEL\n* `BGR` - BGR\n* `HRV` - HRV\n* `CYP` - CYP\n* `CZE` - CZE\n* `DNK` - DNK\n* `EST` - EST\n* `FIN` - FIN\n* `FRA` - FRA\n* `DEU` - DEU\n* `GRC` - GRC\n* `HUN` - HUN\n* `IRL` - IRL\n* `ITA` - ITA\n* `LVA` - LVA\n* `LTU` - LTU\n* `LUX` - LUX\n* `MLT` - MLT\n* `NLD` - NLD\n* `POL` - POL\n* `PRT` - PRT\n* `ROU` - ROU\n* `SVK` - SVK\n* `SVN` - SVN\n* `ESP` - ESP\n* `SWE` - SWE\n* `ISL` - ISL\n* `LIE` - LIE\n* `NOR` - NOR" + } }, - "DigitalIdWalletApiConnectidWalletVerificationStartRequest": { + "ComplianceList": { "type": "object", - "description": "Start a ConnectID verification (AUS).", + "description": "A blocklist, allowlist or custom list of one application.", + "required": [ + "uuid", + "name", + "description", + "list_type", + "entry_type", + "is_system", + "entry_count", + "last_entry_added_at", + "created_at", + "updated_at" + ], "properties": { - "vendor_data": { + "uuid": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "format": "uuid", + "description": "List identifier. Use it as `list_uuid` in the list and entry endpoints, and as the value of a workflow or transaction rule condition with `value_type: \"list\"`." }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "name": { + "type": "string", + "maxLength": 255, + "description": "List name, unique among the application's lists." }, - "sandbox_scenario": { + "description": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "description": "Free-text description. Empty string when not set." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiConnectidWalletVerificationStartCountryEnum" - } + "list_type": { + "type": "string", + "enum": [ + "blocklist", + "allowlist", + "custom" ], - "default": "AUS", - "description": "ISO 3166-1 alpha-3 country of the ConnectID identity. Defaults to AUS, the only country it serves.\n\n* `AUS` - AUS" + "description": "`blocklist`: a match declines the session or transaction (system lists only, one per entry type). `allowlist`: matching items are trusted, for example a face allowlist skips the duplicated-face action; never overrides a blocklist match. `custom`: used in workflow and transaction rule conditions (`value_type: \"list\"`)." }, - "authorisation_server_id": { + "entry_type": { "type": "string", - "minLength": 1, - "description": "Authorisation server ID selected with ConnectID's bank selector. Sandbox defaults to Mock Bank A.", - "maxLength": 36 + "enum": [ + "face", + "document", + "phone", + "email", + "ip_address", + "device_fingerprint", + "wallet_address", + "bank_account", + "country", + "user", + "business", + "key" + ], + "description": "What the list holds: `face`, `document` (document number), `phone`, `email`, `ip_address` (IP address or CIDR range), `device_fingerprint`, `wallet_address`, `bank_account`, `country` (ISO 3166-1 alpha-3), `user` or `business` (your `vendor_data`), or `key` (any string). It decides how entry values are validated and matched." }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "is_system": { + "type": "boolean", + "description": "`true` for the lists Didit provisions: one blocklist per entry type (`face`, `document`, `phone`, `email`, `ip_address`, `device_fingerprint`, `wallet_address`, `bank_account`, `user`, `business`), the **High Risk Countries** custom list, and system allowlists. System lists cannot be renamed, edited or deleted; you can add and delete their entries." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" - } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiConnectidWalletVerificationStartCountryEnum": { - "enum": [ - "AUS" - ], - "type": "string", - "description": "* `AUS` - AUS" - }, - "DigitalIdWalletApiAudkenniWalletVerificationStartRequest": { - "type": "object", - "description": "Start a Auðkenni verification (ISL).", - "properties": { - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "entry_count": { + "type": "integer", + "minimum": 0, + "description": "Number of entries currently in the list." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "last_entry_added_at": { + "type": "string", + "format": "date-time", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "When the newest current entry was added. `null` for an empty list." }, - "sandbox_scenario": { + "created_at": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." - }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiAudkenniWalletVerificationStartCountryEnum" - } - ], - "default": "ISL", - "description": "ISO 3166-1 alpha-3 country of the Auðkenni identity. Defaults to ISL, the only country it serves.\n\n* `ISL` - ISL" + "format": "date-time", + "description": "When the list was created." }, - "return_url": { + "updated_at": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 - }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "format": "date-time", + "description": "When the list was last updated." } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiAudkenniWalletVerificationStartCountryEnum": { - "enum": [ - "ISL" - ], - "type": "string", - "description": "* `ISL` - ISL" + } }, - "DigitalIdWalletApiFranceconnectWalletVerificationStartRequest": { + "ComplianceListEntry": { "type": "object", - "description": "Start a FranceConnect verification (FRA).", + "description": "One value in a list.", + "required": [ + "uuid", + "value", + "display_label", + "comment", + "added_by", + "reference_session_id", + "reference_session_number", + "reference_session_type", + "reference_object_uuid", + "metadata", + "created_at" + ], "properties": { - "vendor_data": { + "uuid": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "format": "uuid", + "description": "Entry identifier. Use it as `entry_uuid` in Delete a list entry." }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "value": { + "type": "string", + "description": "The listed value: a phone number in E.164, an email address, a document number, an IP address or CIDR range, a device fingerprint, a wallet address, a bank account, an ISO 3166-1 alpha-3 country code, a `vendor_data`, any string on a `key` list, or the UUID of the stored face on a face list. Values you send are stored in lower case, except phone numbers and country codes. Matching is case-insensitive." }, - "sandbox_scenario": { + "display_label": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "nullable": true, + "maxLength": 512, + "description": "Human-readable label. Didit fills it for entries taken from a session (for example the holder's name and document number) when you send none." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiFranceconnectWalletVerificationStartCountryEnum" - } - ], - "default": "FRA", - "description": "ISO 3166-1 alpha-3 country of the FranceConnect identity. Defaults to FRA, the only country it serves.\n\n* `FRA` - FRA" + "comment": { + "type": "string", + "description": "Free-text note. Empty string when not set." }, - "return_url": { + "added_by": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "nullable": true, + "description": "Who added the entry: `api` (an API key), a console user's email address or `console`, `system` or `migration` (entries Didit created), or `rule` (a transaction or monitoring rule action)." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" - } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiFranceconnectWalletVerificationStartCountryEnum": { - "enum": [ - "FRA" - ], - "type": "string", - "description": "* `FRA` - FRA" - }, - "DigitalIdWalletApiDiiaWalletVerificationStartRequest": { - "type": "object", - "description": "Start a Diia verification (UKR).", - "properties": { - "vendor_data": { + "reference_session_id": { "type": "string", + "format": "uuid", "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "description": "Verification session the entry was taken from. `null` when the entry was added by value." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "reference_session_number": { + "type": "integer", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "Application-scoped number of that session, as shown in the console." }, - "sandbox_scenario": { + "reference_session_type": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." - }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDiiaWalletVerificationStartCountryEnum" - } + "nullable": true, + "enum": [ + "kyc", + "kyb", + null ], - "default": "UKR", - "description": "ISO 3166-1 alpha-3 country of the Diia identity. Defaults to UKR, the only country it serves.\n\n* `UKR` - UKR" + "description": "`kyc` for a person verification session, `kyb` for a business verification session, `null` when there is no session." }, - "return_url": { + "reference_object_uuid": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "format": "uuid", + "nullable": true, + "description": "The record the entry stands for: the face, document, phone or email record read from a session, the face created by Upload a face to a list, or the UUID you sent (for example a transaction UUID or a user's `didit_internal_id`)." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" - } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiDiiaWalletVerificationStartCountryEnum": { - "enum": [ - "UKR" - ], - "type": "string", - "description": "* `UKR` - UKR" + "metadata": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Extra context. Holds the keys Didit sets (listed here) merged with any `metadata` you sent when creating the entry, returned as stored. `null` when there is none.", + "properties": { + "reference_image_url": { + "type": "string", + "description": "Face entries: short-lived signed URL, issued again on every call; `null` for a scoped API key without `media: read`. Absent when no image belongs to the entry." + }, + "front_image_url": { + "type": "string", + "description": "Document entries that carry a document front image: short-lived signed URL, issued again on every call; `null` for a scoped API key without `media: read`." + }, + "full_name": { + "type": "string", + "description": "Document entries taken from a session: holder's full name." + }, + "issuing_state": { + "type": "string", + "description": "Document entries taken from a session: ISO 3166-1 alpha-3 issuing country." + }, + "document_type": { + "type": "string", + "description": "Document entries taken from a session: document type code, for example `P`, `ID` or `DL`." + }, + "region": { + "type": "string", + "description": "Document entries taken from a session: issuing state or province of a regionally issued document, empty when not applicable. Not set by Add session items to the blocklist." + }, + "document_number": { + "type": "string", + "description": "Document entries taken from a session: the document number." + }, + "face_image_source": { + "type": "string", + "enum": [ + "front_image_camera_front", + "back_image_camera_front" + ], + "description": "Face entries taken from a document-capture selfie: which selfie. Absent for liveness faces." + }, + "reference_node_id": { + "type": "string", + "nullable": true, + "description": "Face entries taken from a document-capture selfie: the ID verification node the selfie came from." + }, + "fingerprint_tier": { + "type": "string", + "nullable": true, + "enum": [ + "persistent_id", + "composite_hash", + "legacy_fp", + "unknown", + null + ], + "description": "Device fingerprint entries taken from a session: how the fingerprint was derived. `persistent_id` identifies one device; `composite_hash` and `legacy_fp` can be shared by devices of the same model." + }, + "sandbox": { + "type": "boolean", + "description": "`true` on a face entry uploaded in a sandbox application; such an entry matches nothing." + }, + "reference_type": { + "type": "string", + "enum": [ + "transaction", + "vendor_user", + "vendor_business" + ], + "description": "Set by you: what `reference_object_uuid` points to, so the console links back to it." + }, + "reference_id": { + "type": "string", + "description": "Set by you: identifier of the source record." + } + } + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the entry was added." + } + } }, - "DigitalIdWalletApiMojeidWalletVerificationStartRequest": { + "BlocklistedItem": { "type": "object", - "description": "Start a MojeID verification (CZE).", + "description": "One blocklisted face, identity document, phone number or email address of a verification session. Fields that do not apply to the item's `type` are `null`.", + "required": [ + "type", + "session_id", + "session_number", + "blocklisted_at", + "face_image", + "document_type", + "document_number", + "front_image", + "phone_number", + "country_code", + "email" + ], "properties": { - "vendor_data": { + "type": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "enum": [ + "face", + "document", + "phone", + "email" + ], + "description": "What was blocklisted." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "session_id": { + "type": "string", + "format": "uuid", + "description": "Verification session the item belongs to." + }, + "session_number": { + "type": "integer", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "Application-scoped number of that session, as shown in the console." }, - "sandbox_scenario": { + "blocklisted_at": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." - }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiCountryE2dEnum" - } - ], - "default": "CZE", - "description": "ISO 3166-1 alpha-3 country of the MojeID identity. Defaults to CZE, the only country it serves.\n\n* `CZE` - CZE" + "format": "date-time", + "description": "When the item was last blocklisted." }, - "return_url": { + "face_image": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "nullable": true, + "description": "Face items: short-lived signed URL, issued again on every call; `null` for a scoped API key without `media: read`. Also `null` when no reference image is stored." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" - } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiCountryE2dEnum": { - "enum": [ - "CZE" - ], - "type": "string", - "description": "* `CZE` - CZE" - }, - "DigitalIdWalletApiBankidCzWalletVerificationStartRequest": { - "type": "object", - "description": "Start a Bank iD verification (CZE).", - "properties": { - "vendor_data": { + "document_type": { "type": "string", "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "description": "Document items: document type code, for example `P` (passport), `ID` (national ID), `DL` (driver's license) or `RP` (residence permit)." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "document_number": { + "type": "string", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "Document items: the document number, or the personal number when the document has none." }, - "sandbox_scenario": { + "front_image": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "nullable": true, + "description": "Document items: front image of the document, short-lived signed URL, issued again on every call; `null` for a scoped API key without `media: read`." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiCountryE2dEnum" - } - ], - "default": "CZE", - "description": "ISO 3166-1 alpha-3 country of the Bank iD identity. Defaults to CZE, the only country it serves.\n\n* `CZE` - CZE" + "phone_number": { + "type": "string", + "nullable": true, + "description": "Phone items: the phone number in E.164." }, - "return_url": { + "country_code": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "nullable": true, + "description": "Phone items: ISO 3166-1 alpha-2 country of the number." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "email": { + "type": "string", + "nullable": true, + "description": "Email items: the email address." } - }, - "required": [ - "return_url" - ] + } }, - "DigitalIdWalletApiEparakstsMobileWalletVerificationStartRequest": { + "TransactionNote": { "type": "object", - "description": "Start a eParaksts mobile verification (LVA).", + "description": "One note on a monitored transaction's review timeline.", + "required": [ + "uuid", + "note", + "actor_email", + "actor_name", + "metadata", + "created_at" + ], "properties": { - "vendor_data": { + "uuid": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "format": "uuid", + "description": "Note identifier." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "note": { + "type": "string", + "description": "Plain-text note body." + }, + "actor_email": { + "type": "string", + "format": "email", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "Email of the Console user who wrote the note or changed the status. `null` for notes created with an API key and for notes Didit adds automatically." }, - "sandbox_scenario": { + "actor_name": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "nullable": true, + "description": "Display name of the Console user (their email when they have no name). `Rule Engine` on notes Didit adds automatically. `null` for notes created with an API key." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiEparakstsMobileWalletVerificationStartCountryEnum" + "metadata": { + "type": "object", + "description": "Tells the note types apart. Notes written through the API or the Console carry `mentioned_emails`. A status change made in the Console carries `type: status_change` with `previous_status`, `new_status`, `reason_code` and `reason_label`. Notes Didit adds carry `source`: `rule_action` (with `rule_uuid`) for a rule's `add_note` action, or `remediation_session` for a problem creating the user-action verification session.", + "properties": { + "mentioned_emails": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "description": "Mentioned organization members, lower-cased and de-duplicated. Empty when nobody was mentioned." + }, + "type": { + "type": "string", + "enum": [ + "status_change" + ], + "description": "Present on notes recording a status change made in the Console." + }, + "previous_status": { + "type": "string", + "enum": [ + "APPROVED", + "IN_REVIEW", + "DECLINED", + "AWAITING_USER" + ], + "description": "Status before the change." + }, + "new_status": { + "type": "string", + "enum": [ + "APPROVED", + "IN_REVIEW", + "DECLINED", + "AWAITING_USER" + ], + "description": "Status after the change." + }, + "reason_code": { + "type": "string", + "nullable": true, + "description": "Reason code given with the change." + }, + "reason_label": { + "type": "string", + "nullable": true, + "description": "Reason label given with the change." + }, + "source": { + "type": "string", + "enum": [ + "rule_action", + "remediation_session" + ], + "description": "Present on notes Didit adds automatically." + }, + "rule_uuid": { + "type": "string", + "format": "uuid", + "description": "Rule whose `add_note` action wrote the note (with `source: rule_action`)." } - ], - "default": "LVA", - "description": "ISO 3166-1 alpha-3 country of the eParaksts mobile identity. Defaults to LVA, the only country it serves.\n\n* `LVA` - LVA" + } }, - "return_url": { + "created_at": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 - }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "format": "date-time", + "description": "When the note was stored." } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiEparakstsMobileWalletVerificationStartCountryEnum": { - "enum": [ - "LVA" - ], - "type": "string", - "description": "* `LVA` - LVA" + } }, - "DigitalIdWalletApiIdcardEeWalletVerificationStartRequest": { + "TransactionSummary": { "type": "object", - "description": "Start a Estonian ID-card verification (EST).", + "description": "Summary of a monitored transaction, the shape `GET /v3/transactions/` lists. Call `GET /v3/transactions/{transaction_id}/` with `uuid` for parties, alerts and rule runs.", "properties": { - "vendor_data": { + "uuid": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "format": "uuid", + "description": "Didit transaction UUID. Use it as `{transaction_id}` on the transaction endpoints." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "transaction_number": { + "type": "integer", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "Application-scoped sequential number shown in the Console." }, - "sandbox_scenario": { + "txn_id": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." - }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiIdcardEeWalletVerificationStartCountryEnum" - } - ], - "default": "EST", - "description": "ISO 3166-1 alpha-3 country of the Estonian ID-card identity. Defaults to EST, the only country it serves.\n\n* `EST` - EST" + "nullable": true, + "description": "Your own identifier from the create call. Unique per application." }, - "return_url": { + "transaction_type": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "description": "Category as stored: `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`, `gamblingLimitChange`, `gamblingBonusChange` or `auditTrailEvent`." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" - } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiIdcardEeWalletVerificationStartCountryEnum": { - "enum": [ - "EST" - ], - "type": "string", - "description": "* `EST` - EST" - }, - "DigitalIdWalletApiMobileIdWalletVerificationStartRequest": { - "type": "object", - "description": "Start a Mobile-ID verification (EST, LTU).", - "properties": { - "vendor_data": { + "action_type": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "description": "Sub-type within the category, for example `withdrawal`." }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "status": { + "type": "string", + "enum": [ + "APPROVED", + "IN_REVIEW", + "DECLINED", + "AWAITING_USER" + ], + "description": "Current monitoring verdict." }, - "sandbox_scenario": { + "source": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "enum": [ + "LIVE", + "IMPORTED" + ], + "description": "`LIVE` for transactions submitted for monitoring, `IMPORTED` for historical imports." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiMobileIdWalletVerificationStartCountryEnum" - } + "direction": { + "type": "string", + "enum": [ + "INBOUND", + "OUTBOUND" ], - "description": "ISO 3166-1 alpha-3 country of the Mobile-ID identity.\n\n* `EST` - EST\n* `LTU` - LTU" + "description": "Direction relative to the subject." }, - "personal_code": { + "amount": { "type": "string", - "minLength": 1, - "description": "The user's national personal identification code. It addresses the user's Mobile-ID app; the user confirms the comparison code and enters their PIN in the app. Never collect the PIN.", - "pattern": "^[0-9-]{6,20}$" + "description": "Amount as a decimal string with trailing zeros removed (up to 18 decimal places)." }, - "phone_number": { + "currency": { "type": "string", - "minLength": 1, - "description": "The Mobile-ID phone number in E.164 format (+372... or +370...).", - "maxLength": 16 - } - }, - "required": [ - "country", - "personal_code", - "phone_number" - ] - }, - "DigitalIdWalletApiMobileIdWalletVerificationStartCountryEnum": { - "enum": [ - "EST", - "LTU" - ], - "type": "string", - "description": "* `EST` - EST\n* `LTU` - LTU" - }, - "DigitalIdWalletApiSmartIdWalletVerificationStartRequest": { - "type": "object", - "description": "Start a Smart-ID verification (EST, LVA, LTU, BEL).", - "properties": { - "vendor_data": { + "description": "Currency code or crypto asset symbol of `amount`." + }, + "currency_type": { "type": "string", "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "description": "`fiat` or `crypto` when known." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "preferred_currency_amount": { + "type": "string", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "`amount` converted to the application's preferred currency, when a rate was available." }, - "sandbox_scenario": { + "preferred_currency_code": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "nullable": true, + "description": "Currency of `preferred_currency_amount`." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiSmartIdWalletVerificationStartCountryEnum" - } - ], - "description": "ISO 3166-1 alpha-3 country of the Smart-ID identity.\n\n* `EST` - EST\n* `LVA` - LVA\n* `LTU` - LTU\n* `BEL` - BEL" + "payment_details": { + "type": "string", + "nullable": true, + "description": "Free-text payment reference." }, - "personal_code": { + "txn_date": { "type": "string", - "minLength": 1, - "description": "The user's national personal identification code. It addresses the user's Smart-ID app; the user confirms the comparison code and enters their PIN in the app. Never collect the PIN.", - "pattern": "^[0-9-]{6,20}$" - } - }, - "required": [ - "country", - "personal_code" - ] - }, - "DigitalIdWalletApiSmartIdWalletVerificationStartCountryEnum": { - "enum": [ - "EST", - "LVA", - "LTU", - "BEL" - ], - "type": "string", - "description": "* `EST` - EST\n* `LVA` - LVA\n* `LTU` - LTU\n* `BEL` - BEL" - }, - "DigitalIdWalletApiGovukWalletWalletVerificationStartRequest": { - "type": "object", - "description": "Start a GOV.UK Wallet verification (GBR).", - "properties": { - "vendor_data": { + "format": "date-time", + "description": "When the transaction occurred." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When Didit recorded it." + }, + "score": { + "type": "integer", + "description": "Risk score. Higher is riskier." + }, + "severity": { "type": "string", "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "enum": [ + "UNKNOWN", + "LOW", + "MEDIUM", + "HIGH", + "CRITICAL", + null + ], + "description": "Risk severity, `null` until something sets it." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "decision_reason_code": { + "type": "string", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "Machine-readable reason for the current `status`." }, - "sandbox_scenario": { + "decision_reason_label": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "nullable": true, + "description": "Human-readable label for `decision_reason_code`." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiCountry520Enum" + "tags": { + "type": "array", + "description": "Tags on the transaction.", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "color": { + "type": "string", + "description": "Hex color, for example `#F59E0B`." + }, + "description": { + "type": "string", + "nullable": true + }, + "source": { + "type": "string", + "enum": [ + "didit", + "custom" + ], + "description": "Didit preset or custom tag." + } } - ], - "default": "GBR", - "description": "ISO 3166-1 alpha-3 country of the GOV.UK Wallet identity. Defaults to GBR, the only country it serves.\n\n* `GBR` - GBR" + } }, - "return_url": { + "subject_name": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "nullable": true, + "description": "Full name of the `APPLICANT` party." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" - } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiCountry520Enum": { - "enum": [ - "GBR" - ], - "type": "string", - "description": "* `GBR` - GBR" - }, - "DigitalIdWalletApiOneidWalletVerificationStartRequest": { - "type": "object", - "description": "Start a OneID verification (GBR).", - "properties": { - "vendor_data": { + "subject_vendor_data": { + "type": "string", + "nullable": true, + "description": "Your `vendor_data` for the applicant." + }, + "subject_type": { "type": "string", "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "enum": [ + "user", + "business", + null + ], + "description": "Whether the applicant is linked to a Didit user or business; `null` when it is linked to neither." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "subject_uuid": { + "type": "string", + "format": "uuid", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "UUID of the Didit user or business the applicant is linked to." }, - "sandbox_scenario": { + "subject_country": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "nullable": true, + "description": "Country code of the applicant." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiCountry520Enum" - } - ], - "default": "GBR", - "description": "ISO 3166-1 alpha-3 country of the OneID identity. Defaults to GBR, the only country it serves.\n\n* `GBR` - GBR" + "counterparty_name": { + "type": "string", + "nullable": true, + "description": "Full name of the `COUNTERPARTY` party, if any." }, - "return_url": { + "counterparty_vendor_data": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "nullable": true, + "description": "Your `vendor_data` for the counterparty." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" - } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiGovbrWalletVerificationStartRequest": { - "type": "object", - "description": "Start a gov.br verification (BRA).", - "properties": { - "vendor_data": { + "counterparty_country": { "type": "string", "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "description": "Country code of the counterparty." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "assigned_to_name": { + "type": "string", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "Name of the Console reviewer assigned to the transaction." }, - "sandbox_scenario": { + "travel_rule_status": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "nullable": true, + "description": "Status of the transaction's Travel Rule transfer, or `null` when it has none." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiGovbrWalletVerificationStartCountryEnum" - } - ], - "default": "BRA", - "description": "ISO 3166-1 alpha-3 country of the gov.br identity. Defaults to BRA, the only country it serves.\n\n* `BRA` - BRA" + "application_id": { + "type": "string", + "format": "uuid", + "description": "Application that owns the transaction." }, - "return_url": { + "application_name": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "nullable": true, + "description": "Name of that application." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "organization_id": { + "type": "string", + "format": "uuid", + "description": "Organization that owns the application." + }, + "organization_name": { + "type": "string", + "nullable": true, + "description": "Name of that organization." } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiGovbrWalletVerificationStartCountryEnum": { - "enum": [ - "BRA" - ], - "type": "string", - "description": "* `BRA` - BRA" + } }, - "DigitalIdWalletApiUaePassWalletVerificationStartRequest": { + "ApplicationAnalyticsDataPoint": { "type": "object", - "description": "Start a UAE PASS verification (ARE).", + "description": "Session counts for one day, by creation date (UTC). Each count reflects the session's current status. Points of the previous period carry at least the required fields.", + "required": [ + "rounded_date", + "total", + "approved", + "declined", + "canceled", + "pending" + ], "properties": { - "vendor_data": { + "rounded_date": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "format": "date", + "description": "The day, `YYYY-MM-DD`." }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "total": { + "type": "integer", + "description": "Sessions created that day." }, - "sandbox_scenario": { - "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "approved": { + "type": "integer", + "description": "Of those, sessions `Approved` or `Kyc Expired`." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiUaePassWalletVerificationStartCountryEnum" - } - ], - "default": "ARE", - "description": "ISO 3166-1 alpha-3 country of the UAE PASS identity. Defaults to ARE, the only country it serves.\n\n* `ARE` - ARE" + "declined": { + "type": "integer", + "description": "Sessions `Declined`." }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "canceled": { + "type": "integer", + "description": "Sessions `Expired` or `Abandoned`." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "pending": { + "type": "integer", + "description": "Sessions `Not Started`, `In Progress` or `In Review`." + }, + "in_review": { + "type": "integer", + "description": "Sessions `In Review`." + }, + "expired": { + "type": "integer", + "description": "Sessions `Expired`." + }, + "abandoned": { + "type": "integer", + "description": "Sessions `Abandoned`." + }, + "in_progress": { + "type": "integer", + "description": "Sessions `In Progress`." + }, + "not_started": { + "type": "integer", + "description": "Sessions `Not Started`." + }, + "manually_approved": { + "type": "integer", + "description": "Sessions a reviewer approved after the workflow gave another verdict." + }, + "manually_declined": { + "type": "integer", + "description": "Sessions a reviewer declined after the workflow gave another verdict." } - }, - "required": [ - "return_url" - ] + } }, - "DigitalIdWalletApiUaePassWalletVerificationStartCountryEnum": { - "enum": [ - "ARE" + "ApplicationAnalyticsRequestBreakdown": { + "type": "object", + "description": "Status totals for the range. The previous-period breakdown carries at least the required fields.", + "required": [ + "total_approved", + "total_in_review", + "total_declined", + "total_canceled", + "total_pending" ], - "type": "string", - "description": "* `ARE` - ARE" + "properties": { + "total_approved": { + "type": "integer", + "description": "Sessions `Approved` or `Kyc Expired`." + }, + "total_in_review": { + "type": "integer", + "description": "Sessions `In Review`." + }, + "total_declined": { + "type": "integer", + "description": "Sessions `Declined`." + }, + "total_canceled": { + "type": "integer", + "description": "Sessions `Expired` or `Abandoned`." + }, + "total_pending": { + "type": "integer", + "description": "Sessions `Not Started`, `In Progress` or `In Review` (includes `total_in_review`)." + }, + "total_expired": { + "type": "integer", + "description": "Sessions `Expired`." + }, + "total_abandoned": { + "type": "integer", + "description": "Sessions `Abandoned`." + }, + "total_in_progress": { + "type": "integer", + "description": "Sessions `In Progress`." + }, + "total_not_started": { + "type": "integer", + "description": "Sessions `Not Started`." + }, + "total_manually_approved": { + "type": "integer", + "description": "Approved by a reviewer." + }, + "total_manually_declined": { + "type": "integer", + "description": "Declined by a reviewer." + } + } }, - "DigitalIdWalletApiFrejaidWalletVerificationStartRequest": { + "ApplicationAnalyticsCountryCount": { "type": "object", - "description": "Start a Freja eID verification (SWE).", "properties": { - "vendor_data": { + "country": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." - }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "Country name." }, - "sandbox_scenario": { + "country_code": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "description": "Two-letter country code." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiCountryEe9Enum" - } - ], - "default": "SWE", - "description": "ISO 3166-1 alpha-3 country of the Freja eID identity. Defaults to SWE, the only country it serves.\n\n* `SWE` - SWE" + "session_count": { + "type": "integer", + "description": "Sessions from that country." + } + } + }, + "ApplicationAnalyticsLocationStats": { + "type": "object", + "properties": { + "ip_countries": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsCountryCount" + }, + "description": "Countries of the users' IP addresses, most sessions first." }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "countries": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsCountryCount" + }, + "description": "Same list as `ip_countries`." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + "id_countries": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsCountryCount" + }, + "description": "Issuing countries of the verified ID documents, most sessions first." + }, + "conversion_by_country": { + "type": "array", + "description": "Per document issuing country: finished sessions (approved, declined or in review) and how many were approved. Most finished first.", + "items": { + "type": "object", + "properties": { + "country": { + "type": "string" + }, + "country_code": { + "type": "string" + }, + "finished": { + "type": "integer" + }, + "approved": { + "type": "integer" + } } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "cities": { + "type": "array", + "description": "Up to 100 IP cities, most sessions first.", + "items": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "`City, Country`." + }, + "session_count": { + "type": "integer" + }, + "latitude": { + "type": "number", + "nullable": true + }, + "longitude": { + "type": "number", + "nullable": true + } + } + } } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiCountryEe9Enum": { - "enum": [ - "SWE" - ], - "type": "string", - "description": "* `SWE` - SWE" + } }, - "DigitalIdWalletApiPersonalausweisWalletVerificationStartRequest": { + "ApplicationAnalyticsDemographicStats": { "type": "object", - "description": "Start a Personalausweis verification (DEU).", "properties": { - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." - }, - "metadata": { + "age_distribution": { "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." - }, - "sandbox_scenario": { - "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "description": "Sessions per age band from the ID document. Empty when there is no data.", + "properties": { + "under_18": { + "type": "integer" + }, + "18_24": { + "type": "integer" + }, + "25_34": { + "type": "integer" + }, + "35_44": { + "type": "integer" + }, + "45_64": { + "type": "integer" + }, + "65_plus": { + "type": "integer" + } + }, + "additionalProperties": { + "type": "integer" + } }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiPersonalausweisWalletVerificationStartCountryEnum" + "gender_distribution": { + "type": "object", + "description": "Sessions per document gender code: `M`, `F` or `U` (unknown).", + "additionalProperties": { + "type": "integer" + } + } + } + }, + "ApplicationAnalyticsDeviceDetails": { + "type": "object", + "description": "Top five of each, most sessions first.", + "properties": { + "device_models": { + "type": "array", + "description": "Device brands: `Apple`, `Android`, `LG`, everything else grouped as `Other`.", + "items": { + "type": "object", + "properties": { + "device": { + "type": "string" + }, + "count": { + "type": "integer" + } } - ], - "default": "DEU", - "description": "ISO 3166-1 alpha-3 country of the Personalausweis identity. Defaults to DEU, the only country it serves.\n\n* `DEU` - DEU" + } }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "browsers": { + "type": "array", + "description": "Browsers: `Chrome`, `Safari`, `Didit App`, `Firefox`, everything else grouped as `Other`.", + "items": { + "type": "object", + "properties": { + "browser": { + "type": "string" + }, + "count": { + "type": "integer" + } + } + } }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + "operating_systems": { + "type": "array", + "description": "Operating systems.", + "items": { + "type": "object", + "properties": { + "os": { + "type": "string" + }, + "count": { + "type": "integer" + } } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } } - }, - "required": [ - "return_url" - ] + } }, - "DigitalIdWalletApiPersonalausweisWalletVerificationStartCountryEnum": { - "enum": [ - "DEU" - ], - "type": "string", - "description": "* `DEU` - DEU" + "ApplicationAnalyticsWorkflowFunnel": { + "type": "object", + "description": "Keyed by workflow ID. `total` is the sessions started on that workflow; `steps` counts the sessions that reached each step (`OCR`, `LIVENESS`, `FACE_MATCH`, `AML`, `POA`, `PHONE`, `EMAIL`, `NFC`, `DATABASE_VALIDATION`, `QUESTIONNAIRE`, `IP_ANALYSIS`, `AGE_ESTIMATION`, `KYB_REGISTRY`, `KYB_DOCUMENTS`, `KYB_KEY_PEOPLE`). Reaching a step is not passing it.", + "additionalProperties": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "steps": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "reached": { + "type": "integer" + } + } + } + } + } + } }, - "DigitalIdWalletApiFtnWalletVerificationStartRequest": { + "ApplicationAnalyticsVerificationTimeStats": { "type": "object", - "description": "Start a Finnish Trust Network verification (FIN).", + "description": "Active time users spent verifying, ignoring idle gaps. An empty object when there is no data.", "properties": { - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "overall_avg_seconds": { + "type": "number", + "description": "Average per session, one decimal." }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "overall_count": { + "type": "integer", + "description": "Sessions measured." }, - "sandbox_scenario": { - "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "per_feature": { + "type": "object", + "description": "Keyed by feature, for example `OCR` or `LIVENESS`.", + "additionalProperties": { + "type": "object", + "properties": { + "avg_seconds": { + "type": "number" + }, + "total_seconds": { + "type": "number" + }, + "count": { + "type": "integer" + } + } + } }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiFtnWalletVerificationStartCountryEnum" + "per_sub_step": { + "type": "object", + "description": "Keyed by feature, then by sub-step, for example `front_id_photo`.", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "avg_seconds": { + "type": "number" + }, + "total_seconds": { + "type": "number" + }, + "count": { + "type": "integer" + } + } } - ], - "default": "FIN", - "description": "ISO 3166-1 alpha-3 country of the Finnish Trust Network identity. Defaults to FIN, the only country it serves.\n\n* `FIN` - FIN" + } + } + } + }, + "ApplicationAnalyticsResubmissionStats": { + "type": "object", + "properties": { + "first_pass": { + "type": "integer", + "description": "Finished sessions with no resubmission." }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "second": { + "type": "integer", + "description": "Finished sessions resubmitted once." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "third_plus": { + "type": "integer", + "description": "Finished sessions resubmitted two or more times." + }, + "abandoned": { + "type": "integer", + "description": "Abandoned sessions." } - }, - "required": [ - "return_url" - ] + } }, - "DigitalIdWalletApiFtnWalletVerificationStartCountryEnum": { - "enum": [ - "FIN" - ], - "type": "string", - "description": "* `FIN` - FIN" + "ApplicationAnalyticsOriginalStatusStats": { + "type": "object", + "description": "The workflow's automatic verdict, before any reviewer changed it.", + "properties": { + "approved": { + "type": "integer" + }, + "in_review": { + "type": "integer" + }, + "declined": { + "type": "integer" + } + } + }, + "ApplicationAnalyticsWarningStats": { + "type": "object", + "description": "Keyed by feature (`ID_VERIFICATION`, `LIVENESS`, `FACEMATCH`, `AML`, `PROOF_OF_ADDRESS`, `PHONE`, `EMAIL`, `NFC`, `LOCATION`, `DATABASE_VALIDATION`, `QUESTIONNAIRE` and the KYB features). `total` is the sessions that reached the feature; `risks` counts sessions per warning code and level.", + "additionalProperties": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "risks": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "error": { + "type": "integer" + }, + "warning": { + "type": "integer" + }, + "information": { + "type": "integer" + } + } + } + } + } + } }, - "DigitalIdWalletApiIdinWalletVerificationStartRequest": { + "ApplicationAnalyticsDemographicPoint": { "type": "object", - "description": "Start a iDIN verification (NLD).", "properties": { - "vendor_data": { + "date": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "description": "Period key: `YYYY-MM-DD` for a day, the Monday of a week, or `YYYY-MM` for a month, following `grouping_mode`." }, - "metadata": { + "age_distribution": { "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "additionalProperties": { + "type": "integer" + } }, - "sandbox_scenario": { + "gender_distribution": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + } + } + }, + "ApplicationAnalyticsDevicePoint": { + "type": "object", + "properties": { + "date": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "description": "Period key: `YYYY-MM-DD` for a day, the Monday of a week, or `YYYY-MM` for a month, following `grouping_mode`." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiIdinWalletVerificationStartCountryEnum" - } - ], - "default": "NLD", - "description": "ISO 3166-1 alpha-3 country of the iDIN identity. Defaults to NLD, the only country it serves.\n\n* `NLD` - NLD" + "devices": { + "type": "object", + "additionalProperties": { + "type": "integer" + } }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "browsers": { + "type": "object", + "additionalProperties": { + "type": "integer" + } }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "operating_systems": { + "type": "object", + "additionalProperties": { + "type": "integer" + } } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiIdinWalletVerificationStartCountryEnum": { - "enum": [ - "NLD" - ], - "type": "string", - "description": "* `NLD` - NLD" + } }, - "DigitalIdWalletApiItsmeWalletVerificationStartRequest": { + "ApplicationAnalyticsVerificationTimePoint": { "type": "object", - "description": "Start a itsme verification (BEL, LUX, NLD).", "properties": { - "vendor_data": { + "date": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "description": "Period key: `YYYY-MM-DD` for a day, the Monday of a week, or `YYYY-MM` for a month, following `grouping_mode`." }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "overall_avg": { + "type": "number", + "description": "Average active seconds per session." }, - "sandbox_scenario": { + "per_feature": { + "type": "object", + "description": "Average active seconds per feature.", + "additionalProperties": { + "type": "number" + } + } + } + }, + "ApplicationAnalyticsResubmissionPoint": { + "type": "object", + "properties": { + "date": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "description": "Period key: `YYYY-MM-DD` for a day, the Monday of a week, or `YYYY-MM` for a month, following `grouping_mode`." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiItsmeWalletVerificationStartCountryEnum" - } - ], - "description": "ISO 3166-1 alpha-3 country of the itsme identity.\n\n* `BEL` - BEL\n* `LUX` - LUX\n* `NLD` - NLD" + "first_pass": { + "type": "integer" }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "second": { + "type": "integer" }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "third_plus": { + "type": "integer" + }, + "abandoned": { + "type": "integer" } - }, - "required": [ - "country", - "return_url" - ] - }, - "DigitalIdWalletApiItsmeWalletVerificationStartCountryEnum": { - "enum": [ - "BEL", - "LUX", - "NLD" - ], - "type": "string", - "description": "* `BEL` - BEL\n* `LUX` - LUX\n* `NLD` - NLD" + } }, - "DigitalIdWalletApiBuypassWalletVerificationStartRequest": { + "ApplicationAnalyticsOriginalStatusPoint": { "type": "object", - "description": "Start a Buypass ID verification (NOR).", "properties": { - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." - }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." - }, - "sandbox_scenario": { + "date": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "description": "Period key: `YYYY-MM-DD` for a day, the Monday of a week, or `YYYY-MM` for a month, following `grouping_mode`." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiCountryAfcEnum" - } - ], - "default": "NOR", - "description": "ISO 3166-1 alpha-3 country of the Buypass ID identity. Defaults to NOR, the only country it serves.\n\n* `NOR` - NOR" + "approved": { + "type": "integer" }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "in_review": { + "type": "integer" }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "declined": { + "type": "integer" } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiCountryAfcEnum": { - "enum": [ - "NOR" - ], - "type": "string", - "description": "* `NOR` - NOR" + } }, - "DigitalIdWalletApiVippsWalletVerificationStartRequest": { + "ApplicationAnalyticsSummary": { "type": "object", - "description": "Start a Vipps verification (NOR).", + "description": "User verification (KYC) summary, returned when neither `scope` nor `widget_keys` is sent.", + "required": [ + "data_points", + "request_breakdown", + "previous_data_points", + "previous_request_breakdown", + "conversion_rate", + "location_stats", + "demographic_stats", + "device_details", + "workflow_funnel_stats", + "verification_time_stats", + "resubmission_stats", + "warning_stats", + "original_status_stats", + "grouping_mode", + "demographic_time_series", + "device_time_series", + "verification_time_time_series", + "resubmission_time_series", + "original_status_time_series" + ], "properties": { - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." + "data_points": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsDataPoint" + }, + "description": "One point per day, from the first day with sessions in the range to `end_date`. Always daily, whatever `grouping_mode` says." }, - "metadata": { - "type": "object", - "additionalProperties": {}, + "request_breakdown": { + "$ref": "#/components/schemas/ApplicationAnalyticsRequestBreakdown" + }, + "previous_data_points": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsDataPoint" + }, + "description": "Daily points for the period of the same length right before `start_date`." + }, + "previous_request_breakdown": { + "$ref": "#/components/schemas/ApplicationAnalyticsRequestBreakdown" + }, + "conversion_rate": { + "type": "number", "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "description": "`total_approved / (total_approved + total_declined + total_in_review) x 100`, one decimal. `null` when nothing finished." }, - "sandbox_scenario": { - "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "location_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsLocationStats" }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiCountryAfcEnum" - } - ], - "default": "NOR", - "description": "ISO 3166-1 alpha-3 country of the Vipps identity. Defaults to NOR, the only country it serves.\n\n* `NOR` - NOR" + "demographic_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsDemographicStats" }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "device_details": { + "$ref": "#/components/schemas/ApplicationAnalyticsDeviceDetails" }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } + "workflow_funnel_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsWorkflowFunnel" + }, + "verification_time_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsVerificationTimeStats" + }, + "resubmission_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsResubmissionStats" + }, + "warning_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsWarningStats" + }, + "original_status_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsOriginalStatusStats" + }, + "grouping_mode": { + "type": "string", + "enum": [ + "day", + "week", + "month" ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "description": "How the `*_time_series` lists are grouped: `day` for up to 14 days, `week` up to 90, `month` beyond. Measured from the first day with sessions in the range." + }, + "demographic_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsDemographicPoint" + } + }, + "device_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsDevicePoint" + } + }, + "verification_time_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsVerificationTimePoint" + } + }, + "resubmission_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsResubmissionPoint" + } + }, + "original_status_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsOriginalStatusPoint" + } } - }, - "required": [ - "return_url" - ] + } }, - "DigitalIdWalletApiBankidNoWalletVerificationStartRequest": { + "ApplicationAnalyticsWidget": { "type": "object", - "description": "Start a BankID verification (NOR).", + "description": "One dashboard widget. Every widget has `kind` and `title`; the other fields depend on `kind`. The session widgets reuse the summary objects (`data_points`, `request_breakdown`, `workflow_funnel_stats` and so on); list widgets put their entries in `items`; transaction metrics use `value`.", + "required": [ + "kind", + "title" + ], "properties": { - "vendor_data": { + "kind": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." - }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "enum": [ + "sessions_volume", + "workflow_funnel", + "average_verification_time", + "country_distribution", + "warning_insights", + "conversion_rate", + "conversion_by_country", + "resubmission_insights", + "original_status_distribution", + "database_validation_coverage", + "database_validation_latency", + "demographics", + "device_details", + "horizontal_bar", + "kyb_registry_coverage", + "kyb_registry_data_completeness", + "metric", + "time_series", + "breakdown", + "status_time_series", + "leaderboard", + "histogram" + ], + "description": "Widget type." }, - "sandbox_scenario": { + "title": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." + "description": "Display title, for example `Verification volume`." }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiCountryAfcEnum" - } + "grouping_mode": { + "type": "string", + "enum": [ + "day", + "week", + "month" ], - "default": "NOR", - "description": "ISO 3166-1 alpha-3 country of the BankID identity. Defaults to NOR, the only country it serves.\n\n* `NOR` - NOR" + "description": "How the `*_time_series` lists are grouped: `day` for up to 14 days, `week` up to 90, `month` beyond. Measured from the first day with sessions in the range." }, - "return_url": { + "data_points": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsDataPoint" + } + }, + "request_breakdown": { + "$ref": "#/components/schemas/ApplicationAnalyticsRequestBreakdown" + }, + "previous_data_points": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsDataPoint" + } + }, + "previous_request_breakdown": { + "$ref": "#/components/schemas/ApplicationAnalyticsRequestBreakdown" + }, + "conversion_rate": { + "type": "number", + "nullable": true + }, + "workflow_funnel_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsWorkflowFunnel" + }, + "verification_time_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsVerificationTimeStats" + }, + "verification_time_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsVerificationTimePoint" + } + }, + "warning_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsWarningStats" + }, + "resubmission_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsResubmissionStats" + }, + "resubmission_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsResubmissionPoint" + } + }, + "original_status_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsOriginalStatusStats" + }, + "original_status_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsOriginalStatusPoint" + } + }, + "demographic_stats": { + "$ref": "#/components/schemas/ApplicationAnalyticsDemographicStats" + }, + "demographic_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsDemographicPoint" + } + }, + "device_details": { + "$ref": "#/components/schemas/ApplicationAnalyticsDeviceDetails" + }, + "device_time_series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationAnalyticsDevicePoint" + } + }, + "items": { + "type": "array", + "description": "Entries of a list widget. `country_distribution` (KYC): `country`, `country_code`, `session_count`. `conversion_by_country`: `country`, `country_code`, `finished`, `approved`. `time_series`: `date`, `value`. `status_time_series`: `date`, `approved`, `declined`, `flagged`. `breakdown`: `transaction_type` or `action_type` with `count` and `volume`, or `tag_id`, `tag_name`, `count`. `leaderboard`: `rule_title`, `trigger_count` or `assignee`, `handled_count`, `average_processing_seconds`, each with `approved_percentage`, `declined_percentage`, `flagged_percentage`. `histogram`: `score`, `count`.", + "items": { + "type": "object", + "additionalProperties": {} + } + }, + "previous_items": { + "type": "array", + "description": "Transaction time series for the period of the same length right before `start_date`.", + "items": { + "type": "object", + "additionalProperties": {} + } + }, + "value": { + "type": "number", + "description": "`metric` widgets: the figure for the range." + }, + "currency": { "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "nullable": true, + "description": "Transaction volume and amount widgets: the application's default transaction currency." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" + "thresholds": { + "type": "object", + "description": "`histogram` widget: the application's review and decline score thresholds.", + "properties": { + "review": { + "type": "integer" + }, + "decline": { + "type": "integer" } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + } + }, + "search": { + "type": "object", + "description": "`kyb_registry_coverage` widget: registry search totals.", + "properties": { + "searches_total": { + "type": "integer" + }, + "searches_with_no_candidates": { + "type": "integer" + } + } } - }, - "required": [ - "return_url" - ] + } }, - "DigitalIdWalletApiBankidSeWalletVerificationStartRequest": { + "ApplicationAnalyticsWidgetResponse": { "type": "object", - "description": "Start a BankID verification (SWE).", + "description": "Dashboard widgets, returned when `scope` or `widget_keys` is sent.", + "required": [ + "scope", + "available_widget_keys", + "requested_widget_keys", + "widgets" + ], "properties": { - "vendor_data": { - "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." - }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." - }, - "sandbox_scenario": { + "scope": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." - }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiCountryEe9Enum" - } + "enum": [ + "home", + "kyc", + "kyb", + "transactions" ], - "default": "SWE", - "description": "ISO 3166-1 alpha-3 country of the BankID identity. Defaults to SWE, the only country it serves.\n\n* `SWE` - SWE" + "description": "Dashboard tab." }, - "return_url": { - "type": "string", - "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "available_widget_keys": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Every widget key this scope accepts." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" + "requested_widget_keys": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The widget keys answered, in order: your valid `widget_keys`, or the scope's defaults." + }, + "widgets": { + "type": "object", + "description": "Keyed by widget key, in the order of `requested_widget_keys`.", + "additionalProperties": { + "$ref": "#/components/schemas/ApplicationAnalyticsWidget" + } } - }, - "required": [ - "return_url" - ] + } }, - "DigitalIdWalletApiMitidWalletVerificationStartRequest": { + "WebhookConfigDestination": { "type": "object", - "description": "Start a MitID verification (DNK).", + "description": "A webhook destination as listed in the webhook configuration, without delivery metrics. `GET /v3/webhook/destinations/{destination_uuid}/` adds its delivery summary.", + "required": [ + "uuid", + "label", + "url", + "enabled", + "webhook_version", + "secret_shared_key", + "subscribed_events", + "created_at", + "updated_at" + ], "properties": { - "vendor_data": { + "uuid": { "type": "string", - "nullable": true, - "description": "Your own reference for this verification (e.g. your user id). Returned in results and webhooks." - }, - "metadata": { - "type": "object", - "additionalProperties": {}, - "nullable": true, - "description": "Optional JSON object stored with the verification and returned in results and webhooks." + "format": "uuid", + "description": "Destination ID." }, - "sandbox_scenario": { + "label": { "type": "string", - "minLength": 1, - "description": "Sandbox applications only: the simulated outcome - 'wallet_cancelled', 'wallet_timeout' or 'wallet_provider_error'; any other scenario (or none) verifies. Rejected on live applications. The full catalog is available at GET /v1/sandbox/scenarios/." - }, - "country": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiMitidWalletVerificationStartCountryEnum" - } - ], - "default": "DNK", - "description": "ISO 3166-1 alpha-3 country of the MitID identity. Defaults to DNK, the only country it serves.\n\n* `DNK` - DNK" + "maxLength": 255, + "description": "Human-readable name." }, - "return_url": { + "url": { "type": "string", "format": "uri", - "minLength": 1, - "description": "https URL the user's browser returns to after the wallet, with request_id and result appended. Its host must be registered in the application's allowed_return_hosts (or be its white-label domain). Never trust result on its own: read the outcome with GET .../verifications/{request_id}/ or the webhook.", - "maxLength": 2048 + "maxLength": 2000, + "description": "HTTPS endpoint Didit posts events to." }, - "device": { - "allOf": [ - { - "$ref": "#/components/schemas/DigitalIdWalletApiDeviceEnum" - } - ], - "default": "same_device", - "description": "same_device when the user signs in on the device showing your page, cross_device when they use another one (e.g. BankID's QR code).\n\n* `same_device` - same_device\n* `cross_device` - cross_device" - } - }, - "required": [ - "return_url" - ] - }, - "DigitalIdWalletApiMitidWalletVerificationStartCountryEnum": { - "enum": [ - "DNK" - ], - "type": "string", - "description": "* `DNK` - DNK" - }, - "DigitalIdWalletApiWalletCatalogResponse": { - "type": "object", - "properties": { - "wallets": { + "enabled": { + "type": "boolean", + "description": "`false` pauses deliveries to this destination." + }, + "webhook_version": { + "type": "string", + "enum": [ + "v1", + "v2", + "v3" + ], + "description": "Payload version sent to this destination." + }, + "secret_shared_key": { + "type": "string", + "description": "This destination's signing secret (43-character URL-safe token). Verify each webhook with the secret of the destination it was sent to. API-key callers always receive it in full; Console users without `read:webhooks` receive an empty string." + }, + "subscribed_events": { "type": "array", + "uniqueItems": true, + "description": "Event types delivered to this destination. Empty when the destination was created without events, in which case it receives nothing.", "items": { - "$ref": "#/components/schemas/DigitalIdWalletApiWalletCatalogEntry" + "$ref": "#/components/schemas/WebhookEvent" } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" } - }, - "required": [ - "wallets" - ] + } }, - "DigitalIdWalletApiWalletCatalogEntry": { + "WebhookConfig": { "type": "object", + "description": "Application-level webhook configuration, retention policy and Digital ID wallet return hosts, with every webhook destination.", + "required": [ + "webhook_url", + "webhook_version", + "secret_shared_key", + "capture_method", + "data_retention_months", + "face_retention_policy", + "face_retention_days", + "available_events", + "allowed_return_hosts", + "endpoints" + ], "properties": { - "id": { - "type": "string" + "webhook_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Application-level (legacy) webhook URL. Didit sends to it only while the application has no enabled destination, and then only `status.updated` and `data.updated`. Every create, update or delete of a destination sets it to the URL of the oldest enabled destination, or `null` when none is enabled." }, - "name": { - "type": "string" + "webhook_version": { + "type": "string", + "enum": [ + "v1", + "v2", + "v3" + ], + "description": "Payload version of the legacy `webhook_url`. Each destination has its own `webhook_version`." }, - "path": { + "secret_shared_key": { "type": "string", - "description": "The wallet's own start endpoint." + "description": "Application-level signing secret (43-character URL-safe token). It signs only deliveries to the legacy `webhook_url`; each destination signs with its own `secret_shared_key`. API-key callers always receive it in full; Console users without `read:webhooks` receive an empty string." }, - "countries": { - "type": "array", - "items": { - "type": "string" - } + "capture_method": { + "type": "string", + "enum": [ + "MOBILE", + "MOBILE_AND_DESKTOP" + ], + "description": "Legacy application setting. The verification flow does not read it: capture options are configured per workflow." }, - "issuing_authority": { - "type": "string" + "data_retention_months": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 120, + "description": "Automatic deletion window. User verification sessions created more than this many months ago (a month counts as 30 days) are deleted automatically, and that deletion is irreversible. `null` means sessions are never deleted automatically. See [Data retention](https://docs.didit.me/console/data-retention)." }, - "level_of_assurance": { + "face_retention_policy": { "type": "string", - "nullable": true + "enum": [ + "delete_with_session", + "retain_until_user_deleted" + ], + "description": "What happens to a session's face embedding when the session is deleted. `delete_with_session` (default) deletes it with the session. `retain_until_user_deleted` is the opt-in [biometric template retention](https://docs.didit.me/console/data-retention#biometric-template-retention): an image-free biometric template linked to the User is managed separately until its retention period ends, the User is deleted, or you purge it." }, - "interaction": { - "$ref": "#/components/schemas/DigitalIdWalletApiInteractionEnum" + "face_retention_days": { + "type": "integer", + "nullable": true, + "minimum": 1, + "maximum": 3650, + "description": "Retention period, in days, of biometric templates under `retain_until_user_deleted`. Required when that policy is set." }, - "required_fields": { + "available_events": { "type": "array", + "description": "Every event type a destination can subscribe to, with a display label.", "items": { - "type": "string" + "type": "object", + "required": [ + "value", + "label" + ], + "properties": { + "value": { + "$ref": "#/components/schemas/WebhookEvent" + }, + "label": { + "type": "string", + "description": "Display label, for example `Session status updated`." + } + } } }, - "available": { - "type": "boolean", - "description": "Whether this application can start it in this environment now. Listed does not mean live." + "allowed_return_hosts": { + "type": "array", + "maxItems": 20, + "description": "Hostnames (exact match, HTTPS only) a standalone Digital ID wallet verification may send the user back to with its `return_url`. Your white-label domain is always allowed as well. See [Register your return destination](https://docs.didit.me/standalone-apis/digital-id-wallets#register-your-return-destination).", + "items": { + "type": "string", + "maxLength": 253 + } + }, + "endpoints": { + "type": "array", + "description": "Every webhook destination of the application, oldest first, each with its signing secret. Read only here: manage destinations with the `/v3/webhook/destinations/` endpoints.", + "items": { + "$ref": "#/components/schemas/WebhookConfigDestination" + } } - }, - "required": [ - "available", - "countries", - "id", - "interaction", - "issuing_authority", - "level_of_assurance", - "name", - "path", - "required_fields" - ] + } }, - "CaseDuplicateCaseDuplicate": { + "WebhookDestinationDelivery": { "type": "object", + "description": "One delivery attempt to a webhook destination. The first send of an event, each automatic retry and each resend are separate deliveries.", + "required": [ + "uuid", + "session_id", + "request_url", + "response_status", + "response_time_ms", + "status", + "webhook_type", + "created_at" + ], "properties": { "uuid": { "type": "string", "format": "uuid", - "readOnly": true + "description": "Delivery ID, for the detail and resend calls." }, - "case_number": { + "session_id": { "type": "string", + "format": "uuid", "nullable": true, - "maxLength": 32 + "description": "User verification session the event is about. `null` for business session, user, business, transaction, Travel Rule and workflow events." }, - "title": { + "request_url": { "type": "string", - "maxLength": 255 + "format": "uri", + "nullable": true, + "description": "URL the delivery was sent to: the destination's URL when the event was generated." + }, + "response_status": { + "type": "integer", + "nullable": true, + "description": "HTTP status your endpoint returned. `null` while the delivery is queued and not yet attempted. When no HTTP answer was received Didit records a status of its own: `504` when your endpoint did not answer within 5 seconds, `503` for a connection failure, `400` when the URL was refused as unsafe (for example a private or internal address), and `404` for any other request error. `400` or above counts as a failed delivery." + }, + "response_time_ms": { + "type": "integer", + "nullable": true, + "minimum": 0, + "description": "Time the attempt took, in milliseconds. `null` while the delivery is queued." }, "status": { - "$ref": "#/components/schemas/CaseDuplicateStatusEnum" + "type": "string", + "nullable": true, + "description": "Status the event reported when it was generated: the session status for session events (for example `Approved` or `In Review`), the person's or company's status for user and business events, and the transaction status for transaction events. `null` for events that carry no status." }, - "priority": { - "$ref": "#/components/schemas/CaseDuplicatePriorityEnum" + "webhook_type": { + "$ref": "#/components/schemas/WebhookEvent" }, "created_at": { "type": "string", "format": "date-time", - "readOnly": true + "description": "When the delivery was created." } - }, - "required": [ - "created_at", - "title", - "uuid" - ] + } }, - "CaseDuplicateCaseDuplicatePage": { + "WebhookDestinationDeliveryDetail": { "type": "object", + "description": "One delivery attempt to a webhook destination, with the exact request headers and body Didit sent.", + "required": [ + "uuid", + "session_id", + "request_url", + "response_status", + "response_time_ms", + "status", + "webhook_type", + "created_at", + "request_header", + "request_body" + ], "properties": { - "count": { - "type": "integer" + "uuid": { + "type": "string", + "format": "uuid", + "description": "Delivery ID, for the detail and resend calls." }, - "next_offset": { + "session_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "User verification session the event is about. `null` for business session, user, business, transaction, Travel Rule and workflow events." + }, + "request_url": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "URL the delivery was sent to: the destination's URL when the event was generated." + }, + "response_status": { "type": "integer", - "nullable": true + "nullable": true, + "description": "HTTP status your endpoint returned. `null` while the delivery is queued and not yet attempted. When no HTTP answer was received Didit records a status of its own: `504` when your endpoint did not answer within 5 seconds, `503` for a connection failure, `400` when the URL was refused as unsafe (for example a private or internal address), and `404` for any other request error. `400` or above counts as a failed delivery." }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseDuplicateCaseDuplicate" - } + "response_time_ms": { + "type": "integer", + "nullable": true, + "minimum": 0, + "description": "Time the attempt took, in milliseconds. `null` while the delivery is queued." + }, + "status": { + "type": "string", + "nullable": true, + "description": "Status the event reported when it was generated: the session status for session events (for example `Approved` or `In Review`), the person's or company's status for user and business events, and the transaction status for transaction events. `null` for events that carry no status." + }, + "webhook_type": { + "$ref": "#/components/schemas/WebhookEvent" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the delivery was created." + }, + "request_header": { + "type": "string", + "description": "JSON text (a string) of the request headers Didit sent: `Content-Type`, `X-Signature`, `X-Signature-V2`, `X-Signature-Simple`, `X-Timestamp` and `User-Agent`. Parse it with a JSON parser. It holds signatures, never the signing secret." + }, + "request_body": { + "type": "string", + "description": "JSON text (a string) of the exact payload Didit sent, keys sorted, including `event_id` and `timestamp`. Parse it with a JSON parser; see [Payload shape](https://docs.didit.me/integration/webhooks#payload-shape). For a scoped key without `media: read`, media URLs inside it are `null`; without `sessions: write`, session tokens and hosted verification links are `null`. A body changed that way no longer matches its signatures. Didit does not record the response body your endpoint returned." } - }, - "required": [ - "count", - "next_offset", - "results" - ] + } }, - "RoutingPublicationPolicyDraftResponse": { + "SessionWebhookDelivery": { "type": "object", - "properties": { - "draft": { - "allOf": [ - { - "$ref": "#/components/schemas/RoutingPublicationPolicyDraft" - } - ], - "nullable": true - } - }, + "description": "One webhook delivery attempt recorded for a verification session, with the exact request headers and body Didit sent.", "required": [ - "draft" - ] - }, - "RoutingPublicationPolicyDraftWriteRequest": { - "type": "object", + "uuid", + "session_id", + "request_url", + "response_status", + "status", + "webhook_type", + "request_header", + "request_body", + "created_at" + ], "properties": { - "expected_version": { - "type": "integer", - "minimum": 0 + "uuid": { + "type": "string", + "format": "uuid", + "description": "Delivery ID. Use it as `{webhook_uuid}` to get or resend this delivery." }, - "expected_revision": { + "session_id": { "type": "string", "format": "uuid", - "nullable": true + "description": "The session the delivery belongs to (the session in the path)." }, - "changes": { - "type": "object", - "additionalProperties": {} - } - }, - "required": [ - "changes", - "expected_revision", - "expected_version" - ] - }, - "CasePublicationPolicyDraft": { - "type": "object", - "properties": { - "revision": { + "request_url": { "type": "string", - "format": "uuid" + "format": "uri", + "nullable": true, + "description": "URL the delivery was sent to." }, - "base_version": { + "response_status": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "nullable": true, + "description": "HTTP status your endpoint returned. `null` while the delivery is queued and not yet attempted. When no HTTP answer was received Didit records a status of its own: `504` when your endpoint did not answer within 5 seconds, `503` for a connection failure, `400` when the URL was refused as unsafe (for example a private or internal address), and `404` for any other request error. `400` or above counts as a failed delivery." }, - "changes": {}, - "actor_email": { + "status": { "type": "string", - "format": "email", "nullable": true, - "maxLength": 254 + "description": "Session status when the event was generated, for example `In Progress`, `Approved` or `Declined`." }, - "updated_at": { + "webhook_type": { + "$ref": "#/components/schemas/WebhookEvent" + }, + "request_header": { "type": "string", - "format": "date-time", - "readOnly": true - } - }, - "required": [ - "base_version", - "updated_at" - ] - }, - "CasePublicationPolicyDraftPreconditionRequest": { - "type": "object", - "properties": { - "expected_version": { - "type": "integer", - "minimum": 0 + "description": "JSON text (a string) of the request headers Didit sent: `Content-Type`, `X-Signature`, `X-Signature-V2`, `X-Signature-Simple`, `X-Timestamp` and `User-Agent`. Parse it with a JSON parser. It holds signatures, never the signing secret." }, - "expected_revision": { + "request_body": { "type": "string", - "format": "uuid", - "nullable": true + "description": "JSON text (a string) of the exact payload Didit sent, keys sorted, including `event_id` and `timestamp`. Parse it with a JSON parser; see [Payload shape](https://docs.didit.me/integration/webhooks#payload-shape). For a scoped key without `media: read`, media URLs inside it are `null`; without `sessions: write`, session tokens and hosted verification links are `null`. A body changed that way no longer matches its signatures. Didit does not record the response body your endpoint returned." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the delivery was created." } - }, - "required": [ - "expected_revision", - "expected_version" - ] + } }, - "CaseCollaborationCaseNote": { + "SessionEvent": { "type": "object", + "description": "One entry of a verification session's event timeline.", + "required": [ + "uuid", + "event_type", + "created_at", + "component", + "feature_step", + "step", + "details", + "ip_address", + "user_agent", + "device_info", + "file_url", + "sent_at" + ], "properties": { "uuid": { "type": "string", "format": "uuid", - "readOnly": true + "description": "Event identifier." }, - "comment": { - "type": "string" + "event_type": { + "type": "string", + "enum": [ + "SESSION_CREATED", + "SESSION_COMPLETED", + "SESSION_STATUS_CHANGED", + "LINK_OPENED", + "FORM_SUBMITTED", + "BUTTON_CLICKED", + "FILE_UPLOADED", + "ATTEMPT_FAILED", + "FEATURE_STEP_CHANGED", + "STEP_RENDERED", + "NOTE_ADDED", + "MANUAL_REVIEW_STARTED", + "WORKFLOW_BRANCH_EVALUATED", + "WORKFLOW_STATUS_NODE_REACHED", + "LOG" + ], + "description": "What happened. Reported by the device during the hosted flow or SDK: `LINK_OPENED`, `STEP_RENDERED`, `BUTTON_CLICKED`, `FORM_SUBMITTED`, `FILE_UPLOADED`, `ATTEMPT_FAILED` and `LOG` (debugging). Recorded by the server: `SESSION_CREATED`, `SESSION_STATUS_CHANGED` (`details.old_status`, `details.current_status`), `FEATURE_STEP_CHANGED` (`details.old_feature_step`, `details.current_feature_step`), `WORKFLOW_BRANCH_EVALUATED` and `WORKFLOW_STATUS_NODE_REACHED` (the workflow routing trace). `SESSION_COMPLETED`, `NOTE_ADDED` and `MANUAL_REVIEW_STARTED` can appear on older sessions." }, - "actor_email": { + "created_at": { + "type": "string", + "format": "date-time", + "description": "When Didit stored the event. Events are stored asynchronously, a few seconds after `sent_at`." + }, + "component": { "type": "string", - "format": "email", "nullable": true, - "maxLength": 255 + "description": "UI component that reported the event (device events only)." }, - "actor_name": { + "feature_step": { "type": "string", "nullable": true, - "maxLength": 255 + "enum": [ + "OCR", + "OCR_BACK", + "NFC", + "AML", + "FACE", + "LIVENESS", + "FACE_MATCH", + "IP_ANALYSIS", + "AGE_ESTIMATION", + "PROOF_OF_ADDRESS", + "PHONE_VERIFICATION", + "EMAIL_VERIFICATION", + "FACE_SEARCH", + "DATABASE_VALIDATION", + "QUESTIONNAIRE", + "KYB_REGISTRY", + "KYB_DOCUMENTS", + "KYB_KEY_PEOPLE", + "DOCUMENT_AI", + "GEOLOCATION", + null + ], + "description": "Verification feature the session was on when the event happened." }, - "mentioned_emails": {}, - "is_internal": { - "type": "boolean" + "step": { + "type": "string", + "nullable": true, + "description": "Screen within the feature, for example `DOCUMENT_CAPTURE_FRONT` or `FACE_PASSIVE_LIVENESS`." }, - "attachments": {}, - "tags": {}, - "created_at": { + "details": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Event-specific data. The keys depend on `event_type`." + }, + "ip_address": { "type": "string", - "format": "date-time", - "readOnly": true + "nullable": true, + "description": "IP address the device reported the event from. `null` for server-recorded events." }, - "reply_to": { - "allOf": [ - { - "$ref": "#/components/schemas/CaseCollaborationCaseNoteReply" - } - ], + "user_agent": { + "type": "string", "nullable": true, - "readOnly": true + "description": "User agent of the device. `null` for server-recorded events." }, - "is_pinned": { - "type": "boolean" + "device_info": { + "type": "object", + "nullable": true, + "additionalProperties": true, + "description": "Device details the SDK or browser reported." }, - "client_reference": { + "file_url": { "type": "string", - "format": "uuid", - "nullable": true + "format": "uri", + "nullable": true, + "description": "Short-lived presigned link to a file attached to the event (for example a captured image). `null` when there is none, and always `null` for a scoped API key without `media: read`." + }, + "sent_at": { + "type": "integer", + "format": "int64", + "description": "When the event happened, as a Unix timestamp in milliseconds. Results are ordered by it." } - }, - "required": [ - "comment", - "created_at", - "reply_to", - "uuid" - ] + } }, - "CustomerMessagingPaginatedCaseRequestList": { + "AMLDatasetSourceFreshness": { "type": "object", + "description": "Freshness of one family of AML datasets.", "required": [ - "count", - "results" + "last_updated_at", + "oldest_updated_at", + "dataset_count", + "dated_dataset_count" ], "properties": { - "count": { - "type": "integer", - "example": 123 + "last_updated_at": { + "type": "string", + "format": "date-time", + "description": "Most recent update of any dataset in the family." }, - "next": { + "oldest_updated_at": { "type": "string", + "format": "date-time", "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?offset=400&limit=100" + "description": "Staleness bound: every dataset in the family is at least this current. `null` when at least one dataset in the family reported no update date, because the bound is then unknown." }, - "previous": { + "dataset_count": { + "type": "integer", + "description": "Number of datasets in the family." + }, + "dated_dataset_count": { + "type": "integer", + "description": "Number of those datasets that reported an update date." + } + } + }, + "AMLDatasetFreshness": { + "type": "object", + "description": "How current the AML datasets were when a screening ran. Dates are never estimated.", + "required": [ + "captured_at", + "provider", + "catalog_version", + "sources" + ], + "properties": { + "captured_at": { + "type": "string", + "format": "date-time", + "description": "When Didit read the dataset catalog this freshness comes from." + }, + "provider": { + "type": "string", + "description": "Opaque identifier of the AML data source. Treat it as an opaque string; do not branch on it.", + "example": "aml_data_source" + }, + "catalog_version": { "type": "string", "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?offset=200&limit=100" + "description": "Version identifier of the dataset catalog, when the source reports one." }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequest" + "sources": { + "type": "object", + "description": "Freshness per dataset family. A family whose datasets reported no update date is absent rather than filled with `null`.", + "properties": { + "sanctions": { + "$ref": "#/components/schemas/AMLDatasetSourceFreshness" + }, + "pep": { + "$ref": "#/components/schemas/AMLDatasetSourceFreshness" + }, + "warnings": { + "$ref": "#/components/schemas/AMLDatasetSourceFreshness" + }, + "adverse_media": { + "$ref": "#/components/schemas/AMLDatasetSourceFreshness" + } } } } }, - "FraudCheckResponse": { + "AMLScreeningRun": { "type": "object", + "description": "One completed ongoing-monitoring re-screen of an AML check.", + "required": [ + "uuid", + "completed_at", + "started_at", + "outcome", + "trigger", + "provider", + "status", + "score", + "total_hits", + "dataset_freshness" + ], "properties": { - "check_id": { + "uuid": { "type": "string", - "format": "uuid" + "format": "uuid", + "description": "Identifier of this screening run." }, - "request_id": { + "completed_at": { "type": "string", - "format": "uuid", - "description": "Native verification session created for this check." + "format": "date-time", + "description": "When this subject's re-screen finished. Results are ordered by it, newest first." }, - "client_reference": { + "started_at": { "type": "string", - "nullable": true + "format": "date-time", + "description": "When the monitoring batch this re-screen belongs to started." }, - "processing_status": { - "$ref": "#/components/schemas/FraudProcessingStatusEnum" + "outcome": { + "type": "string", + "enum": [ + "NO_CHANGE", + "CHANGED", + "FAILED" + ], + "description": "`NO_CHANGE`: completed and the result did not move. `CHANGED`: completed and the score, status, hit count or hits changed. `FAILED`: no usable answer for this subject, so the check was left as it was." }, - "profile_id": { - "type": "string" + "trigger": { + "type": "string", + "enum": [ + "ONGOING_MONITORING" + ], + "description": "What started the screening. Always `ONGOING_MONITORING` today." }, - "profile_version": { - "type": "string" + "provider": { + "type": "string", + "description": "Opaque identifier of the AML data source that answered. Treat it as an opaque string; do not branch on it.", + "example": "aml_data_source" }, - "created_at": { + "status": { "type": "string", - "format": "date-time" + "nullable": true, + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested", + null + ], + "description": "AML check status this run left the check in." }, - "completed_at": { + "score": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 100, + "description": "AML score this run left the check in." + }, + "total_hits": { + "type": "integer", + "nullable": true, + "description": "Number of hits this run left the check in." + }, + "dataset_freshness": { + "allOf": [ + { + "$ref": "#/components/schemas/AMLDatasetFreshness" + } + ], + "nullable": true, + "description": "Dataset freshness behind this run, or `null` when no dates were available." + } + } + }, + "KYBKeyPeopleParty": { + "type": "object", + "description": "A Key People party of a Business Verification (KYB) session, with its child verification. The same party appears in `key_people_checks[].submitted.parties[]` of the business session decision.", + "required": [ + "uuid", + "entity_type", + "source", + "name", + "is_active", + "is_skipped", + "requires_verification", + "roles", + "kyc_session_deleted", + "kyb_sub_session_deleted" + ], + "properties": { + "uuid": { "type": "string", - "format": "date-time", - "nullable": true + "format": "uuid", + "description": "Party identifier." }, - "inputs": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FraudCheckInput" - } + "entity_type": { + "type": "string", + "enum": [ + "person", + "company" + ], + "description": "Whether the party is an individual or a company." }, - "modules": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FraudCheckModuleReport" - } + "source": { + "type": "string", + "enum": [ + "REGISTRY", + "USER" + ], + "description": "`REGISTRY`: taken from the company registry. `USER`: entered in the Key People screen." }, - "attribute_evidence": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": {} - } + "name": { + "type": "string", + "description": "Display name of the party." }, - "compound_evidence": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": {} - } + "first_name": { + "type": "string", + "nullable": true, + "description": "First name (persons)." + }, + "last_name": { + "type": "string", + "nullable": true, + "description": "Last name (persons)." }, - "risk": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FraudCheckRisk" - } + "company_name": { + "type": "string", + "nullable": true, + "description": "Company name (company parties)." }, - "network": { - "type": "object", - "additionalProperties": {} + "registration_number": { + "type": "string", + "nullable": true, + "description": "Company registration number (company parties)." }, - "enrichment": { - "type": "object", - "additionalProperties": {} + "email": { + "type": "string", + "format": "email", + "nullable": true, + "description": "Email the verification link is sent to." }, - "identity_relationship": { - "allOf": [ - { - "$ref": "#/components/schemas/FraudCheckIdentityRelationship" - } - ], - "nullable": true + "nationality": { + "type": "string", + "nullable": true, + "description": "Nationality for persons, country of incorporation for companies." }, - "recommendation": { - "$ref": "#/components/schemas/FraudRecommendationEnum" + "phone_number": { + "type": "string", + "nullable": true, + "description": "Phone number collected for the party." }, - "next_action": { - "$ref": "#/components/schemas/FraudNextActionEnum" + "date_of_birth": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Date of birth (persons)." }, - "decision_reasons": { - "type": "array", - "items": { - "type": "string" - } + "position": { + "type": "string", + "nullable": true, + "description": "Free-text job title (persons)." }, - "coverage": { + "custom_fields": { "type": "object", - "additionalProperties": {} + "additionalProperties": true, + "description": "Workflow-defined custom fields collected for the party, keyed by field key." }, - "usage": { + "is_active": { + "type": "boolean", + "description": "Whether the party is active." + }, + "is_skipped": { + "type": "boolean", + "description": "Whether the party is exempt from verification. Regenerating clears it." + }, + "requires_verification": { + "type": "boolean", + "description": "Whether the role and ownership rules require a verification for this party." + }, + "roles": { "type": "array", + "description": "Roles the party holds in the company.", "items": { "type": "object", - "additionalProperties": {} + "required": [ + "role", + "ownership_percent", + "voting_percent" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "director", + "non_executive_director", + "secretary", + "chairman", + "ubo", + "shareholder", + "representative", + "founder", + "legal_advisor", + "authorized_signatory", + "trustee", + "beneficiary", + "settlor", + "protector", + "company_officer", + "investor", + "other" + ], + "description": "Role code." + }, + "ownership_percent": { + "type": "number", + "nullable": true, + "description": "Ownership percentage for this role, when known." + }, + "voting_percent": { + "type": "number", + "nullable": true, + "description": "Voting-rights percentage for this role, when known." + } + } } }, - "vendor_data": { + "kyc_session_id": { "type": "string", - "nullable": true + "format": "uuid", + "nullable": true, + "description": "Child individual KYC session (persons)." }, - "metadata": { - "nullable": true - } - }, - "required": [ - "attribute_evidence", - "check_id", - "client_reference", - "completed_at", - "compound_evidence", - "coverage", - "created_at", - "decision_reasons", - "inputs", - "modules", - "network", - "next_action", - "processing_status", - "profile_id", - "profile_version", - "recommendation", - "risk", - "usage" - ] - }, - "CaseReviewTeam": { - "type": "object", - "properties": { - "name": { + "kyc_session_status": { "type": "string", - "maxLength": 100 + "nullable": true, + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User", + null + ], + "description": "Status of the child KYC session. A deleted session keeps the status it had when it was deleted." }, - "description": { + "kyc_session_url": { "type": "string", - "maxLength": 500 - }, - "members": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseReviewTeamMember" - }, - "maxItems": 250 + "format": "uri", + "nullable": true, + "description": "Hosted link of the child KYC session. `null` when there is none or it was deleted, and for a scoped API key without `sessions: write`." }, - "is_active": { - "type": "boolean" + "kyc_session_deleted": { + "type": "boolean", + "description": "The child KYC session was deleted, so it can never settle." }, - "uuid": { + "kyb_sub_session_id": { "type": "string", "format": "uuid", - "readOnly": true + "nullable": true, + "description": "Child KYB session (company parties)." }, - "created_at": { + "kyb_sub_session_status": { "type": "string", - "format": "date-time", - "readOnly": true + "nullable": true, + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User", + null + ], + "description": "Status of the child KYB session." }, - "updated_at": { + "kyb_sub_session_url": { "type": "string", - "format": "date-time", - "readOnly": true - } - } - }, - "CaseActionCaseEntityActionRequest": { - "type": "object", - "properties": { - "client_reference": { - "type": "string", - "format": "uuid" + "format": "uri", + "nullable": true, + "description": "Hosted link of the child KYB session, with the same `null` rules as `kyc_session_url`." }, - "retry": { + "kyb_sub_session_deleted": { "type": "boolean", - "default": false + "description": "Company-party counterpart of `kyc_session_deleted`." }, - "action": { - "$ref": "#/components/schemas/CaseActionActionEnum" + "verification_workflow_type": { + "type": "string", + "nullable": true, + "enum": [ + "kyc", + "kyb", + "adaptive_age_verification", + "biometric_authentication", + "address_verification", + "questionnaire_verification", + "email_verification", + "phone_verification", + null + ], + "description": "Type of the workflow the child session runs." }, - "status": { + "verification_workflow_id": { "type": "string", - "nullable": true + "format": "uuid", + "nullable": true, + "description": "Workflow the child session runs." }, - "tags": { + "verification_workflow_label": { + "type": "string", + "nullable": true, + "description": "Label of that workflow." + }, + "verification_workflow_features": { "type": "array", + "nullable": true, + "description": "Features of the child session's workflow with their current status.", "items": { - "type": "string", - "minLength": 1 + "type": "object", + "required": [ + "feature", + "status" + ], + "properties": { + "feature": { + "type": "string", + "description": "Feature code, for example `ID_VERIFICATION` or `LIVENESS`." + }, + "status": { + "type": "string", + "description": "Feature status, `Not Finished` until it runs." + } + } } }, - "note": { + "didit_internal_id": { "type": "string", - "nullable": true + "format": "uuid", + "nullable": true, + "description": "Didit's identifier of the verified person, once known." } - }, - "required": [ - "action" - ] + } }, - "RoutingPublicationPaginatedPolicyRevisionList": { + "TravelRuleDelivery": { "type": "object", + "description": "Where the latest message the transfer owes the other side stands. Didit contacts the counterparty after the request that created or changed the transfer has returned, so this object says whether that message got through: queued, being sent, waiting to be retried after an error, delivered, or failed. A failure the network may still recover from (no answer in time, a failed connection, HTTP `408`, `429` or `5xx`) is tried again after 30 seconds, 2 minutes, 10 minutes, 30 minutes and 1 hour. When a first message (`kind: SEND`) fails for good, because the network refused it or the sixth attempt failed too, the transfer moves to `COUNTERPARTY_VASP_NOT_REACHABLE` with `last_error` in its `timeline`; any other message that fails for good is noted in the `timeline`. The transfer's `status` stays the source of truth for the exchange itself.", "required": [ - "count", - "results" + "kind", + "status", + "rail", + "attempts", + "last_error", + "next_attempt_at", + "delivered_at" ], "properties": { - "count": { + "kind": { + "type": "string", + "enum": [ + "SEND", + "ANSWER", + "TXID", + "CANCEL", + "POLL" + ], + "description": "What the message carries. `SEND`: the transfer's first message to the counterparty (outbound; the rail is chosen when it is sent). `ANSWER`: your accept or decline, owed to the network that asked (inbound). `TXID`: the on-chain transaction hash, after you finish the transfer. `CANCEL`: the cancellation, after you cancel it. `POLL`: a status query to a network that offers one, repeated every 5 minutes while the transfer waits on the counterparty." + }, + "status": { + "type": "string", + "enum": [ + "QUEUED", + "SENDING", + "RETRYING", + "DELIVERED", + "FAILED" + ], + "description": "`QUEUED`: waiting to be sent. `SENDING`: an attempt is running. `RETRYING`: an attempt failed and the next one runs at `next_attempt_at`. `DELIVERED`: the other side took the message. `FAILED`: the other side refused it, or the last attempt failed." + }, + "rail": { + "type": "string", + "enum": [ + "INTERNAL", + "TRP", + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA", + "EMAIL", + "" + ], + "description": "The rail that carries the message. Empty until Didit has chosen one for a first message." + }, + "attempts": { "type": "integer", - "example": 123 + "minimum": 0, + "description": "Attempts made so far for this message. A message that is not a status poll is attempted at most 6 times." }, - "next": { + "last_error": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?offset=400&limit=100" + "description": "The kind of failure the last attempt hit, `\"\"` when none did: for example `email undeliverable`, `trp identity endpoint unreachable` or `trp transfer inquiry delivery failed`. It never carries the counterparty's own answer, which stays on the transfer's `timeline`.\nWhen GTR answers with an error, its own status code and message are included. Response bodies are never included.", + "example": "email undeliverable" }, - "previous": { + "next_attempt_at": { "type": "string", + "format": "date-time", "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?offset=200&limit=100" + "description": "When the next attempt runs while the message is `QUEUED` or `RETRYING`." }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RoutingPublicationPolicyRevision" - } + "delivered_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the other side took the message." } } }, - "FraudRecommendationEnum": { - "enum": [ - "approve", - "review", - "reject" - ], - "type": "string", - "description": "* `approve` - Approve\n* `review` - Review\n* `reject` - Reject" - }, - "CustomerMessagingCaseRequestPreviewResponse": { + "TravelRuleDiscoveryStep": { "type": "object", + "description": "One step of the counterparty discovery Didit ran for an outbound transfer, in the order it ran them. The first step that names a VASP with a route ends the chain.", "properties": { - "request_id": { + "step": { "type": "string", - "format": "uuid" + "enum": [ + "internal_address_book", + "wallet_attribution", + "network_lookup", + "customer_hint", + "end_user" + ], + "description": "`internal_address_book`: another Didit customer that proved control of the destination wallet. `wallet_attribution`: the wallet's blockchain analytics attribution. `network_lookup`: a network that can say which VASP holds the address. `customer_hint`: the VASP name, LEI or jurisdiction you sent in `beneficiary_data`. `end_user`: the counterparty the end user (or you) picked, or the request to pick one." }, - "expires_at": { + "outcome": { "type": "string", - "format": "date-time" + "enum": [ + "match", + "miss", + "skipped" + ], + "description": "`match`: the step named a counterparty. `miss`: it ran and found none. `skipped`: it could not run, and `reason` says why." }, - "recipient": { + "reason": { "type": "string", - "format": "email" - }, - "sender": { - "type": "string" - }, - "subject": { - "type": "string" - }, - "html": { - "type": "string" - }, - "requires_user_action": { - "type": "boolean" + "description": "Machine-readable reason, `\"\"` on a match: `no_proven_internal_wallet`, `attribution_disabled`, `already_attributed`, `no_attributed_entity`, `directory_match_no_route`, `gtr_address_lookup_runs_on_dispatch`, `no_membership`, `network_switched_off`, `no_network_address_lookup`, `no_directory_match`, `awaiting_end_user` or `picked_member_without_proven_wallet`." }, - "preview_token": { - "type": "string" - } - }, - "required": [ - "expires_at", - "html", - "preview_token", - "recipient", - "request_id", - "requires_user_action", - "sender", - "subject" - ] - }, - "CustomerMessagingCaseCustomerResponseRequest": { - "type": "object", - "properties": { - "full_name": { + "vasp": { "type": "string", - "minLength": 1, - "maxLength": 512 + "format": "uuid", + "nullable": true, + "description": "The directory VASP the step named, when it named one." }, - "date_of_birth": { + "rail": { "type": "string", - "format": "date" + "nullable": true, + "description": "The rail that reaches that counterparty, when the step found one." }, - "file": { + "at": { "type": "string", - "format": "binary", - "writeOnly": true + "format": "date-time" } } }, - "CaseCollaborationCaseNoteReply": { + "TravelRuleNextAction": { "type": "object", + "description": "One thing that moves a `COUNTERPARTY_VASP_NOT_FOUND` or `COUNTERPARTY_VASP_NOT_REACHABLE` transfer on. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck).", "properties": { - "uuid": { - "type": "string", - "format": "uuid" - }, - "actor_name": { - "type": "string", - "nullable": true - }, - "actor_email": { + "action": { "type": "string", - "format": "email", - "nullable": true + "enum": [ + "select_counterparty_vasp", + "declare_self_hosted", + "add_vasp_hint", + "update_compliance_email", + "resend" + ], + "description": "`select_counterparty_vasp`: the end user picks the destination VASP in the wallet-ownership widget. `declare_self_hosted`: the end user declares the wallet self-hosted in the widget. `add_vasp_hint`: tell Didit who the counterparty is, with a VASP name, LEI or travel address (for example a `travel_address` on the destination wallet's address-book entry), then resend. `update_compliance_email` and `resend` are `action` values of `PATCH /v3/transactions/{transaction_id}/travel-rule/`: give the counterparty's compliance email again, and re-run routing." }, - "comment": { + "url": { "type": "string", - "maxLength": 300 + "format": "uri", + "description": "The wallet-ownership widget link, on `select_counterparty_vasp` and `declare_self_hosted` while the transfer has an open widget session." } }, "required": [ - "actor_email", - "actor_name", - "comment", - "uuid" + "action" ] }, - "FraudCheckModuleReport": { + "TransactionTravelRule": { "type": "object", + "description": "A transaction's Travel Rule transfer, as `GET /v3/transactions/{transaction_id}/`, the `201` of `POST /v3/transactions/` and each row of `GET /v3/transactions/` return it. `direction`, `rail`, `counterparty_vasp`, `delivery`, `discovery`, `reason` and `next_actions` have the same shape as on `PATCH /v3/transactions/{transaction_id}/travel-rule/`, so one read of a transaction says why its transfer is stuck and what moves it on.", "properties": { - "module": { - "$ref": "#/components/schemas/FraudExcludeEnum" + "uuid": { + "type": "string", + "format": "uuid" }, "status": { - "$ref": "#/components/schemas/FraudStatusEnum" + "type": "string" }, - "reason": { + "protocol": { "type": "string", "nullable": true }, - "latency_ms": { + "required": { + "type": "boolean" + }, + "obligations_count": { "type": "integer" }, - "freshness": { - "type": "string", - "nullable": true + "originator_data": { + "type": "object", + "description": "Originator party. For an inbound transfer the counterparty's values are masked until ownership is confirmed." }, - "billable_units": { - "type": "integer" - } - }, - "required": [ - "billable_units", - "freshness", - "latency_ms", - "module", - "reason", - "status" - ] - }, - "CaseDuplicateEntityTypeEnum": { - "enum": [ - "session", - "business_session", - "transaction" - ], - "type": "string", - "description": "* `session` - Session\n* `business_session` - Business Session\n* `transaction` - Transaction" - }, - "FraudClientCredentialsErrorMessage": { - "type": "object", - "properties": { - "detail": { - "type": "string", - "default": "Authentication failed: Invalid client access token." - } - } - }, - "FraudAvailabilityEnum": { - "enum": [ - "available", - "not_requested", - "no_reference", - "insufficient_history", - "unsupported_region", - "source_unavailable" - ], - "type": "string", - "description": "* `available` - Available\n* `not_requested` - Not Requested\n* `no_reference` - No Reference\n* `insufficient_history` - Insufficient History\n* `unsupported_region` - Unsupported Region\n* `source_unavailable` - Source Unavailable" - }, - "CasePublicationPolicyDraftResponse": { - "type": "object", - "properties": { - "draft": { - "allOf": [ - { - "$ref": "#/components/schemas/CasePublicationPolicyDraft" - } - ], - "nullable": true - } - }, - "required": [ - "draft" - ] - }, - "FraudCheckAttributesRequest": { - "type": "object", - "description": "Corroborating attributes. All optional; they widen module coverage.", - "properties": { - "first_name": { - "type": "string", - "maxLength": 128 + "beneficiary_data": { + "type": "object", + "description": "Beneficiary party. For an inbound transfer the counterparty's values are masked until ownership is confirmed." }, - "last_name": { - "type": "string", - "maxLength": 128 + "name_match": { + "type": "object", + "nullable": true, + "description": "The name comparison that decided the transfer (score and strictness). Null until one ran. The compared names are masked together with the parties." }, - "full_name": { - "type": "string", - "maxLength": 256 + "data_match": { + "$ref": "#/components/schemas/TravelRuleDataMatch" }, - "date_of_birth": { - "type": "string", - "format": "date" + "mismatched_fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Shortcut for `data_match.mismatched_fields`. Empty when nothing differs." }, - "nationality": { + "data_unmasked": { + "$ref": "#/components/schemas/TravelRuleDataUnmasked" + }, + "data_masked": { + "type": "boolean", + "description": "True while the counterparty's personal data is masked in this response." + }, + "metadata": { + "type": "object" + }, + "direction": { "type": "string", - "maxLength": 3 + "enum": [ + "INBOUND", + "OUTBOUND" + ], + "description": "Which side of the exchange this transfer is." }, - "gender": { + "rail": { "type": "string", - "maxLength": 16 + "nullable": true, + "description": "The rail the transfer went over: `INTERNAL`, `TRP`, `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA` or `EMAIL` (`TEST_VASP` on a sandbox application, when the Didit Test VASP answered), or `null` until a rail is chosen." + }, + "counterparty_vasp": { + "type": "object", + "nullable": true, + "properties": { + "name": { + "type": "string" + }, + "lei": { + "type": "string" + }, + "dd_score": { + "type": "integer", + "description": "Due-diligence score from 0 to 100, where higher is better. Same scale as in the VASP directory." + }, + "dd_score_breakdown": { + "type": "object", + "description": "Per-component contributions to dd_score, one object per component as in the VASP directory (jurisdiction, verification, protocol, lei, history, essentials, responsiveness), each {points, max, reason, detail}. {} until the counterparty has been assessed.", + "additionalProperties": { + "type": "object", + "properties": { + "points": { + "type": "number", + "description": "Points awarded for this component." + }, + "max": { + "type": "integer", + "description": "Maximum points this component can award." + }, + "reason": { + "type": "string", + "description": "Machine-readable code of the component's verdict." + }, + "detail": { + "type": "string", + "description": "Human-readable reason for the awarded points." + } + } + } + }, + "dd_assessed_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "description": "When the due-diligence assessment last ran. Null until the counterparty has been assessed." + } + }, + "description": "The counterparty's directory entry, or `null` when the transfer links none." + }, + "delivery": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/TravelRuleDelivery" + } + ], + "description": "The newest message the transfer owes another party. `null` when it has owed none, for example below `threshold_amount` or for a declared self-hosted wallet." + }, + "discovery": { + "type": "array", + "description": "How Didit looked for the counterparty, one entry per step that ran. Empty when no discovery ran, for example on an inbound transfer.", + "items": { + "$ref": "#/components/schemas/TravelRuleDiscoveryStep" + } }, - "address": { + "reason": { "type": "string", - "maxLength": 512 + "description": "Why a `COUNTERPARTY_VASP_NOT_FOUND` or `COUNTERPARTY_VASP_NOT_REACHABLE` transfer is stuck; `\"\"` for any other status. A failed delivery decides it first, on `COUNTERPARTY_VASP_NOT_REACHABLE` only: `email_undeliverable` (the mail provider refused the compliance email and no address is left to try: when it was sent, on a reminder, or as a permanent bounce reported later) or `delivery_failed` (the rail that was delivering failed for good: a network rail refused the message or ran out of retries, a `TRP` identity host did not answer, or the counterparty's `TRP` host did not accept the inquiry). Otherwise the `discovery` trace decides it: `awaiting_end_user` (no step named a counterparty Didit can reach), `directory_match_no_route` (a step named a directory VASP that has no route), `picked_member_without_proven_wallet` (the end user picked a Didit customer that has not proven control of the wallet), or `no_directory_match` when the trace gives no other reason. A resend moves the transfer on, so the reason clears. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck)." }, - "city": { - "type": "string", - "maxLength": 128 + "next_actions": { + "type": "array", + "description": "What moves the transfer on; `[]` for any status but `COUNTERPARTY_VASP_NOT_FOUND` or `COUNTERPARTY_VASP_NOT_REACHABLE`. `email_undeliverable` offers `update_compliance_email`, then `resend`. `delivery_failed` offers `resend`. A discovery reason offers `select_counterparty_vasp`, `declare_self_hosted`, `add_vasp_hint` and `resend`.", + "items": { + "$ref": "#/components/schemas/TravelRuleNextAction" + } }, - "state": { - "type": "string", - "maxLength": 128 + "chain_details": { + "$ref": "#/components/schemas/TravelRuleChainDetails" + } + } + }, + "NetworkListCounts": { + "type": "object", + "description": "Network counts for the list's views and filter options, each network counted once. Returned only when `summary` is not `false`. `scopes` count every network visible to the application. `views` count the visible networks inside the selected `scope`. `facets` count the networks in the selected `scope` and `status` view (dismissed networks are left out unless `status` includes them). The other list filters do not change these counts.", + "properties": { + "scopes": { + "type": "object", + "description": "Visible networks by product.", + "properties": { + "all": { + "type": "integer" + }, + "identity": { + "type": "integer", + "description": "Every other network." + }, + "transactions": { + "type": "integer", + "description": "Networks whose members are all transaction parties." + } + } }, - "postal_code": { - "type": "string", - "maxLength": 32 + "views": { + "type": "object", + "description": "Visible networks inside the selected `scope`, by status, plus two saved views.", + "properties": { + "all": { + "type": "integer" + }, + "active": { + "type": "integer" + }, + "in_review": { + "type": "integer" + }, + "resolved": { + "type": "integer" + }, + "dismissed": { + "type": "integer" + }, + "mine": { + "type": "integer", + "description": "Networks assigned to the calling console member. Always 0 for an API key." + }, + "high_risk": { + "type": "integer", + "description": "Networks with a risk score of 80 or more that are not dismissed." + } + } + }, + "facets": { + "type": "object", + "description": "Counts for each filter option, inside the selected `scope` and status view.", + "properties": { + "total": { + "type": "integer", + "description": "Networks in the selected `scope` and status view." + }, + "last_activity_days": { + "type": "object", + "description": "Networks with activity in the last 7, 30 and 90 days, keyed by the number of days.", + "properties": { + "7": { + "type": "integer" + }, + "30": { + "type": "integer" + }, + "90": { + "type": "integer" + } + } + }, + "risk_min": { + "type": "object", + "description": "Networks with a risk score of at least 50 and at least 80, keyed by the threshold.", + "properties": { + "50": { + "type": "integer" + }, + "80": { + "type": "integer" + } + } + }, + "signal_types": { + "type": "object", + "description": "Networks with at least one signal of each type.", + "properties": { + "ip_address": { + "type": "integer" + }, + "device": { + "type": "integer" + }, + "face": { + "type": "integer" + }, + "document_number": { + "type": "integer" + }, + "phone": { + "type": "integer" + }, + "email": { + "type": "integer" + }, + "address": { + "type": "integer" + } + } + }, + "priority": { + "type": "object", + "description": "Networks by priority. `none` counts networks without a priority.", + "properties": { + "low": { + "type": "integer" + }, + "medium": { + "type": "integer" + }, + "high": { + "type": "integer" + }, + "none": { + "type": "integer" + } + } + }, + "assignee": { + "type": "object", + "description": "Networks by assignee.", + "properties": { + "me": { + "type": "integer", + "description": "Assigned to the calling console member. Always 0 for an API key." + }, + "unassigned": { + "type": "integer", + "description": "Assigned to nobody." + }, + "users": { + "type": "array", + "description": "One entry per assignee, most networks first.", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + }, + "full_name": { + "type": "string", + "description": "The assignee's name, or their email when no name is set." + }, + "email": { + "type": "string" + }, + "count": { + "type": "integer" + } + } + } + } + } + }, + "has_case": { + "type": "integer", + "description": "Networks this application opened a case for." + }, + "min_size": { + "type": "object", + "description": "Networks with at least 10 members, keyed by the minimum size.", + "properties": { + "10": { + "type": "integer" + } + } + } + } } } }, - "NetworkNote": { + "NetworkTag": { "type": "object", - "required": [ - "uuid", - "actor_email", - "comment", - "client_reference", - "created_at" - ], + "description": "One of your application's tags on the network.", "properties": { "uuid": { "type": "string", "format": "uuid", - "readOnly": true + "description": "The tag's UUID, the same value you pass as `tag_uuid`." }, - "actor_email": { + "name": { + "type": "string" + }, + "color": { "type": "string", - "format": "email", - "readOnly": true, - "description": "Authenticated author, assigned by the server." + "description": "Hex color, for example `#6366F1`." }, - "comment": { + "source": { "type": "string", - "minLength": 1, - "maxLength": 10000 + "enum": [ + "didit", + "custom" + ], + "description": "`didit` for a Didit preset tag, `custom` for a tag your team created." }, - "client_reference": { + "added_by": { "type": "string", - "format": "uuid" + "nullable": true, + "description": "Email of the console member who added the tag. Null when an API key added it." }, - "created_at": { + "added_at": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "date-time" } } }, - "CaseActionEntityTypeEnum": { - "enum": [ - "user", - "business" - ], - "type": "string", - "description": "* `user` - user\n* `business` - business" - }, - "CaseRoutingCondition": { + "NetworkOutcomes": { "type": "object", + "description": "Where the network's people and businesses stand. Each member you may see counts once, by the status of their most recent verification that reached a decision. Transaction parties are not counted.", "properties": { - "field": { - "type": "string", - "enum": [ - "source", - "priority", - "tags" - ] + "approved": { + "type": "integer", + "description": "Members whose most recent decided verification was approved." }, - "operator": { - "type": "string", - "enum": [ - "equals", - "in", - "contains" - ] + "declined": { + "type": "integer", + "description": "Members whose most recent decided verification was declined." }, - "value": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "in_review": { + "type": "integer", + "description": "Members whose most recent decided verification is in review." + }, + "total": { + "type": "integer", + "description": "Members with at least one verification, decided or not. A member whose verifications are all undecided (not started, in progress, expired or abandoned) counts here and in no other key." } - }, - "required": [ - "field", - "operator", - "value" - ], - "description": "Source and priority support equals or in with supported values. Tags support contains with one non-empty tag." + } }, - "RoutingPublicationPolicyRevision": { + "NetworkPatternScore": { "type": "object", + "description": "How alike one kind of the members' data is across the whole network. A pattern is not a signal: no shared value links the members.", "properties": { - "version": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "snapshot": {}, - "actor_email": { + "type": { "type": "string", - "format": "email", + "enum": [ + "background" + ], + "description": "`background`: the mean similarity of every pair of member selfie backgrounds, with the person masked out. It reads the newest selfie of each person, at most the 50 newest, and leaves out age assurance sessions. It covers the whole network, not only the members of your applications." + }, + "score": { + "type": "number", "nullable": true, - "maxLength": 254 + "minimum": 0, + "maximum": 1, + "description": "From 0 to 1, rounded to 4 decimals. Null when `reason` is set." }, - "published_at": { - "type": "string", - "format": "date-time" - } - }, - "required": [ - "published_at", - "snapshot", - "version" - ] - }, - "CasePublicationPolicyRevision": { - "type": "object", - "properties": { - "version": { + "members": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "nullable": true, + "description": "How many members' selfies the score compared. A selfie that cannot be read, or shows too little background, is not compared. Null while `computing`; 0 or 1 with `not_enough_selfies`." }, - "snapshot": {}, - "actor_email": { + "reason": { "type": "string", - "format": "email", + "enum": [ + "computing", + "not_enough_selfies" + ], "nullable": true, - "maxLength": 254 + "description": "Why there is no score. `computing`: the score for the current members is being computed; read the network again later. `not_enough_selfies`: fewer than two members have a usable selfie. Null when there is a score." }, - "published_at": { + "computed_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "nullable": true, + "description": "When the score was computed. Null while `computing`." } - }, - "required": [ - "published_at", - "snapshot", - "version" - ] + } }, - "FraudCheckIdentityRelationship": { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/FraudResultEnum" - }, - "reference_type": { - "type": "string" - }, - "country": { - "type": "string" - }, - "availability": { - "$ref": "#/components/schemas/FraudAvailabilityEnum" + "NetworkDetailResponse": { + "description": "The network detail, plus the organization's sharing mode, whether cross-organization signals apply to it, your application's tags on the network, member outcomes and pattern scores.", + "allOf": [ + { + "$ref": "#/components/schemas/NetworkDetail" }, - "reason_codes": { - "type": "array", - "items": { - "type": "string" + { + "type": "object", + "properties": { + "sharing_mode": { + "type": "string", + "enum": [ + "off", + "private", + "global" + ], + "description": "The organization's cross-organization sharing mode. Only `global` compares networks across organizations; `private` and `off` keep them to the organization's own history.", + "example": "global" + }, + "cross_org_available": { + "type": "boolean", + "description": "Whether cross-organization signals apply to this organization. They need both the Identity Enterprise plan and the `global` sharing mode.", + "example": true + }, + "cross_org_unavailable_reason": { + "type": "string", + "enum": [ + "plan", + "sharing_mode" + ], + "nullable": true, + "description": "Why cross-organization signals do not apply, checked in this order. `plan`: the organization's Identity plan does not include the cross-organization fraud network (it needs Identity Enterprise), whatever the sharing mode. `sharing_mode`: the plan includes it, but the sharing mode is `private` or `off`. Null when `cross_org_available` is `true`.", + "example": null + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkTag" + }, + "description": "Your application's tags on the network, oldest first. Each application sees only its own tags. `fraud_tags` is separate and unchanged: the tags the network's members carry." + }, + "outcomes": { + "$ref": "#/components/schemas/NetworkOutcomes" + }, + "patterns": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkPatternScore" + }, + "description": "Pattern scores over the whole network. Today one item, `background`." + } } } - }, - "required": [ - "availability", - "country", - "reason_codes", - "reference_type", - "result" ] }, - "CasePublicationPaginatedPolicyRevisionList": { + "NetworkTagRequest": { "type": "object", "required": [ - "count", - "results" + "tag_uuid" ], "properties": { - "count": { - "type": "integer", - "example": 123 - }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?offset=400&limit=100" - }, - "previous": { + "tag_uuid": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?offset=200&limit=100" - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CasePublicationPolicyRevision" - } + "format": "uuid", + "description": "UUID of one of your application's tags." } + }, + "example": { + "tag_uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7" } }, - "CaseDuplicateBlankEnum": { - "enum": [ - "" - ] - }, - "FraudCheckPlan": { + "NetworkTimeline": { + "title": "Default timeline (no contract)", "type": "object", + "description": "Without `contract`: the network's own events, oldest first, not paginated.", "properties": { - "profile_id": { - "type": "string" - }, - "profile_version": { - "type": "string" - }, - "modules": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/FraudCheckPlannedModule" + "$ref": "#/components/schemas/NetworkTimelineEvent" } - }, - "estimated_price": { - "type": "string", - "nullable": true - }, - "currency": { - "type": "string", - "nullable": true - }, - "is_sandbox": { - "type": "boolean" - }, - "can_afford": { - "type": "boolean" } - }, - "required": [ - "can_afford", - "currency", - "estimated_price", - "is_sandbox", - "modules", - "profile_id", - "profile_version" - ] + } }, - "CaseReviewTeamUpdate": { + "NetworkTimelinePage": { + "title": "Typed timeline page (contract=2)", "type": "object", + "description": "With `contract=2`: one page of typed events, newest first.", "properties": { - "name": { - "type": "string", - "maxLength": 100 - }, - "description": { - "type": "string", - "maxLength": 500 - }, - "members": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/CaseReviewTeamMember" - }, - "maxItems": 250 + "$ref": "#/components/schemas/NetworkTypedTimelineEvent" + } }, - "is_active": { - "type": "boolean" + "next_cursor": { + "type": "string", + "nullable": true, + "description": "Pass as `cursor` to read the next page. Null on the last page." } } }, - "RoutingPublicationPolicyDraft": { + "NetworkTypedTimelineEvent": { "type": "object", + "description": "One typed timeline event: the four default fields, plus its id, when Didit stored it, the member account and record it belongs to, and the detail block of its event type. The other detail blocks are null.", "properties": { - "revision": { + "id": { "type": "string", - "format": "uuid" - }, - "base_version": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "description": "`:`, unique within the timeline.", + "example": "session:3fa85f64-5717-4562-b3fc-2c963f66afa6" }, - "changes": {}, - "actor_email": { + "event_type": { "type": "string", - "format": "email", - "nullable": true, - "maxLength": 254 + "enum": [ + "network_detected", + "signal_observed", + "network_active", + "network_in_review", + "network_resolved", + "network_dismissed", + "verification_attempt", + "transaction", + "login", + "rule_hit", + "case_linked", + "note_added" + ] }, - "updated_at": { + "occurred_at": { "type": "string", "format": "date-time", - "readOnly": true - } - }, - "required": [ - "base_version", - "updated_at" - ] - }, - "FraudCheckRequestRequest": { - "type": "object", - "properties": { - "client_reference": { - "type": "string", - "minLength": 1, - "description": "Your own reference for this check. Doubles as the idempotency key: reusing it with the same body returns the original check without running or billing the modules again; reusing it with a different body returns 409.", - "maxLength": 255 + "description": "When it happened." }, - "profile": { + "ingested_at": { "type": "string", - "minLength": 1, - "default": "standard", - "description": "Orchestration profile id. Defaults to the Didit `standard` profile.", - "maxLength": 64 + "format": "date-time", + "description": "When Didit stored it." }, - "profile_version": { + "label": { "type": "string", - "minLength": 1, - "description": "Pin a frozen profile version. Defaults to the current version of the profile.", - "maxLength": 32 + "description": "A short English label, for example `Verification attempt 2 of 3`." }, - "subject": { - "$ref": "#/components/schemas/FraudCheckSubjectRequest" + "metadata": { + "type": "object", + "description": "For the network's own events, the same metadata as the default timeline. Empty otherwise." }, - "context": { - "$ref": "#/components/schemas/FraudCheckContextRequest" + "account": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineAccount" + } + ], + "nullable": true, + "description": "The member account. Null for the network's own events, `case_linked` and `note_added`." }, - "checks": { - "$ref": "#/components/schemas/FraudCheckModuleSelectionRequest" + "source_ref": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineSourceRef" + } + ], + "nullable": true, + "description": "The source record. Null for the network's own events and `note_added`." }, - "vendor_data": { - "type": "string", - "maxLength": 1000 + "verification_attempt": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineAttempt" + } + ], + "nullable": true, + "description": "Set on `verification_attempt` events." }, - "metadata": {} - }, - "required": [ - "subject" - ] - }, - "CaseDuplicateCaseSubjectInputRequest": { - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/CaseDuplicateTypeEnum" + "transaction": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineTransaction" + } + ], + "nullable": true, + "description": "Set on `transaction` events." }, - "uuid": { - "type": "string", - "format": "uuid" + "login": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineLogin" + } + ], + "nullable": true, + "description": "Set on `login` events." + }, + "rule_hit": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineRuleHit" + } + ], + "nullable": true, + "description": "Set on `rule_hit` events." + }, + "case_linked": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineCaseLinked" + } + ], + "nullable": true, + "description": "Set on `case_linked` events." + }, + "note_added": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineNoteAdded" + } + ], + "nullable": true, + "description": "Set on `note_added` events. Never returned to an API key." } - }, - "required": [ - "type", - "uuid" - ] + } }, - "CaseReviewTeamMember": { + "NetworkTimelineAccount": { "type": "object", + "description": "The member account the event belongs to.", "properties": { - "user_id": { + "node_id": { "type": "string", - "format": "uuid" + "description": "`:`, where kind is `vendor_user`, `vendor_business` or `transaction_party`.", + "example": "vendor_user:3fa85f64-5717-4562-b3fc-2c963f66afa6" }, - "availability": { + "node_type": { "type": "string", "enum": [ - "available", - "unavailable" - ], - "default": "available" - }, - "capacity": { - "type": "integer", - "minimum": 1, - "maximum": 1000, - "default": 10 + "account", + "business_account", + "transaction_party" + ] }, - "skills": { - "type": "array", - "items": { - "type": "string", - "maxLength": 64 - }, - "maxItems": 25 + "uuid": { + "type": "string", + "format": "uuid" }, - "available_from": { + "vendor_data": { "type": "string", - "format": "date-time", "nullable": true }, - "available_until": { + "display_name": { "type": "string", - "format": "date-time", "nullable": true + }, + "ownership": { + "type": "string", + "enum": [ + "owned", + "organization" + ], + "description": "`owned` when the record belongs to the application you called (for a console member, to an application they were granted). `organization` when it belongs to another application of your organization." + }, + "route": { + "$ref": "#/components/schemas/NetworkTimelineRoute" } - }, - "required": [ - "user_id" - ] + } }, - "CaseRoutingPreviewRequest": { + "NetworkTimelineSourceRef": { "type": "object", + "description": "The record the event was read from.", "properties": { - "source": { + "kind": { "type": "string", "enum": [ - "MANUAL", - "AML_SCREENING", - "TRANSACTION_RULE", - "WORKFLOW_BUILDER" + "session", + "business_session", + "transaction", + "monitoring_event", + "case" ] }, - "priority": { + "id": { + "type": "string", + "format": "uuid" + }, + "ownership": { "type": "string", "enum": [ - "LOW", - "MEDIUM", - "HIGH" + "owned", + "organization" ], - "default": "MEDIUM" + "description": "`owned` when the record belongs to the application you called (for a console member, to an application they were granted). `organization` when it belongs to another application of your organization." }, - "tags": { - "type": "array", - "items": { - "type": "string", - "maxLength": 64 - }, - "maxItems": 50 + "route": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineRoute" + } + ], + "description": "Absent for `monitoring_event` and `case`." + } + } + }, + "NetworkTimelineRoute": { + "type": "object", + "description": "Where the record opens: its kind, its UUID and the application it belongs to.", + "properties": { + "kind": { + "type": "string", + "description": "`user`, `business` or `transaction_party` for an account; `session`, `business_session` or `transaction` for a source record." }, - "rule_uuid": { + "id": { "type": "string", "format": "uuid" }, - "draft": { - "$ref": "#/components/schemas/CaseRoutingRuleCreate" + "application_id": { + "type": "string", + "format": "uuid" } - }, - "required": [ - "source" - ] - }, - "FraudExcludeEnum": { - "enum": [ - "normalization", - "email_risk", - "email_social", - "phone_social", - "phone_risk", - "ip_risk", - "database_validation", - "networks_same_org", - "networks_cross_org", - "biometric_identity" - ], - "type": "string", - "description": "* `normalization` - Normalization\n* `email_risk` - Email Risk\n* `email_social` - Email Social\n* `phone_social` - Phone Social\n* `phone_risk` - Phone Risk\n* `ip_risk` - Ip Risk\n* `database_validation` - Database Validation\n* `networks_same_org` - Networks Same Org\n* `networks_cross_org` - Networks Cross Org\n* `biometric_identity` - Biometric Identity" + } }, - "CaseDuplicateCaseLinkCreateRequest": { + "NetworkTimelineAttempt": { "type": "object", + "description": "One verification session of the account.", "properties": { - "entity_type": { - "$ref": "#/components/schemas/CaseDuplicateEntityTypeEnum" + "session_id": { + "type": "string", + "format": "uuid", + "description": "The session UUID, or the business session UUID for a company." }, - "entity_uuid": { + "session_number": { + "type": "integer", + "nullable": true + }, + "attempt": { + "type": "integer", + "description": "This session's position among the account's sessions, in creation order." + }, + "attempt_count": { + "type": "integer", + "description": "How many sessions the account has." + }, + "status": { "type": "string", - "format": "uuid" + "description": "The session status, for example `Approved` or `Declined`." + }, + "status_changed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "decline_reasons": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkTimelineDeclineReason" + }, + "description": "For a declined session only: its warnings, oldest first, once per warning. Empty otherwise." } - }, - "required": [ - "entity_type", - "entity_uuid" - ] - }, - "CaseDuplicateStatusEnum": { - "enum": [ - "OPEN", - "AWAITING_USER", - "RESOLVED" - ], - "type": "string", - "description": "* `OPEN` - Open\n* `AWAITING_USER` - Awaiting user\n* `RESOLVED` - Resolved" - }, - "FraudBandEnum": { - "enum": [ - "low", - "medium", - "high", - "unknown" - ], - "type": "string", - "description": "* `low` - Low\n* `medium` - Medium\n* `high` - High\n* `unknown` - Unknown" - }, - "CaseDuplicatePriorityEnum": { - "enum": [ - "LOW", - "MEDIUM", - "HIGH" - ], - "type": "string", - "description": "* `LOW` - Low\n* `MEDIUM` - Medium\n* `HIGH` - High" + } }, - "FraudStatusEnum": { - "enum": [ - "planned", - "completed", - "skipped", - "failed" - ], - "type": "string", - "description": "* `planned` - Planned\n* `completed` - Completed\n* `skipped` - Skipped\n* `failed` - Failed" + "NetworkTimelineDeclineReason": { + "type": "object", + "properties": { + "risk": { + "type": "string", + "description": "The warning code." + }, + "feature": { + "type": "string", + "description": "The feature that raised the warning." + }, + "short_description": { + "type": "string" + } + } }, - "FraudCheckInput": { + "NetworkTimelineTransaction": { "type": "object", + "description": "A transaction a member account took part in, dated by its transaction date.", "properties": { - "reference": { + "transaction_id": { "type": "string", - "description": "Redacted handle for the submitted identifier." + "format": "uuid" }, - "type": { - "$ref": "#/components/schemas/FraudTypeEnum" + "transaction_number": { + "type": "integer", + "nullable": true }, - "namespace": { - "type": "object", - "additionalProperties": {} + "transaction_type": { + "type": "string" }, - "quality": { + "action_type": { "type": "string" }, - "normalization": { - "type": "object", - "additionalProperties": {} + "amount": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineAmount" + } + ], + "nullable": true, + "description": "Null when the transaction has no amount." }, - "availability": { - "$ref": "#/components/schemas/FraudAvailabilityEnum" + "status": { + "type": "string" + }, + "decision_reason_code": { + "type": "string", + "nullable": true + }, + "decision_reason_label": { + "type": "string", + "nullable": true + }, + "party_role": { + "type": "string", + "description": "The account's role in the transaction: `APPLICANT`, `REMITTER`, `BENEFICIARY` or `COUNTERPARTY`." } - }, - "required": [ - "availability", - "namespace", - "normalization", - "quality", - "reference", - "type" - ] - }, - "CustomerMessagingPurposesEnum": { - "enum": [ - "identity_document", - "proof_of_address", - "business_document", - "confirm_details", - "review_in_progress" - ], - "type": "string", - "description": "* `identity_document` - identity_document\n* `proof_of_address` - proof_of_address\n* `business_document` - business_document\n* `confirm_details` - confirm_details\n* `review_in_progress` - review_in_progress" - }, - "FraudTypeEnum": { - "enum": [ - "email", - "phone", - "national_id", - "tax_number", - "passport_number", - "driving_licence_number", - "residence_permit_number", - "voter_id", - "external_id" - ], - "type": "string", - "description": "* `email` - Email\n* `phone` - Phone\n* `national_id` - National Id\n* `tax_number` - Tax Number\n* `passport_number` - Passport Number\n* `driving_licence_number` - Driving Licence Number\n* `residence_permit_number` - Residence Permit Number\n* `voter_id` - Voter Id\n* `external_id` - External Id" + } }, - "CaseDuplicateCaseSubjectInput": { + "NetworkTimelineAmount": { "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/CaseDuplicateTypeEnum" + "value": { + "type": "string", + "description": "Decimal amount as a string." }, - "uuid": { + "currency": { + "type": "string" + }, + "direction": { "type": "string", - "format": "uuid" + "nullable": true } - }, - "required": [ - "type", - "uuid" - ] + } }, - "CaseReviewTeamCreate": { + "NetworkTimelineLogin": { "type": "object", + "description": "A sign-in event you posted for a person through the events API.", "properties": { - "name": { + "action": { "type": "string", - "maxLength": 100 + "description": "`.`: `auth.login` or `security.login_succeeded` for a sign-in, `auth.login_failed` or `security.login_failed` for a failed one." }, - "description": { + "succeeded": { + "type": "boolean" + }, + "status": { "type": "string", - "maxLength": 500 + "description": "The event's status." }, - "members": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseReviewTeamMember" - }, - "maxItems": 250 + "ip": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineIP" + } + ], + "nullable": true, + "description": "Null when the event has no IP address." }, - "is_active": { - "type": "boolean" + "device": { + "allOf": [ + { + "$ref": "#/components/schemas/NetworkTimelineDevice" + } + ], + "nullable": true, + "description": "Null when the event has no device fingerprint or context." + }, + "is_first_login": { + "type": "boolean", + "description": "The account's first successful sign-in on record." + }, + "is_new_device": { + "type": "boolean", + "description": "A successful sign-in from a device none of the account's earlier successful sign-ins came from. Never true on the first sign-in." } - }, - "required": [ - "name", - "members" - ] + } }, - "CaseDuplicateCaseDuplicateQueryRequest": { + "NetworkTimelineIP": { "type": "object", "properties": { - "subject": { - "$ref": "#/components/schemas/CaseDuplicateCaseSubjectInputRequest" + "address": { + "type": "string" }, - "links": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseDuplicateCaseLinkCreateRequest" - } + "country_code": { + "type": "string", + "nullable": true }, - "offset": { - "type": "integer", - "maximum": 1000000, - "minimum": 0, - "default": 0 + "city": { + "type": "string", + "nullable": true + }, + "connection_type": { + "type": "string", + "nullable": true, + "description": "The connection type label for the address, as free text." + }, + "is_vpn_or_tor": { + "type": "boolean" + }, + "is_data_center": { + "type": "boolean" } } }, - "CaseDuplicateResolutionEnum": { - "enum": [ - "FALSE_POSITIVE", - "VALID_THREAT" - ], - "type": "string", - "description": "* `FALSE_POSITIVE` - False positive\n* `VALID_THREAT` - Valid threat" + "NetworkTimelineDevice": { + "type": "object", + "properties": { + "fingerprint": { + "type": "string", + "nullable": true + }, + "label": { + "type": "string", + "nullable": true, + "description": "A readable device label built from the event's device context." + } + } }, - "CasePublicationPolicyDraftWriteRequest": { + "NetworkTimelineRuleHit": { "type": "object", + "description": "A live rule that matched a member's transaction or event. Test-mode matches are not included.", "properties": { - "expected_version": { - "type": "integer", - "minimum": 0 + "rule_id": { + "type": "string", + "format": "uuid" }, - "expected_revision": { + "rule_title": { + "type": "string" + }, + "rule_category": { + "type": "string" + }, + "rule_source": { + "type": "string" + }, + "severity": { "type": "string", - "format": "uuid", "nullable": true }, - "changes": { - "type": "object", - "additionalProperties": {} + "score_delta": { + "type": "integer" + }, + "status_target": { + "type": "string", + "nullable": true + }, + "action_summary": { + "type": "string", + "nullable": true } - }, - "required": [ - "changes", - "expected_revision", - "expected_version" - ] + } }, - "FraudCheckSubjectRequest": { + "NetworkTimelineCaseLinked": { "type": "object", + "description": "A case opened for the network in your organization. `source_ref.ownership` says whether it belongs to the application you called.", "properties": { - "selfie": { + "case_id": { "type": "string", - "minLength": 1, - "description": "Base64 JPEG, PNG or WebP selfie, up to 6 MB. No reference image or URL.", - "maxLength": 8388672 + "format": "uuid" }, - "identifiers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FraudCheckIdentifierRequest" - }, - "description": "One or more typed identifiers. One is enough.", - "maxItems": 10, - "minItems": 1 + "case_number": { + "type": "string", + "nullable": true }, - "attributes": { - "$ref": "#/components/schemas/FraudCheckAttributesRequest" + "status": { + "type": "string" + }, + "author_email": { + "type": "string", + "nullable": true + }, + "author_name": { + "type": "string", + "nullable": true } - }, - "required": [ - "identifiers" - ] + } }, - "RoutingPublicationRoutingCondition": { + "NetworkTimelineNoteAdded": { "type": "object", + "description": "A note left on the network. The note text is not included.", "properties": { - "field": { - "$ref": "#/components/schemas/RoutingPublicationFieldEnum" - }, - "operator": { - "$ref": "#/components/schemas/RoutingPublicationOperatorEnum" + "note_id": { + "type": "string", + "format": "uuid" }, - "value": {} - }, - "required": [ - "field", - "operator", - "value" - ] + "author_email": { + "type": "string" + } + } }, - "CaseActionCaseActionReceipt": { + "FeatureStatusOverride": { "type": "object", + "description": "One override of a check (feature) status made with `PATCH /v3/session/{sessionId}/features/{nodeId}/update-status/`: what it changes, whether it applied or still waits for the person to finish the flow, who asked and why.", + "required": [ + "id", + "node_id", + "feature_type", + "feature_model", + "previous_status", + "new_status", + "state", + "forced", + "reason", + "requested_by", + "requested_at", + "applied_at", + "cancelled_at", + "cancel_reason" + ], "properties": { - "uuid": { + "id": { "type": "string", "format": "uuid", - "readOnly": true + "description": "Identifier of the override. Pass it to the cancel endpoint.", + "example": "7c9e6679-7425-40de-944b-e07fc1f90ae7" }, - "client_reference": { + "node_id": { "type": "string", - "format": "uuid" + "description": "The workflow node the request named (`default` for a step without a node).", + "example": "feature_ocr_1" }, - "action": { + "feature_type": { "type": "string", - "maxLength": 32 + "description": "The `feature_type` the request passed, or an empty string when it passed none.", + "example": "" }, - "status": { - "$ref": "#/components/schemas/CaseActionStatusEnum" + "feature_model": { + "type": "string", + "description": "The kind of check that was overridden, for example `KYC`, `Face`, `AML` or `GeolocationCheck`.", + "example": "KYC" + }, + "previous_status": { + "type": "string", + "enum": [ + "Not Finished", + "Approved", + "Declined", + "In Review", + "Resub Requested" + ], + "description": "The check's status when the override was requested.", + "example": "In Review" + }, + "new_status": { + "type": "string", + "enum": [ + "Approved", + "Declined", + "In Review" + ], + "description": "The status the override sets.", + "example": "Approved" + }, + "state": { + "type": "string", + "enum": [ + "applied", + "pending_until_flow_finishes", + "cancelled" + ], + "description": "`applied`: the check has the new status. `pending_until_flow_finishes`: the override waits and applies when the person finishes the flow. `cancelled`: it waited and never applied (see `cancel_reason`).", + "example": "applied" + }, + "forced": { + "type": "boolean", + "description": "`true` when the override applied at once while the person was still in the flow.", + "example": false + }, + "reason": { + "type": "string", + "nullable": true, + "description": "The `comment` sent with the request, or `null`.", + "example": "Document manually verified by compliance." + }, + "requested_by": { + "type": "object", + "nullable": true, + "description": "Who requested the override, or `null` when that is not known.", + "properties": { + "email": { + "type": "string", + "format": "email", + "nullable": true, + "description": "Email of the console user, or `null` for API keys and the system.", + "example": "reviewer@example.com" + }, + "name": { + "type": "string", + "nullable": true, + "description": "Display name of the requester, or `null`.", + "example": "Alex Reviewer" + }, + "type": { + "type": "string", + "enum": [ + "CONSOLE_USER", + "API_KEY", + "SYSTEM" + ], + "description": "Who made the request: a console user, an API key, or Didit itself.", + "example": "CONSOLE_USER" + } + } }, - "result": {}, - "error": { + "requested_at": { "type": "string", - "maxLength": 255 + "format": "date-time", + "description": "When the override was requested.", + "example": "2026-10-13T09:15:00Z" }, - "actor_email": { + "applied_at": { "type": "string", - "format": "email", + "format": "date-time", "nullable": true, - "maxLength": 254 - }, - "attempts": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "description": "When the check took the new status, or `null` while it waits or once it is cancelled.", + "example": "2026-10-13T09:15:00Z" }, - "created_at": { + "cancelled_at": { "type": "string", "format": "date-time", - "readOnly": true + "nullable": true, + "description": "When a waiting override was cancelled, or `null`.", + "example": null }, - "updated_at": { + "cancel_reason": { "type": "string", - "format": "date-time", - "readOnly": true + "nullable": true, + "enum": [ + "cancelled_by_reviewer", + "replaced", + "check_changed", + "session_closed", + null + ], + "description": "Why a waiting override never applied, or `null`. `cancelled_by_reviewer`: cancelled with the cancel endpoint. `replaced`: a newer override on the same check replaced it. `check_changed`: the person redid the check before finishing the flow. `session_closed`: the session left the flow before the person finished it (abandoned, expired, or decided mid-flow).", + "example": null } - }, - "required": [ - "action", - "client_reference", - "created_at", - "status", - "updated_at", - "uuid" - ] + } }, - "FraudCheckContextRequest": { + "SandboxSimulateRequest": { "type": "object", + "description": "Force a sandbox session's status. Every field is optional, but at least one of `new_status`, `scenario` or `risk` is required.", "properties": { - "ip_address": { + "new_status": { "type": "string", - "minLength": 1 + "enum": [ + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned" + ], + "description": "The status to set. When you leave it out, it defaults to the default status of the risk case (`risk`, or the first risk the scenario's magic values carry), else to the scenario's default status.", + "example": "Declined" }, - "user_agent": { + "comment": { "type": "string", - "maxLength": 512 + "maxLength": 500, + "description": "Note added to the activity entry the status change records.", + "example": "Testing the decline path" }, - "country": { + "scenario": { "type": "string", - "description": "Country the subject claims to be in. Contextual only - never proof of identity ownership.", - "maxLength": 3 + "maxLength": 100, + "description": "A scenario slug from the catalog (`GET /v1/sandbox/scenarios/`). It supplies the default status and, without `risk`, the warning its magic values carry. An unknown slug returns `400`.", + "example": "decline_aml_hit" }, - "purpose": { + "risk": { "type": "string", - "maxLength": 128 + "maxLength": 128, + "description": "A risk case from the catalog. Its warning is added to the session, with a log type that follows `new_status`. An unknown value returns `400`.", + "example": "POSSIBLE_MATCH_FOUND" }, - "consent_obtained": { - "type": "boolean" + "node_id": { + "type": "string", + "maxLength": 128, + "description": "Workflow node the risk warning is attached to. Defaults to the session's current node.", + "example": "feature_aml_1" + }, + "send_webhooks": { + "type": "boolean", + "default": true, + "description": "`true` sends exactly the webhooks a real status change sends to the sandbox application's destinations: `status.updated`, plus the `user.status.updated` / `user.data.updated` (or `business.status.updated` / `business.data.updated`) events the change causes, for destinations subscribed to them. `false` changes the status without logging or sending any webhook: the missed-webhook recovery never re-sends that decision, the console still updates live, and the next status change of the session sends webhooks as usual.", + "example": true } } }, - "CustomerMessagingCaseCustomerResponseResult": { + "SandboxSimulateResponse": { "type": "object", - "properties": { - "responded_at": { - "type": "string", - "format": "date-time" - } - }, "required": [ - "responded_at" - ] - }, - "CustomerMessagingCaseRequestContact": { - "type": "object", + "session_id", + "previous_status", + "status", + "scenario", + "risk", + "send_webhooks" + ], "properties": { - "uuid": { + "session_id": { "type": "string", - "format": "uuid" + "format": "uuid", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }, - "email": { + "previous_status": { "type": "string", - "format": "email" + "enum": [ + "Not Started", + "In Progress", + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned", + "Kyc Expired", + "Resubmitted", + "Awaiting User" + ], + "description": "The session's status before the simulation.", + "example": "In Progress" }, - "verified_at": { + "status": { "type": "string", - "format": "date-time" - } - }, - "required": [ - "email", - "uuid", - "verified_at" - ] - }, - "FraudClientCredentialsForbiddenErrorMessage": { - "type": "object", - "properties": { - "detail": { + "enum": [ + "Approved", + "Declined", + "In Review", + "Expired", + "Abandoned" + ], + "description": "The status the simulation set.", + "example": "Declined" + }, + "scenario": { "type": "string", - "default": "Forbidden - User doesn't have permission" + "nullable": true, + "description": "The scenario slug you passed, or `null`.", + "example": "decline_aml_hit" + }, + "risk": { + "type": "string", + "nullable": true, + "description": "The risk case applied (`risk`, or the one taken from the scenario), or `null` when none.", + "example": "POSSIBLE_MATCH_FOUND" + }, + "send_webhooks": { + "type": "boolean", + "description": "The `send_webhooks` value the simulation applied.", + "example": false } } }, - "CasePublicationAssignmentMethodEnum": { - "enum": [ - "MANUAL", - "AUTOMATIC" - ], - "type": "string", - "description": "* `MANUAL` - Manual\n* `AUTOMATIC` - Automatic" - }, - "CaseDuplicateNullEnum": { - "enum": [ - null - ] - }, - "CustomerMessagingLanguageEnum": { - "enum": [ - "en", - "es", - "pt" - ], - "type": "string", - "description": "* `en` - en\n* `es` - es\n* `pt` - pt" - }, - "CaseDuplicateCaseFraudSourceContextRequest": { + "CaseTeamSummary": { "type": "object", + "nullable": true, + "description": "The review team that owns the case, or null when no team owns it. Routing sets it when a routing rule places the case with one of its teams. Change or clear it with `PATCH .../cases/{case_uuid}/` (`team`).", "properties": { - "check_id": { - "type": "string", - "format": "uuid" - }, - "request_id": { + "uuid": { "type": "string", "format": "uuid" }, - "source_section": { - "type": "string", - "minLength": 1, - "pattern": "^[A-Z][A-Z0-9_:-]{0,63}$" + "name": { + "type": "string" } - }, - "required": [ - "check_id", - "request_id", - "source_section" - ] + } }, - "FraudCheckPlannedModule": { + "CaseChecker": { "type": "object", + "nullable": true, + "description": "The member named to approve or reject the pending resolution. Set only while a four-eyes resolution waits on a named checker. Null when nothing is pending, while the pending resolution waits on the checker queue, and once the resolution is approved, rejected, or returned for a new review because the evidence changed.", "properties": { - "module": { - "$ref": "#/components/schemas/FraudExcludeEnum" - }, - "available": { - "type": "boolean" - }, - "availability_reason": { + "member_uuid": { "type": "string", - "nullable": true - }, - "will_run": { - "type": "boolean" + "format": "uuid", + "description": "The member's user uuid: the value you pass as `checker` when you resolve the case." }, - "selection_reason": { + "name": { "type": "string", - "nullable": true + "nullable": true, + "description": "The member's full name, or null when it is not known." }, - "estimated_price": { + "email": { "type": "string", - "nullable": true + "format": "email" } - }, - "required": [ - "availability_reason", - "available", - "estimated_price", - "module", - "selection_reason", - "will_run" - ] + } }, - "CustomerMessagingCaseRequestOptions": { + "ServiceStatusFeed": { "type": "object", + "description": "Public service status: the state of each Didit service, the overall state, and the incidents that are ongoing or were resolved in the last 7 days.", + "required": [ + "status", + "updated_at", + "services", + "incidents" + ], "properties": { - "contacts": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CustomerMessagingCaseRequestContact" - } + "status": { + "type": "string", + "enum": [ + "operational", + "degraded", + "down" + ], + "description": "The worst state of any service right now, decided by the ongoing incidents only.", + "example": "degraded" }, - "purposes": { + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When Didit built this response (ISO 8601, UTC). Responses are cached for 30 seconds, so it can be up to 30 seconds old.", + "example": "2026-10-13T09:41:07.512345Z" + }, + "services": { "type": "array", + "description": "One entry per service, every service always listed.", "items": { - "$ref": "#/components/schemas/CustomerMessagingPurposesEnum" + "$ref": "#/components/schemas/ServiceStatusService" } }, - "languages": { + "incidents": { "type": "array", + "description": "Newest `started_at` first. Every ongoing incident, plus the incidents resolved in the last 7 days (at most 50 resolved incidents). Empty when nothing happened in that time.", "items": { - "$ref": "#/components/schemas/CustomerMessagingLanguagesEnum" + "$ref": "#/components/schemas/ServiceStatusIncident" } } - }, - "required": [ - "contacts", - "languages", - "purposes" - ] + } }, - "CaseCollaborationPaginatedCaseNoteList": { + "ServiceStatusService": { "type": "object", "required": [ - "count", - "results" + "key", + "name", + "status" ], "properties": { - "count": { - "type": "integer", - "example": 123 - }, - "next": { + "key": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?offset=400&limit=100" + "enum": [ + "service_id_verification", + "service_liveness_face_match", + "service_aml_screening", + "service_kyb_registries", + "service_database_validation", + "service_phone_delivery", + "service_email_delivery", + "service_transaction_monitoring" + ], + "description": "Stable service key. Match on this, never on the display name. `service_id_verification`: ID verification, `service_liveness_face_match`: Liveness and face match, `service_aml_screening`: AML screening, `service_kyb_registries`: KYB registries, `service_database_validation`: Database validation, `service_phone_delivery`: Phone verification delivery, `service_email_delivery`: Email verification delivery, `service_transaction_monitoring`: Transaction monitoring.", + "example": "service_database_validation" }, - "previous": { + "name": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?offset=200&limit=100" + "description": "English display name of the service.", + "example": "Database validation" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseCollaborationCaseNote" - } + "status": { + "type": "string", + "enum": [ + "operational", + "degraded", + "down" + ], + "description": "`operational` when no incident is ongoing for the service, otherwise the worst state of its ongoing incidents.", + "example": "degraded" } } }, - "FraudProcessingStatusEnum": { - "enum": [ - "pending", - "running", - "completed", - "failed" - ], - "type": "string", - "description": "* `pending` - Pending\n* `running` - Running\n* `completed` - Completed\n* `failed` - Failed" - }, - "CasePublicationCaseBlueprint": { + "ServiceStatusIncident": { "type": "object", + "required": [ + "id", + "service", + "service_name", + "state", + "status", + "title", + "body", + "affected", + "country_name", + "started_at", + "resolved_at", + "duration_minutes" + ], "properties": { - "uuid": { + "id": { "type": "string", "format": "uuid", - "readOnly": true + "description": "Incident ID.", + "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }, - "name": { + "service": { "type": "string", - "maxLength": 24 + "enum": [ + "service_id_verification", + "service_liveness_face_match", + "service_aml_screening", + "service_kyb_registries", + "service_database_validation", + "service_phone_delivery", + "service_email_delivery", + "service_transaction_monitoring" + ], + "description": "Key of the affected service (see `services[].key`).", + "example": "service_database_validation" }, - "description": { + "service_name": { "type": "string", - "nullable": true - }, - "assignment_method": { - "$ref": "#/components/schemas/CasePublicationAssignmentMethodEnum" + "description": "English display name of the affected service.", + "example": "Database validation" }, - "assignee_pool": {}, - "max_active_cases": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true - }, - "escalation_enabled": { - "type": "boolean" - }, - "escalation_blueprint": { + "state": { "type": "string", - "format": "uuid", - "nullable": true + "enum": [ + "degraded", + "down" + ], + "description": "How the service is affected. A resolved incident keeps the last state it had.", + "example": "degraded" }, - "escalation_blueprint_name": { + "status": { "type": "string", - "readOnly": true - }, - "transfer_enabled": { - "type": "boolean" + "enum": [ + "ongoing", + "resolved" + ], + "description": "`ongoing` until the service recovers, then `resolved`.", + "example": "ongoing" }, - "four_eyes_checker_blueprint": { + "title": { "type": "string", - "format": "uuid", - "nullable": true + "description": "English headline. To translate it, build your own text from `service`, `state` and `country_name`.", + "example": "Database validation is degraded in Brazil." }, - "four_eyes_checker_blueprint_name": { + "body": { "type": "string", - "readOnly": true - }, - "require_resolution_note": { - "type": "boolean" + "description": "English detail line: since when checks may be slow or fail, or when it recovered.", + "example": "Checks against Brazil (CPF) may be slow or fail since 08:55 UTC." }, - "content_config": {}, - "version": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "affected": { + "type": "string", + "description": "Countries or registries affected, for example `Brazil (CPF)`. Empty when the incident is not tied to a country or registry.", + "example": "Brazil (CPF)" }, - "default_deadline_hours": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true + "country_name": { + "type": "string", + "description": "Country affected, in English. Empty when the incident is not tied to a country.", + "example": "Brazil" }, - "is_active": { - "type": "boolean" + "started_at": { + "type": "string", + "format": "date-time", + "description": "When the incident started (ISO 8601, UTC).", + "example": "2026-10-13T08:55:00Z" }, - "preset_key": { + "resolved_at": { "type": "string", + "format": "date-time", "nullable": true, - "maxLength": 64 + "description": "When the service recovered (ISO 8601, UTC). `null` while the incident is ongoing." }, - "creation_sources": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": {} - }, - "readOnly": true + "duration_minutes": { + "type": "integer", + "nullable": true, + "description": "Whole minutes from `started_at` to `resolved_at`. `null` while the incident is ongoing." + } + } + }, + "TravelRuleJoinInvitation": { + "type": "object", + "description": "What the join page shows before anything is typed.", + "properties": { + "email": { + "type": "string", + "format": "email", + "description": "The address the invitation went to." }, - "receives_escalation_from": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": {} - }, - "readOnly": true + "vasp_name": { + "type": "string" }, - "created_at": { + "source": { "type": "string", - "format": "date-time", - "readOnly": true + "description": "Where the invitation came from, for example `pickup`." }, - "updated_at": { + "expires_at": { "type": "string", "format": "date-time", - "readOnly": true + "nullable": true + }, + "prefill": { + "type": "object", + "properties": { + "vasp_id": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "legal_name": { + "type": "string" + }, + "lei": { + "type": "string" + }, + "jurisdiction": { + "type": "string" + }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + } + } } - }, - "required": [ - "created_at", - "creation_sources", - "escalation_blueprint_name", - "four_eyes_checker_blueprint_name", - "name", - "receives_escalation_from", - "updated_at", - "uuid" - ] + } }, - "CaseRoutingRuleUpdate": { + "TravelRuleJoinRegisterRequest": { "type": "object", + "required": [ + "email", + "password" + ], "properties": { - "name": { + "email": { "type": "string", - "maxLength": 100 + "format": "email", + "maxLength": 254, + "description": "Must be the address the invitation went to." }, - "description": { + "password": { "type": "string", - "maxLength": 500 + "minLength": 8, + "maxLength": 128 + } + } + }, + "TravelRuleJoinVerifyRequest": { + "type": "object", + "required": [ + "email", + "code" + ], + "properties": { + "email": { + "type": "string", + "format": "email", + "maxLength": 254 }, - "priority": { - "type": "integer", - "minimum": 0 + "code": { + "type": "string", + "minLength": 6, + "maxLength": 6, + "description": "The 6-character code emailed after registering." + } + } + }, + "TravelRuleJoinCredentials": { + "type": "object", + "description": "The new free organization and the credentials its application calls the API with.", + "properties": { + "organization": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + } }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseRoutingCondition" - }, - "maxItems": 20 + "application": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "format": "uuid" + } + } }, - "required_skills": { - "type": "array", - "items": { - "type": "string", - "maxLength": 64 - }, - "maxItems": 25 + "client_id": { + "type": "string" }, - "blueprint": { + "api_key": { "type": "string", - "format": "uuid" - }, - "team": { + "description": "Send as the `x-api-key` header on the workspace endpoints." + } + } + }, + "TravelRuleWorkspaceClaimRequest": { + "type": "object", + "required": [ + "vasp_id" + ], + "properties": { + "vasp_id": { "type": "string", "format": "uuid", - "nullable": true - }, - "is_active": { - "type": "boolean" + "description": "The directory entry this workspace is. Read ids from `GET /v3/travel-rule/vasps/`." + } + } + }, + "TravelRuleWorkspaceDomainConfirmRequest": { + "type": "object", + "required": [ + "code" + ], + "properties": { + "code": { + "type": "string", + "minLength": 6, + "maxLength": 6 + } + } + }, + "TravelRuleWorkspaceBillingRequest": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "description": "Only `true` is accepted: outbound billing cannot be turned back off through the API." + } + } + }, + "TravelRuleWorkspaceDomainCodeResponse": { + "type": "object", + "properties": { + "domain": { + "type": "string" }, - "expected_version": { - "type": "integer", - "minimum": 1, - "writeOnly": true, - "description": "Version opened by the editor. When supplied, stale writes return 409 without changing the rule." + "status": { + "type": "string", + "enum": [ + "unverified", + "code_sent", + "verified" + ] + }, + "txt": { + "$ref": "#/components/schemas/TravelRuleWorkspaceTxtRecord" } } }, - "CustomerMessagingCaseRequestPreviewRequest": { + "TravelRuleWorkspaceTxtRecord": { "type": "object", + "description": "The TXT record to publish for the DNS proof. Empty until a proof has been started.", "properties": { - "contact_id": { + "host": { "type": "string", - "format": "uuid" - }, - "purpose": { - "$ref": "#/components/schemas/CustomerMessagingPurposeEnum" + "example": "didit-tr-verify.example.com" }, - "language": { - "allOf": [ - { - "$ref": "#/components/schemas/CustomerMessagingLanguageEnum" - } - ], - "default": "en" + "value": { + "type": "string" } - }, - "required": [ - "contact_id", - "purpose" - ] + } }, - "CaseDuplicateCaseFraudSourceContext": { + "TravelRuleWorkspace": { "type": "object", + "description": "The free Travel Rule workspace of an application that joined free.", "properties": { - "check_id": { + "joined_free": { + "type": "boolean" + }, + "work_email": { "type": "string", - "format": "uuid" + "format": "email" }, - "request_id": { + "joined_at": { "type": "string", - "format": "uuid" + "format": "date-time", + "nullable": true }, - "source_section": { + "claim": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "unclaimed", + "pending_review", + "claimed", + "rejected" + ] + }, + "proved_by_domain": { + "type": "boolean" + }, + "claimed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reviewed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "review_note": { + "type": "string" + }, + "vasp_id": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "vasp_name": { + "type": "string" + } + } + }, + "prefill": { + "type": "object", + "description": "Profile values taken from the directory entry: `vasp_id`, `legal_name`, `lei`, `jurisdiction`, `compliance_email`, `domains`. Nothing is written until the claim is made." + }, + "domain": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "unverified", + "code_sent", + "verified" + ] + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "txt": { + "$ref": "#/components/schemas/TravelRuleWorkspaceTxtRecord" + } + } + }, + "billing_enabled": { + "type": "boolean", + "description": "Receiving Travel Rule data is free. Sending is billed only after billing is enabled." + }, + "billing_enabled_at": { "type": "string", - "pattern": "^[A-Z][A-Z0-9_:-]{0,63}$" + "format": "date-time", + "nullable": true + }, + "didit_network": { + "type": "object", + "description": "Whether the Didit network lists this VASP and, when not, each condition it still misses." + }, + "settings": { + "$ref": "#/components/schemas/TravelRuleSettingsDetail" } - }, - "required": [ - "check_id", - "request_id", - "source_section" - ] - }, - "RoutingPublicationOperatorEnum": { - "enum": [ - "equals", - "in", - "contains" - ], - "type": "string", - "description": "* `equals` - equals\n* `in` - in\n* `contains` - contains" + } }, - "CaseRoutingPreview": { + "InvestigatorDecision": { "type": "object", + "description": "How the item left the Investigator queue. `decided_by`, `original_outcome` and `overrides` are returned by the accept and override endpoints and the report; the `investigator` block of a session decision carries the other fields.", "properties": { - "rule_uuid": { + "outcome": { "type": "string", - "format": "uuid", - "nullable": true + "enum": [ + "accepted", + "edited", + "overridden", + "decided_elsewhere", + "automated" + ], + "description": "`accepted` or `edited` took the recommendation (`edited` with a rewritten narrative), `overridden` took another action, `decided_elsewhere` is a decision taken outside the Investigator while the report was open, `automated` is a decision the Investigator took on its own." }, - "rule_name": { + "action": { + "type": "string", + "enum": [ + "approve", + "decline", + "escalate", + "request" + ] + }, + "actor_email": { "type": "string", "nullable": true }, - "rule_version": { - "type": "integer", + "actor_name": { + "type": "string", "nullable": true }, - "blueprint_uuid": { + "override_category": { "type": "string", - "format": "uuid", + "enum": [ + "missing_context", + "needs_information", + "policy_differs", + "evidence_misread", + "other", + "" + ], "nullable": true }, - "team_uuid": { + "override_reason": { "type": "string", - "format": "uuid", "nullable": true }, - "assignee_user_id": { + "final_status": { "type": "string", - "format": "uuid", + "description": "The item's status after the decision." + }, + "agreed": { + "type": "boolean", + "description": "Whether the decision matched the recommendation.", "nullable": true }, - "reason": { - "type": "string" + "decided_at": { + "type": "string", + "format": "date-time" }, - "candidates": { + "billed": { + "type": "boolean", + "description": "The review was charged." + }, + "decided_by": { + "type": "string", + "enum": [ + "investigator", + "member" + ] + }, + "original_outcome": { + "type": "object", + "description": "The decision the Investigator took on its own, unchanged by later overrides. Null for a decision the team took.", + "properties": { + "action": { + "type": "string" + }, + "final_status": { + "type": "string" + }, + "decided_at": { + "type": "string", + "format": "date-time" + } + }, + "nullable": true + }, + "overrides": { "type": "array", + "description": "Every override of a decision the Investigator took on its own, oldest first.", "items": { "type": "object", "properties": { - "user_id": { - "type": "string", - "format": "uuid" - }, - "eligible": { - "type": "boolean" + "action": { + "type": "string" }, - "active_cases": { - "type": "integer", + "actor_email": { + "type": "string", "nullable": true }, - "reason": { + "actor_name": { "type": "string", "nullable": true - } - } - } - }, - "matched_rules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "uuid": { - "type": "string", - "format": "uuid" }, - "name": { + "override_category": { "type": "string" }, - "priority": { - "type": "integer" + "override_reason": { + "type": "string" + }, + "previous_status": { + "type": "string" + }, + "final_status": { + "type": "string" + }, + "agreed": { + "type": "boolean", + "nullable": true + }, + "overridden_at": { + "type": "string", + "format": "date-time" } } } } } }, - "FraudDimensionEnum": { - "enum": [ - "identity", - "contact", - "device_network", - "behavioral", - "fraud_network" - ], - "type": "string", - "description": "* `identity` - Identity\n* `contact` - Contact\n* `device_network` - Device Network\n* `behavioral` - Behavioral\n* `fraud_network` - Fraud Network" - }, - "FraudBadRequestErrorMessage": { + "InvestigatorBlock": { "type": "object", + "nullable": true, + "description": "The item's latest Investigator review, or null when it never had one. Carried by the V3 session and business session decision (and so by `status.updated` webhooks).", "properties": { - "detail": { + "run_id": { "type": "string", - "default": "Bad Request - Invalid request data" - } - } - }, - "FraudNextActionEnum": { - "enum": [ - "none", - "manual_review", - "step_up", - "resubmit_capture" - ], - "type": "string", - "description": "* `none` - None\n* `manual_review` - Manual Review\n* `step_up` - Step Up\n* `resubmit_capture` - Resubmit Capture" - }, - "RoutingPublicationPolicyDraftPreconditionRequest": { - "type": "object", - "properties": { - "expected_version": { - "type": "integer", - "minimum": 0 + "format": "uuid" }, - "expected_revision": { + "status": { "type": "string", - "format": "uuid", + "enum": [ + "running", + "completed", + "failed" + ] + }, + "stage": { + "type": "string", + "enum": [ + "shadow", + "live" + ] + }, + "queue": { + "type": "string", + "enum": [ + "kyc", + "kyb", + "aml", + "transactions", + "cases" + ] + }, + "is_open": { + "type": "boolean" + }, + "recommendation": { + "type": "string", + "enum": [ + "approve", + "decline", + "escalate", + "request" + ], + "nullable": true + }, + "recommendation_detail": { + "type": "string", + "nullable": true + }, + "confidence": { + "type": "string", + "enum": [ + "high", + "moderate", + "low" + ], + "nullable": true + }, + "summary": { + "type": "string", + "nullable": true + }, + "ready_to_accept": { + "type": "boolean" + }, + "prepared_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "automated": { + "type": "boolean", + "description": "The Investigator decided on its own, even if the decision was later overridden." + }, + "decided_by": { + "type": "string", + "enum": [ + "investigator", + "member" + ], "nullable": true + }, + "decision": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/InvestigatorDecision" + } + ], + "type": "object" } - }, - "required": [ - "expected_revision", - "expected_version" - ] + } }, - "FraudCheckRisk": { + "InvestigatorQueueRow": { "type": "object", "properties": { - "dimension": { - "$ref": "#/components/schemas/FraudDimensionEnum" + "run_id": { + "type": "string", + "format": "uuid" }, - "score": { - "type": "integer", - "nullable": true + "kind": { + "type": "string", + "enum": [ + "session", + "business_session", + "transaction", + "case" + ] }, - "band": { - "$ref": "#/components/schemas/FraudBandEnum" + "queue": { + "type": "string", + "enum": [ + "kyc", + "kyb", + "aml", + "transactions", + "cases" + ] }, - "availability": { - "$ref": "#/components/schemas/FraudAvailabilityEnum" + "stage": { + "type": "string", + "enum": [ + "shadow", + "live" + ] }, - "requested_but_unavailable": { - "type": "boolean", - "description": "True when the request supplied everything this dimension needed and the platform still could not answer it. Such a check is never recommended for approval on the strength of the dimensions that did answer." + "subject": { + "type": "object", + "description": "The item under review: `id`, `number`, `status`, `name` and, depending on the kind, `country`, `workflow`, `vendor_data`, `txn_id`, `amount`, `currency`, `direction`, `title` or `priority`.", + "additionalProperties": true }, - "reason_codes": { - "type": "array", - "items": { - "type": "string" - } + "recommendation": { + "type": "string", + "enum": [ + "approve", + "decline", + "escalate", + "request" + ] }, - "evidence_indexes": { - "type": "array", - "items": { - "type": "integer" + "recommendation_detail": { + "type": "string" + }, + "confidence": { + "type": "string", + "enum": [ + "high", + "moderate", + "low" + ] + }, + "summary": { + "type": "string" + }, + "sources_count": { + "type": "integer" + }, + "ready_to_accept": { + "type": "boolean" + }, + "guardrails": { + "description": "The locked guardrails that hold this item, if any." + }, + "waiting_since": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "due_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "prepared_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "InvestigatorRunDetail": { + "allOf": [ + { + "$ref": "#/components/schemas/InvestigatorQueueRow" + }, + { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "is_open": { + "type": "boolean" + }, + "lookups": { + "type": "integer", + "description": "How many sections and linked records it read." + }, + "duration_ms": { + "type": "integer", + "nullable": true + }, + "agent_version": { + "type": "string" + }, + "prepared_before_opened": { + "type": "boolean", + "nullable": true + }, + "report": { + "type": "object", + "additionalProperties": true, + "description": "The report: reasoning, evidence points with their verdict (`ok`, `risk`, `look`, `explained`, `context`) and cited sources, screening hit verdicts (`same`, `not_same`, `unclear`) and suggested next steps." + }, + "trail": { + "description": "What I checked: the overview, sections and records it read, in order." + }, + "decision": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/InvestigatorDecision" + } + ], + "type": "object" + }, + "autonomy": { + "type": "object", + "additionalProperties": true, + "description": "What autonomy made of the report (`result`, `reason`, `tier`, `policy_version`, `rule`, `agreement`, `decisions`, `threshold`, `at`). Null when the queue is not automated.", + "nullable": true + } } } - }, - "required": [ - "availability", - "band", - "dimension", - "evidence_indexes", - "reason_codes", - "requested_but_unavailable", - "score" ] }, - "CaseDuplicateSourceEnum": { - "enum": [ - "MANUAL", - "AML_SCREENING", - "TRANSACTION_RULE", - "WORKFLOW_BUILDER" - ], - "type": "string", - "description": "* `MANUAL` - Manual\n* `AML_SCREENING` - AML screening\n* `TRANSACTION_RULE` - Transaction rule\n* `WORKFLOW_BUILDER` - Workflow builder" - }, - "CaseCollaborationPatchedCaseNoteUpdateRequest": { + "InvestigatorAcceptRequest": { "type": "object", "properties": { - "is_pinned": { - "type": "boolean" + "narrative": { + "type": "string", + "maxLength": 4000, + "description": "A rewritten narrative. Sending one records the decision as edited and accepted." + }, + "send_email": { + "type": "boolean", + "default": false, + "description": "Email the person about the decision." + }, + "email_address": { + "type": "string", + "format": "email", + "description": "Required when `send_email` is true." + }, + "email_language": { + "type": "string", + "default": "en", + "maxLength": 16 } } }, - "CustomerMessagingPurposeEnum": { - "enum": [ - "identity_document", - "proof_of_address", - "business_document", - "confirm_details", - "review_in_progress" - ], - "type": "string", - "description": "* `identity_document` - identity_document\n* `proof_of_address` - proof_of_address\n* `business_document` - business_document\n* `confirm_details` - confirm_details\n* `review_in_progress` - review_in_progress" - }, - "CaseDuplicateTypeEnum": { - "enum": [ - "user", - "business" - ], - "type": "string", - "description": "* `user` - user\n* `business` - business" - }, - "CaseCollaborationCaseNoteCreateRequest": { + "InvestigatorOverrideRequest": { "type": "object", + "required": [ + "action", + "category", + "reason" + ], "properties": { - "comment": { + "action": { "type": "string", - "minLength": 1, - "maxLength": 10000 + "enum": [ + "approve", + "decline", + "escalate", + "request" + ] }, - "reply_to": { + "category": { "type": "string", - "format": "uuid", - "nullable": true + "enum": [ + "missing_context", + "needs_information", + "policy_differs", + "evidence_misread", + "other" + ] }, - "client_reference": { + "reason": { "type": "string", - "format": "uuid", - "nullable": true - }, - "mentioned_emails": { - "type": "array", - "items": { - "type": "string", - "format": "email", - "minLength": 1 - } + "minLength": 10, + "maxLength": 500 }, - "is_internal": { + "send_email": { "type": "boolean", - "default": false + "default": false, + "description": "Email the person about the decision." }, - "attachments": { + "email_address": { + "type": "string", + "format": "email", + "description": "Required when `send_email` is true." + }, + "email_language": { + "type": "string", + "default": "en", + "maxLength": 16 + } + } + }, + "InvestigatorBatchAcceptRequest": { + "type": "object", + "required": [ + "run_ids" + ], + "properties": { + "run_ids": { "type": "array", + "minItems": 1, + "maxItems": 50, "items": { - "type": "object", - "additionalProperties": {} + "type": "string", + "format": "uuid" } + } + } + }, + "InvestigatorBatchAcceptResponse": { + "type": "object", + "properties": { + "accepted": { + "type": "integer" + }, + "skipped": { + "type": "integer" }, - "tags": { + "results": { "type": "array", "items": { - "type": "string", - "minLength": 1 + "type": "object", + "properties": { + "run_id": { + "type": "string", + "format": "uuid" + }, + "result": { + "type": "string", + "enum": [ + "accepted", + "skipped" + ] + }, + "reason": { + "type": "string", + "enum": [ + "not_found", + "forbidden", + "already_decided", + "not_ready", + "guardrail", + "invalid" + ] + }, + "detail": {}, + "action": { + "type": "string" + }, + "final_status": { + "type": "string" + } + } } } - }, - "required": [ - "comment" - ] - }, - "CustomerMessagingLanguagesEnum": { - "enum": [ - "en", - "es", - "pt" - ], - "type": "string", - "description": "* `en` - en\n* `es` - es\n* `pt` - pt" + } }, - "CustomerMessagingCaseRequestConfirmRequest": { + "InvestigatorSettings": { "type": "object", "properties": { - "contact_id": { + "stage": { "type": "string", - "format": "uuid" - }, - "purpose": { - "$ref": "#/components/schemas/CustomerMessagingPurposeEnum" - }, - "language": { - "allOf": [ - { - "$ref": "#/components/schemas/CustomerMessagingLanguageEnum" - } - ], - "default": "en" + "enum": [ + "none", + "shadow", + "live" + ] }, - "request_id": { + "stage_changed_at": { "type": "string", - "format": "uuid" + "format": "date-time", + "readOnly": true, + "nullable": true }, - "expires_at": { + "stage_changed_by": { "type": "string", - "format": "date-time" + "readOnly": true }, - "preview_token": { + "monthly_spend_limit": { "type": "string", - "minLength": 1, - "maxLength": 512 + "format": "decimal", + "description": "Monthly limit in your billing currency, 0.01 to 1,000,000. Null turns it off.", + "nullable": true + }, + "spend_limit": { + "type": "object", + "readOnly": true, + "properties": { + "enabled": { + "type": "boolean" + }, + "limit": { + "type": "string", + "format": "decimal", + "nullable": true + }, + "spent": { + "type": "string", + "format": "decimal" + }, + "reviews": { + "type": "integer" + }, + "reserved": { + "type": "string", + "format": "decimal", + "description": "The price of every open live report, held back because accepting it charges it." + }, + "currency": { + "type": "string" + }, + "paused": { + "type": "boolean", + "description": "At the limit: no new review starts." + } + } } - }, - "required": [ - "contact_id", - "expires_at", - "preview_token", - "purpose", - "request_id" - ] + } }, - "CaseRoutingRule": { + "TravelRuleUtxoEntry": { "type": "object", + "description": "One input or output of a UTXO transaction, as the chain recorded it.", + "required": [ + "address" + ], "properties": { - "name": { + "address": { "type": "string", - "maxLength": 100 + "maxLength": 128 }, - "description": { + "amount": { "type": "string", - "maxLength": 500 + "nullable": true, + "description": "Decimal string in the coin's own unit (BTC, not satoshis). Optional.", + "example": "0.0125" }, - "priority": { - "type": "integer", - "minimum": 0 + "txid": { + "type": "string", + "maxLength": 128, + "description": "Optional. The transaction an input spends." }, - "conditions": { + "vout": { + "type": "integer", + "nullable": true, + "minimum": 0, + "description": "Optional. The output index an input spends." + } + } + }, + "TravelRuleUtxo": { + "type": "object", + "description": "The inputs and outputs of a UTXO transfer (BTC, LTC, BCH, DOGE). Only accepted on those chains: on any other chain it is a `400`. From them Didit infers the originator wallet (the declared one, else the input address that contributed the most) and the beneficiary output (the declared one, else the only output that does not pay an input address back as change, else the one output paying the transfer amount). The result is on the transfer's `chain_details`.", + "required": [ + "outputs" + ], + "properties": { + "inputs": { "type": "array", + "maxItems": 1000, "items": { - "$ref": "#/components/schemas/CaseRoutingCondition" + "$ref": "#/components/schemas/TravelRuleUtxoEntry" }, - "maxItems": 20 + "description": "Optional. Without inputs no originator is inferred and no change output is recognised." }, - "required_skills": { + "outputs": { "type": "array", + "minItems": 1, + "maxItems": 1000, "items": { - "type": "string", - "maxLength": 64 - }, - "maxItems": 25 - }, - "blueprint": { + "$ref": "#/components/schemas/TravelRuleUtxoEntry" + } + } + } + }, + "TravelRuleChainSide": { + "type": "object", + "description": "One side's wallet as Didit resolved it from the chain data.", + "properties": { + "address": { "type": "string", - "format": "uuid" + "description": "The wallet. On a memo network the classic address (an XRP X-address is split into its classic address and its tag); on Lightning the payee node id or Lightning Address." }, - "team": { + "memo": { "type": "string", - "format": "uuid", - "nullable": true - }, - "is_active": { - "type": "boolean" + "description": "The destination tag or memo that is part of the wallet; `\"\"` for none." }, - "uuid": { + "source": { "type": "string", - "format": "uuid", - "readOnly": true - }, - "created_at": { + "enum": [ + "declared", + "x_address", + "largest_input", + "only_non_change_output", + "amount_match", + "invoice_payee", + "updated" + ], + "description": "Where the wallet came from: `declared` (your request), `x_address` (split from an XRP X-address), `largest_input` (the UTXO input address that contributed the most), `only_non_change_output` (the only UTXO output that is not change), `amount_match` (the one UTXO output paying the transfer amount), `invoice_payee` (the payee a Lightning payment request names), `updated` (set later with `action: update_originator_wallet`)." + } + } + }, + "TravelRuleChainDetails": { + "type": "object", + "description": "How the transfer's chain names its two wallets, when that is more than one address paying another. Empty (`{}`) for an account-chain transfer (EVM, Solana, Tron), where the two addresses are the wallets. Matching, routing and the address book read the wallets from here first.", + "properties": { + "chain": { "type": "string", - "format": "date-time", - "readOnly": true + "description": "Canonical chain, e.g. `BTC`, `XRP`, or `LIGHTNING` for a Lightning transfer." }, - "updated_at": { + "shape": { "type": "string", - "format": "date-time", - "readOnly": true + "enum": [ + "utxo", + "memo", + "lightning" + ] }, - "version": { - "type": "integer", - "readOnly": true + "originator": { + "$ref": "#/components/schemas/TravelRuleChainSide" }, - "blueprint_name": { - "type": "string", - "readOnly": true + "beneficiary": { + "$ref": "#/components/schemas/TravelRuleChainSide" }, - "team_name": { + "utxo": { + "type": "object", + "description": "On a UTXO transfer: the inputs and outputs as stored (amounts as decimal strings) and which outputs Didit read as what.", + "properties": { + "inputs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TravelRuleUtxoEntry" + } + }, + "outputs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TravelRuleUtxoEntry" + } + }, + "change_output_indexes": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "Outputs that pay an input address back." + }, + "beneficiary_output_index": { + "type": "integer", + "nullable": true, + "description": "The output that pays the beneficiary; null when none could be told apart." + } + } + }, + "lightning": { + "type": "object", + "description": "On a Lightning transfer: what the payment request says. Always `kind` (`bolt11`, `bolt12_offer`, `bolt12_invoice`, `bolt12_invoice_request`, `lnurl`, `lightning_address`, `node_id` or `unknown`); a BOLT11 invoice adds `network`, `payee_node_id`, `payment_hash`, `amount_msat`, `created_at` and `expires_at`; a BOLT12 string `blinded_paths` and `payee_node_id`; an LNURL or a Lightning Address its `domain`. Nothing is resolved over the network: the request is decoded only.", + "additionalProperties": true + }, + "warnings": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "declared_originator_not_an_input", + "declared_beneficiary_not_an_output", + "beneficiary_output_ambiguous", + "beneficiary_output_amount_differs" + ] + }, + "description": "UTXO inconsistencies worth a look: the declared originator is not an input, the declared beneficiary is not an output, no single beneficiary output could be told apart, or the beneficiary output pays a different amount than the transaction states." + }, + "not_applicable_reason": { "type": "string", - "nullable": true, - "readOnly": true + "enum": [ + "LIGHTNING_NO_PAYEE_IDENTIFIER", + "LIGHTNING_BOLT12_BLINDED_PAYEE", + "LIGHTNING_LNURL_NAMES_NO_PAYEE" + ], + "description": "On an outbound Lightning transfer whose payment request names no payee: why the transfer is `NOT_APPLICABLE`. `LIGHTNING_NO_PAYEE_IDENTIFIER` (the request names no payee node, or could not be decoded), `LIGHTNING_BOLT12_BLINDED_PAYEE` (the offer hides its payee behind blinded paths), `LIGHTNING_LNURL_NAMES_NO_PAYEE` (an LNURL names a service, not a payee). The same code opens the transfer's last `timeline` entry." } } }, - "FraudResultEnum": { - "enum": [ - "match", - "mismatch", - "inconclusive", - "no_reference" - ], - "type": "string", - "description": "* `match` - Match\n* `mismatch` - Mismatch\n* `inconclusive` - Inconclusive\n* `no_reference` - No Reference" - }, - "CaseRoutingRuleCreate": { + "WalletAddressImportRow": { "type": "object", + "description": "One wallet to import: by its `address`, its `address_hash`, or both. The camelCase forms of these fields are accepted too, as are `walletAddress`, `walletAddressHash`, `destinationTag` and `externalUserId`.", "properties": { - "name": { + "address": { "type": "string", - "maxLength": 100 + "maxLength": 128, + "description": "The wallet address. Alias: `walletAddress`." }, - "description": { + "address_hash": { "type": "string", - "maxLength": 500 - }, - "priority": { - "type": "integer", - "minimum": 0 - }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaseRoutingCondition" - }, - "maxItems": 20 + "description": "SHA-256 of the lowercased address, as 64 hex characters or 44 base64 characters. Send it alone to register a wallet without disclosing its address; sent with `address`, it must be that address's hash. Alias: `walletAddressHash`." }, - "required_skills": { - "type": "array", - "items": { - "type": "string", - "maxLength": 64 - }, - "maxItems": 25 + "chain": { + "type": "string", + "maxLength": 32 }, - "blueprint": { + "memo": { "type": "string", - "format": "uuid" + "maxLength": 256, + "description": "The destination tag or memo. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a `400`." }, - "team": { + "holder_name": { "type": "string", - "format": "uuid", - "nullable": true + "maxLength": 255 }, - "is_active": { - "type": "boolean" + "holder_vendor_data": { + "type": "string", + "maxLength": 255, + "description": "Your internal identifier for the holder. Alias: `externalUserId`." }, - "publish": { - "type": "boolean", - "writeOnly": true, - "description": "Set false to save a never-published draft. The record remains inactive at version 0 until the draft is explicitly published. Omitting this field preserves immediate creation for existing integrations." - } - }, - "required": [ - "name", - "conditions", - "blueprint" - ] - }, - "FraudCheckIdentifierRequest": { - "type": "object", - "properties": { - "type": { - "allOf": [ - { - "$ref": "#/components/schemas/FraudTypeEnum" - } + "entity_type": { + "type": "string", + "enum": [ + "individual", + "company" ], - "description": "Identifier type. Determines which namespace fields are required.\n\n* `email` - Email\n* `phone` - Phone\n* `national_id` - National Id\n* `tax_number` - Tax Number\n* `passport_number` - Passport Number\n* `driving_licence_number` - Driving Licence Number\n* `residence_permit_number` - Residence Permit Number\n* `voter_id` - Voter Id\n* `external_id` - External Id" + "default": "individual" }, - "value": { + "label": { "type": "string", - "minLength": 1, - "description": "The identifier value, in any customary format.", "maxLength": 255 }, - "country": { + "owner_type": { "type": "string", - "description": "ISO-3166 alpha-2 or alpha-3 issuing country. Required for government identifier types: the same digits mean different people in different countries.", - "maxLength": 3 + "enum": [ + "customer", + "internal", + "vasp" + ], + "default": "customer" }, - "issuer": { + "travel_address": { "type": "string", - "description": "Optional issuing authority or sub-national issuer, when the country alone is not unique.", - "maxLength": 128 + "maxLength": 512 + }, + "self_declared": { + "type": "boolean", + "default": false, + "description": "Create the entry already verified through a self-declaration, when your settings allow it." } - }, - "required": [ - "type", - "value" - ] - }, - "CaseActionActionEnum": { - "enum": [ - "set_status", - "add_tags", - "add_note", - "add_to_blocklist" - ], - "type": "string", - "description": "* `set_status` - set_status\n* `add_tags` - add_tags\n* `add_note` - add_note\n* `add_to_blocklist` - add_to_blocklist" + } }, - "CaseActionCaseActionReceiptPage": { + "WalletAddressImportReport": { "type": "object", + "description": "What happened to each row of an import, in the order sent.", "properties": { - "count": { + "total": { "type": "integer" }, - "next_offset": { - "type": "integer", - "nullable": true + "created": { + "type": "integer" }, - "results": { + "existing": { + "type": "integer" + }, + "duplicate": { + "type": "integer" + }, + "failed": { + "type": "integer" + }, + "rows": { "type": "array", "items": { - "$ref": "#/components/schemas/CaseActionCaseActionReceipt" + "type": "object", + "properties": { + "row": { + "type": "integer", + "description": "The row's number, from 1." + }, + "status": { + "type": "string", + "enum": [ + "created", + "existing", + "duplicate", + "failed" + ], + "description": "`created`: added to the book. `existing`: the wallet is already in the book and was left untouched. `duplicate`: repeats an earlier row of this import. `failed`: see `errors`." + }, + "entry_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "The address-book entry the row created or found." + }, + "errors": { + "type": "object", + "description": "Field errors of a `failed` row; empty otherwise." + }, + "duplicate_of_row": { + "type": "integer", + "nullable": true, + "description": "On a `duplicate` row, the row it repeats." + } + } } } - }, - "required": [ - "count", - "next_offset", - "results" - ] + } }, - "CaseActionCaseEntityActionResult": { + "TravelRuleCounterpartyContact": { "type": "object", + "description": "A compliance address the EMAIL rail writes to for one directory entry.", "properties": { - "receipt": { - "$ref": "#/components/schemas/CaseActionCaseActionReceipt" - }, - "entity_type": { - "$ref": "#/components/schemas/CaseActionEntityTypeEnum" - }, - "entity_uuid": { + "uuid": { "type": "string", "format": "uuid" }, - "action": { - "type": "string" - }, - "previous_status": { - "type": "string" + "counterparty": { + "type": "string", + "format": "uuid", + "description": "The directory entry's `id`, as GET /v3/travel-rule/vasps/ shows it." }, - "new_status": { + "counterparty_name": { "type": "string" }, - "tags_added": { - "type": "array", - "items": { - "type": "string" - } - }, - "comment_uuid": { + "email": { "type": "string", - "format": "uuid" + "format": "email" }, - "blocklisted": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "CustomerMessagingCaseRequest": { - "type": "object", - "properties": { - "uuid": { + "name": { "type": "string", - "format": "uuid", - "readOnly": true + "description": "Optional label, for example the team name." }, - "purpose": { + "source": { "type": "string", - "readOnly": true + "enum": [ + "staff", + "application", + "learned" + ], + "description": "staff: curated by Didit. learned: an address at one of the VASP's own domains that answered a request through its response link. application: added by your application." }, - "language": { + "scope": { "type": "string", - "readOnly": true + "enum": [ + "platform", + "application" + ], + "description": "platform: curated by Didit or learned from a verified answer, used for every Didit customer, read-only for you. application: private to your application." }, - "recipient": { + "created_by": { "type": "string", - "format": "email", - "readOnly": true + "description": "Empty for platform contacts and for contacts an API key added." }, - "expires_at": { + "verified_at": { "type": "string", "format": "date-time", - "readOnly": true + "nullable": true, + "description": "When this address last answered a request through its response link." }, - "actor_email": { - "type": "string", - "format": "email", - "readOnly": true, - "nullable": true + "is_invalid": { + "type": "boolean", + "description": "true once the address bounced or complained; the EMAIL rail skips it." }, - "delivery_status": { + "invalid_reason": { "type": "string", - "readOnly": true + "enum": [ + "", + "hard_bounce", + "soft_bounce", + "spam_complaint", + "suppressed" + ] }, - "delivered_at": { + "invalid_detail": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "description": "The receiving mail server's reason, when it gave one." }, - "cancelled_at": { + "invalid_at": { "type": "string", "format": "date-time", - "readOnly": true, "nullable": true }, - "responded_at": { + "created_at": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "response": { - "readOnly": true, - "nullable": true + "format": "date-time" + } + } + }, + "TravelRuleCounterpartyContactCreate": { + "type": "object", + "required": [ + "counterparty", + "email" + ], + "properties": { + "counterparty": { + "type": "string", + "format": "uuid", + "description": "The directory entry's `id`." }, - "has_attachment": { - "type": "boolean", - "readOnly": true + "email": { + "type": "string", + "format": "email", + "maxLength": 254 }, - "created_at": { + "name": { "type": "string", - "format": "date-time", - "readOnly": true + "description": "Optional label.", + "maxLength": 255 } - }, - "required": [ - "actor_email", - "cancelled_at", - "created_at", - "delivered_at", - "delivery_status", - "expires_at", - "has_attachment", - "language", - "purpose", - "recipient", - "responded_at", - "response", - "uuid" - ] + } }, - "CustomerMessagingCaseRequestPublic": { + "TravelRuleNameMatch": { "type": "object", + "nullable": true, + "description": "The name comparison that decided the transfer, or null until one ran. While the parties are masked, and on the other customer's copy of an INTERNAL exchange, `transfer_name`, `compared_name`, `matched_names` and `mismatched_names` are hidden. `score` and `matched` can disagree: `matched` follows the strictness, `score` is the similarity of the two full names.", "properties": { - "request_id": { - "type": "string", - "format": "uuid" + "score": { + "type": "integer", + "minimum": 0, + "maximum": 100, + "description": "Similarity of the two full names, 0 to 100. 0 when either name is blank." }, - "purpose": { - "$ref": "#/components/schemas/CustomerMessagingPurposeEnum" + "matched": { + "type": "boolean", + "description": "Whether the names satisfy `strictness`. The transfer's outcome follows `data_match`, which compares every field, so a transfer can be COUNTERPARTY_MISMATCHED_DATA while this is true." }, - "language": { - "$ref": "#/components/schemas/CustomerMessagingLanguageEnum" + "method": { + "type": "string", + "enum": [ + "none", + "exact_token", + "token_subset", + "token_containment", + "sequence_ratio" + ], + "description": "How `matched` was decided: none (`NONE`), exact_token (`STRICT`), token_subset (`DEFAULT`), token_containment (`CONTAINS`) or sequence_ratio (`FUZZY`)." + }, + "strictness": { + "type": "string", + "enum": [ + "NONE", + "STRICT", + "CONTAINS", + "DEFAULT", + "FUZZY" + ] }, - "expires_at": { + "compared_with": { "type": "string", - "format": "date-time" + "enum": [ + "wallet_holder", + "counterparty_vasp" + ], + "description": "wallet_holder: the holder recorded for the wallet. counterparty_vasp: the name the counterparty answered on the email response page, or the Didit Test VASP's answer in sandbox." }, - "responded_at": { + "transfer_name": { "type": "string", - "format": "date-time", - "nullable": true - }, - "copy": { - "type": "object", - "additionalProperties": {} + "description": "The name on the transfer." }, - "organization_name": { - "type": "string" + "compared_name": { + "type": "string", + "description": "The name it was compared with." }, - "is_white_label": { - "type": "boolean" + "matched_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Words of `transfer_name` found in `compared_name`." }, - "customization": { - "type": "object", - "additionalProperties": {}, - "nullable": true + "mismatched_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Words of `transfer_name` missing from `compared_name`, then words of `compared_name` missing from `transfer_name`." } - }, - "required": [ - "copy", - "customization", - "expires_at", - "is_white_label", - "language", - "organization_name", - "purpose", - "request_id", - "responded_at" - ] - }, - "RoutingPublicationFieldEnum": { - "enum": [ - "source", - "priority", - "tags" - ], - "type": "string", - "description": "* `source` - source\n* `priority` - priority\n* `tags` - tags" + } }, - "RoutingPublicationCaseRoutingRule": { + "TravelRuleTransferRow": { "type": "object", + "description": "One Travel Rule transfer as the transfers list shows it: VASP names, wallets and memos only, never the originator or beneficiary person data.", "properties": { - "uuid": { + "id": { "type": "string", "format": "uuid", - "readOnly": true + "description": "The transfer's id." }, - "name": { + "transaction_id": { "type": "string", - "maxLength": 100 + "format": "uuid", + "description": "The Didit transaction the transfer belongs to." }, - "description": { + "txn_id": { "type": "string", - "maxLength": 500 + "nullable": true, + "description": "Your own id for the transaction. Transfers Didit books for you, such as a Didit Test VASP inbound transfer, carry a generated one." }, - "priority": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "txn_date": { + "type": "string", + "format": "date-time" }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RoutingPublicationRoutingCondition" - } + "kind": { + "type": "string", + "enum": [ + "withdrawal", + "deposit", + "message" + ], + "description": "withdrawal: outbound. message: an inbound transfer a rail created from a counterparty's message (a TRP inquiry, the INTERNAL inbound leg, or a GTR, VerifyVASP or Sygna Bridge message). deposit: any other inbound transfer." }, - "required_skills": { - "type": "array", - "items": { - "type": "string", - "maxLength": 64 - }, - "maxItems": 25 + "direction": { + "type": "string", + "enum": [ + "INBOUND", + "OUTBOUND" + ] }, - "blueprint": { + "status": { "type": "string", - "format": "uuid" + "enum": [ + "AWAITING_COUNTERPARTY", + "ON_HOLD", + "COMPLETED", + "FINISHED", + "CANCELLED", + "EXPIRED", + "COUNTERPARTY_MISMATCHED_DATA", + "COUNTERPARTY_VASP_NOT_FOUND", + "COUNTERPARTY_VASP_NOT_REACHABLE", + "UNCONFIRMED_OWNERSHIP", + "COUNTERPARTY_VASP_GENERAL_DECLINE", + "NOT_ENOUGH_COUNTERPARTY_DATA", + "NOT_APPLICABLE" + ] }, - "blueprint_name": { + "reason": { "type": "string", - "readOnly": true + "description": "Why a `COUNTERPARTY_VASP_NOT_FOUND` or `COUNTERPARTY_VASP_NOT_REACHABLE` transfer is stuck, the same value as the transfer's own `reason`, so the list tells a bounced compliance email from a counterparty with no route without opening each transfer. `\"\"` for any other status. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck)." }, - "team": { + "rail": { "type": "string", - "format": "uuid", - "nullable": true + "nullable": true, + "enum": [ + "INTERNAL", + "TRP", + "GTR", + "TRUST", + "VERIFYVASP", + "SYGNA", + "EMAIL", + "TEST_VASP", + null + ] }, - "team_name": { + "asset": { "type": "string", - "readOnly": true + "description": "The transaction's currency." }, - "is_active": { - "type": "boolean" + "amount": { + "type": "string", + "description": "The amount as a plain decimal, for example \"1.25\"." }, - "version": { - "type": "integer", - "readOnly": true + "originator_vasp": { + "type": "string", + "description": "Outbound: your VASP's legal name. Inbound: the counterparty's name, from its directory entry, the Didit network member, the TRP sender, the Didit Test VASP, or the name the transfer declared. Empty when none is known." }, - "created_at": { + "beneficiary_vasp": { "type": "string", - "format": "date-time", - "readOnly": true + "description": "Outbound: the counterparty's name, worked out the same way. Inbound: your VASP's legal name." }, - "updated_at": { + "originator_wallet": { + "type": "string" + }, + "beneficiary_wallet": { + "type": "string" + }, + "originator_memo": { + "type": "string", + "description": "Memo or destination tag, empty on chains without one." + }, + "beneficiary_memo": { + "type": "string" + }, + "originator_wallet_type": { + "type": "string", + "enum": [ + "vasp", + "self_hosted", + "own" + ], + "description": "vasp, self_hosted (declared unhosted) or own (a wallet your address book records as yours). Your own side is always vasp." + }, + "beneficiary_wallet_type": { + "type": "string", + "enum": [ + "vasp", + "self_hosted", + "own" + ] + }, + "matched_transaction_id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "The other side of a deposit you matched to an announced transfer by hand." + }, + "deadline_at": { "type": "string", "format": "date-time", - "readOnly": true - } - }, - "required": [ - "blueprint", - "blueprint_name", - "conditions", - "created_at", - "name", - "team_name", - "updated_at", - "uuid", - "version" - ] - }, - "CaseActionStatusEnum": { - "enum": [ - "APPLIED", - "FAILED" - ], - "type": "string", - "description": "* `APPLIED` - Applied\n* `FAILED` - Failed" - }, - "FraudCheckModuleSelectionRequest": { - "type": "object", - "properties": { - "include": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FraudExcludeEnum" - } + "nullable": true, + "description": "When the transfer expires if it is still waiting on its counterparty. Set on every transfer you create with `POST /v3/transactions/` that needs an exchange, outbound or inbound. Null below the threshold, on a Lightning payment the Travel Rule does not apply to, and on inbound transfers Didit creates when a counterparty reaches you (an inbound TRP inquiry, a network message or the INTERNAL inbound leg)." }, - "exclude": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FraudExcludeEnum" - } + "created_at": { + "type": "string", + "format": "date-time" } } } @@ -69944,6 +103880,451 @@ } } } + }, + "parameters": { + "WebhookSignature": { + "name": "X-Signature", + "in": "header", + "required": true, + "schema": { + "type": "string" + }, + "description": "Lowercase hex HMAC-SHA256, keyed with the destination's secret, of the exact raw request body." + }, + "WebhookSignatureV2": { + "name": "X-Signature-V2", + "in": "header", + "required": true, + "schema": { + "type": "string" + }, + "description": "Lowercase hex HMAC-SHA256, keyed with the destination's secret, of the canonical JSON of the body (keys sorted, no whitespace, non-ASCII characters unescaped). Recommended, because it survives middleware that re-encodes the body." + }, + "WebhookSignatureSimple": { + "name": "X-Signature-Simple", + "in": "header", + "required": true, + "deprecated": true, + "schema": { + "type": "string" + }, + "description": "Lowercase hex HMAC-SHA256, keyed with the destination's secret, of `{timestamp}:{session_id}:{status}:{webhook_type}` (missing fields as empty strings). Covers the envelope only, never the payload." + }, + "WebhookTimestamp": { + "name": "X-Timestamp", + "in": "header", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unix time in seconds, equal to the body's `timestamp`. Reject deliveries older than a few minutes." + } + } + }, + "x-webhooks": { + "status.updated": { + "post": { + "summary": "Session status changed", + "description": "A verification session changed status. `decision` is sent only for `Approved`, `Declined`, `In Review` and `Abandoned`; it has the shape of `GET /v3/session/{sessionId}/decision/` for destinations on webhook version 3 (the default), and the older reduced shape for version 1 or 2 destinations. Business sessions always receive the version 3 business decision.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionStatusUpdatedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "data.updated": { + "post": { + "summary": "Session data changed", + "description": "Data of a verification session changed without a status change, for example after a manual review edit, a step override or an ongoing-monitoring rescreen. `decision` is sent only for `Approved`, `Declined`, `In Review` and `Abandoned`; it has the shape of `GET /v3/session/{sessionId}/decision/` for destinations on webhook version 3 (the default), and the older reduced shape for version 1 or 2 destinations. Business sessions always receive the version 3 business decision.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionDataUpdatedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "user.status.updated": { + "post": { + "summary": "User status changed", + "description": "A user changed status. Not sent when the user is created.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserStatusUpdatedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "user.data.updated": { + "post": { + "summary": "User data changed", + "description": "Fields of a user changed. Not sent when the user is created. One save can produce both a status and a data event.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserDataUpdatedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "business.status.updated": { + "post": { + "summary": "Business status changed", + "description": "A business changed status. Not sent when the business is created.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessStatusUpdatedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "business.data.updated": { + "post": { + "summary": "Business data changed", + "description": "Fields of a business changed. Not sent when the business is created. One save can produce both a status and a data event.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessDataUpdatedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "transaction.created": { + "post": { + "summary": "Transaction created", + "description": "A transaction was created and screened. Sent for live applications only.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionCreatedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "transaction.status.updated": { + "post": { + "summary": "Transaction status changed", + "description": "A transaction changed status, from a rule, a review or an end-user action. Sent for live applications only.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransactionStatusUpdatedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "travel_rule.status.updated": { + "post": { + "summary": "Travel Rule status changed", + "description": "A Travel Rule exchange changed status. Sent on every transition up to and including `FINISHED`, `CANCELLED` or `EXPIRED`, never after. Sent for live applications only. There is no top-level `status`.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TravelRuleStatusUpdatedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "workflow.compliance_changed": { + "post": { + "summary": "Workflow compliance changed", + "description": "The compliance status of a published workflow changed after a re-evaluation. One event per workflow. There is no top-level `status`.", + "tags": [ + "Webhook" + ], + "parameters": [ + { + "$ref": "#/components/parameters/WebhookSignature" + }, + { + "$ref": "#/components/parameters/WebhookSignatureV2" + }, + { + "$ref": "#/components/parameters/WebhookSignatureSimple" + }, + { + "$ref": "#/components/parameters/WebhookTimestamp" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkflowComplianceChangedEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } + }, + "kyb.registry_search.resolved": { + "post": { + "summary": "Company search resolved (unsigned callback)", + "description": "Body of the callback sent to the `webhook_url` of `POST /v3/kyb/search/` once the registry search resolves. It is not signed: the request carries `X-Didit-Unsigned-Callback: true` and none of the signature headers, so confirm it by matching `request_id` against a search you started. Uses `event_type`, not `webhook_type`.", + "tags": [ + "Webhook" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KYBRegistrySearchResolvedCallback" + } + } + } + }, + "responses": { + "2XX": { + "description": "Any 2xx response acknowledges the delivery. Other responses and timeouts are retried; see https://docs.didit.me/integration/webhooks." + } + } + } } } } diff --git a/src/main/java/me/didit/resources/AccountApplicationsResource.java b/src/main/java/me/didit/resources/AccountApplicationsResource.java index 06cedb1..297375b 100644 --- a/src/main/java/me/didit/resources/AccountApplicationsResource.java +++ b/src/main/java/me/didit/resources/AccountApplicationsResource.java @@ -1,15 +1,16 @@ // Code generated by codegen from openapi/*.json. DO NOT EDIT. package me.didit.resources; -import com.fasterxml.jackson.databind.JsonNode; import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; import me.didit.core.ApiRequest; import me.didit.core.Conv; +import me.didit.core.Page; import me.didit.core.RawResponse; import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.AccountApplicationsListParams; import me.didit.types.Application; import me.didit.types.ApplicationCreateRequest; import me.didit.types.ApplicationUpdateRequest; @@ -36,47 +37,94 @@ public WithRawResponse withRawResponse() { /** * List the organization's applications * - *

List every application in the organization. Use this to discover app_id values when recovering credentials from a new machine — follow with GET /organizations/me/{org_id}/applications/{app_id}/ for the API key. Requires an owner or admin Bearer token. + *

List the organization's applications, 50 per page by default. Use this to discover app_id values when recovering credentials from a new machine, then call GET /organizations/me/{org_id}/applications/{app_id}/ for the API key. Requires an owner or admin Bearer token. * *

{@code GET /organizations/me/{org_id}/applications/} + *

Returns one page; iterate it to fetch every following page automatically. * * @param orgId UUID of the organization. Look it up with `GET /organizations/me/`. + * @param params the request parameters * @param options per-request options that override the client options * @return the response body */ - public JsonNode list(String orgId, RequestOptions options) { - return client.execute(listRequest(orgId), options, LIST_RESPONSE).data(); + public Page list(String orgId, AccountApplicationsListParams params, RequestOptions options) { + return client.execute(listRequest(orgId, params), options, LIST_RESPONSE).data(); } /** - * Asynchronous {@link #list(String, RequestOptions)}. + * Asynchronous {@link #list(String, AccountApplicationsListParams, RequestOptions)}. * * @param orgId the {@code org_id} + * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture listAsync(String orgId, RequestOptions options) { - return client.executeAsync(listRequest(orgId), options, LIST_RESPONSE).thenApply(RawResponse::data); + public CompletableFuture> listAsync(String orgId, AccountApplicationsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(orgId, params), options, LIST_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #list(String, RequestOptions)}, with the client's options. + * Same as {@link #list(String, AccountApplicationsListParams, RequestOptions)}, with the client's options. * * @param orgId the {@code org_id} + * @param params the request parameters * @return the result */ - public JsonNode list(String orgId) { - return client.execute(listRequest(orgId), RequestOptions.none(), LIST_RESPONSE).data(); + public Page list(String orgId, AccountApplicationsListParams params) { + return client.execute(listRequest(orgId, params), RequestOptions.none(), LIST_RESPONSE).data(); } /** - * Asynchronous {@link #list(String, RequestOptions)}, with the client's options. + * Asynchronous {@link #list(String, AccountApplicationsListParams, RequestOptions)}, with the client's options. * * @param orgId the {@code org_id} + * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture listAsync(String orgId) { - return client.executeAsync(listRequest(orgId), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + public CompletableFuture> listAsync(String orgId, AccountApplicationsListParams params) { + return client.executeAsync(listRequest(orgId, params), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(String, AccountApplicationsListParams, RequestOptions)}, without parameters. + * + * @param orgId the {@code org_id} + * @param options per-request options + * @return the result + */ + public Page list(String orgId, RequestOptions options) { + return client.execute(listRequest(orgId, AccountApplicationsListParams.builder().build()), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, AccountApplicationsListParams, RequestOptions)}, without parameters. + * + * @param orgId the {@code org_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String orgId, RequestOptions options) { + return client.executeAsync(listRequest(orgId, AccountApplicationsListParams.builder().build()), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(String, AccountApplicationsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @param orgId the {@code org_id} + * @return the result + */ + public Page list(String orgId) { + return client.execute(listRequest(orgId, AccountApplicationsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, AccountApplicationsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @param orgId the {@code org_id} + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String orgId) { + return client.executeAsync(listRequest(orgId, AccountApplicationsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); } /** @@ -224,7 +272,7 @@ public CompletableFuture retrieveAsync(String orgId, String appId) /** * Update Application * - *

Update application metadata (name, URLs, redirect URIs). api_key and client_id are never rotated. Requires owner/admin JWT. + *

Update application metadata (name, URLs, redirect URIs). api_key and client_id are never rotated. products is read-only: a products key in the body is ignored. Requires owner/admin JWT. * *

{@code PATCH /organizations/me/{org_id}/applications/{app_id}/} * @@ -275,13 +323,15 @@ public CompletableFuture updateAsync(String orgId, String appId, Ap return client.executeAsync(updateRequest(orgId, appId, params), RequestOptions.none(), UPDATE_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler LIST_RESPONSE = Responses.jsonNode(); + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(Application::fromJson)); - private static ApiRequest listRequest(String orgId) { + private static ApiRequest listRequest(String orgId, AccountApplicationsListParams params) { return ApiRequest.builder("GET", "/organizations/me/{org_id}/applications/") .account() .auth(ApiRequest.Auth.ACCOUNT_TOKEN) .pathParam("org_id", orgId) + .params(params) + .queryFields("limit", "offset") .build(); } @@ -330,45 +380,91 @@ public static final class WithRawResponse { } /** - * {@link #list(String, RequestOptions)} with the raw response. + * {@link #list(String, AccountApplicationsListParams, RequestOptions)} with the raw response. + * + * @param orgId the {@code org_id} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(String orgId, AccountApplicationsListParams params, RequestOptions options) { + return client.execute(listRequest(orgId, params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, AccountApplicationsListParams, RequestOptions)} with the raw response. + * + * @param orgId the {@code org_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String orgId, AccountApplicationsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(orgId, params), options, LIST_RESPONSE); + } + + /** + * {@link #list(String, AccountApplicationsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param orgId the {@code org_id} + * @param params the request parameters + * @return the result + */ + public RawResponse> list(String orgId, AccountApplicationsListParams params) { + return client.execute(listRequest(orgId, params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, AccountApplicationsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param orgId the {@code org_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String orgId, AccountApplicationsListParams params) { + return client.executeAsync(listRequest(orgId, params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(String, AccountApplicationsListParams, RequestOptions)} with the raw response, without parameters. * * @param orgId the {@code org_id} * @param options per-request options * @return the result */ - public RawResponse list(String orgId, RequestOptions options) { - return client.execute(listRequest(orgId), options, LIST_RESPONSE); + public RawResponse> list(String orgId, RequestOptions options) { + return client.execute(listRequest(orgId, AccountApplicationsListParams.builder().build()), options, LIST_RESPONSE); } /** - * Asynchronous {@link #list(String, RequestOptions)} with the raw response. + * Asynchronous {@link #list(String, AccountApplicationsListParams, RequestOptions)} with the raw response, without parameters. * * @param orgId the {@code org_id} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listAsync(String orgId, RequestOptions options) { - return client.executeAsync(listRequest(orgId), options, LIST_RESPONSE); + public CompletableFuture>> listAsync(String orgId, RequestOptions options) { + return client.executeAsync(listRequest(orgId, AccountApplicationsListParams.builder().build()), options, LIST_RESPONSE); } /** - * {@link #list(String, RequestOptions)} with the raw response, with the client's options. + * {@link #list(String, AccountApplicationsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param orgId the {@code org_id} * @return the result */ - public RawResponse list(String orgId) { - return client.execute(listRequest(orgId), RequestOptions.none(), LIST_RESPONSE); + public RawResponse> list(String orgId) { + return client.execute(listRequest(orgId, AccountApplicationsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); } /** - * Asynchronous {@link #list(String, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #list(String, AccountApplicationsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param orgId the {@code org_id} * @return a future completed with the result */ - public CompletableFuture> listAsync(String orgId) { - return client.executeAsync(listRequest(orgId), RequestOptions.none(), LIST_RESPONSE); + public CompletableFuture>> listAsync(String orgId) { + return client.executeAsync(listRequest(orgId, AccountApplicationsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); } /** diff --git a/src/main/java/me/didit/resources/AccountOrganizationsResource.java b/src/main/java/me/didit/resources/AccountOrganizationsResource.java index 4fc8373..820fb1a 100644 --- a/src/main/java/me/didit/resources/AccountOrganizationsResource.java +++ b/src/main/java/me/didit/resources/AccountOrganizationsResource.java @@ -1,15 +1,16 @@ // Code generated by codegen from openapi/*.json. DO NOT EDIT. package me.didit.resources; -import java.util.List; import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; import me.didit.core.ApiRequest; import me.didit.core.Conv; +import me.didit.core.Page; import me.didit.core.RawResponse; import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.AccountOrganizationsListParams; import me.didit.types.Organization; /** The {@code account.organizations} resource: {@code client.account().organizations()}. */ @@ -37,48 +38,93 @@ public WithRawResponse withRawResponse() { *

List organizations the authenticated user belongs to. Each entry's uuid is the org_id used on the application endpoints. * *

{@code GET /organizations/me/} + *

Returns one page; iterate it to fetch every following page automatically. * + * @param params the request parameters * @param options per-request options that override the client options * @return the response body */ - public List list(RequestOptions options) { - return client.execute(listRequest(), options, LIST_RESPONSE).data(); + public Page list(AccountOrganizationsListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE).data(); } /** - * Asynchronous {@link #list(RequestOptions)}. + * Asynchronous {@link #list(AccountOrganizationsListParams, RequestOptions)}. * + * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listAsync(RequestOptions options) { - return client.executeAsync(listRequest(), options, LIST_RESPONSE).thenApply(RawResponse::data); + public CompletableFuture> listAsync(AccountOrganizationsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #list(RequestOptions)}, with the client's options. + * Same as {@link #list(AccountOrganizationsListParams, RequestOptions)}, with the client's options. * + * @param params the request parameters * @return the result */ - public List list() { - return client.execute(listRequest(), RequestOptions.none(), LIST_RESPONSE).data(); + public Page list(AccountOrganizationsListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE).data(); } /** - * Asynchronous {@link #list(RequestOptions)}, with the client's options. + * Asynchronous {@link #list(AccountOrganizationsListParams, RequestOptions)}, with the client's options. * + * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> listAsync() { - return client.executeAsync(listRequest(), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + public CompletableFuture> listAsync(AccountOrganizationsListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler> LIST_RESPONSE = Responses.json(Conv.list(Conv.model(Organization::fromJson))); + /** + * Same as {@link #list(AccountOrganizationsListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public Page list(RequestOptions options) { + return client.execute(listRequest(AccountOrganizationsListParams.builder().build()), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(AccountOrganizationsListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(AccountOrganizationsListParams.builder().build()), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(AccountOrganizationsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public Page list() { + return client.execute(listRequest(AccountOrganizationsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(AccountOrganizationsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> listAsync() { + return client.executeAsync(listRequest(AccountOrganizationsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(Organization::fromJson)); - private static ApiRequest listRequest() { + private static ApiRequest listRequest(AccountOrganizationsListParams params) { return ApiRequest.builder("GET", "/organizations/me/") .account() .auth(ApiRequest.Auth.ACCOUNT_TOKEN) + .params(params) + .queryFields("limit", "offset") .build(); } @@ -91,41 +137,83 @@ public static final class WithRawResponse { } /** - * {@link #list(RequestOptions)} with the raw response. + * {@link #list(AccountOrganizationsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(AccountOrganizationsListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(AccountOrganizationsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(AccountOrganizationsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE); + } + + /** + * {@link #list(AccountOrganizationsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse> list(AccountOrganizationsListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(AccountOrganizationsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(AccountOrganizationsListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(AccountOrganizationsListParams, RequestOptions)} with the raw response, without parameters. * * @param options per-request options * @return the result */ - public RawResponse> list(RequestOptions options) { - return client.execute(listRequest(), options, LIST_RESPONSE); + public RawResponse> list(RequestOptions options) { + return client.execute(listRequest(AccountOrganizationsListParams.builder().build()), options, LIST_RESPONSE); } /** - * Asynchronous {@link #list(RequestOptions)} with the raw response. + * Asynchronous {@link #list(AccountOrganizationsListParams, RequestOptions)} with the raw response, without parameters. * * @param options per-request options * @return a future completed with the result */ - public CompletableFuture>> listAsync(RequestOptions options) { - return client.executeAsync(listRequest(), options, LIST_RESPONSE); + public CompletableFuture>> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(AccountOrganizationsListParams.builder().build()), options, LIST_RESPONSE); } /** - * {@link #list(RequestOptions)} with the raw response, with the client's options. + * {@link #list(AccountOrganizationsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @return the result */ - public RawResponse> list() { - return client.execute(listRequest(), RequestOptions.none(), LIST_RESPONSE); + public RawResponse> list() { + return client.execute(listRequest(AccountOrganizationsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); } /** - * Asynchronous {@link #list(RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #list(AccountOrganizationsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @return a future completed with the result */ - public CompletableFuture>> listAsync() { - return client.executeAsync(listRequest(), RequestOptions.none(), LIST_RESPONSE); + public CompletableFuture>> listAsync() { + return client.executeAsync(listRequest(AccountOrganizationsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); } } } diff --git a/src/main/java/me/didit/resources/AccountResource.java b/src/main/java/me/didit/resources/AccountResource.java index e2201a2..3ff7590 100644 --- a/src/main/java/me/didit/resources/AccountResource.java +++ b/src/main/java/me/didit/resources/AccountResource.java @@ -1,7 +1,6 @@ // Code generated by codegen from openapi/*.json. DO NOT EDIT. package me.didit.resources; -import com.fasterxml.jackson.databind.JsonNode; import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; import me.didit.core.ApiRequest; @@ -11,6 +10,7 @@ import me.didit.core.ResponseHandler; import me.didit.core.Responses; import me.didit.types.AccountResendOtpParams; +import me.didit.types.AccountResendOtpResponse; import me.didit.types.LoginRequest; import me.didit.types.LoginResponse; import me.didit.types.RegisterRequest; @@ -210,7 +210,7 @@ public CompletableFuture loginAsync(LoginRequest params) { * @param options per-request options that override the client options * @return the response body */ - public JsonNode resendOtp(AccountResendOtpParams params, RequestOptions options) { + public AccountResendOtpResponse resendOtp(AccountResendOtpParams params, RequestOptions options) { return client.execute(resendOtpRequest(params), options, RESEND_OTP_RESPONSE).data(); } @@ -221,7 +221,7 @@ public JsonNode resendOtp(AccountResendOtpParams params, RequestOptions options) * @param options per-request options * @return a future completed with the result */ - public CompletableFuture resendOtpAsync(AccountResendOtpParams params, RequestOptions options) { + public CompletableFuture resendOtpAsync(AccountResendOtpParams params, RequestOptions options) { return client.executeAsync(resendOtpRequest(params), options, RESEND_OTP_RESPONSE).thenApply(RawResponse::data); } @@ -231,7 +231,7 @@ public CompletableFuture resendOtpAsync(AccountResendOtpParams params, * @param params the request parameters * @return the result */ - public JsonNode resendOtp(AccountResendOtpParams params) { + public AccountResendOtpResponse resendOtp(AccountResendOtpParams params) { return client.execute(resendOtpRequest(params), RequestOptions.none(), RESEND_OTP_RESPONSE).data(); } @@ -241,7 +241,7 @@ public JsonNode resendOtp(AccountResendOtpParams params) { * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture resendOtpAsync(AccountResendOtpParams params) { + public CompletableFuture resendOtpAsync(AccountResendOtpParams params) { return client.executeAsync(resendOtpRequest(params), RequestOptions.none(), RESEND_OTP_RESPONSE).thenApply(RawResponse::data); } @@ -278,7 +278,7 @@ private static ApiRequest loginRequest(LoginRequest params) { .build(); } - private static final ResponseHandler RESEND_OTP_RESPONSE = Responses.jsonNode(); + private static final ResponseHandler RESEND_OTP_RESPONSE = Responses.json(Conv.model(AccountResendOtpResponse::fromJson)); private static ApiRequest resendOtpRequest(AccountResendOtpParams params) { return ApiRequest.builder("POST", "/programmatic/resend-otp/") @@ -430,7 +430,7 @@ public CompletableFuture> loginAsync(LoginRequest par * @param options per-request options * @return the result */ - public RawResponse resendOtp(AccountResendOtpParams params, RequestOptions options) { + public RawResponse resendOtp(AccountResendOtpParams params, RequestOptions options) { return client.execute(resendOtpRequest(params), options, RESEND_OTP_RESPONSE); } @@ -441,7 +441,7 @@ public RawResponse resendOtp(AccountResendOtpParams params, RequestOpt * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> resendOtpAsync(AccountResendOtpParams params, RequestOptions options) { + public CompletableFuture> resendOtpAsync(AccountResendOtpParams params, RequestOptions options) { return client.executeAsync(resendOtpRequest(params), options, RESEND_OTP_RESPONSE); } @@ -451,7 +451,7 @@ public CompletableFuture> resendOtpAsync(AccountResendOtpP * @param params the request parameters * @return the result */ - public RawResponse resendOtp(AccountResendOtpParams params) { + public RawResponse resendOtp(AccountResendOtpParams params) { return client.execute(resendOtpRequest(params), RequestOptions.none(), RESEND_OTP_RESPONSE); } @@ -461,7 +461,7 @@ public RawResponse resendOtp(AccountResendOtpParams params) { * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> resendOtpAsync(AccountResendOtpParams params) { + public CompletableFuture> resendOtpAsync(AccountResendOtpParams params) { return client.executeAsync(resendOtpRequest(params), RequestOptions.none(), RESEND_OTP_RESPONSE); } } diff --git a/src/main/java/me/didit/resources/AmlResource.java b/src/main/java/me/didit/resources/AmlResource.java index 40869c6..0ce1c65 100644 --- a/src/main/java/me/didit/resources/AmlResource.java +++ b/src/main/java/me/didit/resources/AmlResource.java @@ -9,6 +9,7 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.AmlRetrieveDatasetFreshnessResponse; import me.didit.types.AmlScreenParams; import me.didit.types.AmlScreenResponse; @@ -92,6 +93,52 @@ public CompletableFuture screenAsync(AmlScreenParams params) return client.executeAsync(screenRequest(params), RequestOptions.none(), SCREEN_RESPONSE).thenApply(RawResponse::data); } + /** + * Get AML dataset freshness + * + *

Report how current the datasets behind AML screening are, per source family (sanctions, pep, warnings, adverse_media), as audit evidence for ongoing AML monitoring. The answer is platform-wide: no session in the path, no customer data in the body, the same answer for every application. + * + *

Always 200 for an accepted key. Check availability first: when it is not available, dataset_freshness is null. Dates are never estimated: a family whose datasets reported no date is absent from sources, and a family where only some datasets reported one has oldest_updated_at: null. Readings are taken while ongoing monitoring runs, not on each call; captured_at says when. + * + *

A scoped API key needs sessions: read; a key limited to some workflows or to approved sessions cannot call this endpoint. Nothing is billed. + * + *

{@code GET /v3/aml/dataset-freshness/} + * + * @param options per-request options that override the client options + * @return the response body + */ + public AmlRetrieveDatasetFreshnessResponse retrieveDatasetFreshness(RequestOptions options) { + return client.execute(retrieveDatasetFreshnessRequest(), options, RETRIEVE_DATASET_FRESHNESS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveDatasetFreshness(RequestOptions)}. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveDatasetFreshnessAsync(RequestOptions options) { + return client.executeAsync(retrieveDatasetFreshnessRequest(), options, RETRIEVE_DATASET_FRESHNESS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveDatasetFreshness(RequestOptions)}, with the client's options. + * + * @return the result + */ + public AmlRetrieveDatasetFreshnessResponse retrieveDatasetFreshness() { + return client.execute(retrieveDatasetFreshnessRequest(), RequestOptions.none(), RETRIEVE_DATASET_FRESHNESS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveDatasetFreshness(RequestOptions)}, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture retrieveDatasetFreshnessAsync() { + return client.executeAsync(retrieveDatasetFreshnessRequest(), RequestOptions.none(), RETRIEVE_DATASET_FRESHNESS_RESPONSE).thenApply(RawResponse::data); + } + private static final ResponseHandler SCREEN_RESPONSE = Responses.json(Conv.model(AmlScreenResponse::fromJson)); private static ApiRequest screenRequest(AmlScreenParams params) { @@ -101,6 +148,13 @@ private static ApiRequest screenRequest(AmlScreenParams params) { .build(); } + private static final ResponseHandler RETRIEVE_DATASET_FRESHNESS_RESPONSE = Responses.json(Conv.model(AmlRetrieveDatasetFreshnessResponse::fromJson)); + + private static ApiRequest retrieveDatasetFreshnessRequest() { + return ApiRequest.builder("GET", "/v3/aml/dataset-freshness/") + .build(); + } + /** The {@code aml} operations returning {@link RawResponse}. */ public static final class WithRawResponse { private final ApiClient client; @@ -150,5 +204,43 @@ public RawResponse screen(AmlScreenParams params) { public CompletableFuture> screenAsync(AmlScreenParams params) { return client.executeAsync(screenRequest(params), RequestOptions.none(), SCREEN_RESPONSE); } + + /** + * {@link #retrieveDatasetFreshness(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return the result + */ + public RawResponse retrieveDatasetFreshness(RequestOptions options) { + return client.execute(retrieveDatasetFreshnessRequest(), options, RETRIEVE_DATASET_FRESHNESS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDatasetFreshness(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveDatasetFreshnessAsync(RequestOptions options) { + return client.executeAsync(retrieveDatasetFreshnessRequest(), options, RETRIEVE_DATASET_FRESHNESS_RESPONSE); + } + + /** + * {@link #retrieveDatasetFreshness(RequestOptions)} with the raw response, with the client's options. + * + * @return the result + */ + public RawResponse retrieveDatasetFreshness() { + return client.execute(retrieveDatasetFreshnessRequest(), RequestOptions.none(), RETRIEVE_DATASET_FRESHNESS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDatasetFreshness(RequestOptions)} with the raw response, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> retrieveDatasetFreshnessAsync() { + return client.executeAsync(retrieveDatasetFreshnessRequest(), RequestOptions.none(), RETRIEVE_DATASET_FRESHNESS_RESPONSE); + } } } diff --git a/src/main/java/me/didit/resources/AnalyticsResource.java b/src/main/java/me/didit/resources/AnalyticsResource.java new file mode 100644 index 0000000..9fefb7d --- /dev/null +++ b/src/main/java/me/didit/resources/AnalyticsResource.java @@ -0,0 +1,221 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.AnalyticsRetrieveParams; +import me.didit.types.AnalyticsRetrieveResponse; + +/** The {@code analytics} resource: {@code client.analytics()}. */ +public final class AnalyticsResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + AnalyticsResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * Get application analytics + * + *

Read the numbers behind the Business Console analytics dashboard for a date range. Without scope and widget_keys the response is the user verification (KYC) summary (ApplicationAnalyticsSummary): daily session counts, status totals, conversion rate, the same figures for the preceding period of equal length, locations, demographics, devices, workflow funnels, active verification time, resubmissions, warnings and time series. With scope or widget_keys it is the widgets of one dashboard tab (ApplicationAnalyticsWidgetResponse): kyc, kyb, transactions or home. + * + *

Session counts are by session creation date (UTC) and reflect each session's current status. They come from daily rollups refreshed in the background, so the newest activity can take a short while to appear. Invalid workflow IDs and versions are ignored rather than rejected. Nothing is billed. + * + *

Only a full-access API key can call this endpoint: every scoped key gets 403, whatever access it holds, and so does a key of another application. See API keys. + * + *

{@code GET /v3/organization/{organization_id}/application/{application_id}/analytics/} + *

Application scoped: the organization and application ids come from the client options. + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public AnalyticsRetrieveResponse retrieve(AnalyticsRetrieveParams params, RequestOptions options) { + return client.execute(retrieveRequest(params), options, RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync(AnalyticsRetrieveParams params, RequestOptions options) { + return client.executeAsync(retrieveRequest(params), options, RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public AnalyticsRetrieveResponse retrieve(AnalyticsRetrieveParams params) { + return client.execute(retrieveRequest(params), RequestOptions.none(), RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync(AnalyticsRetrieveParams params) { + return client.executeAsync(retrieveRequest(params), RequestOptions.none(), RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public AnalyticsRetrieveResponse retrieve(RequestOptions options) { + return client.execute(retrieveRequest(AnalyticsRetrieveParams.builder().build()), options, RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync(RequestOptions options) { + return client.executeAsync(retrieveRequest(AnalyticsRetrieveParams.builder().build()), options, RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public AnalyticsRetrieveResponse retrieve() { + return client.execute(retrieveRequest(AnalyticsRetrieveParams.builder().build()), RequestOptions.none(), RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync() { + return client.executeAsync(retrieveRequest(AnalyticsRetrieveParams.builder().build()), RequestOptions.none(), RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler RETRIEVE_RESPONSE = Responses.json(Conv.union(AnalyticsRetrieveResponse::fromJson)); + + private static ApiRequest retrieveRequest(AnalyticsRetrieveParams params) { + return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/analytics/") + .applicationScoped() + .params(params) + .queryFields("start_date", "end_date", "scope", "widget_keys", "workflow_ids", "versions", "branch_path_ids", "tag", "transaction_type", "action_type") + .build(); + } + + /** The {@code analytics} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse retrieve(AnalyticsRetrieveParams params, RequestOptions options) { + return client.execute(retrieveRequest(params), options, RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync(AnalyticsRetrieveParams params, RequestOptions options) { + return client.executeAsync(retrieveRequest(params), options, RETRIEVE_RESPONSE); + } + + /** + * {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse retrieve(AnalyticsRetrieveParams params) { + return client.execute(retrieveRequest(params), RequestOptions.none(), RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync(AnalyticsRetrieveParams params) { + return client.executeAsync(retrieveRequest(params), RequestOptions.none(), RETRIEVE_RESPONSE); + } + + /** + * {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse retrieve(RequestOptions options) { + return client.execute(retrieveRequest(AnalyticsRetrieveParams.builder().build()), options, RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync(RequestOptions options) { + return client.executeAsync(retrieveRequest(AnalyticsRetrieveParams.builder().build()), options, RETRIEVE_RESPONSE); + } + + /** + * {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse retrieve() { + return client.execute(retrieveRequest(AnalyticsRetrieveParams.builder().build()), RequestOptions.none(), RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(AnalyticsRetrieveParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync() { + return client.executeAsync(retrieveRequest(AnalyticsRetrieveParams.builder().build()), RequestOptions.none(), RETRIEVE_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/BillingResource.java b/src/main/java/me/didit/resources/BillingResource.java index 1f7f074..ec507d6 100644 --- a/src/main/java/me/didit/resources/BillingResource.java +++ b/src/main/java/me/didit/resources/BillingResource.java @@ -77,7 +77,7 @@ public CompletableFuture retrieveBalanceAsync() /** * Top up credits * - *

Add credits via a hosted Stripe Checkout session ($50 minimum); not available on annual plans. Redirect the customer to the returned checkout_session_url. + *

Add credits via a hosted checkout session ($50 minimum); not available on annual plans. Redirect the customer to the returned checkout_session_url. * *

{@code POST /v3/billing/top-up/} * diff --git a/src/main/java/me/didit/resources/BlocklistResource.java b/src/main/java/me/didit/resources/BlocklistResource.java new file mode 100644 index 0000000..52e64ec --- /dev/null +++ b/src/main/java/me/didit/resources/BlocklistResource.java @@ -0,0 +1,415 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.Page; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.BlocklistAddParams; +import me.didit.types.BlocklistAddResponse; +import me.didit.types.BlocklistListParams; +import me.didit.types.BlocklistRemoveParams; +import me.didit.types.BlocklistRemoveResponse; +import me.didit.types.BlocklistedItem; + +/** The {@code blocklist} resource: {@code client.blocklist()}. */ +public final class BlocklistResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + BlocklistResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * List blocklisted items + * + *

Returns one paginated feed of the faces, identity documents, phone numbers and email addresses of the application's verification sessions that are currently blocklisted, newest blocklisted_at first; filter with item_type. Items of deleted sessions are not included. The feed covers records blocklisted from a session (with Add session items to the blocklist, from the console, or by a list entry created from a session); values added to a list by hand, uploaded faces, faces from a document-capture selfie and the other entry types are read with List list entries. face_image and front_image are short-lived signed URLs, and a scoped API key without media: read receives null. A scoped API key needs workflows: read. + * + *

{@code GET /v3/blocklist/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page list(BlocklistListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(BlocklistListParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(BlocklistListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(BlocklistListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public Page list(BlocklistListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(BlocklistListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listAsync(BlocklistListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(BlocklistListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public Page list(RequestOptions options) { + return client.execute(listRequest(BlocklistListParams.builder().build()), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(BlocklistListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(BlocklistListParams.builder().build()), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(BlocklistListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public Page list() { + return client.execute(listRequest(BlocklistListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(BlocklistListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> listAsync() { + return client.executeAsync(listRequest(BlocklistListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Add session items to the blocklist + * + *

Blocklists the face, identity document, phone number or email address of one person verification session of the application. For each selected type, Didit takes the session's most recent record, flags it as blocklisted and adds an entry linked to the session to the matching system blocklist (added_by: "api"), so new sessions with a matching face, document number, phone number or email address are declined. Every selected item is checked first, so if one is missing nothing is blocklisted; repeating the call for an item already listed keeps its single entry and refreshes blocklisted_at. The session itself and sessions that already finished are not changed. A scoped API key needs workflows: write. + * + *

{@code POST /v3/blocklist/add/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public BlocklistAddResponse add(BlocklistAddParams params, RequestOptions options) { + return client.execute(addRequest(params), options, ADD_RESPONSE).data(); + } + + /** + * Asynchronous {@link #add(BlocklistAddParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture addAsync(BlocklistAddParams params, RequestOptions options) { + return client.executeAsync(addRequest(params), options, ADD_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #add(BlocklistAddParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public BlocklistAddResponse add(BlocklistAddParams params) { + return client.execute(addRequest(params), RequestOptions.none(), ADD_RESPONSE).data(); + } + + /** + * Asynchronous {@link #add(BlocklistAddParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture addAsync(BlocklistAddParams params) { + return client.executeAsync(addRequest(params), RequestOptions.none(), ADD_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Remove session items from the blocklist + * + *

Lifts the block on the face, identity document, phone number or email address of one person verification session of the application. For each selected type, Didit takes the session's most recent record, clears its blocklisted flag and deletes the system blocklist entries created from that record, so it no longer declines new sessions. Entries added to a list by value are not touched: find them with List list entries and delete them with Delete a list entry. Unblocking a record that is not blocklisted succeeds and changes nothing, and sessions that already finished are not changed. A scoped API key needs workflows: write. + * + *

{@code POST /v3/blocklist/remove/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public BlocklistRemoveResponse remove(BlocklistRemoveParams params, RequestOptions options) { + return client.execute(removeRequest(params), options, REMOVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #remove(BlocklistRemoveParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture removeAsync(BlocklistRemoveParams params, RequestOptions options) { + return client.executeAsync(removeRequest(params), options, REMOVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #remove(BlocklistRemoveParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public BlocklistRemoveResponse remove(BlocklistRemoveParams params) { + return client.execute(removeRequest(params), RequestOptions.none(), REMOVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #remove(BlocklistRemoveParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture removeAsync(BlocklistRemoveParams params) { + return client.executeAsync(removeRequest(params), RequestOptions.none(), REMOVE_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(BlocklistedItem::fromJson)); + + private static ApiRequest listRequest(BlocklistListParams params) { + return ApiRequest.builder("GET", "/v3/blocklist/") + .params(params) + .queryFields("item_type", "limit", "offset") + .build(); + } + + private static final ResponseHandler ADD_RESPONSE = Responses.json(Conv.model(BlocklistAddResponse::fromJson)); + + private static ApiRequest addRequest(BlocklistAddParams params) { + return ApiRequest.builder("POST", "/v3/blocklist/add/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler REMOVE_RESPONSE = Responses.json(Conv.model(BlocklistRemoveResponse::fromJson)); + + private static ApiRequest removeRequest(BlocklistRemoveParams params) { + return ApiRequest.builder("POST", "/v3/blocklist/remove/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + /** The {@code blocklist} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #list(BlocklistListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(BlocklistListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(BlocklistListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(BlocklistListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE); + } + + /** + * {@link #list(BlocklistListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse> list(BlocklistListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(BlocklistListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(BlocklistListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(BlocklistListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse> list(RequestOptions options) { + return client.execute(listRequest(BlocklistListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(BlocklistListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(BlocklistListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * {@link #list(BlocklistListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse> list() { + return client.execute(listRequest(BlocklistListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(BlocklistListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>> listAsync() { + return client.executeAsync(listRequest(BlocklistListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #add(BlocklistAddParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse add(BlocklistAddParams params, RequestOptions options) { + return client.execute(addRequest(params), options, ADD_RESPONSE); + } + + /** + * Asynchronous {@link #add(BlocklistAddParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> addAsync(BlocklistAddParams params, RequestOptions options) { + return client.executeAsync(addRequest(params), options, ADD_RESPONSE); + } + + /** + * {@link #add(BlocklistAddParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse add(BlocklistAddParams params) { + return client.execute(addRequest(params), RequestOptions.none(), ADD_RESPONSE); + } + + /** + * Asynchronous {@link #add(BlocklistAddParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> addAsync(BlocklistAddParams params) { + return client.executeAsync(addRequest(params), RequestOptions.none(), ADD_RESPONSE); + } + + /** + * {@link #remove(BlocklistRemoveParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse remove(BlocklistRemoveParams params, RequestOptions options) { + return client.execute(removeRequest(params), options, REMOVE_RESPONSE); + } + + /** + * Asynchronous {@link #remove(BlocklistRemoveParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> removeAsync(BlocklistRemoveParams params, RequestOptions options) { + return client.executeAsync(removeRequest(params), options, REMOVE_RESPONSE); + } + + /** + * {@link #remove(BlocklistRemoveParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse remove(BlocklistRemoveParams params) { + return client.execute(removeRequest(params), RequestOptions.none(), REMOVE_RESPONSE); + } + + /** + * Asynchronous {@link #remove(BlocklistRemoveParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> removeAsync(BlocklistRemoveParams params) { + return client.executeAsync(removeRequest(params), RequestOptions.none(), REMOVE_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/BusinessSessionsResource.java b/src/main/java/me/didit/resources/BusinessSessionsResource.java index 7ae1fbb..eed901a 100644 --- a/src/main/java/me/didit/resources/BusinessSessionsResource.java +++ b/src/main/java/me/didit/resources/BusinessSessionsResource.java @@ -9,7 +9,7 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; -import me.didit.types.BusinessSessionsListNetworksResponse; +import me.didit.types.EntityNetworkMembershipList; /** The {@code business_sessions} resource: {@code client.businessSessions()}. */ public final class BusinessSessionsResource { @@ -42,7 +42,7 @@ public WithRawResponse withRawResponse() { * @param options per-request options that override the client options * @return the response body */ - public BusinessSessionsListNetworksResponse listNetworks(String businessSessionId, RequestOptions options) { + public EntityNetworkMembershipList listNetworks(String businessSessionId, RequestOptions options) { return client.execute(listNetworksRequest(businessSessionId), options, LIST_NETWORKS_RESPONSE).data(); } @@ -53,7 +53,7 @@ public BusinessSessionsListNetworksResponse listNetworks(String businessSessionI * @param options per-request options * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String businessSessionId, RequestOptions options) { + public CompletableFuture listNetworksAsync(String businessSessionId, RequestOptions options) { return client.executeAsync(listNetworksRequest(businessSessionId), options, LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } @@ -63,7 +63,7 @@ public CompletableFuture listNetworksAsync * @param businessSessionId the {@code business_session_id} * @return the result */ - public BusinessSessionsListNetworksResponse listNetworks(String businessSessionId) { + public EntityNetworkMembershipList listNetworks(String businessSessionId) { return client.execute(listNetworksRequest(businessSessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE).data(); } @@ -73,11 +73,11 @@ public BusinessSessionsListNetworksResponse listNetworks(String businessSessionI * @param businessSessionId the {@code business_session_id} * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String businessSessionId) { + public CompletableFuture listNetworksAsync(String businessSessionId) { return client.executeAsync(listNetworksRequest(businessSessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(BusinessSessionsListNetworksResponse::fromJson)); + private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(EntityNetworkMembershipList::fromJson)); private static ApiRequest listNetworksRequest(String businessSessionId) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/business-sessions/{business_session_id}/networks/") @@ -101,7 +101,7 @@ public static final class WithRawResponse { * @param options per-request options * @return the result */ - public RawResponse listNetworks(String businessSessionId, RequestOptions options) { + public RawResponse listNetworks(String businessSessionId, RequestOptions options) { return client.execute(listNetworksRequest(businessSessionId), options, LIST_NETWORKS_RESPONSE); } @@ -112,7 +112,7 @@ public RawResponse listNetworks(String bus * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String businessSessionId, RequestOptions options) { + public CompletableFuture> listNetworksAsync(String businessSessionId, RequestOptions options) { return client.executeAsync(listNetworksRequest(businessSessionId), options, LIST_NETWORKS_RESPONSE); } @@ -122,7 +122,7 @@ public CompletableFuture> list * @param businessSessionId the {@code business_session_id} * @return the result */ - public RawResponse listNetworks(String businessSessionId) { + public RawResponse listNetworks(String businessSessionId) { return client.execute(listNetworksRequest(businessSessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } @@ -132,7 +132,7 @@ public RawResponse listNetworks(String bus * @param businessSessionId the {@code business_session_id} * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String businessSessionId) { + public CompletableFuture> listNetworksAsync(String businessSessionId) { return client.executeAsync(listNetworksRequest(businessSessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } } diff --git a/src/main/java/me/didit/resources/BusinessesResource.java b/src/main/java/me/didit/resources/BusinessesResource.java index c2c2c5a..060606f 100644 --- a/src/main/java/me/didit/resources/BusinessesResource.java +++ b/src/main/java/me/didit/resources/BusinessesResource.java @@ -4,6 +4,7 @@ import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; import me.didit.core.ApiRequest; +import me.didit.core.BinaryResponse; import me.didit.core.Conv; import me.didit.core.Page; import me.didit.core.RawResponse; @@ -15,18 +16,17 @@ import me.didit.types.BusinessesBatchDeleteParams; import me.didit.types.BusinessesBatchDeleteResponse; import me.didit.types.BusinessesCreateParams; -import me.didit.types.BusinessesListNetworksResponse; import me.didit.types.BusinessesListParams; -import me.didit.types.BusinessesRetrieveOngoingMonitoringJobResponse; -import me.didit.types.BusinessesRetrieveSessionOngoingMonitoringJobResponse; import me.didit.types.BusinessesSetAmlMonitoringParams; import me.didit.types.BusinessesSetAmlMonitoringResponse; -import me.didit.types.BusinessesSetOngoingMonitoringParams; import me.didit.types.BusinessesSetOngoingMonitoringResponse; import me.didit.types.BusinessesSetSessionOngoingMonitoringParams; import me.didit.types.BusinessesSetSessionOngoingMonitoringResponse; import me.didit.types.BusinessesUpdateParams; import me.didit.types.BusinessesUpdateStatusParams; +import me.didit.types.EntityNetworkMembershipList; +import me.didit.types.OngoingMonitoringJob; +import me.didit.types.OngoingMonitoringToggleRequest; /** The {@code businesses} resource: {@code client.businesses()}. */ public final class BusinessesResource { @@ -181,7 +181,7 @@ public CompletableFuture retrieveAsync(String vendorData) { /** * Update business * - *

Partial update of a business: display_name, legal_name, registration_number, country_code, status, metadata. metadata fully replaces. Setting status to BLOCKED adds the vendor_data to the system blocklist; moving away from BLOCKED removes that entry. + *

Partial update of a business: display_name, legal_name, registration_number, country_code, status, metadata. metadata fully replaces. Setting status to BLOCKED adds the vendor_data to the system blocklist; moving away from BLOCKED removes that entry. Every edit is recorded in the company's activity: a status change with the optional reason, and a change to display_name, legal_name, registration_number, country_code or metadata as a profile update. * *

{@code PATCH /v3/businesses/{vendor_data}/} * @@ -273,7 +273,7 @@ public CompletableFuture updateAsync(String vendorData) { /** * Update business status * - *

Flip only the lifecycle status of a business — VendorBusinessStatusChoices (ACTIVE/FLAGGED/BLOCKED, NOT session statuses). BLOCKED adds vendor_data to the system blocklist. + *

Flip only the lifecycle status of a business: ACTIVE, FLAGGED or BLOCKED (not session statuses). BLOCKED adds the vendor_data to the system blocklist; moving away from BLOCKED removes that entry. Each change is recorded in the company's activity, with the optional reason. Sending the current status changes nothing and records no reason. The business.status.updated webhook does not carry reason. * *

{@code PATCH /v3/businesses/{vendor_data}/update-status/} * @@ -548,38 +548,38 @@ public CompletableFuture createAsync() { * @param options per-request options that override the client options * @return the response body */ - public BusinessesSetOngoingMonitoringResponse setOngoingMonitoring(BusinessesSetOngoingMonitoringParams params, RequestOptions options) { + public BusinessesSetOngoingMonitoringResponse setOngoingMonitoring(OngoingMonitoringToggleRequest params, RequestOptions options) { return client.execute(setOngoingMonitoringRequest(params), options, SET_ONGOING_MONITORING_RESPONSE).data(); } /** - * Asynchronous {@link #setOngoingMonitoring(BusinessesSetOngoingMonitoringParams, RequestOptions)}. + * Asynchronous {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)}. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture setOngoingMonitoringAsync(BusinessesSetOngoingMonitoringParams params, RequestOptions options) { + public CompletableFuture setOngoingMonitoringAsync(OngoingMonitoringToggleRequest params, RequestOptions options) { return client.executeAsync(setOngoingMonitoringRequest(params), options, SET_ONGOING_MONITORING_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #setOngoingMonitoring(BusinessesSetOngoingMonitoringParams, RequestOptions)}, with the client's options. + * Same as {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return the result */ - public BusinessesSetOngoingMonitoringResponse setOngoingMonitoring(BusinessesSetOngoingMonitoringParams params) { + public BusinessesSetOngoingMonitoringResponse setOngoingMonitoring(OngoingMonitoringToggleRequest params) { return client.execute(setOngoingMonitoringRequest(params), RequestOptions.none(), SET_ONGOING_MONITORING_RESPONSE).data(); } /** - * Asynchronous {@link #setOngoingMonitoring(BusinessesSetOngoingMonitoringParams, RequestOptions)}, with the client's options. + * Asynchronous {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture setOngoingMonitoringAsync(BusinessesSetOngoingMonitoringParams params) { + public CompletableFuture setOngoingMonitoringAsync(OngoingMonitoringToggleRequest params) { return client.executeAsync(setOngoingMonitoringRequest(params), RequestOptions.none(), SET_ONGOING_MONITORING_RESPONSE).thenApply(RawResponse::data); } @@ -595,7 +595,7 @@ public CompletableFuture setOngoingMonit * @param options per-request options that override the client options * @return the response body */ - public BusinessesRetrieveOngoingMonitoringJobResponse retrieveOngoingMonitoringJob(String jobId, RequestOptions options) { + public OngoingMonitoringJob retrieveOngoingMonitoringJob(String jobId, RequestOptions options) { return client.execute(retrieveOngoingMonitoringJobRequest(jobId), options, RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE).data(); } @@ -606,7 +606,7 @@ public BusinessesRetrieveOngoingMonitoringJobResponse retrieveOngoingMonitoringJ * @param options per-request options * @return a future completed with the result */ - public CompletableFuture retrieveOngoingMonitoringJobAsync(String jobId, RequestOptions options) { + public CompletableFuture retrieveOngoingMonitoringJobAsync(String jobId, RequestOptions options) { return client.executeAsync(retrieveOngoingMonitoringJobRequest(jobId), options, RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE).thenApply(RawResponse::data); } @@ -616,7 +616,7 @@ public CompletableFuture retriev * @param jobId the {@code job_id} * @return the result */ - public BusinessesRetrieveOngoingMonitoringJobResponse retrieveOngoingMonitoringJob(String jobId) { + public OngoingMonitoringJob retrieveOngoingMonitoringJob(String jobId) { return client.execute(retrieveOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE).data(); } @@ -626,7 +626,7 @@ public BusinessesRetrieveOngoingMonitoringJobResponse retrieveOngoingMonitoringJ * @param jobId the {@code job_id} * @return a future completed with the result */ - public CompletableFuture retrieveOngoingMonitoringJobAsync(String jobId) { + public CompletableFuture retrieveOngoingMonitoringJobAsync(String jobId) { return client.executeAsync(retrieveOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE).thenApply(RawResponse::data); } @@ -689,7 +689,7 @@ public CompletableFuture setSessi * @param options per-request options that override the client options * @return the response body */ - public BusinessesRetrieveSessionOngoingMonitoringJobResponse retrieveSessionOngoingMonitoringJob(String jobId, RequestOptions options) { + public OngoingMonitoringJob retrieveSessionOngoingMonitoringJob(String jobId, RequestOptions options) { return client.execute(retrieveSessionOngoingMonitoringJobRequest(jobId), options, RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE).data(); } @@ -700,7 +700,7 @@ public BusinessesRetrieveSessionOngoingMonitoringJobResponse retrieveSessionOngo * @param options per-request options * @return a future completed with the result */ - public CompletableFuture retrieveSessionOngoingMonitoringJobAsync(String jobId, RequestOptions options) { + public CompletableFuture retrieveSessionOngoingMonitoringJobAsync(String jobId, RequestOptions options) { return client.executeAsync(retrieveSessionOngoingMonitoringJobRequest(jobId), options, RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE).thenApply(RawResponse::data); } @@ -710,7 +710,7 @@ public CompletableFuture * @param jobId the {@code job_id} * @return the result */ - public BusinessesRetrieveSessionOngoingMonitoringJobResponse retrieveSessionOngoingMonitoringJob(String jobId) { + public OngoingMonitoringJob retrieveSessionOngoingMonitoringJob(String jobId) { return client.execute(retrieveSessionOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE).data(); } @@ -720,7 +720,7 @@ public BusinessesRetrieveSessionOngoingMonitoringJobResponse retrieveSessionOngo * @param jobId the {@code job_id} * @return a future completed with the result */ - public CompletableFuture retrieveSessionOngoingMonitoringJobAsync(String jobId) { + public CompletableFuture retrieveSessionOngoingMonitoringJobAsync(String jobId) { return client.executeAsync(retrieveSessionOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE).thenApply(RawResponse::data); } @@ -736,7 +736,7 @@ public CompletableFuture * @param options per-request options that override the client options * @return the response body */ - public BusinessesListNetworksResponse listNetworks(String vendorBusinessUuid, RequestOptions options) { + public EntityNetworkMembershipList listNetworks(String vendorBusinessUuid, RequestOptions options) { return client.execute(listNetworksRequest(vendorBusinessUuid), options, LIST_NETWORKS_RESPONSE).data(); } @@ -747,7 +747,7 @@ public BusinessesListNetworksResponse listNetworks(String vendorBusinessUuid, Re * @param options per-request options * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String vendorBusinessUuid, RequestOptions options) { + public CompletableFuture listNetworksAsync(String vendorBusinessUuid, RequestOptions options) { return client.executeAsync(listNetworksRequest(vendorBusinessUuid), options, LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } @@ -757,7 +757,7 @@ public CompletableFuture listNetworksAsync(Strin * @param vendorBusinessUuid the {@code vendor_business_uuid} * @return the result */ - public BusinessesListNetworksResponse listNetworks(String vendorBusinessUuid) { + public EntityNetworkMembershipList listNetworks(String vendorBusinessUuid) { return client.execute(listNetworksRequest(vendorBusinessUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE).data(); } @@ -767,10 +767,62 @@ public BusinessesListNetworksResponse listNetworks(String vendorBusinessUuid) { * @param vendorBusinessUuid the {@code vendor_business_uuid} * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String vendorBusinessUuid) { + public CompletableFuture listNetworksAsync(String vendorBusinessUuid) { return client.executeAsync(listNetworksRequest(vendorBusinessUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } + /** + * Download a business session PDF report + * + *

Render the PDF report of one Business Verification (KYB) session: company registry data, key people and beneficial owners, documents, AML results and the other workflow features, plus the session's tags and review activity. With white-label customization on, the report carries your logo and privacy policy URL. The body is the PDF itself (application/pdf), not JSON. + * + *

The session must be Approved, Declined or In Review; any other status returns 403, so wait for the status.updated webhook first. The report is rendered on every call and nothing is billed, so store the file instead of fetching it repeatedly. GET /v3/session/{sessionId}/generate-pdf/ renders the same kind of report by session ID alone. + * + *

A PDF report counts as media: a scoped API key needs businesses: read and media: read, and a key limited to some workflows or to approved sessions cannot call it. + * + *

{@code GET /v3/organization/{organization_id}/application/{application_id}/businesses/{business_session_id}/pdf/} + *

Application scoped: the organization and application ids come from the client options. + *

Returns the file; close the {@link me.didit.core.BinaryResponse} when you stream it. + * + * @param businessSessionId `session_id` of the Business Verification (KYB) session, as returned when the session was created. A session of another application returns `404`. + * @param options per-request options that override the client options + * @return the response body + */ + public BinaryResponse generateSessionPdf(String businessSessionId, RequestOptions options) { + return client.execute(generateSessionPdfRequest(businessSessionId), options, GENERATE_SESSION_PDF_RESPONSE).data(); + } + + /** + * Asynchronous {@link #generateSessionPdf(String, RequestOptions)}. + * + * @param businessSessionId the {@code business_session_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture generateSessionPdfAsync(String businessSessionId, RequestOptions options) { + return client.executeAsync(generateSessionPdfRequest(businessSessionId), options, GENERATE_SESSION_PDF_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #generateSessionPdf(String, RequestOptions)}, with the client's options. + * + * @param businessSessionId the {@code business_session_id} + * @return the result + */ + public BinaryResponse generateSessionPdf(String businessSessionId) { + return client.execute(generateSessionPdfRequest(businessSessionId), RequestOptions.none(), GENERATE_SESSION_PDF_RESPONSE).data(); + } + + /** + * Asynchronous {@link #generateSessionPdf(String, RequestOptions)}, with the client's options. + * + * @param businessSessionId the {@code business_session_id} + * @return a future completed with the result + */ + public CompletableFuture generateSessionPdfAsync(String businessSessionId) { + return client.executeAsync(generateSessionPdfRequest(businessSessionId), RequestOptions.none(), GENERATE_SESSION_PDF_RESPONSE).thenApply(RawResponse::data); + } + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(BusinessListItem::fromJson)); private static ApiRequest listRequest(BusinessesListParams params) { @@ -837,7 +889,7 @@ private static ApiRequest createRequest(BusinessesCreateParams params) { private static final ResponseHandler SET_ONGOING_MONITORING_RESPONSE = Responses.byStatus(BusinessesSetOngoingMonitoringResponse::fromResponse); - private static ApiRequest setOngoingMonitoringRequest(BusinessesSetOngoingMonitoringParams params) { + private static ApiRequest setOngoingMonitoringRequest(OngoingMonitoringToggleRequest params) { return ApiRequest.builder("PATCH", "/v3/organization/{organization_id}/application/{application_id}/vendor-businesses/toggle-ongoing-monitoring/") .applicationScoped() .params(params) @@ -845,7 +897,7 @@ private static ApiRequest setOngoingMonitoringRequest(BusinessesSetOngoingMonito .build(); } - private static final ResponseHandler RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE = Responses.json(Conv.model(BusinessesRetrieveOngoingMonitoringJobResponse::fromJson)); + private static final ResponseHandler RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE = Responses.json(Conv.model(OngoingMonitoringJob::fromJson)); private static ApiRequest retrieveOngoingMonitoringJobRequest(String jobId) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/vendor-businesses/toggle-ongoing-monitoring/{job_id}/") @@ -864,7 +916,7 @@ private static ApiRequest setSessionOngoingMonitoringRequest(BusinessesSetSessio .build(); } - private static final ResponseHandler RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE = Responses.json(Conv.model(BusinessesRetrieveSessionOngoingMonitoringJobResponse::fromJson)); + private static final ResponseHandler RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE = Responses.json(Conv.model(OngoingMonitoringJob::fromJson)); private static ApiRequest retrieveSessionOngoingMonitoringJobRequest(String jobId) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/businesses/toggle-ongoing-monitoring/{job_id}/") @@ -873,7 +925,7 @@ private static ApiRequest retrieveSessionOngoingMonitoringJobRequest(String jobI .build(); } - private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(BusinessesListNetworksResponse::fromJson)); + private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(EntityNetworkMembershipList::fromJson)); private static ApiRequest listNetworksRequest(String vendorBusinessUuid) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/businesses/{vendor_business_uuid}/networks/") @@ -882,6 +934,16 @@ private static ApiRequest listNetworksRequest(String vendorBusinessUuid) { .build(); } + private static final ResponseHandler GENERATE_SESSION_PDF_RESPONSE = Responses.binary(); + + private static ApiRequest generateSessionPdfRequest(String businessSessionId) { + return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/businesses/{business_session_id}/pdf/") + .applicationScoped() + .pathParam("business_session_id", businessSessionId) + .accept("application/pdf") + .build(); + } + /** The {@code businesses} operations returning {@link RawResponse}. */ public static final class WithRawResponse { private final ApiClient client; @@ -1349,44 +1411,44 @@ public CompletableFuture> createAsync() { } /** - * {@link #setOngoingMonitoring(BusinessesSetOngoingMonitoringParams, RequestOptions)} with the raw response. + * {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse setOngoingMonitoring(BusinessesSetOngoingMonitoringParams params, RequestOptions options) { + public RawResponse setOngoingMonitoring(OngoingMonitoringToggleRequest params, RequestOptions options) { return client.execute(setOngoingMonitoringRequest(params), options, SET_ONGOING_MONITORING_RESPONSE); } /** - * Asynchronous {@link #setOngoingMonitoring(BusinessesSetOngoingMonitoringParams, RequestOptions)} with the raw response. + * Asynchronous {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> setOngoingMonitoringAsync(BusinessesSetOngoingMonitoringParams params, RequestOptions options) { + public CompletableFuture> setOngoingMonitoringAsync(OngoingMonitoringToggleRequest params, RequestOptions options) { return client.executeAsync(setOngoingMonitoringRequest(params), options, SET_ONGOING_MONITORING_RESPONSE); } /** - * {@link #setOngoingMonitoring(BusinessesSetOngoingMonitoringParams, RequestOptions)} with the raw response, with the client's options. + * {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return the result */ - public RawResponse setOngoingMonitoring(BusinessesSetOngoingMonitoringParams params) { + public RawResponse setOngoingMonitoring(OngoingMonitoringToggleRequest params) { return client.execute(setOngoingMonitoringRequest(params), RequestOptions.none(), SET_ONGOING_MONITORING_RESPONSE); } /** - * Asynchronous {@link #setOngoingMonitoring(BusinessesSetOngoingMonitoringParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> setOngoingMonitoringAsync(BusinessesSetOngoingMonitoringParams params) { + public CompletableFuture> setOngoingMonitoringAsync(OngoingMonitoringToggleRequest params) { return client.executeAsync(setOngoingMonitoringRequest(params), RequestOptions.none(), SET_ONGOING_MONITORING_RESPONSE); } @@ -1397,7 +1459,7 @@ public CompletableFuture> se * @param options per-request options * @return the result */ - public RawResponse retrieveOngoingMonitoringJob(String jobId, RequestOptions options) { + public RawResponse retrieveOngoingMonitoringJob(String jobId, RequestOptions options) { return client.execute(retrieveOngoingMonitoringJobRequest(jobId), options, RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1408,7 +1470,7 @@ public RawResponse retrieveOngoi * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> retrieveOngoingMonitoringJobAsync(String jobId, RequestOptions options) { + public CompletableFuture> retrieveOngoingMonitoringJobAsync(String jobId, RequestOptions options) { return client.executeAsync(retrieveOngoingMonitoringJobRequest(jobId), options, RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1418,7 +1480,7 @@ public CompletableFuture retrieveOngoingMonitoringJob(String jobId) { + public RawResponse retrieveOngoingMonitoringJob(String jobId) { return client.execute(retrieveOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1428,7 +1490,7 @@ public RawResponse retrieveOngoi * @param jobId the {@code job_id} * @return a future completed with the result */ - public CompletableFuture> retrieveOngoingMonitoringJobAsync(String jobId) { + public CompletableFuture> retrieveOngoingMonitoringJobAsync(String jobId) { return client.executeAsync(retrieveOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1481,7 +1543,7 @@ public CompletableFuture retrieveSessionOngoingMonitoringJob(String jobId, RequestOptions options) { + public RawResponse retrieveSessionOngoingMonitoringJob(String jobId, RequestOptions options) { return client.execute(retrieveSessionOngoingMonitoringJobRequest(jobId), options, RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1492,7 +1554,7 @@ public RawResponse retrie * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> retrieveSessionOngoingMonitoringJobAsync(String jobId, RequestOptions options) { + public CompletableFuture> retrieveSessionOngoingMonitoringJobAsync(String jobId, RequestOptions options) { return client.executeAsync(retrieveSessionOngoingMonitoringJobRequest(jobId), options, RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1502,7 +1564,7 @@ public CompletableFuture retrieveSessionOngoingMonitoringJob(String jobId) { + public RawResponse retrieveSessionOngoingMonitoringJob(String jobId) { return client.execute(retrieveSessionOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1512,7 +1574,7 @@ public RawResponse retrie * @param jobId the {@code job_id} * @return a future completed with the result */ - public CompletableFuture> retrieveSessionOngoingMonitoringJobAsync(String jobId) { + public CompletableFuture> retrieveSessionOngoingMonitoringJobAsync(String jobId) { return client.executeAsync(retrieveSessionOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_SESSION_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1523,7 +1585,7 @@ public CompletableFuture listNetworks(String vendorBusinessUuid, RequestOptions options) { + public RawResponse listNetworks(String vendorBusinessUuid, RequestOptions options) { return client.execute(listNetworksRequest(vendorBusinessUuid), options, LIST_NETWORKS_RESPONSE); } @@ -1534,7 +1596,7 @@ public RawResponse listNetworks(String vendorBus * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String vendorBusinessUuid, RequestOptions options) { + public CompletableFuture> listNetworksAsync(String vendorBusinessUuid, RequestOptions options) { return client.executeAsync(listNetworksRequest(vendorBusinessUuid), options, LIST_NETWORKS_RESPONSE); } @@ -1544,7 +1606,7 @@ public CompletableFuture> listNetwor * @param vendorBusinessUuid the {@code vendor_business_uuid} * @return the result */ - public RawResponse listNetworks(String vendorBusinessUuid) { + public RawResponse listNetworks(String vendorBusinessUuid) { return client.execute(listNetworksRequest(vendorBusinessUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } @@ -1554,8 +1616,50 @@ public RawResponse listNetworks(String vendorBus * @param vendorBusinessUuid the {@code vendor_business_uuid} * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String vendorBusinessUuid) { + public CompletableFuture> listNetworksAsync(String vendorBusinessUuid) { return client.executeAsync(listNetworksRequest(vendorBusinessUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } + + /** + * {@link #generateSessionPdf(String, RequestOptions)} with the raw response. + * + * @param businessSessionId the {@code business_session_id} + * @param options per-request options + * @return the result + */ + public RawResponse generateSessionPdf(String businessSessionId, RequestOptions options) { + return client.execute(generateSessionPdfRequest(businessSessionId), options, GENERATE_SESSION_PDF_RESPONSE); + } + + /** + * Asynchronous {@link #generateSessionPdf(String, RequestOptions)} with the raw response. + * + * @param businessSessionId the {@code business_session_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> generateSessionPdfAsync(String businessSessionId, RequestOptions options) { + return client.executeAsync(generateSessionPdfRequest(businessSessionId), options, GENERATE_SESSION_PDF_RESPONSE); + } + + /** + * {@link #generateSessionPdf(String, RequestOptions)} with the raw response, with the client's options. + * + * @param businessSessionId the {@code business_session_id} + * @return the result + */ + public RawResponse generateSessionPdf(String businessSessionId) { + return client.execute(generateSessionPdfRequest(businessSessionId), RequestOptions.none(), GENERATE_SESSION_PDF_RESPONSE); + } + + /** + * Asynchronous {@link #generateSessionPdf(String, RequestOptions)} with the raw response, with the client's options. + * + * @param businessSessionId the {@code business_session_id} + * @return a future completed with the result + */ + public CompletableFuture> generateSessionPdfAsync(String businessSessionId) { + return client.executeAsync(generateSessionPdfRequest(businessSessionId), RequestOptions.none(), GENERATE_SESSION_PDF_RESPONSE); + } } } diff --git a/src/main/java/me/didit/resources/CasesBlueprintsResource.java b/src/main/java/me/didit/resources/CasesBlueprintsResource.java index c4b23cc..718b18a 100644 --- a/src/main/java/me/didit/resources/CasesBlueprintsResource.java +++ b/src/main/java/me/didit/resources/CasesBlueprintsResource.java @@ -12,6 +12,7 @@ import me.didit.core.Responses; import me.didit.types.CaseBlueprintDetail; import me.didit.types.CaseBlueprintListItem; +import me.didit.types.CaseBlueprintPresetInstallResult; import me.didit.types.CasePublicationCaseBlueprint; import me.didit.types.CasePublicationPolicyDraft; import me.didit.types.CasePublicationPolicyDraftPreconditionRequest; @@ -20,7 +21,6 @@ import me.didit.types.CasePublicationPolicyRevision; import me.didit.types.CasesBlueprintsCreateParams; import me.didit.types.CasesBlueprintsInstallPresetsParams; -import me.didit.types.CasesBlueprintsInstallPresetsResponse; import me.didit.types.CasesBlueprintsListHistoryParams; import me.didit.types.CasesBlueprintsListParams; import me.didit.types.CasesBlueprintsListPresetsResponse; @@ -419,7 +419,7 @@ public CompletableFuture listPresetsAsync() * @param options per-request options that override the client options * @return the response body */ - public CasesBlueprintsInstallPresetsResponse installPresets(CasesBlueprintsInstallPresetsParams params, RequestOptions options) { + public CaseBlueprintPresetInstallResult installPresets(CasesBlueprintsInstallPresetsParams params, RequestOptions options) { return client.execute(installPresetsRequest(params), options, INSTALL_PRESETS_RESPONSE).data(); } @@ -430,7 +430,7 @@ public CasesBlueprintsInstallPresetsResponse installPresets(CasesBlueprintsInsta * @param options per-request options * @return a future completed with the result */ - public CompletableFuture installPresetsAsync(CasesBlueprintsInstallPresetsParams params, RequestOptions options) { + public CompletableFuture installPresetsAsync(CasesBlueprintsInstallPresetsParams params, RequestOptions options) { return client.executeAsync(installPresetsRequest(params), options, INSTALL_PRESETS_RESPONSE).thenApply(RawResponse::data); } @@ -440,7 +440,7 @@ public CompletableFuture installPresetsAs * @param params the request parameters * @return the result */ - public CasesBlueprintsInstallPresetsResponse installPresets(CasesBlueprintsInstallPresetsParams params) { + public CaseBlueprintPresetInstallResult installPresets(CasesBlueprintsInstallPresetsParams params) { return client.execute(installPresetsRequest(params), RequestOptions.none(), INSTALL_PRESETS_RESPONSE).data(); } @@ -450,7 +450,7 @@ public CasesBlueprintsInstallPresetsResponse installPresets(CasesBlueprintsInsta * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture installPresetsAsync(CasesBlueprintsInstallPresetsParams params) { + public CompletableFuture installPresetsAsync(CasesBlueprintsInstallPresetsParams params) { return client.executeAsync(installPresetsRequest(params), RequestOptions.none(), INSTALL_PRESETS_RESPONSE).thenApply(RawResponse::data); } @@ -460,7 +460,7 @@ public CompletableFuture installPresetsAs * @param options per-request options * @return the result */ - public CasesBlueprintsInstallPresetsResponse installPresets(RequestOptions options) { + public CaseBlueprintPresetInstallResult installPresets(RequestOptions options) { return client.execute(installPresetsRequest(CasesBlueprintsInstallPresetsParams.builder().build()), options, INSTALL_PRESETS_RESPONSE).data(); } @@ -470,7 +470,7 @@ public CasesBlueprintsInstallPresetsResponse installPresets(RequestOptions optio * @param options per-request options * @return a future completed with the result */ - public CompletableFuture installPresetsAsync(RequestOptions options) { + public CompletableFuture installPresetsAsync(RequestOptions options) { return client.executeAsync(installPresetsRequest(CasesBlueprintsInstallPresetsParams.builder().build()), options, INSTALL_PRESETS_RESPONSE).thenApply(RawResponse::data); } @@ -479,7 +479,7 @@ public CompletableFuture installPresetsAs * * @return the result */ - public CasesBlueprintsInstallPresetsResponse installPresets() { + public CaseBlueprintPresetInstallResult installPresets() { return client.execute(installPresetsRequest(CasesBlueprintsInstallPresetsParams.builder().build()), RequestOptions.none(), INSTALL_PRESETS_RESPONSE).data(); } @@ -488,7 +488,7 @@ public CasesBlueprintsInstallPresetsResponse installPresets() { * * @return a future completed with the result */ - public CompletableFuture installPresetsAsync() { + public CompletableFuture installPresetsAsync() { return client.executeAsync(installPresetsRequest(CasesBlueprintsInstallPresetsParams.builder().build()), RequestOptions.none(), INSTALL_PRESETS_RESPONSE).thenApply(RawResponse::data); } @@ -821,7 +821,7 @@ private static ApiRequest listPresetsRequest() { .build(); } - private static final ResponseHandler INSTALL_PRESETS_RESPONSE = Responses.byStatus(CasesBlueprintsInstallPresetsResponse::fromResponse); + private static final ResponseHandler INSTALL_PRESETS_RESPONSE = Responses.json(Conv.model(CaseBlueprintPresetInstallResult::fromJson)); private static ApiRequest installPresetsRequest(CasesBlueprintsInstallPresetsParams params) { return ApiRequest.builder("POST", "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/presets/install/") @@ -1231,7 +1231,7 @@ public CompletableFuture> listPr * @param options per-request options * @return the result */ - public RawResponse installPresets(CasesBlueprintsInstallPresetsParams params, RequestOptions options) { + public RawResponse installPresets(CasesBlueprintsInstallPresetsParams params, RequestOptions options) { return client.execute(installPresetsRequest(params), options, INSTALL_PRESETS_RESPONSE); } @@ -1242,7 +1242,7 @@ public RawResponse installPresets(CasesBl * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> installPresetsAsync(CasesBlueprintsInstallPresetsParams params, RequestOptions options) { + public CompletableFuture> installPresetsAsync(CasesBlueprintsInstallPresetsParams params, RequestOptions options) { return client.executeAsync(installPresetsRequest(params), options, INSTALL_PRESETS_RESPONSE); } @@ -1252,7 +1252,7 @@ public CompletableFuture> ins * @param params the request parameters * @return the result */ - public RawResponse installPresets(CasesBlueprintsInstallPresetsParams params) { + public RawResponse installPresets(CasesBlueprintsInstallPresetsParams params) { return client.execute(installPresetsRequest(params), RequestOptions.none(), INSTALL_PRESETS_RESPONSE); } @@ -1262,7 +1262,7 @@ public RawResponse installPresets(CasesBl * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> installPresetsAsync(CasesBlueprintsInstallPresetsParams params) { + public CompletableFuture> installPresetsAsync(CasesBlueprintsInstallPresetsParams params) { return client.executeAsync(installPresetsRequest(params), RequestOptions.none(), INSTALL_PRESETS_RESPONSE); } @@ -1272,7 +1272,7 @@ public CompletableFuture> ins * @param options per-request options * @return the result */ - public RawResponse installPresets(RequestOptions options) { + public RawResponse installPresets(RequestOptions options) { return client.execute(installPresetsRequest(CasesBlueprintsInstallPresetsParams.builder().build()), options, INSTALL_PRESETS_RESPONSE); } @@ -1282,7 +1282,7 @@ public RawResponse installPresets(Request * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> installPresetsAsync(RequestOptions options) { + public CompletableFuture> installPresetsAsync(RequestOptions options) { return client.executeAsync(installPresetsRequest(CasesBlueprintsInstallPresetsParams.builder().build()), options, INSTALL_PRESETS_RESPONSE); } @@ -1291,7 +1291,7 @@ public CompletableFuture> ins * * @return the result */ - public RawResponse installPresets() { + public RawResponse installPresets() { return client.execute(installPresetsRequest(CasesBlueprintsInstallPresetsParams.builder().build()), RequestOptions.none(), INSTALL_PRESETS_RESPONSE); } @@ -1300,7 +1300,7 @@ public RawResponse installPresets() { * * @return a future completed with the result */ - public CompletableFuture> installPresetsAsync() { + public CompletableFuture> installPresetsAsync() { return client.executeAsync(installPresetsRequest(CasesBlueprintsInstallPresetsParams.builder().build()), RequestOptions.none(), INSTALL_PRESETS_RESPONSE); } diff --git a/src/main/java/me/didit/resources/CasesRequestsResource.java b/src/main/java/me/didit/resources/CasesRequestsResource.java index 36e696c..194a2a8 100644 --- a/src/main/java/me/didit/resources/CasesRequestsResource.java +++ b/src/main/java/me/didit/resources/CasesRequestsResource.java @@ -12,6 +12,7 @@ import me.didit.core.ResponseHandler; import me.didit.core.Responses; import me.didit.types.CasesRequestsListParams; +import me.didit.types.CasesRequestsRemindResponse; import me.didit.types.CustomerMessagingCaseRequest; import me.didit.types.CustomerMessagingCaseRequestConfirmRequest; import me.didit.types.CustomerMessagingCaseRequestOptions; @@ -363,6 +364,57 @@ public CompletableFuture previewAsy return client.executeAsync(previewRequest(caseUuid, params), RequestOptions.none(), PREVIEW_RESPONSE).thenApply(RawResponse::data); } + /** + * Remind the customer of a request + * + *

Send a waiting customer request's email again, to the same verified recipient, with the same content. You can remind a request at most once every 24 hours. A reminder changes nothing else: the request keeps its deadline and the case keeps its status. Each reminder is logged on the case as a CUSTOMER_REQUEST event with action reminded. The response's delivery_status is how this reminder's delivery ended: SENT, SIMULATED (the email was not sent), or UNKNOWN (the email might have been accepted; it is not sent again automatically). next_reminder_at is when you can remind this request again. Returns 409 with a reason when the customer no longer has the request to answer, and 429 with retry_at and a Retry-After header when the request was reminded in the last 24 hours. Requires write:cases. + * + *

{@code POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/remind/} + *

Application scoped: the organization and application ids come from the client options. + * + * @param caseUuid Case UUID. + * @param requestUuid Customer request UUID, from the case's request list. + * @param options per-request options that override the client options + * @return the response body + */ + public CasesRequestsRemindResponse remind(String caseUuid, String requestUuid, RequestOptions options) { + return client.execute(remindRequest(caseUuid, requestUuid), options, REMIND_RESPONSE).data(); + } + + /** + * Asynchronous {@link #remind(String, String, RequestOptions)}. + * + * @param caseUuid the {@code case_uuid} + * @param requestUuid the {@code request_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture remindAsync(String caseUuid, String requestUuid, RequestOptions options) { + return client.executeAsync(remindRequest(caseUuid, requestUuid), options, REMIND_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #remind(String, String, RequestOptions)}, with the client's options. + * + * @param caseUuid the {@code case_uuid} + * @param requestUuid the {@code request_uuid} + * @return the result + */ + public CasesRequestsRemindResponse remind(String caseUuid, String requestUuid) { + return client.execute(remindRequest(caseUuid, requestUuid), RequestOptions.none(), REMIND_RESPONSE).data(); + } + + /** + * Asynchronous {@link #remind(String, String, RequestOptions)}, with the client's options. + * + * @param caseUuid the {@code case_uuid} + * @param requestUuid the {@code request_uuid} + * @return a future completed with the result + */ + public CompletableFuture remindAsync(String caseUuid, String requestUuid) { + return client.executeAsync(remindRequest(caseUuid, requestUuid), RequestOptions.none(), REMIND_RESPONSE).thenApply(RawResponse::data); + } + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(CustomerMessagingCaseRequest::fromJson)); private static ApiRequest listRequest(String caseUuid, CasesRequestsListParams params) { @@ -426,6 +478,16 @@ private static ApiRequest previewRequest(String caseUuid, CustomerMessagingCaseR .build(); } + private static final ResponseHandler REMIND_RESPONSE = Responses.json(Conv.model(CasesRequestsRemindResponse::fromJson)); + + private static ApiRequest remindRequest(String caseUuid, String requestUuid) { + return ApiRequest.builder("POST", "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/remind/") + .applicationScoped() + .pathParam("case_uuid", caseUuid) + .pathParam("request_uuid", requestUuid) + .build(); + } + /** The {@code cases.requests} operations returning {@link RawResponse}. */ public static final class WithRawResponse { private final ApiClient client; @@ -747,5 +809,51 @@ public RawResponse preview(String c public CompletableFuture> previewAsync(String caseUuid, CustomerMessagingCaseRequestPreviewRequest params) { return client.executeAsync(previewRequest(caseUuid, params), RequestOptions.none(), PREVIEW_RESPONSE); } + + /** + * {@link #remind(String, String, RequestOptions)} with the raw response. + * + * @param caseUuid the {@code case_uuid} + * @param requestUuid the {@code request_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse remind(String caseUuid, String requestUuid, RequestOptions options) { + return client.execute(remindRequest(caseUuid, requestUuid), options, REMIND_RESPONSE); + } + + /** + * Asynchronous {@link #remind(String, String, RequestOptions)} with the raw response. + * + * @param caseUuid the {@code case_uuid} + * @param requestUuid the {@code request_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> remindAsync(String caseUuid, String requestUuid, RequestOptions options) { + return client.executeAsync(remindRequest(caseUuid, requestUuid), options, REMIND_RESPONSE); + } + + /** + * {@link #remind(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param caseUuid the {@code case_uuid} + * @param requestUuid the {@code request_uuid} + * @return the result + */ + public RawResponse remind(String caseUuid, String requestUuid) { + return client.execute(remindRequest(caseUuid, requestUuid), RequestOptions.none(), REMIND_RESPONSE); + } + + /** + * Asynchronous {@link #remind(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param caseUuid the {@code case_uuid} + * @param requestUuid the {@code request_uuid} + * @return a future completed with the result + */ + public CompletableFuture> remindAsync(String caseUuid, String requestUuid) { + return client.executeAsync(remindRequest(caseUuid, requestUuid), RequestOptions.none(), REMIND_RESPONSE); + } } } diff --git a/src/main/java/me/didit/resources/CasesResource.java b/src/main/java/me/didit/resources/CasesResource.java index be21271..e413882 100644 --- a/src/main/java/me/didit/resources/CasesResource.java +++ b/src/main/java/me/didit/resources/CasesResource.java @@ -1,9 +1,7 @@ // Code generated by codegen from openapi/*.json. DO NOT EDIT. package me.didit.resources; -import com.fasterxml.jackson.databind.JsonNode; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; import me.didit.core.ApiRequest; @@ -13,10 +11,12 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.AMLScreening; import me.didit.types.CaseActionCaseActionReceipt; import me.didit.types.CaseActionCaseActionReceiptPage; import me.didit.types.CaseActionCaseEntityActionRequest; import me.didit.types.CaseActionCaseEntityActionResult; +import me.didit.types.CaseBulkUpdateResult; import me.didit.types.CaseDetail; import me.didit.types.CaseDuplicateCaseDuplicatePage; import me.didit.types.CaseDuplicateCaseDuplicateQueryRequest; @@ -24,9 +24,7 @@ import me.didit.types.CaseListItem; import me.didit.types.CasesAssignParams; import me.didit.types.CasesBulkAssignParams; -import me.didit.types.CasesBulkAssignResponse; import me.didit.types.CasesBulkUpdateStatusParams; -import me.didit.types.CasesBulkUpdateStatusResponse; import me.didit.types.CasesCreateParams; import me.didit.types.CasesListActionReceiptsParams; import me.didit.types.CasesListEventsParams; @@ -331,7 +329,7 @@ public CompletableFuture retrieveAsync(String caseUuid) { /** * Update case * - *

Update the case's title, priority, due date, or tags. Logs PRIORITY_CHANGED/DUE_DATE_CHANGED/TAG_ADDED/TAG_REMOVED events as applicable. + *

Update the case's title, priority, due date, tags, or owning review team. Send only the fields you want to change; fields you omit keep their value. tags replaces the whole tag list. team takes the uuid of an active review team of this application (list them with GET .../cases/teams/), or null to clear the team. Requires a full-access API key, or write:cases for a console member: a key with restricted access gets 403. Logs PRIORITY_CHANGED, DUE_DATE_CHANGED, TAG_ADDED, TAG_REMOVED and TEAM_CHANGED events as applicable; a title change is saved without a timeline event. Returns the updated case. * *

{@code PATCH /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/} *

Application scoped: the organization and application ids come from the client options. @@ -520,7 +518,7 @@ public CompletableFuture assignAsync(String caseUuid, CasesAssignPar /** * Resolve case * - *

Resolve the case as false_positive or valid_threat. Blocked with 400 if the blueprint's checklist is not optional and items remain incomplete, or if the blueprint requires a resolution note and none is given. When the case's blueprint has a four_eyes_checker_blueprint configured, the resolution is staged (case.pending_resolution) and the case moves to the checker blueprint instead of resolving immediately; a different user must call approve or reject-approval. + *

Resolve the case with one of five resolutions: FALSE_POSITIVE (no concern found), VALID_THREAT (concern confirmed), INCONCLUSIVE, REQUIREMENTS_NOT_MET or REQUIREMENTS_SATISFIED. Blocked with 400 if the blueprint's checklist is not optional and items remain incomplete, or if the blueprint requires a resolution note and none is given. When the case's blueprint has a four-eyes checker step (four_eyes_checker_blueprint), the resolution is staged (case.pending_resolution) and the case moves to the checker blueprint instead of resolving immediately; a different officer must call approve or reject-approval. Submit a four-eyes resolution as a console member: a resolution an API key submits has no recorded maker, so approve and reject-approval return 403 for every caller. On a four-eyes blueprint you can name the member who must approve with checker: then only that member can approve or reject the resolution, and the response's checker names them. Without checker, the resolution waits on the checker blueprint's queue. Sending checker for a blueprint without a checker step returns 400. * *

{@code POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/resolve/} *

Application scoped: the organization and application ids come from the client options. @@ -763,7 +761,7 @@ public CompletableFuture transferAsync(String caseUuid, CasesTransfe /** * Approve staged resolution (4-eyes) * - *

Apply a resolution staged by resolve on a maker blueprint. The caller must be a different user than the one who submitted it, otherwise 403. + *

Apply a resolution staged by resolve on a four-eyes blueprint. The caller must be a different officer than the one who submitted it, otherwise 403. When the resolution names a checker (the case's checker), only that member can approve it: any other caller, including an API key, gets 403. * *

{@code POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/approve/} *

Application scoped: the organization and application ids come from the client options. @@ -810,7 +808,7 @@ public CompletableFuture approveResolutionAsync(String caseUuid) { /** * Reject staged resolution (4-eyes) * - *

Reject a resolution staged by resolve and return the case to its maker blueprint. The caller must be a different user than the one who submitted it, otherwise 403. + *

Reject a resolution staged by resolve and return the case to its maker blueprint. The caller must be a different officer than the one who submitted it, otherwise 403. When the resolution names a checker (the case's checker), only that member can reject it: any other caller, including an API key, gets 403. * *

{@code POST /v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/reject-approval/} *

Application scoped: the organization and application ids come from the client options. @@ -1102,7 +1100,7 @@ public CompletableFuture> listEventsAsync(String caseUuid) { * @param options per-request options that override the client options * @return the response body */ - public List> listAmlHits(String caseUuid, RequestOptions options) { + public List listAmlHits(String caseUuid, RequestOptions options) { return client.execute(listAmlHitsRequest(caseUuid), options, LIST_AML_HITS_RESPONSE).data(); } @@ -1113,7 +1111,7 @@ public List> listAmlHits(String caseUuid, RequestOptions o * @param options per-request options * @return a future completed with the result */ - public CompletableFuture>> listAmlHitsAsync(String caseUuid, RequestOptions options) { + public CompletableFuture> listAmlHitsAsync(String caseUuid, RequestOptions options) { return client.executeAsync(listAmlHitsRequest(caseUuid), options, LIST_AML_HITS_RESPONSE).thenApply(RawResponse::data); } @@ -1123,7 +1121,7 @@ public CompletableFuture>> listAmlHitsAsync(String ca * @param caseUuid the {@code case_uuid} * @return the result */ - public List> listAmlHits(String caseUuid) { + public List listAmlHits(String caseUuid) { return client.execute(listAmlHitsRequest(caseUuid), RequestOptions.none(), LIST_AML_HITS_RESPONSE).data(); } @@ -1133,7 +1131,7 @@ public List> listAmlHits(String caseUuid) { * @param caseUuid the {@code case_uuid} * @return a future completed with the result */ - public CompletableFuture>> listAmlHitsAsync(String caseUuid) { + public CompletableFuture> listAmlHitsAsync(String caseUuid) { return client.executeAsync(listAmlHitsRequest(caseUuid), RequestOptions.none(), LIST_AML_HITS_RESPONSE).thenApply(RawResponse::data); } @@ -1234,7 +1232,7 @@ public CompletableFuture retrieveStatisticsAsyn * @param options per-request options that override the client options * @return the response body */ - public CasesBulkUpdateStatusResponse bulkUpdateStatus(CasesBulkUpdateStatusParams params, RequestOptions options) { + public CaseBulkUpdateResult bulkUpdateStatus(CasesBulkUpdateStatusParams params, RequestOptions options) { return client.execute(bulkUpdateStatusRequest(params), options, BULK_UPDATE_STATUS_RESPONSE).data(); } @@ -1245,7 +1243,7 @@ public CasesBulkUpdateStatusResponse bulkUpdateStatus(CasesBulkUpdateStatusParam * @param options per-request options * @return a future completed with the result */ - public CompletableFuture bulkUpdateStatusAsync(CasesBulkUpdateStatusParams params, RequestOptions options) { + public CompletableFuture bulkUpdateStatusAsync(CasesBulkUpdateStatusParams params, RequestOptions options) { return client.executeAsync(bulkUpdateStatusRequest(params), options, BULK_UPDATE_STATUS_RESPONSE).thenApply(RawResponse::data); } @@ -1255,7 +1253,7 @@ public CompletableFuture bulkUpdateStatusAsync(Ca * @param params the request parameters * @return the result */ - public CasesBulkUpdateStatusResponse bulkUpdateStatus(CasesBulkUpdateStatusParams params) { + public CaseBulkUpdateResult bulkUpdateStatus(CasesBulkUpdateStatusParams params) { return client.execute(bulkUpdateStatusRequest(params), RequestOptions.none(), BULK_UPDATE_STATUS_RESPONSE).data(); } @@ -1265,7 +1263,7 @@ public CasesBulkUpdateStatusResponse bulkUpdateStatus(CasesBulkUpdateStatusParam * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture bulkUpdateStatusAsync(CasesBulkUpdateStatusParams params) { + public CompletableFuture bulkUpdateStatusAsync(CasesBulkUpdateStatusParams params) { return client.executeAsync(bulkUpdateStatusRequest(params), RequestOptions.none(), BULK_UPDATE_STATUS_RESPONSE).thenApply(RawResponse::data); } @@ -1281,7 +1279,7 @@ public CompletableFuture bulkUpdateStatusAsync(Ca * @param options per-request options that override the client options * @return the response body */ - public CasesBulkAssignResponse bulkAssign(CasesBulkAssignParams params, RequestOptions options) { + public CaseBulkUpdateResult bulkAssign(CasesBulkAssignParams params, RequestOptions options) { return client.execute(bulkAssignRequest(params), options, BULK_ASSIGN_RESPONSE).data(); } @@ -1292,7 +1290,7 @@ public CasesBulkAssignResponse bulkAssign(CasesBulkAssignParams params, RequestO * @param options per-request options * @return a future completed with the result */ - public CompletableFuture bulkAssignAsync(CasesBulkAssignParams params, RequestOptions options) { + public CompletableFuture bulkAssignAsync(CasesBulkAssignParams params, RequestOptions options) { return client.executeAsync(bulkAssignRequest(params), options, BULK_ASSIGN_RESPONSE).thenApply(RawResponse::data); } @@ -1302,7 +1300,7 @@ public CompletableFuture bulkAssignAsync(CasesBulkAssig * @param params the request parameters * @return the result */ - public CasesBulkAssignResponse bulkAssign(CasesBulkAssignParams params) { + public CaseBulkUpdateResult bulkAssign(CasesBulkAssignParams params) { return client.execute(bulkAssignRequest(params), RequestOptions.none(), BULK_ASSIGN_RESPONSE).data(); } @@ -1312,7 +1310,7 @@ public CasesBulkAssignResponse bulkAssign(CasesBulkAssignParams params) { * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture bulkAssignAsync(CasesBulkAssignParams params) { + public CompletableFuture bulkAssignAsync(CasesBulkAssignParams params) { return client.executeAsync(bulkAssignRequest(params), RequestOptions.none(), BULK_ASSIGN_RESPONSE).thenApply(RawResponse::data); } @@ -1742,7 +1740,7 @@ private static ApiRequest listEventsRequest(String caseUuid, CasesListEventsPara .build(); } - private static final ResponseHandler>> LIST_AML_HITS_RESPONSE = Responses.json(Conv.list(Conv.OBJECT)); + private static final ResponseHandler> LIST_AML_HITS_RESPONSE = Responses.json(Conv.list(Conv.model(AMLScreening::fromJson))); private static ApiRequest listAmlHitsRequest(String caseUuid) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/aml/") @@ -1761,7 +1759,7 @@ private static ApiRequest retrieveStatisticsRequest(CasesRetrieveStatisticsParam .build(); } - private static final ResponseHandler BULK_UPDATE_STATUS_RESPONSE = Responses.json(Conv.model(CasesBulkUpdateStatusResponse::fromJson)); + private static final ResponseHandler BULK_UPDATE_STATUS_RESPONSE = Responses.json(Conv.model(CaseBulkUpdateResult::fromJson)); private static ApiRequest bulkUpdateStatusRequest(CasesBulkUpdateStatusParams params) { return ApiRequest.builder("POST", "/v3/organization/{organization_id}/application/{application_id}/cases/bulk/status/") @@ -1771,7 +1769,7 @@ private static ApiRequest bulkUpdateStatusRequest(CasesBulkUpdateStatusParams pa .build(); } - private static final ResponseHandler BULK_ASSIGN_RESPONSE = Responses.json(Conv.model(CasesBulkAssignResponse::fromJson)); + private static final ResponseHandler BULK_ASSIGN_RESPONSE = Responses.json(Conv.model(CaseBulkUpdateResult::fromJson)); private static ApiRequest bulkAssignRequest(CasesBulkAssignParams params) { return ApiRequest.builder("POST", "/v3/organization/{organization_id}/application/{application_id}/cases/bulk/assign/") @@ -2699,7 +2697,7 @@ public CompletableFuture>> listEventsAsync(String ca * @param options per-request options * @return the result */ - public RawResponse>> listAmlHits(String caseUuid, RequestOptions options) { + public RawResponse> listAmlHits(String caseUuid, RequestOptions options) { return client.execute(listAmlHitsRequest(caseUuid), options, LIST_AML_HITS_RESPONSE); } @@ -2710,7 +2708,7 @@ public RawResponse>> listAmlHits(String caseUuid, Req * @param options per-request options * @return a future completed with the result */ - public CompletableFuture>>> listAmlHitsAsync(String caseUuid, RequestOptions options) { + public CompletableFuture>> listAmlHitsAsync(String caseUuid, RequestOptions options) { return client.executeAsync(listAmlHitsRequest(caseUuid), options, LIST_AML_HITS_RESPONSE); } @@ -2720,7 +2718,7 @@ public CompletableFuture>>> listAmlHitsAs * @param caseUuid the {@code case_uuid} * @return the result */ - public RawResponse>> listAmlHits(String caseUuid) { + public RawResponse> listAmlHits(String caseUuid) { return client.execute(listAmlHitsRequest(caseUuid), RequestOptions.none(), LIST_AML_HITS_RESPONSE); } @@ -2730,7 +2728,7 @@ public RawResponse>> listAmlHits(String caseUuid) { * @param caseUuid the {@code case_uuid} * @return a future completed with the result */ - public CompletableFuture>>> listAmlHitsAsync(String caseUuid) { + public CompletableFuture>> listAmlHitsAsync(String caseUuid) { return client.executeAsync(listAmlHitsRequest(caseUuid), RequestOptions.none(), LIST_AML_HITS_RESPONSE); } @@ -2821,7 +2819,7 @@ public CompletableFuture> retrieveS * @param options per-request options * @return the result */ - public RawResponse bulkUpdateStatus(CasesBulkUpdateStatusParams params, RequestOptions options) { + public RawResponse bulkUpdateStatus(CasesBulkUpdateStatusParams params, RequestOptions options) { return client.execute(bulkUpdateStatusRequest(params), options, BULK_UPDATE_STATUS_RESPONSE); } @@ -2832,7 +2830,7 @@ public RawResponse bulkUpdateStatus(CasesBulkUpda * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> bulkUpdateStatusAsync(CasesBulkUpdateStatusParams params, RequestOptions options) { + public CompletableFuture> bulkUpdateStatusAsync(CasesBulkUpdateStatusParams params, RequestOptions options) { return client.executeAsync(bulkUpdateStatusRequest(params), options, BULK_UPDATE_STATUS_RESPONSE); } @@ -2842,7 +2840,7 @@ public CompletableFuture> bulkUpdateS * @param params the request parameters * @return the result */ - public RawResponse bulkUpdateStatus(CasesBulkUpdateStatusParams params) { + public RawResponse bulkUpdateStatus(CasesBulkUpdateStatusParams params) { return client.execute(bulkUpdateStatusRequest(params), RequestOptions.none(), BULK_UPDATE_STATUS_RESPONSE); } @@ -2852,7 +2850,7 @@ public RawResponse bulkUpdateStatus(CasesBulkUpda * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> bulkUpdateStatusAsync(CasesBulkUpdateStatusParams params) { + public CompletableFuture> bulkUpdateStatusAsync(CasesBulkUpdateStatusParams params) { return client.executeAsync(bulkUpdateStatusRequest(params), RequestOptions.none(), BULK_UPDATE_STATUS_RESPONSE); } @@ -2863,7 +2861,7 @@ public CompletableFuture> bulkUpdateS * @param options per-request options * @return the result */ - public RawResponse bulkAssign(CasesBulkAssignParams params, RequestOptions options) { + public RawResponse bulkAssign(CasesBulkAssignParams params, RequestOptions options) { return client.execute(bulkAssignRequest(params), options, BULK_ASSIGN_RESPONSE); } @@ -2874,7 +2872,7 @@ public RawResponse bulkAssign(CasesBulkAssignParams par * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> bulkAssignAsync(CasesBulkAssignParams params, RequestOptions options) { + public CompletableFuture> bulkAssignAsync(CasesBulkAssignParams params, RequestOptions options) { return client.executeAsync(bulkAssignRequest(params), options, BULK_ASSIGN_RESPONSE); } @@ -2884,7 +2882,7 @@ public CompletableFuture> bulkAssignAsync(C * @param params the request parameters * @return the result */ - public RawResponse bulkAssign(CasesBulkAssignParams params) { + public RawResponse bulkAssign(CasesBulkAssignParams params) { return client.execute(bulkAssignRequest(params), RequestOptions.none(), BULK_ASSIGN_RESPONSE); } @@ -2894,7 +2892,7 @@ public RawResponse bulkAssign(CasesBulkAssignParams par * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> bulkAssignAsync(CasesBulkAssignParams params) { + public CompletableFuture> bulkAssignAsync(CasesBulkAssignParams params) { return client.executeAsync(bulkAssignRequest(params), RequestOptions.none(), BULK_ASSIGN_RESPONSE); } diff --git a/src/main/java/me/didit/resources/DiditResources.java b/src/main/java/me/didit/resources/DiditResources.java index f317ae7..55827c8 100644 --- a/src/main/java/me/didit/resources/DiditResources.java +++ b/src/main/java/me/didit/resources/DiditResources.java @@ -12,8 +12,10 @@ public abstract class DiditResources { private final AgeEstimationResource ageEstimation; private final AmlResource aml; private final AmlMonitoringResource amlMonitoring; + private final AnalyticsResource analytics; private final BillingResource billing; private final BiometricTemplatesResource biometricTemplates; + private final BlocklistResource blocklist; private final BusinessSessionsResource businessSessions; private final BusinessesResource businesses; private final CasesResource cases; @@ -26,8 +28,11 @@ public abstract class DiditResources { private final FraudChecksResource fraudChecks; private final IdVerificationResource idVerification; private final IdWalletsResource idWallets; + private final InvestigatorResource investigator; private final IpResource ip; private final KybResource kyb; + private final ListsResource lists; + private final MonitoringEventsResource monitoringEvents; private final NetworksResource networks; private final PassiveLivenessResource passiveLiveness; private final PhoneResource phone; @@ -53,8 +58,10 @@ protected DiditResources(ApiClient client) { this.ageEstimation = new AgeEstimationResource(client); this.aml = new AmlResource(client); this.amlMonitoring = new AmlMonitoringResource(client); + this.analytics = new AnalyticsResource(client); this.billing = new BillingResource(client); this.biometricTemplates = new BiometricTemplatesResource(client); + this.blocklist = new BlocklistResource(client); this.businessSessions = new BusinessSessionsResource(client); this.businesses = new BusinessesResource(client); this.cases = new CasesResource(client); @@ -67,8 +74,11 @@ protected DiditResources(ApiClient client) { this.fraudChecks = new FraudChecksResource(client); this.idVerification = new IdVerificationResource(client); this.idWallets = new IdWalletsResource(client); + this.investigator = new InvestigatorResource(client); this.ip = new IpResource(client); this.kyb = new KybResource(client); + this.lists = new ListsResource(client); + this.monitoringEvents = new MonitoringEventsResource(client); this.networks = new NetworksResource(client); this.passiveLiveness = new PassiveLivenessResource(client); this.phone = new PhoneResource(client); @@ -121,6 +131,15 @@ public final AmlMonitoringResource amlMonitoring() { return amlMonitoring; } + /** + * The {@code analytics} resource. + * + * @return the resource + */ + public final AnalyticsResource analytics() { + return analytics; + } + /** * The {@code billing} resource. * @@ -139,6 +158,15 @@ public final BiometricTemplatesResource biometricTemplates() { return biometricTemplates; } + /** + * The {@code blocklist} resource. + * + * @return the resource + */ + public final BlocklistResource blocklist() { + return blocklist; + } + /** * The {@code business_sessions} resource. * @@ -247,6 +275,15 @@ public final IdWalletsResource idWallets() { return idWallets; } + /** + * The {@code investigator} resource. + * + * @return the resource + */ + public final InvestigatorResource investigator() { + return investigator; + } + /** * The {@code ip} resource. * @@ -265,6 +302,24 @@ public final KybResource kyb() { return kyb; } + /** + * The {@code lists} resource. + * + * @return the resource + */ + public final ListsResource lists() { + return lists; + } + + /** + * The {@code monitoring_events} resource. + * + * @return the resource + */ + public final MonitoringEventsResource monitoringEvents() { + return monitoringEvents; + } + /** * The {@code networks} resource. * diff --git a/src/main/java/me/didit/resources/InvestigatorResource.java b/src/main/java/me/didit/resources/InvestigatorResource.java new file mode 100644 index 0000000..87a1f3b --- /dev/null +++ b/src/main/java/me/didit/resources/InvestigatorResource.java @@ -0,0 +1,1877 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.BinaryResponse; +import me.didit.core.Conv; +import me.didit.core.Page; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.InvestigatorAcceptRequest; +import me.didit.types.InvestigatorBatchAcceptRequest; +import me.didit.types.InvestigatorBatchAcceptResponse; +import me.didit.types.InvestigatorDecision; +import me.didit.types.InvestigatorExportActivityParams; +import me.didit.types.InvestigatorListActivityParams; +import me.didit.types.InvestigatorListGuardrailsResponse; +import me.didit.types.InvestigatorListQueueParams; +import me.didit.types.InvestigatorOverrideRequest; +import me.didit.types.InvestigatorQueueRow; +import me.didit.types.InvestigatorRetrieveDashboardParams; +import me.didit.types.InvestigatorRetrieveDashboardResponse; +import me.didit.types.InvestigatorRetrievePerformanceParams; +import me.didit.types.InvestigatorRetrievePerformanceResponse; +import me.didit.types.InvestigatorRetrieveSummaryParams; +import me.didit.types.InvestigatorRetrieveSummaryResponse; +import me.didit.types.InvestigatorRetrieveTransactionResponse; +import me.didit.types.InvestigatorRunDetail; +import me.didit.types.InvestigatorSettings; + +/** The {@code investigator} resource: {@code client.investigator()}. */ +public final class InvestigatorResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + InvestigatorResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * Investigator summary + * + *

Stage, queue counts, reports prepared and decided today, 30-day agreement, prices and the autonomy level of each queue. Restricted keys need the decisions scope (read); transaction alerts also need transactions. + * + *

{@code GET /v3/investigator/summary/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorRetrieveSummaryResponse retrieveSummary(InvestigatorRetrieveSummaryParams params, RequestOptions options) { + return client.execute(retrieveSummaryRequest(params), options, RETRIEVE_SUMMARY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveSummaryAsync(InvestigatorRetrieveSummaryParams params, RequestOptions options) { + return client.executeAsync(retrieveSummaryRequest(params), options, RETRIEVE_SUMMARY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public InvestigatorRetrieveSummaryResponse retrieveSummary(InvestigatorRetrieveSummaryParams params) { + return client.execute(retrieveSummaryRequest(params), RequestOptions.none(), RETRIEVE_SUMMARY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture retrieveSummaryAsync(InvestigatorRetrieveSummaryParams params) { + return client.executeAsync(retrieveSummaryRequest(params), RequestOptions.none(), RETRIEVE_SUMMARY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public InvestigatorRetrieveSummaryResponse retrieveSummary(RequestOptions options) { + return client.execute(retrieveSummaryRequest(InvestigatorRetrieveSummaryParams.builder().build()), options, RETRIEVE_SUMMARY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveSummaryAsync(RequestOptions options) { + return client.executeAsync(retrieveSummaryRequest(InvestigatorRetrieveSummaryParams.builder().build()), options, RETRIEVE_SUMMARY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public InvestigatorRetrieveSummaryResponse retrieveSummary() { + return client.execute(retrieveSummaryRequest(InvestigatorRetrieveSummaryParams.builder().build()), RequestOptions.none(), RETRIEVE_SUMMARY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture retrieveSummaryAsync() { + return client.executeAsync(retrieveSummaryRequest(InvestigatorRetrieveSummaryParams.builder().build()), RequestOptions.none(), RETRIEVE_SUMMARY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * List the Investigator queue + * + *

Open reports, Ready to accept first, then by deadline, risk and wait. counts covers every kind in scope. count is capped at 100. Restricted keys need the decisions scope (read); transaction alerts also need transactions. + * + *

{@code GET /v3/investigator/queue/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page listQueue(InvestigatorListQueueParams params, RequestOptions options) { + return client.execute(listQueueRequest(params), options, LIST_QUEUE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listQueue(InvestigatorListQueueParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listQueueAsync(InvestigatorListQueueParams params, RequestOptions options) { + return client.executeAsync(listQueueRequest(params), options, LIST_QUEUE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listQueue(InvestigatorListQueueParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public Page listQueue(InvestigatorListQueueParams params) { + return client.execute(listQueueRequest(params), RequestOptions.none(), LIST_QUEUE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listQueue(InvestigatorListQueueParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listQueueAsync(InvestigatorListQueueParams params) { + return client.executeAsync(listQueueRequest(params), RequestOptions.none(), LIST_QUEUE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listQueue(InvestigatorListQueueParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public Page listQueue(RequestOptions options) { + return client.execute(listQueueRequest(InvestigatorListQueueParams.builder().build()), options, LIST_QUEUE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listQueue(InvestigatorListQueueParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listQueueAsync(RequestOptions options) { + return client.executeAsync(listQueueRequest(InvestigatorListQueueParams.builder().build()), options, LIST_QUEUE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listQueue(InvestigatorListQueueParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public Page listQueue() { + return client.execute(listQueueRequest(InvestigatorListQueueParams.builder().build()), RequestOptions.none(), LIST_QUEUE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listQueue(InvestigatorListQueueParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> listQueueAsync() { + return client.executeAsync(listQueueRequest(InvestigatorListQueueParams.builder().build()), RequestOptions.none(), LIST_QUEUE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Get an Investigator report + * + *

The full report of one run. 404 when the run has no completed report, or its item was deleted. Restricted keys need the decisions scope (read); transaction alerts also need transactions. + * + *

{@code GET /v3/investigator/runs/{run_id}/} + * + * @param runId the {@code run_id} + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorRunDetail retrieveRun(String runId, RequestOptions options) { + return client.execute(retrieveRunRequest(runId), options, RETRIEVE_RUN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveRun(String, RequestOptions)}. + * + * @param runId the {@code run_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveRunAsync(String runId, RequestOptions options) { + return client.executeAsync(retrieveRunRequest(runId), options, RETRIEVE_RUN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveRun(String, RequestOptions)}, with the client's options. + * + * @param runId the {@code run_id} + * @return the result + */ + public InvestigatorRunDetail retrieveRun(String runId) { + return client.execute(retrieveRunRequest(runId), RequestOptions.none(), RETRIEVE_RUN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveRun(String, RequestOptions)}, with the client's options. + * + * @param runId the {@code run_id} + * @return a future completed with the result + */ + public CompletableFuture retrieveRunAsync(String runId) { + return client.executeAsync(retrieveRunRequest(runId), RequestOptions.none(), RETRIEVE_RUN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Accept a recommendation + * + *

Applies the recommended action through the item's normal path, so your usual webhooks fire. A narrative makes it edited and accepted. Restricted keys need the decisions scope (write); transaction alerts also need transactions (write). Escalating a verification or transaction opens a case. + * + *

{@code POST /v3/investigator/runs/{run_id}/accept/} + * + * @param runId the {@code run_id} + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorDecision acceptRun(String runId, InvestigatorAcceptRequest params, RequestOptions options) { + return client.execute(acceptRunRequest(runId, params), options, ACCEPT_RUN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #acceptRun(String, InvestigatorAcceptRequest, RequestOptions)}. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture acceptRunAsync(String runId, InvestigatorAcceptRequest params, RequestOptions options) { + return client.executeAsync(acceptRunRequest(runId, params), options, ACCEPT_RUN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #acceptRun(String, InvestigatorAcceptRequest, RequestOptions)}, with the client's options. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @return the result + */ + public InvestigatorDecision acceptRun(String runId, InvestigatorAcceptRequest params) { + return client.execute(acceptRunRequest(runId, params), RequestOptions.none(), ACCEPT_RUN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #acceptRun(String, InvestigatorAcceptRequest, RequestOptions)}, with the client's options. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture acceptRunAsync(String runId, InvestigatorAcceptRequest params) { + return client.executeAsync(acceptRunRequest(runId, params), RequestOptions.none(), ACCEPT_RUN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Override a recommendation + * + *

Takes another action with a category and a reason. Also overrides a decision the Investigator took on its own while it still stands. Restricted keys need the decisions scope (write); transaction alerts also need transactions (write). Escalating a verification or transaction opens a case. + * + *

{@code POST /v3/investigator/runs/{run_id}/override/} + * + * @param runId the {@code run_id} + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorDecision overrideRun(String runId, InvestigatorOverrideRequest params, RequestOptions options) { + return client.execute(overrideRunRequest(runId, params), options, OVERRIDE_RUN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #overrideRun(String, InvestigatorOverrideRequest, RequestOptions)}. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture overrideRunAsync(String runId, InvestigatorOverrideRequest params, RequestOptions options) { + return client.executeAsync(overrideRunRequest(runId, params), options, OVERRIDE_RUN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #overrideRun(String, InvestigatorOverrideRequest, RequestOptions)}, with the client's options. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @return the result + */ + public InvestigatorDecision overrideRun(String runId, InvestigatorOverrideRequest params) { + return client.execute(overrideRunRequest(runId, params), RequestOptions.none(), OVERRIDE_RUN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #overrideRun(String, InvestigatorOverrideRequest, RequestOptions)}, with the client's options. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture overrideRunAsync(String runId, InvestigatorOverrideRequest params) { + return client.executeAsync(overrideRunRequest(runId, params), RequestOptions.none(), OVERRIDE_RUN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Batch accept recommendations + * + *

Accepts up to 50 reports marked Ready to accept. Each is its own decision: one that cannot be accepted is skipped with its reason. Restricted keys need the decisions scope (write); transaction alerts also need transactions (write). Escalating a verification or transaction opens a case. + * + *

{@code POST /v3/investigator/runs/batch-accept/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorBatchAcceptResponse batchAcceptRuns(InvestigatorBatchAcceptRequest params, RequestOptions options) { + return client.execute(batchAcceptRunsRequest(params), options, BATCH_ACCEPT_RUNS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #batchAcceptRuns(InvestigatorBatchAcceptRequest, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture batchAcceptRunsAsync(InvestigatorBatchAcceptRequest params, RequestOptions options) { + return client.executeAsync(batchAcceptRunsRequest(params), options, BATCH_ACCEPT_RUNS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #batchAcceptRuns(InvestigatorBatchAcceptRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public InvestigatorBatchAcceptResponse batchAcceptRuns(InvestigatorBatchAcceptRequest params) { + return client.execute(batchAcceptRunsRequest(params), RequestOptions.none(), BATCH_ACCEPT_RUNS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #batchAcceptRuns(InvestigatorBatchAcceptRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture batchAcceptRunsAsync(InvestigatorBatchAcceptRequest params) { + return client.executeAsync(batchAcceptRunsRequest(params), RequestOptions.none(), BATCH_ACCEPT_RUNS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Get a transaction's Investigator review + * + *

The transaction's latest review, or investigator: null when it never had one. Restricted keys need the decisions and transactions scopes (read). + * + *

{@code GET /v3/investigator/transactions/{transaction_id}/} + * + * @param transactionId the {@code transaction_id} + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorRetrieveTransactionResponse retrieveTransaction(String transactionId, RequestOptions options) { + return client.execute(retrieveTransactionRequest(transactionId), options, RETRIEVE_TRANSACTION_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveTransaction(String, RequestOptions)}. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveTransactionAsync(String transactionId, RequestOptions options) { + return client.executeAsync(retrieveTransactionRequest(transactionId), options, RETRIEVE_TRANSACTION_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveTransaction(String, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return the result + */ + public InvestigatorRetrieveTransactionResponse retrieveTransaction(String transactionId) { + return client.execute(retrieveTransactionRequest(transactionId), RequestOptions.none(), RETRIEVE_TRANSACTION_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveTransaction(String, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return a future completed with the result + */ + public CompletableFuture retrieveTransactionAsync(String transactionId) { + return client.executeAsync(retrieveTransactionRequest(transactionId), RequestOptions.none(), RETRIEVE_TRANSACTION_RESPONSE).thenApply(RawResponse::data); + } + + /** + * List the locked guardrails + * + *

The five guardrails that apply to every queue. They cannot be switched off. Restricted keys need the decisions scope (read); transaction alerts also need transactions. + * + *

{@code GET /v3/investigator/guardrails/} + * + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorListGuardrailsResponse listGuardrails(RequestOptions options) { + return client.execute(listGuardrailsRequest(), options, LIST_GUARDRAILS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listGuardrails(RequestOptions)}. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture listGuardrailsAsync(RequestOptions options) { + return client.executeAsync(listGuardrailsRequest(), options, LIST_GUARDRAILS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listGuardrails(RequestOptions)}, with the client's options. + * + * @return the result + */ + public InvestigatorListGuardrailsResponse listGuardrails() { + return client.execute(listGuardrailsRequest(), RequestOptions.none(), LIST_GUARDRAILS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listGuardrails(RequestOptions)}, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture listGuardrailsAsync() { + return client.executeAsync(listGuardrailsRequest(), RequestOptions.none(), LIST_GUARDRAILS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Investigator dashboard + * + *

The last 30 days: reviews, decisions, automation rate, hours saved, spend, overrides, quality samples since go-live, per-queue and weekly figures. Restricted keys need the decisions scope (read); transaction alerts also need transactions. + * + *

{@code GET /v3/investigator/dashboard/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorRetrieveDashboardResponse retrieveDashboard(InvestigatorRetrieveDashboardParams params, RequestOptions options) { + return client.execute(retrieveDashboardRequest(params), options, RETRIEVE_DASHBOARD_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveDashboardAsync(InvestigatorRetrieveDashboardParams params, RequestOptions options) { + return client.executeAsync(retrieveDashboardRequest(params), options, RETRIEVE_DASHBOARD_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public InvestigatorRetrieveDashboardResponse retrieveDashboard(InvestigatorRetrieveDashboardParams params) { + return client.execute(retrieveDashboardRequest(params), RequestOptions.none(), RETRIEVE_DASHBOARD_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture retrieveDashboardAsync(InvestigatorRetrieveDashboardParams params) { + return client.executeAsync(retrieveDashboardRequest(params), RequestOptions.none(), RETRIEVE_DASHBOARD_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public InvestigatorRetrieveDashboardResponse retrieveDashboard(RequestOptions options) { + return client.execute(retrieveDashboardRequest(InvestigatorRetrieveDashboardParams.builder().build()), options, RETRIEVE_DASHBOARD_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveDashboardAsync(RequestOptions options) { + return client.executeAsync(retrieveDashboardRequest(InvestigatorRetrieveDashboardParams.builder().build()), options, RETRIEVE_DASHBOARD_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public InvestigatorRetrieveDashboardResponse retrieveDashboard() { + return client.execute(retrieveDashboardRequest(InvestigatorRetrieveDashboardParams.builder().build()), RequestOptions.none(), RETRIEVE_DASHBOARD_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture retrieveDashboardAsync() { + return client.executeAsync(retrieveDashboardRequest(InvestigatorRetrieveDashboardParams.builder().build()), RequestOptions.none(), RETRIEVE_DASHBOARD_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Investigator performance + * + *

Agreement with your team, volume, time saved, confidence calibration, per-queue results, override reasons and disagreements for a period. Restricted keys need the decisions scope (read); transaction alerts also need transactions. + * + *

{@code GET /v3/investigator/performance/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorRetrievePerformanceResponse retrievePerformance(InvestigatorRetrievePerformanceParams params, RequestOptions options) { + return client.execute(retrievePerformanceRequest(params), options, RETRIEVE_PERFORMANCE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrievePerformanceAsync(InvestigatorRetrievePerformanceParams params, RequestOptions options) { + return client.executeAsync(retrievePerformanceRequest(params), options, RETRIEVE_PERFORMANCE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public InvestigatorRetrievePerformanceResponse retrievePerformance(InvestigatorRetrievePerformanceParams params) { + return client.execute(retrievePerformanceRequest(params), RequestOptions.none(), RETRIEVE_PERFORMANCE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture retrievePerformanceAsync(InvestigatorRetrievePerformanceParams params) { + return client.executeAsync(retrievePerformanceRequest(params), RequestOptions.none(), RETRIEVE_PERFORMANCE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public InvestigatorRetrievePerformanceResponse retrievePerformance(RequestOptions options) { + return client.execute(retrievePerformanceRequest(InvestigatorRetrievePerformanceParams.builder().build()), options, RETRIEVE_PERFORMANCE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrievePerformanceAsync(RequestOptions options) { + return client.executeAsync(retrievePerformanceRequest(InvestigatorRetrievePerformanceParams.builder().build()), options, RETRIEVE_PERFORMANCE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public InvestigatorRetrievePerformanceResponse retrievePerformance() { + return client.execute(retrievePerformanceRequest(InvestigatorRetrievePerformanceParams.builder().build()), RequestOptions.none(), RETRIEVE_PERFORMANCE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture retrievePerformanceAsync() { + return client.executeAsync(retrievePerformanceRequest(InvestigatorRetrievePerformanceParams.builder().build()), RequestOptions.none(), RETRIEVE_PERFORMANCE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * List Investigator activity + * + *

The append-only audit trail, newest first. A batch accept is one entry. Adds counts per group and actors. Restricted keys need the decisions scope (read); transaction alerts also need transactions. + * + *

{@code GET /v3/investigator/activity/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page> listActivity(InvestigatorListActivityParams params, RequestOptions options) { + return client.execute(listActivityRequest(params), options, LIST_ACTIVITY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listActivity(InvestigatorListActivityParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listActivityAsync(InvestigatorListActivityParams params, RequestOptions options) { + return client.executeAsync(listActivityRequest(params), options, LIST_ACTIVITY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listActivity(InvestigatorListActivityParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public Page> listActivity(InvestigatorListActivityParams params) { + return client.execute(listActivityRequest(params), RequestOptions.none(), LIST_ACTIVITY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listActivity(InvestigatorListActivityParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listActivityAsync(InvestigatorListActivityParams params) { + return client.executeAsync(listActivityRequest(params), RequestOptions.none(), LIST_ACTIVITY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listActivity(InvestigatorListActivityParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public Page> listActivity(RequestOptions options) { + return client.execute(listActivityRequest(InvestigatorListActivityParams.builder().build()), options, LIST_ACTIVITY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listActivity(InvestigatorListActivityParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listActivityAsync(RequestOptions options) { + return client.executeAsync(listActivityRequest(InvestigatorListActivityParams.builder().build()), options, LIST_ACTIVITY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listActivity(InvestigatorListActivityParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public Page> listActivity() { + return client.execute(listActivityRequest(InvestigatorListActivityParams.builder().build()), RequestOptions.none(), LIST_ACTIVITY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listActivity(InvestigatorListActivityParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>> listActivityAsync() { + return client.executeAsync(listActivityRequest(InvestigatorListActivityParams.builder().build()), RequestOptions.none(), LIST_ACTIVITY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Export Investigator activity + * + *

The same entries as CSV, one row per decision, newest first, up to 10,000 entries. Restricted keys need the decisions scope (read); transaction alerts also need transactions. + * + *

{@code GET /v3/investigator/activity/export/} + *

Returns the file; close the {@link me.didit.core.BinaryResponse} when you stream it. + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public BinaryResponse exportActivity(InvestigatorExportActivityParams params, RequestOptions options) { + return client.execute(exportActivityRequest(params), options, EXPORT_ACTIVITY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture exportActivityAsync(InvestigatorExportActivityParams params, RequestOptions options) { + return client.executeAsync(exportActivityRequest(params), options, EXPORT_ACTIVITY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public BinaryResponse exportActivity(InvestigatorExportActivityParams params) { + return client.execute(exportActivityRequest(params), RequestOptions.none(), EXPORT_ACTIVITY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture exportActivityAsync(InvestigatorExportActivityParams params) { + return client.executeAsync(exportActivityRequest(params), RequestOptions.none(), EXPORT_ACTIVITY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public BinaryResponse exportActivity(RequestOptions options) { + return client.execute(exportActivityRequest(InvestigatorExportActivityParams.builder().build()), options, EXPORT_ACTIVITY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture exportActivityAsync(RequestOptions options) { + return client.executeAsync(exportActivityRequest(InvestigatorExportActivityParams.builder().build()), options, EXPORT_ACTIVITY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public BinaryResponse exportActivity() { + return client.execute(exportActivityRequest(InvestigatorExportActivityParams.builder().build()), RequestOptions.none(), EXPORT_ACTIVITY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture exportActivityAsync() { + return client.executeAsync(exportActivityRequest(InvestigatorExportActivityParams.builder().build()), RequestOptions.none(), EXPORT_ACTIVITY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Get Investigator settings + * + *

The stage (none, shadow or live) and the monthly spend limit with this month's spend. Restricted keys need the applications scope (read). + * + *

{@code GET /v3/investigator/settings/} + * + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorSettings retrieveSettings(RequestOptions options) { + return client.execute(retrieveSettingsRequest(), options, RETRIEVE_SETTINGS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSettings(RequestOptions)}. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveSettingsAsync(RequestOptions options) { + return client.executeAsync(retrieveSettingsRequest(), options, RETRIEVE_SETTINGS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveSettings(RequestOptions)}, with the client's options. + * + * @return the result + */ + public InvestigatorSettings retrieveSettings() { + return client.execute(retrieveSettingsRequest(), RequestOptions.none(), RETRIEVE_SETTINGS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSettings(RequestOptions)}, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture retrieveSettingsAsync() { + return client.executeAsync(retrieveSettingsRequest(), RequestOptions.none(), RETRIEVE_SETTINGS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Update Investigator settings + * + *

Change the stage, the monthly spend limit, or both. Moving past none needs a plan that includes the Investigator (Growth or higher); live also needs a contract that prices it, if you have one. Restricted keys need the applications scope (write). + * + *

{@code PATCH /v3/investigator/settings/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public InvestigatorSettings updateSettings(InvestigatorSettings params, RequestOptions options) { + return client.execute(updateSettingsRequest(params), options, UPDATE_SETTINGS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #updateSettings(InvestigatorSettings, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture updateSettingsAsync(InvestigatorSettings params, RequestOptions options) { + return client.executeAsync(updateSettingsRequest(params), options, UPDATE_SETTINGS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #updateSettings(InvestigatorSettings, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public InvestigatorSettings updateSettings(InvestigatorSettings params) { + return client.execute(updateSettingsRequest(params), RequestOptions.none(), UPDATE_SETTINGS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #updateSettings(InvestigatorSettings, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture updateSettingsAsync(InvestigatorSettings params) { + return client.executeAsync(updateSettingsRequest(params), RequestOptions.none(), UPDATE_SETTINGS_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler RETRIEVE_SUMMARY_RESPONSE = Responses.json(Conv.model(InvestigatorRetrieveSummaryResponse::fromJson)); + + private static ApiRequest retrieveSummaryRequest(InvestigatorRetrieveSummaryParams params) { + return ApiRequest.builder("GET", "/v3/investigator/summary/") + .params(params) + .queryFields("scope", "timezone") + .build(); + } + + private static final ResponseHandler> LIST_QUEUE_RESPONSE = Responses.page(Conv.model(InvestigatorQueueRow::fromJson)); + + private static ApiRequest listQueueRequest(InvestigatorListQueueParams params) { + return ApiRequest.builder("GET", "/v3/investigator/queue/") + .params(params) + .queryFields("scope", "kind", "queue", "recommendation", "confidence", "ready", "search", "limit", "offset") + .build(); + } + + private static final ResponseHandler RETRIEVE_RUN_RESPONSE = Responses.json(Conv.model(InvestigatorRunDetail::fromJson)); + + private static ApiRequest retrieveRunRequest(String runId) { + return ApiRequest.builder("GET", "/v3/investigator/runs/{run_id}/") + .pathParam("run_id", runId) + .build(); + } + + private static final ResponseHandler ACCEPT_RUN_RESPONSE = Responses.json(Conv.model(InvestigatorDecision::fromJson)); + + private static ApiRequest acceptRunRequest(String runId, InvestigatorAcceptRequest params) { + return ApiRequest.builder("POST", "/v3/investigator/runs/{run_id}/accept/") + .pathParam("run_id", runId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler OVERRIDE_RUN_RESPONSE = Responses.json(Conv.model(InvestigatorDecision::fromJson)); + + private static ApiRequest overrideRunRequest(String runId, InvestigatorOverrideRequest params) { + return ApiRequest.builder("POST", "/v3/investigator/runs/{run_id}/override/") + .pathParam("run_id", runId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler BATCH_ACCEPT_RUNS_RESPONSE = Responses.json(Conv.model(InvestigatorBatchAcceptResponse::fromJson)); + + private static ApiRequest batchAcceptRunsRequest(InvestigatorBatchAcceptRequest params) { + return ApiRequest.builder("POST", "/v3/investigator/runs/batch-accept/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler RETRIEVE_TRANSACTION_RESPONSE = Responses.json(Conv.model(InvestigatorRetrieveTransactionResponse::fromJson)); + + private static ApiRequest retrieveTransactionRequest(String transactionId) { + return ApiRequest.builder("GET", "/v3/investigator/transactions/{transaction_id}/") + .pathParam("transaction_id", transactionId) + .build(); + } + + private static final ResponseHandler LIST_GUARDRAILS_RESPONSE = Responses.json(Conv.model(InvestigatorListGuardrailsResponse::fromJson)); + + private static ApiRequest listGuardrailsRequest() { + return ApiRequest.builder("GET", "/v3/investigator/guardrails/") + .build(); + } + + private static final ResponseHandler RETRIEVE_DASHBOARD_RESPONSE = Responses.json(Conv.model(InvestigatorRetrieveDashboardResponse::fromJson)); + + private static ApiRequest retrieveDashboardRequest(InvestigatorRetrieveDashboardParams params) { + return ApiRequest.builder("GET", "/v3/investigator/dashboard/") + .params(params) + .queryFields("scope") + .build(); + } + + private static final ResponseHandler RETRIEVE_PERFORMANCE_RESPONSE = Responses.json(Conv.model(InvestigatorRetrievePerformanceResponse::fromJson)); + + private static ApiRequest retrievePerformanceRequest(InvestigatorRetrievePerformanceParams params) { + return ApiRequest.builder("GET", "/v3/investigator/performance/") + .params(params) + .queryFields("scope", "period") + .build(); + } + + private static final ResponseHandler>> LIST_ACTIVITY_RESPONSE = Responses.page(Conv.OBJECT); + + private static ApiRequest listActivityRequest(InvestigatorListActivityParams params) { + return ApiRequest.builder("GET", "/v3/investigator/activity/") + .params(params) + .queryFields("scope", "group", "actor", "since", "until", "limit", "offset") + .build(); + } + + private static final ResponseHandler EXPORT_ACTIVITY_RESPONSE = Responses.binary(); + + private static ApiRequest exportActivityRequest(InvestigatorExportActivityParams params) { + return ApiRequest.builder("GET", "/v3/investigator/activity/export/") + .params(params) + .queryFields("scope", "group", "actor", "since", "until") + .accept("text/csv") + .build(); + } + + private static final ResponseHandler RETRIEVE_SETTINGS_RESPONSE = Responses.json(Conv.model(InvestigatorSettings::fromJson)); + + private static ApiRequest retrieveSettingsRequest() { + return ApiRequest.builder("GET", "/v3/investigator/settings/") + .build(); + } + + private static final ResponseHandler UPDATE_SETTINGS_RESPONSE = Responses.json(Conv.model(InvestigatorSettings::fromJson)); + + private static ApiRequest updateSettingsRequest(InvestigatorSettings params) { + return ApiRequest.builder("PATCH", "/v3/investigator/settings/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + /** The {@code investigator} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse retrieveSummary(InvestigatorRetrieveSummaryParams params, RequestOptions options) { + return client.execute(retrieveSummaryRequest(params), options, RETRIEVE_SUMMARY_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveSummaryAsync(InvestigatorRetrieveSummaryParams params, RequestOptions options) { + return client.executeAsync(retrieveSummaryRequest(params), options, RETRIEVE_SUMMARY_RESPONSE); + } + + /** + * {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse retrieveSummary(InvestigatorRetrieveSummaryParams params) { + return client.execute(retrieveSummaryRequest(params), RequestOptions.none(), RETRIEVE_SUMMARY_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> retrieveSummaryAsync(InvestigatorRetrieveSummaryParams params) { + return client.executeAsync(retrieveSummaryRequest(params), RequestOptions.none(), RETRIEVE_SUMMARY_RESPONSE); + } + + /** + * {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse retrieveSummary(RequestOptions options) { + return client.execute(retrieveSummaryRequest(InvestigatorRetrieveSummaryParams.builder().build()), options, RETRIEVE_SUMMARY_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveSummaryAsync(RequestOptions options) { + return client.executeAsync(retrieveSummaryRequest(InvestigatorRetrieveSummaryParams.builder().build()), options, RETRIEVE_SUMMARY_RESPONSE); + } + + /** + * {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse retrieveSummary() { + return client.execute(retrieveSummaryRequest(InvestigatorRetrieveSummaryParams.builder().build()), RequestOptions.none(), RETRIEVE_SUMMARY_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSummary(InvestigatorRetrieveSummaryParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> retrieveSummaryAsync() { + return client.executeAsync(retrieveSummaryRequest(InvestigatorRetrieveSummaryParams.builder().build()), RequestOptions.none(), RETRIEVE_SUMMARY_RESPONSE); + } + + /** + * {@link #listQueue(InvestigatorListQueueParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> listQueue(InvestigatorListQueueParams params, RequestOptions options) { + return client.execute(listQueueRequest(params), options, LIST_QUEUE_RESPONSE); + } + + /** + * Asynchronous {@link #listQueue(InvestigatorListQueueParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listQueueAsync(InvestigatorListQueueParams params, RequestOptions options) { + return client.executeAsync(listQueueRequest(params), options, LIST_QUEUE_RESPONSE); + } + + /** + * {@link #listQueue(InvestigatorListQueueParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse> listQueue(InvestigatorListQueueParams params) { + return client.execute(listQueueRequest(params), RequestOptions.none(), LIST_QUEUE_RESPONSE); + } + + /** + * Asynchronous {@link #listQueue(InvestigatorListQueueParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listQueueAsync(InvestigatorListQueueParams params) { + return client.executeAsync(listQueueRequest(params), RequestOptions.none(), LIST_QUEUE_RESPONSE); + } + + /** + * {@link #listQueue(InvestigatorListQueueParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse> listQueue(RequestOptions options) { + return client.execute(listQueueRequest(InvestigatorListQueueParams.builder().build()), options, LIST_QUEUE_RESPONSE); + } + + /** + * Asynchronous {@link #listQueue(InvestigatorListQueueParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listQueueAsync(RequestOptions options) { + return client.executeAsync(listQueueRequest(InvestigatorListQueueParams.builder().build()), options, LIST_QUEUE_RESPONSE); + } + + /** + * {@link #listQueue(InvestigatorListQueueParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse> listQueue() { + return client.execute(listQueueRequest(InvestigatorListQueueParams.builder().build()), RequestOptions.none(), LIST_QUEUE_RESPONSE); + } + + /** + * Asynchronous {@link #listQueue(InvestigatorListQueueParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>> listQueueAsync() { + return client.executeAsync(listQueueRequest(InvestigatorListQueueParams.builder().build()), RequestOptions.none(), LIST_QUEUE_RESPONSE); + } + + /** + * {@link #retrieveRun(String, RequestOptions)} with the raw response. + * + * @param runId the {@code run_id} + * @param options per-request options + * @return the result + */ + public RawResponse retrieveRun(String runId, RequestOptions options) { + return client.execute(retrieveRunRequest(runId), options, RETRIEVE_RUN_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveRun(String, RequestOptions)} with the raw response. + * + * @param runId the {@code run_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveRunAsync(String runId, RequestOptions options) { + return client.executeAsync(retrieveRunRequest(runId), options, RETRIEVE_RUN_RESPONSE); + } + + /** + * {@link #retrieveRun(String, RequestOptions)} with the raw response, with the client's options. + * + * @param runId the {@code run_id} + * @return the result + */ + public RawResponse retrieveRun(String runId) { + return client.execute(retrieveRunRequest(runId), RequestOptions.none(), RETRIEVE_RUN_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveRun(String, RequestOptions)} with the raw response, with the client's options. + * + * @param runId the {@code run_id} + * @return a future completed with the result + */ + public CompletableFuture> retrieveRunAsync(String runId) { + return client.executeAsync(retrieveRunRequest(runId), RequestOptions.none(), RETRIEVE_RUN_RESPONSE); + } + + /** + * {@link #acceptRun(String, InvestigatorAcceptRequest, RequestOptions)} with the raw response. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse acceptRun(String runId, InvestigatorAcceptRequest params, RequestOptions options) { + return client.execute(acceptRunRequest(runId, params), options, ACCEPT_RUN_RESPONSE); + } + + /** + * Asynchronous {@link #acceptRun(String, InvestigatorAcceptRequest, RequestOptions)} with the raw response. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> acceptRunAsync(String runId, InvestigatorAcceptRequest params, RequestOptions options) { + return client.executeAsync(acceptRunRequest(runId, params), options, ACCEPT_RUN_RESPONSE); + } + + /** + * {@link #acceptRun(String, InvestigatorAcceptRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @return the result + */ + public RawResponse acceptRun(String runId, InvestigatorAcceptRequest params) { + return client.execute(acceptRunRequest(runId, params), RequestOptions.none(), ACCEPT_RUN_RESPONSE); + } + + /** + * Asynchronous {@link #acceptRun(String, InvestigatorAcceptRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> acceptRunAsync(String runId, InvestigatorAcceptRequest params) { + return client.executeAsync(acceptRunRequest(runId, params), RequestOptions.none(), ACCEPT_RUN_RESPONSE); + } + + /** + * {@link #overrideRun(String, InvestigatorOverrideRequest, RequestOptions)} with the raw response. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse overrideRun(String runId, InvestigatorOverrideRequest params, RequestOptions options) { + return client.execute(overrideRunRequest(runId, params), options, OVERRIDE_RUN_RESPONSE); + } + + /** + * Asynchronous {@link #overrideRun(String, InvestigatorOverrideRequest, RequestOptions)} with the raw response. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> overrideRunAsync(String runId, InvestigatorOverrideRequest params, RequestOptions options) { + return client.executeAsync(overrideRunRequest(runId, params), options, OVERRIDE_RUN_RESPONSE); + } + + /** + * {@link #overrideRun(String, InvestigatorOverrideRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @return the result + */ + public RawResponse overrideRun(String runId, InvestigatorOverrideRequest params) { + return client.execute(overrideRunRequest(runId, params), RequestOptions.none(), OVERRIDE_RUN_RESPONSE); + } + + /** + * Asynchronous {@link #overrideRun(String, InvestigatorOverrideRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param runId the {@code run_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> overrideRunAsync(String runId, InvestigatorOverrideRequest params) { + return client.executeAsync(overrideRunRequest(runId, params), RequestOptions.none(), OVERRIDE_RUN_RESPONSE); + } + + /** + * {@link #batchAcceptRuns(InvestigatorBatchAcceptRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse batchAcceptRuns(InvestigatorBatchAcceptRequest params, RequestOptions options) { + return client.execute(batchAcceptRunsRequest(params), options, BATCH_ACCEPT_RUNS_RESPONSE); + } + + /** + * Asynchronous {@link #batchAcceptRuns(InvestigatorBatchAcceptRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> batchAcceptRunsAsync(InvestigatorBatchAcceptRequest params, RequestOptions options) { + return client.executeAsync(batchAcceptRunsRequest(params), options, BATCH_ACCEPT_RUNS_RESPONSE); + } + + /** + * {@link #batchAcceptRuns(InvestigatorBatchAcceptRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse batchAcceptRuns(InvestigatorBatchAcceptRequest params) { + return client.execute(batchAcceptRunsRequest(params), RequestOptions.none(), BATCH_ACCEPT_RUNS_RESPONSE); + } + + /** + * Asynchronous {@link #batchAcceptRuns(InvestigatorBatchAcceptRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> batchAcceptRunsAsync(InvestigatorBatchAcceptRequest params) { + return client.executeAsync(batchAcceptRunsRequest(params), RequestOptions.none(), BATCH_ACCEPT_RUNS_RESPONSE); + } + + /** + * {@link #retrieveTransaction(String, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return the result + */ + public RawResponse retrieveTransaction(String transactionId, RequestOptions options) { + return client.execute(retrieveTransactionRequest(transactionId), options, RETRIEVE_TRANSACTION_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveTransaction(String, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveTransactionAsync(String transactionId, RequestOptions options) { + return client.executeAsync(retrieveTransactionRequest(transactionId), options, RETRIEVE_TRANSACTION_RESPONSE); + } + + /** + * {@link #retrieveTransaction(String, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return the result + */ + public RawResponse retrieveTransaction(String transactionId) { + return client.execute(retrieveTransactionRequest(transactionId), RequestOptions.none(), RETRIEVE_TRANSACTION_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveTransaction(String, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return a future completed with the result + */ + public CompletableFuture> retrieveTransactionAsync(String transactionId) { + return client.executeAsync(retrieveTransactionRequest(transactionId), RequestOptions.none(), RETRIEVE_TRANSACTION_RESPONSE); + } + + /** + * {@link #listGuardrails(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return the result + */ + public RawResponse listGuardrails(RequestOptions options) { + return client.execute(listGuardrailsRequest(), options, LIST_GUARDRAILS_RESPONSE); + } + + /** + * Asynchronous {@link #listGuardrails(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listGuardrailsAsync(RequestOptions options) { + return client.executeAsync(listGuardrailsRequest(), options, LIST_GUARDRAILS_RESPONSE); + } + + /** + * {@link #listGuardrails(RequestOptions)} with the raw response, with the client's options. + * + * @return the result + */ + public RawResponse listGuardrails() { + return client.execute(listGuardrailsRequest(), RequestOptions.none(), LIST_GUARDRAILS_RESPONSE); + } + + /** + * Asynchronous {@link #listGuardrails(RequestOptions)} with the raw response, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> listGuardrailsAsync() { + return client.executeAsync(listGuardrailsRequest(), RequestOptions.none(), LIST_GUARDRAILS_RESPONSE); + } + + /** + * {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse retrieveDashboard(InvestigatorRetrieveDashboardParams params, RequestOptions options) { + return client.execute(retrieveDashboardRequest(params), options, RETRIEVE_DASHBOARD_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveDashboardAsync(InvestigatorRetrieveDashboardParams params, RequestOptions options) { + return client.executeAsync(retrieveDashboardRequest(params), options, RETRIEVE_DASHBOARD_RESPONSE); + } + + /** + * {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse retrieveDashboard(InvestigatorRetrieveDashboardParams params) { + return client.execute(retrieveDashboardRequest(params), RequestOptions.none(), RETRIEVE_DASHBOARD_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> retrieveDashboardAsync(InvestigatorRetrieveDashboardParams params) { + return client.executeAsync(retrieveDashboardRequest(params), RequestOptions.none(), RETRIEVE_DASHBOARD_RESPONSE); + } + + /** + * {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse retrieveDashboard(RequestOptions options) { + return client.execute(retrieveDashboardRequest(InvestigatorRetrieveDashboardParams.builder().build()), options, RETRIEVE_DASHBOARD_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveDashboardAsync(RequestOptions options) { + return client.executeAsync(retrieveDashboardRequest(InvestigatorRetrieveDashboardParams.builder().build()), options, RETRIEVE_DASHBOARD_RESPONSE); + } + + /** + * {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse retrieveDashboard() { + return client.execute(retrieveDashboardRequest(InvestigatorRetrieveDashboardParams.builder().build()), RequestOptions.none(), RETRIEVE_DASHBOARD_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDashboard(InvestigatorRetrieveDashboardParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> retrieveDashboardAsync() { + return client.executeAsync(retrieveDashboardRequest(InvestigatorRetrieveDashboardParams.builder().build()), RequestOptions.none(), RETRIEVE_DASHBOARD_RESPONSE); + } + + /** + * {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse retrievePerformance(InvestigatorRetrievePerformanceParams params, RequestOptions options) { + return client.execute(retrievePerformanceRequest(params), options, RETRIEVE_PERFORMANCE_RESPONSE); + } + + /** + * Asynchronous {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrievePerformanceAsync(InvestigatorRetrievePerformanceParams params, RequestOptions options) { + return client.executeAsync(retrievePerformanceRequest(params), options, RETRIEVE_PERFORMANCE_RESPONSE); + } + + /** + * {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse retrievePerformance(InvestigatorRetrievePerformanceParams params) { + return client.execute(retrievePerformanceRequest(params), RequestOptions.none(), RETRIEVE_PERFORMANCE_RESPONSE); + } + + /** + * Asynchronous {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> retrievePerformanceAsync(InvestigatorRetrievePerformanceParams params) { + return client.executeAsync(retrievePerformanceRequest(params), RequestOptions.none(), RETRIEVE_PERFORMANCE_RESPONSE); + } + + /** + * {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse retrievePerformance(RequestOptions options) { + return client.execute(retrievePerformanceRequest(InvestigatorRetrievePerformanceParams.builder().build()), options, RETRIEVE_PERFORMANCE_RESPONSE); + } + + /** + * Asynchronous {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrievePerformanceAsync(RequestOptions options) { + return client.executeAsync(retrievePerformanceRequest(InvestigatorRetrievePerformanceParams.builder().build()), options, RETRIEVE_PERFORMANCE_RESPONSE); + } + + /** + * {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse retrievePerformance() { + return client.execute(retrievePerformanceRequest(InvestigatorRetrievePerformanceParams.builder().build()), RequestOptions.none(), RETRIEVE_PERFORMANCE_RESPONSE); + } + + /** + * Asynchronous {@link #retrievePerformance(InvestigatorRetrievePerformanceParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> retrievePerformanceAsync() { + return client.executeAsync(retrievePerformanceRequest(InvestigatorRetrievePerformanceParams.builder().build()), RequestOptions.none(), RETRIEVE_PERFORMANCE_RESPONSE); + } + + /** + * {@link #listActivity(InvestigatorListActivityParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse>> listActivity(InvestigatorListActivityParams params, RequestOptions options) { + return client.execute(listActivityRequest(params), options, LIST_ACTIVITY_RESPONSE); + } + + /** + * Asynchronous {@link #listActivity(InvestigatorListActivityParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>>> listActivityAsync(InvestigatorListActivityParams params, RequestOptions options) { + return client.executeAsync(listActivityRequest(params), options, LIST_ACTIVITY_RESPONSE); + } + + /** + * {@link #listActivity(InvestigatorListActivityParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse>> listActivity(InvestigatorListActivityParams params) { + return client.execute(listActivityRequest(params), RequestOptions.none(), LIST_ACTIVITY_RESPONSE); + } + + /** + * Asynchronous {@link #listActivity(InvestigatorListActivityParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>>> listActivityAsync(InvestigatorListActivityParams params) { + return client.executeAsync(listActivityRequest(params), RequestOptions.none(), LIST_ACTIVITY_RESPONSE); + } + + /** + * {@link #listActivity(InvestigatorListActivityParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse>> listActivity(RequestOptions options) { + return client.execute(listActivityRequest(InvestigatorListActivityParams.builder().build()), options, LIST_ACTIVITY_RESPONSE); + } + + /** + * Asynchronous {@link #listActivity(InvestigatorListActivityParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>>> listActivityAsync(RequestOptions options) { + return client.executeAsync(listActivityRequest(InvestigatorListActivityParams.builder().build()), options, LIST_ACTIVITY_RESPONSE); + } + + /** + * {@link #listActivity(InvestigatorListActivityParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse>> listActivity() { + return client.execute(listActivityRequest(InvestigatorListActivityParams.builder().build()), RequestOptions.none(), LIST_ACTIVITY_RESPONSE); + } + + /** + * Asynchronous {@link #listActivity(InvestigatorListActivityParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>>> listActivityAsync() { + return client.executeAsync(listActivityRequest(InvestigatorListActivityParams.builder().build()), RequestOptions.none(), LIST_ACTIVITY_RESPONSE); + } + + /** + * {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse exportActivity(InvestigatorExportActivityParams params, RequestOptions options) { + return client.execute(exportActivityRequest(params), options, EXPORT_ACTIVITY_RESPONSE); + } + + /** + * Asynchronous {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> exportActivityAsync(InvestigatorExportActivityParams params, RequestOptions options) { + return client.executeAsync(exportActivityRequest(params), options, EXPORT_ACTIVITY_RESPONSE); + } + + /** + * {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse exportActivity(InvestigatorExportActivityParams params) { + return client.execute(exportActivityRequest(params), RequestOptions.none(), EXPORT_ACTIVITY_RESPONSE); + } + + /** + * Asynchronous {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> exportActivityAsync(InvestigatorExportActivityParams params) { + return client.executeAsync(exportActivityRequest(params), RequestOptions.none(), EXPORT_ACTIVITY_RESPONSE); + } + + /** + * {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse exportActivity(RequestOptions options) { + return client.execute(exportActivityRequest(InvestigatorExportActivityParams.builder().build()), options, EXPORT_ACTIVITY_RESPONSE); + } + + /** + * Asynchronous {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> exportActivityAsync(RequestOptions options) { + return client.executeAsync(exportActivityRequest(InvestigatorExportActivityParams.builder().build()), options, EXPORT_ACTIVITY_RESPONSE); + } + + /** + * {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse exportActivity() { + return client.execute(exportActivityRequest(InvestigatorExportActivityParams.builder().build()), RequestOptions.none(), EXPORT_ACTIVITY_RESPONSE); + } + + /** + * Asynchronous {@link #exportActivity(InvestigatorExportActivityParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> exportActivityAsync() { + return client.executeAsync(exportActivityRequest(InvestigatorExportActivityParams.builder().build()), RequestOptions.none(), EXPORT_ACTIVITY_RESPONSE); + } + + /** + * {@link #retrieveSettings(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return the result + */ + public RawResponse retrieveSettings(RequestOptions options) { + return client.execute(retrieveSettingsRequest(), options, RETRIEVE_SETTINGS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSettings(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveSettingsAsync(RequestOptions options) { + return client.executeAsync(retrieveSettingsRequest(), options, RETRIEVE_SETTINGS_RESPONSE); + } + + /** + * {@link #retrieveSettings(RequestOptions)} with the raw response, with the client's options. + * + * @return the result + */ + public RawResponse retrieveSettings() { + return client.execute(retrieveSettingsRequest(), RequestOptions.none(), RETRIEVE_SETTINGS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSettings(RequestOptions)} with the raw response, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> retrieveSettingsAsync() { + return client.executeAsync(retrieveSettingsRequest(), RequestOptions.none(), RETRIEVE_SETTINGS_RESPONSE); + } + + /** + * {@link #updateSettings(InvestigatorSettings, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse updateSettings(InvestigatorSettings params, RequestOptions options) { + return client.execute(updateSettingsRequest(params), options, UPDATE_SETTINGS_RESPONSE); + } + + /** + * Asynchronous {@link #updateSettings(InvestigatorSettings, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> updateSettingsAsync(InvestigatorSettings params, RequestOptions options) { + return client.executeAsync(updateSettingsRequest(params), options, UPDATE_SETTINGS_RESPONSE); + } + + /** + * {@link #updateSettings(InvestigatorSettings, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse updateSettings(InvestigatorSettings params) { + return client.execute(updateSettingsRequest(params), RequestOptions.none(), UPDATE_SETTINGS_RESPONSE); + } + + /** + * Asynchronous {@link #updateSettings(InvestigatorSettings, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> updateSettingsAsync(InvestigatorSettings params) { + return client.executeAsync(updateSettingsRequest(params), RequestOptions.none(), UPDATE_SETTINGS_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/KybResource.java b/src/main/java/me/didit/resources/KybResource.java index d39863a..f51bcb2 100644 --- a/src/main/java/me/didit/resources/KybResource.java +++ b/src/main/java/me/didit/resources/KybResource.java @@ -9,13 +9,11 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; -import me.didit.types.KybListShareholdersParams; +import me.didit.types.KYBRegistryCandidateRequest; import me.didit.types.KybListShareholdersResponse; -import me.didit.types.KybListUbosParams; import me.didit.types.KybListUbosResponse; import me.didit.types.KybSearchParams; import me.didit.types.KybSearchResponse; -import me.didit.types.KybSelectParams; import me.didit.types.KybSelectResponse; /** The {@code kyb} resource: {@code client.kyb()}. */ @@ -46,6 +44,10 @@ public WithRawResponse withRawResponse() { * *

**kyb_response_id is ephemeral.** It is a per-search handle, not a stable company id. Select promptly after searching and re-search to get a fresh handle instead of storing it. * + *

**tier picks the provider chain.** tier names the registry tier the search runs on: basic (the default, the Lite chain), shareholders or ubo. The search uses the provider chain that serves that tier, and it is not always the same chain the Lite search reads, so an empty Lite search is not proof that the registry does not hold the company — search again at the tier you need. A country that does not offer the tier you name is searched at the highest tier it offers up to that one. Retrieve the candidate from the endpoint that sells that tier: POST /v3/kyb/select/ (Lite), POST /v3/kyb/shareholders/ or POST /v3/kyb/ubo/. + * + *

Authentication. POST /v3/kyb/search/ (and /v2/) takes your application's API key in the x-api-key header. The hosted POST /v1/kyb/search/ is the session-authenticated route a hosted verification flow calls: it answers 403 to an API key and takes no tier — the workflow's KYB registry configuration picks the chain. + * *

Two modes. * - *Synchronous (default):* the API polls the registry until the search resolves and returns the final candidate list (search_resolved: true). * - *Asynchronous:* pass webhook_url and the API returns immediately — usually with search_status: "pending" and an empty candidate list — then POSTs a kyb.registry_search.resolved callback to your URL when the registry finishes. The callback body is {"event_id", "event_type": "kyb.registry_search.resolved", "request_id", "vendor_data", "metadata", "search_status", "search_resolved", "kyb_registry", "timestamp", "created_at"} — note timestamp and created_at in the callback are epoch integers, unlike this endpoint's ISO created_at — and is sent unsigned (header X-Didit-Unsigned-Callback: true, no X-Signature). @@ -112,38 +114,38 @@ public CompletableFuture searchAsync(KybSearchParams params) * @param options per-request options that override the client options * @return the response body */ - public KybSelectResponse select(KybSelectParams params, RequestOptions options) { + public KybSelectResponse select(KYBRegistryCandidateRequest params, RequestOptions options) { return client.execute(selectRequest(params), options, SELECT_RESPONSE).data(); } /** - * Asynchronous {@link #select(KybSelectParams, RequestOptions)}. + * Asynchronous {@link #select(KYBRegistryCandidateRequest, RequestOptions)}. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture selectAsync(KybSelectParams params, RequestOptions options) { + public CompletableFuture selectAsync(KYBRegistryCandidateRequest params, RequestOptions options) { return client.executeAsync(selectRequest(params), options, SELECT_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #select(KybSelectParams, RequestOptions)}, with the client's options. + * Same as {@link #select(KYBRegistryCandidateRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return the result */ - public KybSelectResponse select(KybSelectParams params) { + public KybSelectResponse select(KYBRegistryCandidateRequest params) { return client.execute(selectRequest(params), RequestOptions.none(), SELECT_RESPONSE).data(); } /** - * Asynchronous {@link #select(KybSelectParams, RequestOptions)}, with the client's options. + * Asynchronous {@link #select(KYBRegistryCandidateRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture selectAsync(KybSelectParams params) { + public CompletableFuture selectAsync(KYBRegistryCandidateRequest params) { return client.executeAsync(selectRequest(params), RequestOptions.none(), SELECT_RESPONSE).thenApply(RawResponse::data); } @@ -166,38 +168,38 @@ public CompletableFuture selectAsync(KybSelectParams params) * @param options per-request options that override the client options * @return the response body */ - public KybListShareholdersResponse listShareholders(KybListShareholdersParams params, RequestOptions options) { + public KybListShareholdersResponse listShareholders(KYBRegistryCandidateRequest params, RequestOptions options) { return client.execute(listShareholdersRequest(params), options, LIST_SHAREHOLDERS_RESPONSE).data(); } /** - * Asynchronous {@link #listShareholders(KybListShareholdersParams, RequestOptions)}. + * Asynchronous {@link #listShareholders(KYBRegistryCandidateRequest, RequestOptions)}. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture listShareholdersAsync(KybListShareholdersParams params, RequestOptions options) { + public CompletableFuture listShareholdersAsync(KYBRegistryCandidateRequest params, RequestOptions options) { return client.executeAsync(listShareholdersRequest(params), options, LIST_SHAREHOLDERS_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #listShareholders(KybListShareholdersParams, RequestOptions)}, with the client's options. + * Same as {@link #listShareholders(KYBRegistryCandidateRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return the result */ - public KybListShareholdersResponse listShareholders(KybListShareholdersParams params) { + public KybListShareholdersResponse listShareholders(KYBRegistryCandidateRequest params) { return client.execute(listShareholdersRequest(params), RequestOptions.none(), LIST_SHAREHOLDERS_RESPONSE).data(); } /** - * Asynchronous {@link #listShareholders(KybListShareholdersParams, RequestOptions)}, with the client's options. + * Asynchronous {@link #listShareholders(KYBRegistryCandidateRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture listShareholdersAsync(KybListShareholdersParams params) { + public CompletableFuture listShareholdersAsync(KYBRegistryCandidateRequest params) { return client.executeAsync(listShareholdersRequest(params), RequestOptions.none(), LIST_SHAREHOLDERS_RESPONSE).thenApply(RawResponse::data); } @@ -220,38 +222,38 @@ public CompletableFuture listShareholdersAsync(KybL * @param options per-request options that override the client options * @return the response body */ - public KybListUbosResponse listUbos(KybListUbosParams params, RequestOptions options) { + public KybListUbosResponse listUbos(KYBRegistryCandidateRequest params, RequestOptions options) { return client.execute(listUbosRequest(params), options, LIST_UBOS_RESPONSE).data(); } /** - * Asynchronous {@link #listUbos(KybListUbosParams, RequestOptions)}. + * Asynchronous {@link #listUbos(KYBRegistryCandidateRequest, RequestOptions)}. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture listUbosAsync(KybListUbosParams params, RequestOptions options) { + public CompletableFuture listUbosAsync(KYBRegistryCandidateRequest params, RequestOptions options) { return client.executeAsync(listUbosRequest(params), options, LIST_UBOS_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #listUbos(KybListUbosParams, RequestOptions)}, with the client's options. + * Same as {@link #listUbos(KYBRegistryCandidateRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return the result */ - public KybListUbosResponse listUbos(KybListUbosParams params) { + public KybListUbosResponse listUbos(KYBRegistryCandidateRequest params) { return client.execute(listUbosRequest(params), RequestOptions.none(), LIST_UBOS_RESPONSE).data(); } /** - * Asynchronous {@link #listUbos(KybListUbosParams, RequestOptions)}, with the client's options. + * Asynchronous {@link #listUbos(KYBRegistryCandidateRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture listUbosAsync(KybListUbosParams params) { + public CompletableFuture listUbosAsync(KYBRegistryCandidateRequest params) { return client.executeAsync(listUbosRequest(params), RequestOptions.none(), LIST_UBOS_RESPONSE).thenApply(RawResponse::data); } @@ -266,7 +268,7 @@ private static ApiRequest searchRequest(KybSearchParams params) { private static final ResponseHandler SELECT_RESPONSE = Responses.json(Conv.model(KybSelectResponse::fromJson)); - private static ApiRequest selectRequest(KybSelectParams params) { + private static ApiRequest selectRequest(KYBRegistryCandidateRequest params) { return ApiRequest.builder("POST", "/v3/kyb/select/") .params(params) .body(ApiRequest.BodyEncoding.JSON) @@ -275,7 +277,7 @@ private static ApiRequest selectRequest(KybSelectParams params) { private static final ResponseHandler LIST_SHAREHOLDERS_RESPONSE = Responses.json(Conv.model(KybListShareholdersResponse::fromJson)); - private static ApiRequest listShareholdersRequest(KybListShareholdersParams params) { + private static ApiRequest listShareholdersRequest(KYBRegistryCandidateRequest params) { return ApiRequest.builder("POST", "/v3/kyb/shareholders/") .params(params) .body(ApiRequest.BodyEncoding.JSON) @@ -284,7 +286,7 @@ private static ApiRequest listShareholdersRequest(KybListShareholdersParams para private static final ResponseHandler LIST_UBOS_RESPONSE = Responses.json(Conv.model(KybListUbosResponse::fromJson)); - private static ApiRequest listUbosRequest(KybListUbosParams params) { + private static ApiRequest listUbosRequest(KYBRegistryCandidateRequest params) { return ApiRequest.builder("POST", "/v3/kyb/ubo/") .params(params) .body(ApiRequest.BodyEncoding.JSON) @@ -342,128 +344,128 @@ public CompletableFuture> searchAsync(KybSearchPa } /** - * {@link #select(KybSelectParams, RequestOptions)} with the raw response. + * {@link #select(KYBRegistryCandidateRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse select(KybSelectParams params, RequestOptions options) { + public RawResponse select(KYBRegistryCandidateRequest params, RequestOptions options) { return client.execute(selectRequest(params), options, SELECT_RESPONSE); } /** - * Asynchronous {@link #select(KybSelectParams, RequestOptions)} with the raw response. + * Asynchronous {@link #select(KYBRegistryCandidateRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> selectAsync(KybSelectParams params, RequestOptions options) { + public CompletableFuture> selectAsync(KYBRegistryCandidateRequest params, RequestOptions options) { return client.executeAsync(selectRequest(params), options, SELECT_RESPONSE); } /** - * {@link #select(KybSelectParams, RequestOptions)} with the raw response, with the client's options. + * {@link #select(KYBRegistryCandidateRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return the result */ - public RawResponse select(KybSelectParams params) { + public RawResponse select(KYBRegistryCandidateRequest params) { return client.execute(selectRequest(params), RequestOptions.none(), SELECT_RESPONSE); } /** - * Asynchronous {@link #select(KybSelectParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #select(KYBRegistryCandidateRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> selectAsync(KybSelectParams params) { + public CompletableFuture> selectAsync(KYBRegistryCandidateRequest params) { return client.executeAsync(selectRequest(params), RequestOptions.none(), SELECT_RESPONSE); } /** - * {@link #listShareholders(KybListShareholdersParams, RequestOptions)} with the raw response. + * {@link #listShareholders(KYBRegistryCandidateRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse listShareholders(KybListShareholdersParams params, RequestOptions options) { + public RawResponse listShareholders(KYBRegistryCandidateRequest params, RequestOptions options) { return client.execute(listShareholdersRequest(params), options, LIST_SHAREHOLDERS_RESPONSE); } /** - * Asynchronous {@link #listShareholders(KybListShareholdersParams, RequestOptions)} with the raw response. + * Asynchronous {@link #listShareholders(KYBRegistryCandidateRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listShareholdersAsync(KybListShareholdersParams params, RequestOptions options) { + public CompletableFuture> listShareholdersAsync(KYBRegistryCandidateRequest params, RequestOptions options) { return client.executeAsync(listShareholdersRequest(params), options, LIST_SHAREHOLDERS_RESPONSE); } /** - * {@link #listShareholders(KybListShareholdersParams, RequestOptions)} with the raw response, with the client's options. + * {@link #listShareholders(KYBRegistryCandidateRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return the result */ - public RawResponse listShareholders(KybListShareholdersParams params) { + public RawResponse listShareholders(KYBRegistryCandidateRequest params) { return client.execute(listShareholdersRequest(params), RequestOptions.none(), LIST_SHAREHOLDERS_RESPONSE); } /** - * Asynchronous {@link #listShareholders(KybListShareholdersParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #listShareholders(KYBRegistryCandidateRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> listShareholdersAsync(KybListShareholdersParams params) { + public CompletableFuture> listShareholdersAsync(KYBRegistryCandidateRequest params) { return client.executeAsync(listShareholdersRequest(params), RequestOptions.none(), LIST_SHAREHOLDERS_RESPONSE); } /** - * {@link #listUbos(KybListUbosParams, RequestOptions)} with the raw response. + * {@link #listUbos(KYBRegistryCandidateRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse listUbos(KybListUbosParams params, RequestOptions options) { + public RawResponse listUbos(KYBRegistryCandidateRequest params, RequestOptions options) { return client.execute(listUbosRequest(params), options, LIST_UBOS_RESPONSE); } /** - * Asynchronous {@link #listUbos(KybListUbosParams, RequestOptions)} with the raw response. + * Asynchronous {@link #listUbos(KYBRegistryCandidateRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listUbosAsync(KybListUbosParams params, RequestOptions options) { + public CompletableFuture> listUbosAsync(KYBRegistryCandidateRequest params, RequestOptions options) { return client.executeAsync(listUbosRequest(params), options, LIST_UBOS_RESPONSE); } /** - * {@link #listUbos(KybListUbosParams, RequestOptions)} with the raw response, with the client's options. + * {@link #listUbos(KYBRegistryCandidateRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return the result */ - public RawResponse listUbos(KybListUbosParams params) { + public RawResponse listUbos(KYBRegistryCandidateRequest params) { return client.execute(listUbosRequest(params), RequestOptions.none(), LIST_UBOS_RESPONSE); } /** - * Asynchronous {@link #listUbos(KybListUbosParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #listUbos(KYBRegistryCandidateRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> listUbosAsync(KybListUbosParams params) { + public CompletableFuture> listUbosAsync(KYBRegistryCandidateRequest params) { return client.executeAsync(listUbosRequest(params), RequestOptions.none(), LIST_UBOS_RESPONSE); } } diff --git a/src/main/java/me/didit/resources/ListsEntriesResource.java b/src/main/java/me/didit/resources/ListsEntriesResource.java new file mode 100644 index 0000000..47a5042 --- /dev/null +++ b/src/main/java/me/didit/resources/ListsEntriesResource.java @@ -0,0 +1,636 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.Page; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.ComplianceListEntry; +import me.didit.types.ListsEntriesCreateParams; +import me.didit.types.ListsEntriesListParams; +import me.didit.types.ListsEntriesUploadFaceParams; +import org.jspecify.annotations.Nullable; + +/** The {@code lists.entries} resource: {@code client.lists().entries()}. */ +public final class ListsEntriesResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + ListsEntriesResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * List list entries + * + *

Returns the current entries of one list, newest first. search matches part of an entry's value or display_label without regard to case, and reference_session_id narrows the results to the entries taken from one person verification session. Image URLs in metadata are short-lived signed URLs issued again on every call; a scoped API key without media: read receives null in their place. A scoped API key needs workflows: read. + * + *

{@code GET /v3/lists/{list_uuid}/entries/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param listUuid UUID of the list, as returned by List lists or Create a list. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page list(String listUuid, ListsEntriesListParams params, RequestOptions options) { + return client.execute(listRequest(listUuid, params), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, ListsEntriesListParams, RequestOptions)}. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String listUuid, ListsEntriesListParams params, RequestOptions options) { + return client.executeAsync(listRequest(listUuid, params), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(String, ListsEntriesListParams, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return the result + */ + public Page list(String listUuid, ListsEntriesListParams params) { + return client.execute(listRequest(listUuid, params), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, ListsEntriesListParams, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String listUuid, ListsEntriesListParams params) { + return client.executeAsync(listRequest(listUuid, params), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(String, ListsEntriesListParams, RequestOptions)}, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return the result + */ + public Page list(String listUuid, RequestOptions options) { + return client.execute(listRequest(listUuid, ListsEntriesListParams.builder().build()), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, ListsEntriesListParams, RequestOptions)}, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String listUuid, RequestOptions options) { + return client.executeAsync(listRequest(listUuid, ListsEntriesListParams.builder().build()), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(String, ListsEntriesListParams, RequestOptions)}, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return the result + */ + public Page list(String listUuid) { + return client.execute(listRequest(listUuid, ListsEntriesListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, ListsEntriesListParams, RequestOptions)}, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String listUuid) { + return client.executeAsync(listRequest(listUuid, ListsEntriesListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Create a list entry + * + *

Adds one entry to a list, in one of three ways: by value; from a verification session with reference_session_id, where Didit reads the value from the session's most recent record of the list's entry type and links the entry to it; or by value with a reference_object_uuid and metadata.reference_type pointing back to a transaction, user or business. On the system face, document, phone and email blocklists, the record read from the session is also flagged as blocklisted, so new matching sessions are declined; on the system user and business blocklists, matching users or businesses are set to BLOCKED. Sessions and transactions that already finished are not changed. A scoped API key needs workflows: write. See Create a list entry for each mode. + * + *

{@code POST /v3/lists/{list_uuid}/entries/} + * + * @param listUuid UUID of the list to add to. For a face you only have as an image, use Upload a face to a list instead. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public ComplianceListEntry create(String listUuid, ListsEntriesCreateParams params, RequestOptions options) { + return client.execute(createRequest(listUuid, params), options, CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(String, ListsEntriesCreateParams, RequestOptions)}. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createAsync(String listUuid, ListsEntriesCreateParams params, RequestOptions options) { + return client.executeAsync(createRequest(listUuid, params), options, CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #create(String, ListsEntriesCreateParams, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return the result + */ + public ComplianceListEntry create(String listUuid, ListsEntriesCreateParams params) { + return client.execute(createRequest(listUuid, params), RequestOptions.none(), CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(String, ListsEntriesCreateParams, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture createAsync(String listUuid, ListsEntriesCreateParams params) { + return client.executeAsync(createRequest(listUuid, params), RequestOptions.none(), CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #create(String, ListsEntriesCreateParams, RequestOptions)}, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return the result + */ + public ComplianceListEntry create(String listUuid, RequestOptions options) { + return client.execute(createRequest(listUuid, ListsEntriesCreateParams.builder().build()), options, CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(String, ListsEntriesCreateParams, RequestOptions)}, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createAsync(String listUuid, RequestOptions options) { + return client.executeAsync(createRequest(listUuid, ListsEntriesCreateParams.builder().build()), options, CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #create(String, ListsEntriesCreateParams, RequestOptions)}, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return the result + */ + public ComplianceListEntry create(String listUuid) { + return client.execute(createRequest(listUuid, ListsEntriesCreateParams.builder().build()), RequestOptions.none(), CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(String, ListsEntriesCreateParams, RequestOptions)}, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture createAsync(String listUuid) { + return client.executeAsync(createRequest(listUuid, ListsEntriesCreateParams.builder().build()), RequestOptions.none(), CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Delete a list entry + * + *

Deletes one entry from a list, system lists included. The entry stops matching at once, and deletion is irreversible: to list the value again, create a new entry. On a system face, document, phone or email blocklist, the record the entry stands for is no longer flagged as blocklisted; on the system user or business blocklist, users or businesses of the application whose vendor_data equals the entry's value (or whose didit_internal_id equals its reference_object_uuid) and that are BLOCKED return to ACTIVE. Sessions and transactions that already finished keep their status. A scoped API key needs workflows: write. + * + *

{@code DELETE /v3/lists/{list_uuid}/entries/{entry_uuid}/} + * + * @param listUuid UUID of the list that holds the entry. + * @param entryUuid UUID of the entry, as returned by List list entries or Create a list entry. + * @param options per-request options that override the client options + */ + public void delete(String listUuid, String entryUuid, RequestOptions options) { + client.execute(deleteRequest(listUuid, entryUuid), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, String, RequestOptions)}. + * + * @param listUuid the {@code list_uuid} + * @param entryUuid the {@code entry_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture deleteAsync(String listUuid, String entryUuid, RequestOptions options) { + return client.executeAsync(deleteRequest(listUuid, entryUuid), options, DELETE_RESPONSE).thenAccept(response -> {}); + } + + /** + * Same as {@link #delete(String, String, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param entryUuid the {@code entry_uuid} + */ + public void delete(String listUuid, String entryUuid) { + client.execute(deleteRequest(listUuid, entryUuid), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, String, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param entryUuid the {@code entry_uuid} + * @return a future completed with the result + */ + public CompletableFuture deleteAsync(String listUuid, String entryUuid) { + return client.executeAsync(deleteRequest(listUuid, entryUuid), RequestOptions.none(), DELETE_RESPONSE).thenAccept(response -> {}); + } + + /** + * Upload a face to a list + * + *

Adds a face to a face blocklist or face allowlist from a base64-encoded JPEG or PNG image, for when you have no verification session to reference. Didit detects the faces in the image, uses the largest one, and creates an entry whose value and reference_object_uuid are the UUID of the new face. A blocklisted face declines new sessions whose face matches; an allowlisted face skips the duplicated-face action and never overrides a blocklist match. In a sandbox application the image is not analyzed and the entry matches nothing. A scoped API key needs workflows: write. To list a face from an existing session, use Create a list entry with reference_session_id instead. + * + *

{@code POST /v3/lists/{list_uuid}/entries/face-upload/} + * + * @param listUuid UUID of a list with `entry_type: face` and `list_type: blocklist` or `allowlist`. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public ComplianceListEntry uploadFace(String listUuid, ListsEntriesUploadFaceParams params, RequestOptions options) { + return client.execute(uploadFaceRequest(listUuid, params), options, UPLOAD_FACE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #uploadFace(String, ListsEntriesUploadFaceParams, RequestOptions)}. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture uploadFaceAsync(String listUuid, ListsEntriesUploadFaceParams params, RequestOptions options) { + return client.executeAsync(uploadFaceRequest(listUuid, params), options, UPLOAD_FACE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #uploadFace(String, ListsEntriesUploadFaceParams, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return the result + */ + public ComplianceListEntry uploadFace(String listUuid, ListsEntriesUploadFaceParams params) { + return client.execute(uploadFaceRequest(listUuid, params), RequestOptions.none(), UPLOAD_FACE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #uploadFace(String, ListsEntriesUploadFaceParams, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture uploadFaceAsync(String listUuid, ListsEntriesUploadFaceParams params) { + return client.executeAsync(uploadFaceRequest(listUuid, params), RequestOptions.none(), UPLOAD_FACE_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(ComplianceListEntry::fromJson)); + + private static ApiRequest listRequest(String listUuid, ListsEntriesListParams params) { + return ApiRequest.builder("GET", "/v3/lists/{list_uuid}/entries/") + .pathParam("list_uuid", listUuid) + .params(params) + .queryFields("search", "reference_session_id", "limit", "offset") + .build(); + } + + private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(ComplianceListEntry::fromJson)); + + private static ApiRequest createRequest(String listUuid, ListsEntriesCreateParams params) { + return ApiRequest.builder("POST", "/v3/lists/{list_uuid}/entries/") + .pathParam("list_uuid", listUuid) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler<@Nullable Void> DELETE_RESPONSE = Responses.none(); + + private static ApiRequest deleteRequest(String listUuid, String entryUuid) { + return ApiRequest.builder("DELETE", "/v3/lists/{list_uuid}/entries/{entry_uuid}/") + .pathParam("list_uuid", listUuid) + .pathParam("entry_uuid", entryUuid) + .build(); + } + + private static final ResponseHandler UPLOAD_FACE_RESPONSE = Responses.json(Conv.model(ComplianceListEntry::fromJson)); + + private static ApiRequest uploadFaceRequest(String listUuid, ListsEntriesUploadFaceParams params) { + return ApiRequest.builder("POST", "/v3/lists/{list_uuid}/entries/face-upload/") + .pathParam("list_uuid", listUuid) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + /** The {@code lists.entries} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #list(String, ListsEntriesListParams, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(String listUuid, ListsEntriesListParams params, RequestOptions options) { + return client.execute(listRequest(listUuid, params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, ListsEntriesListParams, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String listUuid, ListsEntriesListParams params, RequestOptions options) { + return client.executeAsync(listRequest(listUuid, params), options, LIST_RESPONSE); + } + + /** + * {@link #list(String, ListsEntriesListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return the result + */ + public RawResponse> list(String listUuid, ListsEntriesListParams params) { + return client.execute(listRequest(listUuid, params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, ListsEntriesListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String listUuid, ListsEntriesListParams params) { + return client.executeAsync(listRequest(listUuid, params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(String, ListsEntriesListParams, RequestOptions)} with the raw response, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse> list(String listUuid, RequestOptions options) { + return client.execute(listRequest(listUuid, ListsEntriesListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, ListsEntriesListParams, RequestOptions)} with the raw response, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String listUuid, RequestOptions options) { + return client.executeAsync(listRequest(listUuid, ListsEntriesListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * {@link #list(String, ListsEntriesListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return the result + */ + public RawResponse> list(String listUuid) { + return client.execute(listRequest(listUuid, ListsEntriesListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, ListsEntriesListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String listUuid) { + return client.executeAsync(listRequest(listUuid, ListsEntriesListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #create(String, ListsEntriesCreateParams, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse create(String listUuid, ListsEntriesCreateParams params, RequestOptions options) { + return client.execute(createRequest(listUuid, params), options, CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(String, ListsEntriesCreateParams, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createAsync(String listUuid, ListsEntriesCreateParams params, RequestOptions options) { + return client.executeAsync(createRequest(listUuid, params), options, CREATE_RESPONSE); + } + + /** + * {@link #create(String, ListsEntriesCreateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return the result + */ + public RawResponse create(String listUuid, ListsEntriesCreateParams params) { + return client.execute(createRequest(listUuid, params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(String, ListsEntriesCreateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> createAsync(String listUuid, ListsEntriesCreateParams params) { + return client.executeAsync(createRequest(listUuid, params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * {@link #create(String, ListsEntriesCreateParams, RequestOptions)} with the raw response, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse create(String listUuid, RequestOptions options) { + return client.execute(createRequest(listUuid, ListsEntriesCreateParams.builder().build()), options, CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(String, ListsEntriesCreateParams, RequestOptions)} with the raw response, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createAsync(String listUuid, RequestOptions options) { + return client.executeAsync(createRequest(listUuid, ListsEntriesCreateParams.builder().build()), options, CREATE_RESPONSE); + } + + /** + * {@link #create(String, ListsEntriesCreateParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return the result + */ + public RawResponse create(String listUuid) { + return client.execute(createRequest(listUuid, ListsEntriesCreateParams.builder().build()), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(String, ListsEntriesCreateParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture> createAsync(String listUuid) { + return client.executeAsync(createRequest(listUuid, ListsEntriesCreateParams.builder().build()), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * {@link #delete(String, String, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param entryUuid the {@code entry_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse<@Nullable Void> delete(String listUuid, String entryUuid, RequestOptions options) { + return client.execute(deleteRequest(listUuid, entryUuid), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, String, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param entryUuid the {@code entry_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String listUuid, String entryUuid, RequestOptions options) { + return client.executeAsync(deleteRequest(listUuid, entryUuid), options, DELETE_RESPONSE); + } + + /** + * {@link #delete(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param entryUuid the {@code entry_uuid} + * @return the result + */ + public RawResponse<@Nullable Void> delete(String listUuid, String entryUuid) { + return client.execute(deleteRequest(listUuid, entryUuid), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param entryUuid the {@code entry_uuid} + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String listUuid, String entryUuid) { + return client.executeAsync(deleteRequest(listUuid, entryUuid), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * {@link #uploadFace(String, ListsEntriesUploadFaceParams, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse uploadFace(String listUuid, ListsEntriesUploadFaceParams params, RequestOptions options) { + return client.execute(uploadFaceRequest(listUuid, params), options, UPLOAD_FACE_RESPONSE); + } + + /** + * Asynchronous {@link #uploadFace(String, ListsEntriesUploadFaceParams, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> uploadFaceAsync(String listUuid, ListsEntriesUploadFaceParams params, RequestOptions options) { + return client.executeAsync(uploadFaceRequest(listUuid, params), options, UPLOAD_FACE_RESPONSE); + } + + /** + * {@link #uploadFace(String, ListsEntriesUploadFaceParams, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return the result + */ + public RawResponse uploadFace(String listUuid, ListsEntriesUploadFaceParams params) { + return client.execute(uploadFaceRequest(listUuid, params), RequestOptions.none(), UPLOAD_FACE_RESPONSE); + } + + /** + * Asynchronous {@link #uploadFace(String, ListsEntriesUploadFaceParams, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> uploadFaceAsync(String listUuid, ListsEntriesUploadFaceParams params) { + return client.executeAsync(uploadFaceRequest(listUuid, params), RequestOptions.none(), UPLOAD_FACE_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/ListsResource.java b/src/main/java/me/didit/resources/ListsResource.java new file mode 100644 index 0000000..25051ca --- /dev/null +++ b/src/main/java/me/didit/resources/ListsResource.java @@ -0,0 +1,709 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.Page; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.ComplianceList; +import me.didit.types.ListsCreateParams; +import me.didit.types.ListsListParams; +import me.didit.types.ListsUpdateParams; +import me.didit.types.ListsUpdateResponse; +import org.jspecify.annotations.Nullable; + +/** The {@code lists} resource: {@code client.lists()}. */ +public final class ListsResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + private final ListsEntriesResource entries; + + ListsResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + this.entries = new ListsEntriesResource(client); + } + + /** + * The {@code lists.entries} resource. + * + * @return the resource + */ + public ListsEntriesResource entries() { + return entries; + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * List lists + * + *

Returns the application's lists, newest first: the system lists Didit provisions (one blocklist per entry type, the High Risk Countries custom list, and any system allowlist) and the allowlists and custom lists you created. Filter with list_type and entry_type; deleted lists never appear. Results are paginated with limit and offset. A scoped API key needs workflows: read. See Lists for what each list type does. + * + *

{@code GET /v3/lists/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page list(ListsListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(ListsListParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(ListsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(ListsListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public Page list(ListsListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(ListsListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listAsync(ListsListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(ListsListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public Page list(RequestOptions options) { + return client.execute(listRequest(ListsListParams.builder().build()), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(ListsListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(ListsListParams.builder().build()), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(ListsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public Page list() { + return client.execute(listRequest(ListsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(ListsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> listAsync() { + return client.executeAsync(listRequest(ListsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Create a list + * + *

Creates an allowlist or a custom list in the application the API key belongs to. Blocklists cannot be created: Didit provisions one system blocklist per entry type, and you add to it with Create a list entry. entry_type cannot change after creation and decides how entry values are validated and matched. Allowlist entries mark matching items as trusted (for example, a face allowlist skips the duplicated-face action) and never override a blocklist match; reference any list from a workflow or transaction rule condition with value_type: "list". A scoped API key needs workflows: write. + * + *

{@code POST /v3/lists/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public ComplianceList create(ListsCreateParams params, RequestOptions options) { + return client.execute(createRequest(params), options, CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(ListsCreateParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createAsync(ListsCreateParams params, RequestOptions options) { + return client.executeAsync(createRequest(params), options, CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #create(ListsCreateParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public ComplianceList create(ListsCreateParams params) { + return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(ListsCreateParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture createAsync(ListsCreateParams params) { + return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Get a list + * + *

Returns one list of the application with its entry count and when its newest entry was added. A list of another application, or one that was deleted, returns 404. This returns the list only: read its values with List list entries. A scoped API key needs workflows: read. + * + *

{@code GET /v3/lists/{list_uuid}/} + * + * @param listUuid UUID of the list, as returned by List lists or Create a list. + * @param options per-request options that override the client options + * @return the response body + */ + public ComplianceList retrieve(String listUuid, RequestOptions options) { + return client.execute(retrieveRequest(listUuid), options, RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(String, RequestOptions)}. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync(String listUuid, RequestOptions options) { + return client.executeAsync(retrieveRequest(listUuid), options, RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieve(String, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return the result + */ + public ComplianceList retrieve(String listUuid) { + return client.execute(retrieveRequest(listUuid), RequestOptions.none(), RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(String, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync(String listUuid) { + return client.executeAsync(retrieveRequest(listUuid), RequestOptions.none(), RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Update a list + * + *

Renames a list or changes its description. Only name and description are read; any other field is ignored, so list_type and entry_type keep the values they were created with. System lists cannot be modified and return 400. The response contains only name and description: call Get a list for the full list. A scoped API key needs workflows: write. + * + *

{@code PATCH /v3/lists/{list_uuid}/} + * + * @param listUuid UUID of the list, as returned by List lists or Create a list. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public ListsUpdateResponse update(String listUuid, ListsUpdateParams params, RequestOptions options) { + return client.execute(updateRequest(listUuid, params), options, UPDATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #update(String, ListsUpdateParams, RequestOptions)}. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture updateAsync(String listUuid, ListsUpdateParams params, RequestOptions options) { + return client.executeAsync(updateRequest(listUuid, params), options, UPDATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #update(String, ListsUpdateParams, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return the result + */ + public ListsUpdateResponse update(String listUuid, ListsUpdateParams params) { + return client.execute(updateRequest(listUuid, params), RequestOptions.none(), UPDATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #update(String, ListsUpdateParams, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture updateAsync(String listUuid, ListsUpdateParams params) { + return client.executeAsync(updateRequest(listUuid, params), RequestOptions.none(), UPDATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #update(String, ListsUpdateParams, RequestOptions)}, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return the result + */ + public ListsUpdateResponse update(String listUuid, RequestOptions options) { + return client.execute(updateRequest(listUuid, ListsUpdateParams.builder().build()), options, UPDATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #update(String, ListsUpdateParams, RequestOptions)}, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture updateAsync(String listUuid, RequestOptions options) { + return client.executeAsync(updateRequest(listUuid, ListsUpdateParams.builder().build()), options, UPDATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #update(String, ListsUpdateParams, RequestOptions)}, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return the result + */ + public ListsUpdateResponse update(String listUuid) { + return client.execute(updateRequest(listUuid, ListsUpdateParams.builder().build()), RequestOptions.none(), UPDATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #update(String, ListsUpdateParams, RequestOptions)}, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture updateAsync(String listUuid) { + return client.executeAsync(updateRequest(listUuid, ListsUpdateParams.builder().build()), RequestOptions.none(), UPDATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Delete a list + * + *

Deletes an allowlist or custom list together with its entries. Deletion is irreversible: the list and its entries no longer appear in any read, and workflow and transaction rule conditions that reference the list evaluate it as an empty list from then on. Sessions and transactions already evaluated keep their results, and the name can be reused for a new list. System lists cannot be deleted and return 400; to empty one, delete its entries. A scoped API key needs workflows: write. + * + *

{@code DELETE /v3/lists/{list_uuid}/} + * + * @param listUuid UUID of the list, as returned by List lists or Create a list. + * @param options per-request options that override the client options + */ + public void delete(String listUuid, RequestOptions options) { + client.execute(deleteRequest(listUuid), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)}. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture deleteAsync(String listUuid, RequestOptions options) { + return client.executeAsync(deleteRequest(listUuid), options, DELETE_RESPONSE).thenAccept(response -> {}); + } + + /** + * Same as {@link #delete(String, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + */ + public void delete(String listUuid) { + client.execute(deleteRequest(listUuid), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)}, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture deleteAsync(String listUuid) { + return client.executeAsync(deleteRequest(listUuid), RequestOptions.none(), DELETE_RESPONSE).thenAccept(response -> {}); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(ComplianceList::fromJson)); + + private static ApiRequest listRequest(ListsListParams params) { + return ApiRequest.builder("GET", "/v3/lists/") + .params(params) + .queryFields("list_type", "entry_type", "ordering", "limit", "offset") + .build(); + } + + private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(ComplianceList::fromJson)); + + private static ApiRequest createRequest(ListsCreateParams params) { + return ApiRequest.builder("POST", "/v3/lists/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler RETRIEVE_RESPONSE = Responses.json(Conv.model(ComplianceList::fromJson)); + + private static ApiRequest retrieveRequest(String listUuid) { + return ApiRequest.builder("GET", "/v3/lists/{list_uuid}/") + .pathParam("list_uuid", listUuid) + .build(); + } + + private static final ResponseHandler UPDATE_RESPONSE = Responses.json(Conv.model(ListsUpdateResponse::fromJson)); + + private static ApiRequest updateRequest(String listUuid, ListsUpdateParams params) { + return ApiRequest.builder("PATCH", "/v3/lists/{list_uuid}/") + .pathParam("list_uuid", listUuid) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler<@Nullable Void> DELETE_RESPONSE = Responses.none(); + + private static ApiRequest deleteRequest(String listUuid) { + return ApiRequest.builder("DELETE", "/v3/lists/{list_uuid}/") + .pathParam("list_uuid", listUuid) + .build(); + } + + /** The {@code lists} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #list(ListsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(ListsListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(ListsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(ListsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE); + } + + /** + * {@link #list(ListsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse> list(ListsListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(ListsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(ListsListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(ListsListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse> list(RequestOptions options) { + return client.execute(listRequest(ListsListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(ListsListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(ListsListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * {@link #list(ListsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse> list() { + return client.execute(listRequest(ListsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(ListsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>> listAsync() { + return client.executeAsync(listRequest(ListsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #create(ListsCreateParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse create(ListsCreateParams params, RequestOptions options) { + return client.execute(createRequest(params), options, CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(ListsCreateParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createAsync(ListsCreateParams params, RequestOptions options) { + return client.executeAsync(createRequest(params), options, CREATE_RESPONSE); + } + + /** + * {@link #create(ListsCreateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse create(ListsCreateParams params) { + return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(ListsCreateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> createAsync(ListsCreateParams params) { + return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * {@link #retrieve(String, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse retrieve(String listUuid, RequestOptions options) { + return client.execute(retrieveRequest(listUuid), options, RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(String, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync(String listUuid, RequestOptions options) { + return client.executeAsync(retrieveRequest(listUuid), options, RETRIEVE_RESPONSE); + } + + /** + * {@link #retrieve(String, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return the result + */ + public RawResponse retrieve(String listUuid) { + return client.execute(retrieveRequest(listUuid), RequestOptions.none(), RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(String, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync(String listUuid) { + return client.executeAsync(retrieveRequest(listUuid), RequestOptions.none(), RETRIEVE_RESPONSE); + } + + /** + * {@link #update(String, ListsUpdateParams, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse update(String listUuid, ListsUpdateParams params, RequestOptions options) { + return client.execute(updateRequest(listUuid, params), options, UPDATE_RESPONSE); + } + + /** + * Asynchronous {@link #update(String, ListsUpdateParams, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> updateAsync(String listUuid, ListsUpdateParams params, RequestOptions options) { + return client.executeAsync(updateRequest(listUuid, params), options, UPDATE_RESPONSE); + } + + /** + * {@link #update(String, ListsUpdateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return the result + */ + public RawResponse update(String listUuid, ListsUpdateParams params) { + return client.execute(updateRequest(listUuid, params), RequestOptions.none(), UPDATE_RESPONSE); + } + + /** + * Asynchronous {@link #update(String, ListsUpdateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> updateAsync(String listUuid, ListsUpdateParams params) { + return client.executeAsync(updateRequest(listUuid, params), RequestOptions.none(), UPDATE_RESPONSE); + } + + /** + * {@link #update(String, ListsUpdateParams, RequestOptions)} with the raw response, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse update(String listUuid, RequestOptions options) { + return client.execute(updateRequest(listUuid, ListsUpdateParams.builder().build()), options, UPDATE_RESPONSE); + } + + /** + * Asynchronous {@link #update(String, ListsUpdateParams, RequestOptions)} with the raw response, without parameters. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> updateAsync(String listUuid, RequestOptions options) { + return client.executeAsync(updateRequest(listUuid, ListsUpdateParams.builder().build()), options, UPDATE_RESPONSE); + } + + /** + * {@link #update(String, ListsUpdateParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return the result + */ + public RawResponse update(String listUuid) { + return client.execute(updateRequest(listUuid, ListsUpdateParams.builder().build()), RequestOptions.none(), UPDATE_RESPONSE); + } + + /** + * Asynchronous {@link #update(String, ListsUpdateParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture> updateAsync(String listUuid) { + return client.executeAsync(updateRequest(listUuid, ListsUpdateParams.builder().build()), RequestOptions.none(), UPDATE_RESPONSE); + } + + /** + * {@link #delete(String, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse<@Nullable Void> delete(String listUuid, RequestOptions options) { + return client.execute(deleteRequest(listUuid), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)} with the raw response. + * + * @param listUuid the {@code list_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String listUuid, RequestOptions options) { + return client.executeAsync(deleteRequest(listUuid), options, DELETE_RESPONSE); + } + + /** + * {@link #delete(String, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return the result + */ + public RawResponse<@Nullable Void> delete(String listUuid) { + return client.execute(deleteRequest(listUuid), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)} with the raw response, with the client's options. + * + * @param listUuid the {@code list_uuid} + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String listUuid) { + return client.executeAsync(deleteRequest(listUuid), RequestOptions.none(), DELETE_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/MonitoringEventsResource.java b/src/main/java/me/didit/resources/MonitoringEventsResource.java new file mode 100644 index 0000000..7e1757b --- /dev/null +++ b/src/main/java/me/didit/resources/MonitoringEventsResource.java @@ -0,0 +1,1073 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.Page; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.MonitoringEvent; +import me.didit.types.MonitoringEventCreateRequest; +import me.didit.types.MonitoringEventDetail; +import me.didit.types.MonitoringEventIngestResult; +import me.didit.types.MonitoringEventsCreateBatchParams; +import me.didit.types.MonitoringEventsCreateBatchResponse; +import me.didit.types.MonitoringEventsCreateSdkTokenParams; +import me.didit.types.MonitoringEventsCreateSdkTokenResponse; +import me.didit.types.MonitoringEventsListParams; +import me.didit.types.MonitoringEventsRetrieveEnrichmentMetricsParams; +import me.didit.types.MonitoringEventsRetrieveEnrichmentMetricsResponse; +import me.didit.types.MonitoringEventsRetrieveSubjectTimelineParams; +import org.jspecify.annotations.Nullable; + +/** The {@code monitoring_events} resource: {@code client.monitoringEvents()}. */ +public final class MonitoringEventsResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + MonitoringEventsResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * List monitoring events + * + *

List the monitoring events stored for your application, newest occurred_at first, with optional filters that combine with AND. Multi-value filters (event_category, action_type, status, retention_class) repeat the parameter and match any of the values; the other filters are exact matches, and unknown values simply return no events. Each result is a summary: call GET /v3/events/{event_uuid}/ for the payload, device context, IP enrichment and rule evidence, and prefer GET /v3/events/subjects/{vendor_data}/ for one subject's history. Reads work whether or not event ingestion is switched on. A scoped API key needs transactions: read. See Event ingestion. + * + *

{@code GET /v3/events/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page list(MonitoringEventsListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(MonitoringEventsListParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(MonitoringEventsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(MonitoringEventsListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public Page list(MonitoringEventsListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(MonitoringEventsListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listAsync(MonitoringEventsListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(MonitoringEventsListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public Page list(RequestOptions options) { + return client.execute(listRequest(MonitoringEventsListParams.builder().build()), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(MonitoringEventsListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(MonitoringEventsListParams.builder().build()), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(MonitoringEventsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public Page list() { + return client.execute(listRequest(MonitoringEventsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(MonitoringEventsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> listAsync() { + return client.executeAsync(listRequest(MonitoringEventsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Create monitoring event + * + *

Send one non-monetary event (login, session, gameplay, bonus, affiliate, payment, dispute, security, responsible gambling or custom) from your backend. The event is validated against its category contract, stored, IP-enriched once per unique public address, and evaluated by your real-time event rules before the response returns; cross-subject and long-window rules run asynchronously (correlation_pending: true), and in a sandbox application no rules run. event_id is the idempotency key: the same event_id with the same event_category, action_type, occurred_at, subject.vendor_data and payload returns 200 with idempotent_replay: true and the stored outcome (other envelope fields are not compared), while different values return 409. Event ingestion is opt-in per application: until it is switched on this endpoint returns 403 with code: event_monitoring_disabled and stores nothing. A scoped API key needs transactions: write. See Event ingestion and Rules on events; to send events from a browser or app, use SDK event submission. + * + *

{@code POST /v3/events/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public MonitoringEventIngestResult create(MonitoringEventCreateRequest params, RequestOptions options) { + return client.execute(createRequest(params), options, CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(MonitoringEventCreateRequest, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createAsync(MonitoringEventCreateRequest params, RequestOptions options) { + return client.executeAsync(createRequest(params), options, CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #create(MonitoringEventCreateRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public MonitoringEventIngestResult create(MonitoringEventCreateRequest params) { + return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(MonitoringEventCreateRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture createAsync(MonitoringEventCreateRequest params) { + return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Create monitoring events in batch + * + *

Submit 1 to 500 events in one request, each in the same envelope as POST /v3/events/. Every item is validated, stored and evaluated on its own and gets its own outcome (accepted, replayed or rejected), so one malformed event never rolls back the others: the response is 201 when no item was rejected and 207 when at least one was, and both mean the request was processed. Send {"events": [...]}; a bare JSON array of events is also accepted. Event ingestion must be switched on for the application, otherwise the whole request returns 403 with code: event_monitoring_disabled and nothing is stored. A scoped API key needs transactions: write. See Batches. + * + *

{@code POST /v3/events/batch/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public MonitoringEventsCreateBatchResponse createBatch(MonitoringEventsCreateBatchParams params, RequestOptions options) { + return client.execute(createBatchRequest(params), options, CREATE_BATCH_RESPONSE).data(); + } + + /** + * Asynchronous {@link #createBatch(MonitoringEventsCreateBatchParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createBatchAsync(MonitoringEventsCreateBatchParams params, RequestOptions options) { + return client.executeAsync(createBatchRequest(params), options, CREATE_BATCH_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #createBatch(MonitoringEventsCreateBatchParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public MonitoringEventsCreateBatchResponse createBatch(MonitoringEventsCreateBatchParams params) { + return client.execute(createBatchRequest(params), RequestOptions.none(), CREATE_BATCH_RESPONSE).data(); + } + + /** + * Asynchronous {@link #createBatch(MonitoringEventsCreateBatchParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture createBatchAsync(MonitoringEventsCreateBatchParams params) { + return client.executeAsync(createBatchRequest(params), RequestOptions.none(), CREATE_BATCH_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Mint monitoring SDK token + * + *

Mint a short-lived device credential bound to one subject, so your web or mobile app can send that user's events straight to Didit without an API key. The app sends the returned monitoring_token in the X-Monitoring-Token header to the device lane (POST /v1/events/ and POST /v1/events/batch/), where the subject is always overwritten with this vendor_data, events outside allowed_event_categories are rejected, each submitted event spends one use, and a request whose Origin does not equal bind_to_origin is refused. Call it from your backend only; minting creates the credential and nothing else (no user is created). Event ingestion must be switched on for the application, otherwise this endpoint returns 403 with code: event_monitoring_disabled instead of minting a credential that could never write. A scoped API key needs transactions: write. See SDK event submission. + * + *

{@code POST /v3/events/sdk-token/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public MonitoringEventsCreateSdkTokenResponse createSdkToken(MonitoringEventsCreateSdkTokenParams params, RequestOptions options) { + return client.execute(createSdkTokenRequest(params), options, CREATE_SDK_TOKEN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #createSdkToken(MonitoringEventsCreateSdkTokenParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createSdkTokenAsync(MonitoringEventsCreateSdkTokenParams params, RequestOptions options) { + return client.executeAsync(createSdkTokenRequest(params), options, CREATE_SDK_TOKEN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #createSdkToken(MonitoringEventsCreateSdkTokenParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public MonitoringEventsCreateSdkTokenResponse createSdkToken(MonitoringEventsCreateSdkTokenParams params) { + return client.execute(createSdkTokenRequest(params), RequestOptions.none(), CREATE_SDK_TOKEN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #createSdkToken(MonitoringEventsCreateSdkTokenParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture createSdkTokenAsync(MonitoringEventsCreateSdkTokenParams params) { + return client.executeAsync(createSdkTokenRequest(params), RequestOptions.none(), CREATE_SDK_TOKEN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Get monitoring event + * + *

Retrieve one monitoring event with everything the list returns plus the payload, custom values, device context, field provenance (field_sources), the promoted domain fields, expires_at, every matching rule run with its evidence, the IP enrichment and the tags rules attached. While correlation_pending is true an asynchronous rule still has to run; read the event again a few minutes later for the final status, score and rule_runs. Reads work whether or not event ingestion is switched on. A scoped API key needs transactions: read. See Evidence and IP enrichment. + * + *

{@code GET /v3/events/{event_uuid}/} + * + * @param eventUuid Didit identifier of the event (`event_uuid` from the create, batch, list or timeline response), not your own `event_id`. + * @param options per-request options that override the client options + * @return the response body + */ + public MonitoringEventDetail retrieve(String eventUuid, RequestOptions options) { + return client.execute(retrieveRequest(eventUuid), options, RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(String, RequestOptions)}. + * + * @param eventUuid the {@code event_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync(String eventUuid, RequestOptions options) { + return client.executeAsync(retrieveRequest(eventUuid), options, RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieve(String, RequestOptions)}, with the client's options. + * + * @param eventUuid the {@code event_uuid} + * @return the result + */ + public MonitoringEventDetail retrieve(String eventUuid) { + return client.execute(retrieveRequest(eventUuid), RequestOptions.none(), RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(String, RequestOptions)}, with the client's options. + * + * @param eventUuid the {@code event_uuid} + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync(String eventUuid) { + return client.executeAsync(retrieveRequest(eventUuid), RequestOptions.none(), RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Get subject event timeline + * + *

List every monitoring event of one subject, newest occurred_at first. A vendor_data is unique only within an entity type, so the same id can name an individual and a company: entity_type picks which timeline you get, and it defaults to individual, so a company's events need entity_type=company. The filters of GET /v3/events/ apply on top, and a vendor_data with no events returns an empty page rather than 404. A scoped API key needs transactions: read. See Reading events back. + * + *

{@code GET /v3/events/subjects/{vendor_data}/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param vendorData Your id for the subject (`subject.vendor_data` of its events). URL-encode it; a value containing `/` cannot be addressed by this endpoint. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page retrieveSubjectTimeline(String vendorData, MonitoringEventsRetrieveSubjectTimelineParams params, RequestOptions options) { + return client.execute(retrieveSubjectTimelineRequest(vendorData, params), options, RETRIEVE_SUBJECT_TIMELINE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)}. + * + * @param vendorData the {@code vendor_data} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveSubjectTimelineAsync(String vendorData, MonitoringEventsRetrieveSubjectTimelineParams params, RequestOptions options) { + return client.executeAsync(retrieveSubjectTimelineRequest(vendorData, params), options, RETRIEVE_SUBJECT_TIMELINE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)}, with the client's options. + * + * @param vendorData the {@code vendor_data} + * @param params the request parameters + * @return the result + */ + public Page retrieveSubjectTimeline(String vendorData, MonitoringEventsRetrieveSubjectTimelineParams params) { + return client.execute(retrieveSubjectTimelineRequest(vendorData, params), RequestOptions.none(), RETRIEVE_SUBJECT_TIMELINE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)}, with the client's options. + * + * @param vendorData the {@code vendor_data} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> retrieveSubjectTimelineAsync(String vendorData, MonitoringEventsRetrieveSubjectTimelineParams params) { + return client.executeAsync(retrieveSubjectTimelineRequest(vendorData, params), RequestOptions.none(), RETRIEVE_SUBJECT_TIMELINE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)}, without parameters. + * + * @param vendorData the {@code vendor_data} + * @param options per-request options + * @return the result + */ + public Page retrieveSubjectTimeline(String vendorData, RequestOptions options) { + return client.execute(retrieveSubjectTimelineRequest(vendorData, MonitoringEventsRetrieveSubjectTimelineParams.builder().build()), options, RETRIEVE_SUBJECT_TIMELINE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)}, without parameters. + * + * @param vendorData the {@code vendor_data} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveSubjectTimelineAsync(String vendorData, RequestOptions options) { + return client.executeAsync(retrieveSubjectTimelineRequest(vendorData, MonitoringEventsRetrieveSubjectTimelineParams.builder().build()), options, RETRIEVE_SUBJECT_TIMELINE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)}, without parameters, with the client's options. + * + * @param vendorData the {@code vendor_data} + * @return the result + */ + public Page retrieveSubjectTimeline(String vendorData) { + return client.execute(retrieveSubjectTimelineRequest(vendorData, MonitoringEventsRetrieveSubjectTimelineParams.builder().build()), RequestOptions.none(), RETRIEVE_SUBJECT_TIMELINE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)}, without parameters, with the client's options. + * + * @param vendorData the {@code vendor_data} + * @return a future completed with the result + */ + public CompletableFuture> retrieveSubjectTimelineAsync(String vendorData) { + return client.executeAsync(retrieveSubjectTimelineRequest(vendorData, MonitoringEventsRetrieveSubjectTimelineParams.builder().build()), RequestOptions.none(), RETRIEVE_SUBJECT_TIMELINE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Get event enrichment metrics + * + *

Return the event ingestion and IP-enrichment counters of your application for a period, summed over every lane (server, batch and device), so you can watch event volume and confirm that enrichment is resolved once per unique address rather than once per event. The period covers today plus the previous days days, counted by UTC calendar date. The three ratios are null when their denominator is zero. A scoped API key needs transactions: read. See IP enrichment. + * + *

{@code GET /v3/events/enrichment-metrics/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public MonitoringEventsRetrieveEnrichmentMetricsResponse retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams params, RequestOptions options) { + return client.execute(retrieveEnrichmentMetricsRequest(params), options, RETRIEVE_ENRICHMENT_METRICS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveEnrichmentMetricsAsync(MonitoringEventsRetrieveEnrichmentMetricsParams params, RequestOptions options) { + return client.executeAsync(retrieveEnrichmentMetricsRequest(params), options, RETRIEVE_ENRICHMENT_METRICS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public MonitoringEventsRetrieveEnrichmentMetricsResponse retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams params) { + return client.execute(retrieveEnrichmentMetricsRequest(params), RequestOptions.none(), RETRIEVE_ENRICHMENT_METRICS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture retrieveEnrichmentMetricsAsync(MonitoringEventsRetrieveEnrichmentMetricsParams params) { + return client.executeAsync(retrieveEnrichmentMetricsRequest(params), RequestOptions.none(), RETRIEVE_ENRICHMENT_METRICS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public MonitoringEventsRetrieveEnrichmentMetricsResponse retrieveEnrichmentMetrics(RequestOptions options) { + return client.execute(retrieveEnrichmentMetricsRequest(MonitoringEventsRetrieveEnrichmentMetricsParams.builder().build()), options, RETRIEVE_ENRICHMENT_METRICS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveEnrichmentMetricsAsync(RequestOptions options) { + return client.executeAsync(retrieveEnrichmentMetricsRequest(MonitoringEventsRetrieveEnrichmentMetricsParams.builder().build()), options, RETRIEVE_ENRICHMENT_METRICS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public MonitoringEventsRetrieveEnrichmentMetricsResponse retrieveEnrichmentMetrics() { + return client.execute(retrieveEnrichmentMetricsRequest(MonitoringEventsRetrieveEnrichmentMetricsParams.builder().build()), RequestOptions.none(), RETRIEVE_ENRICHMENT_METRICS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture retrieveEnrichmentMetricsAsync() { + return client.executeAsync(retrieveEnrichmentMetricsRequest(MonitoringEventsRetrieveEnrichmentMetricsParams.builder().build()), RequestOptions.none(), RETRIEVE_ENRICHMENT_METRICS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Delete monitoring event + * + *

Permanently delete one monitoring event together with its rule runs, notes and tag links, for example an event sent by mistake. After the 204 the event is gone from every read (retrieve returns 404, and it no longer appears in the list or the subject timeline), the deletion is irreversible, and its event_id is free to be used for a new event. Not affected: the user or company the event resolved to, cases a rule opened, list entries a rule added, and the scores and statuses already applied to other events. To erase everything about a person or a company, delete the user or the company instead, which also deletes their events. A scoped API key needs transactions: write. + * + *

{@code DELETE /v3/events/{event_uuid}/delete/} + * + * @param eventUuid Didit identifier of the event (`event_uuid` from the create, batch, list or timeline response), not your own `event_id`. + * @param options per-request options that override the client options + */ + public void delete(String eventUuid, RequestOptions options) { + client.execute(deleteRequest(eventUuid), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)}. + * + * @param eventUuid the {@code event_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture deleteAsync(String eventUuid, RequestOptions options) { + return client.executeAsync(deleteRequest(eventUuid), options, DELETE_RESPONSE).thenAccept(response -> {}); + } + + /** + * Same as {@link #delete(String, RequestOptions)}, with the client's options. + * + * @param eventUuid the {@code event_uuid} + */ + public void delete(String eventUuid) { + client.execute(deleteRequest(eventUuid), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)}, with the client's options. + * + * @param eventUuid the {@code event_uuid} + * @return a future completed with the result + */ + public CompletableFuture deleteAsync(String eventUuid) { + return client.executeAsync(deleteRequest(eventUuid), RequestOptions.none(), DELETE_RESPONSE).thenAccept(response -> {}); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(MonitoringEvent::fromJson)); + + private static ApiRequest listRequest(MonitoringEventsListParams params) { + return ApiRequest.builder("GET", "/v3/events/") + .params(params) + .queryFields("limit", "offset", "event_category", "action_type", "status", "retention_class", "subject_vendor_data", "subject_entity_type", "business_vendor_data", "session_id", "device_fingerprint", "ip_address", "game_id", "round_id", "affiliate_code", "payment_reference_id", "counterparty_vendor_data", "matched_only", "occurred_from", "occurred_to", "search") + .build(); + } + + private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(MonitoringEventIngestResult::fromJson)); + + private static ApiRequest createRequest(MonitoringEventCreateRequest params) { + return ApiRequest.builder("POST", "/v3/events/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler CREATE_BATCH_RESPONSE = Responses.byStatus(MonitoringEventsCreateBatchResponse::fromResponse); + + private static ApiRequest createBatchRequest(MonitoringEventsCreateBatchParams params) { + return ApiRequest.builder("POST", "/v3/events/batch/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler CREATE_SDK_TOKEN_RESPONSE = Responses.json(Conv.model(MonitoringEventsCreateSdkTokenResponse::fromJson)); + + private static ApiRequest createSdkTokenRequest(MonitoringEventsCreateSdkTokenParams params) { + return ApiRequest.builder("POST", "/v3/events/sdk-token/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler RETRIEVE_RESPONSE = Responses.json(Conv.model(MonitoringEventDetail::fromJson)); + + private static ApiRequest retrieveRequest(String eventUuid) { + return ApiRequest.builder("GET", "/v3/events/{event_uuid}/") + .pathParam("event_uuid", eventUuid) + .build(); + } + + private static final ResponseHandler> RETRIEVE_SUBJECT_TIMELINE_RESPONSE = Responses.page(Conv.model(MonitoringEvent::fromJson)); + + private static ApiRequest retrieveSubjectTimelineRequest(String vendorData, MonitoringEventsRetrieveSubjectTimelineParams params) { + return ApiRequest.builder("GET", "/v3/events/subjects/{vendor_data}/") + .pathParam("vendor_data", vendorData) + .params(params) + .queryFields("entity_type", "limit", "offset", "event_category", "action_type", "status", "retention_class", "business_vendor_data", "session_id", "device_fingerprint", "ip_address", "game_id", "round_id", "affiliate_code", "payment_reference_id", "counterparty_vendor_data", "matched_only", "occurred_from", "occurred_to", "search") + .build(); + } + + private static final ResponseHandler RETRIEVE_ENRICHMENT_METRICS_RESPONSE = Responses.json(Conv.model(MonitoringEventsRetrieveEnrichmentMetricsResponse::fromJson)); + + private static ApiRequest retrieveEnrichmentMetricsRequest(MonitoringEventsRetrieveEnrichmentMetricsParams params) { + return ApiRequest.builder("GET", "/v3/events/enrichment-metrics/") + .params(params) + .queryFields("days") + .build(); + } + + private static final ResponseHandler<@Nullable Void> DELETE_RESPONSE = Responses.none(); + + private static ApiRequest deleteRequest(String eventUuid) { + return ApiRequest.builder("DELETE", "/v3/events/{event_uuid}/delete/") + .pathParam("event_uuid", eventUuid) + .build(); + } + + /** The {@code monitoring_events} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #list(MonitoringEventsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(MonitoringEventsListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(MonitoringEventsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(MonitoringEventsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE); + } + + /** + * {@link #list(MonitoringEventsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse> list(MonitoringEventsListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(MonitoringEventsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(MonitoringEventsListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(MonitoringEventsListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse> list(RequestOptions options) { + return client.execute(listRequest(MonitoringEventsListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(MonitoringEventsListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(MonitoringEventsListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * {@link #list(MonitoringEventsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse> list() { + return client.execute(listRequest(MonitoringEventsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(MonitoringEventsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>> listAsync() { + return client.executeAsync(listRequest(MonitoringEventsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #create(MonitoringEventCreateRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse create(MonitoringEventCreateRequest params, RequestOptions options) { + return client.execute(createRequest(params), options, CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(MonitoringEventCreateRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createAsync(MonitoringEventCreateRequest params, RequestOptions options) { + return client.executeAsync(createRequest(params), options, CREATE_RESPONSE); + } + + /** + * {@link #create(MonitoringEventCreateRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse create(MonitoringEventCreateRequest params) { + return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(MonitoringEventCreateRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> createAsync(MonitoringEventCreateRequest params) { + return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * {@link #createBatch(MonitoringEventsCreateBatchParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse createBatch(MonitoringEventsCreateBatchParams params, RequestOptions options) { + return client.execute(createBatchRequest(params), options, CREATE_BATCH_RESPONSE); + } + + /** + * Asynchronous {@link #createBatch(MonitoringEventsCreateBatchParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createBatchAsync(MonitoringEventsCreateBatchParams params, RequestOptions options) { + return client.executeAsync(createBatchRequest(params), options, CREATE_BATCH_RESPONSE); + } + + /** + * {@link #createBatch(MonitoringEventsCreateBatchParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse createBatch(MonitoringEventsCreateBatchParams params) { + return client.execute(createBatchRequest(params), RequestOptions.none(), CREATE_BATCH_RESPONSE); + } + + /** + * Asynchronous {@link #createBatch(MonitoringEventsCreateBatchParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> createBatchAsync(MonitoringEventsCreateBatchParams params) { + return client.executeAsync(createBatchRequest(params), RequestOptions.none(), CREATE_BATCH_RESPONSE); + } + + /** + * {@link #createSdkToken(MonitoringEventsCreateSdkTokenParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse createSdkToken(MonitoringEventsCreateSdkTokenParams params, RequestOptions options) { + return client.execute(createSdkTokenRequest(params), options, CREATE_SDK_TOKEN_RESPONSE); + } + + /** + * Asynchronous {@link #createSdkToken(MonitoringEventsCreateSdkTokenParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createSdkTokenAsync(MonitoringEventsCreateSdkTokenParams params, RequestOptions options) { + return client.executeAsync(createSdkTokenRequest(params), options, CREATE_SDK_TOKEN_RESPONSE); + } + + /** + * {@link #createSdkToken(MonitoringEventsCreateSdkTokenParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse createSdkToken(MonitoringEventsCreateSdkTokenParams params) { + return client.execute(createSdkTokenRequest(params), RequestOptions.none(), CREATE_SDK_TOKEN_RESPONSE); + } + + /** + * Asynchronous {@link #createSdkToken(MonitoringEventsCreateSdkTokenParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> createSdkTokenAsync(MonitoringEventsCreateSdkTokenParams params) { + return client.executeAsync(createSdkTokenRequest(params), RequestOptions.none(), CREATE_SDK_TOKEN_RESPONSE); + } + + /** + * {@link #retrieve(String, RequestOptions)} with the raw response. + * + * @param eventUuid the {@code event_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse retrieve(String eventUuid, RequestOptions options) { + return client.execute(retrieveRequest(eventUuid), options, RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(String, RequestOptions)} with the raw response. + * + * @param eventUuid the {@code event_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync(String eventUuid, RequestOptions options) { + return client.executeAsync(retrieveRequest(eventUuid), options, RETRIEVE_RESPONSE); + } + + /** + * {@link #retrieve(String, RequestOptions)} with the raw response, with the client's options. + * + * @param eventUuid the {@code event_uuid} + * @return the result + */ + public RawResponse retrieve(String eventUuid) { + return client.execute(retrieveRequest(eventUuid), RequestOptions.none(), RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(String, RequestOptions)} with the raw response, with the client's options. + * + * @param eventUuid the {@code event_uuid} + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync(String eventUuid) { + return client.executeAsync(retrieveRequest(eventUuid), RequestOptions.none(), RETRIEVE_RESPONSE); + } + + /** + * {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)} with the raw response. + * + * @param vendorData the {@code vendor_data} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> retrieveSubjectTimeline(String vendorData, MonitoringEventsRetrieveSubjectTimelineParams params, RequestOptions options) { + return client.execute(retrieveSubjectTimelineRequest(vendorData, params), options, RETRIEVE_SUBJECT_TIMELINE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)} with the raw response. + * + * @param vendorData the {@code vendor_data} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> retrieveSubjectTimelineAsync(String vendorData, MonitoringEventsRetrieveSubjectTimelineParams params, RequestOptions options) { + return client.executeAsync(retrieveSubjectTimelineRequest(vendorData, params), options, RETRIEVE_SUBJECT_TIMELINE_RESPONSE); + } + + /** + * {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)} with the raw response, with the client's options. + * + * @param vendorData the {@code vendor_data} + * @param params the request parameters + * @return the result + */ + public RawResponse> retrieveSubjectTimeline(String vendorData, MonitoringEventsRetrieveSubjectTimelineParams params) { + return client.execute(retrieveSubjectTimelineRequest(vendorData, params), RequestOptions.none(), RETRIEVE_SUBJECT_TIMELINE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)} with the raw response, with the client's options. + * + * @param vendorData the {@code vendor_data} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> retrieveSubjectTimelineAsync(String vendorData, MonitoringEventsRetrieveSubjectTimelineParams params) { + return client.executeAsync(retrieveSubjectTimelineRequest(vendorData, params), RequestOptions.none(), RETRIEVE_SUBJECT_TIMELINE_RESPONSE); + } + + /** + * {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)} with the raw response, without parameters. + * + * @param vendorData the {@code vendor_data} + * @param options per-request options + * @return the result + */ + public RawResponse> retrieveSubjectTimeline(String vendorData, RequestOptions options) { + return client.execute(retrieveSubjectTimelineRequest(vendorData, MonitoringEventsRetrieveSubjectTimelineParams.builder().build()), options, RETRIEVE_SUBJECT_TIMELINE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)} with the raw response, without parameters. + * + * @param vendorData the {@code vendor_data} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> retrieveSubjectTimelineAsync(String vendorData, RequestOptions options) { + return client.executeAsync(retrieveSubjectTimelineRequest(vendorData, MonitoringEventsRetrieveSubjectTimelineParams.builder().build()), options, RETRIEVE_SUBJECT_TIMELINE_RESPONSE); + } + + /** + * {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param vendorData the {@code vendor_data} + * @return the result + */ + public RawResponse> retrieveSubjectTimeline(String vendorData) { + return client.execute(retrieveSubjectTimelineRequest(vendorData, MonitoringEventsRetrieveSubjectTimelineParams.builder().build()), RequestOptions.none(), RETRIEVE_SUBJECT_TIMELINE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSubjectTimeline(String, MonitoringEventsRetrieveSubjectTimelineParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param vendorData the {@code vendor_data} + * @return a future completed with the result + */ + public CompletableFuture>> retrieveSubjectTimelineAsync(String vendorData) { + return client.executeAsync(retrieveSubjectTimelineRequest(vendorData, MonitoringEventsRetrieveSubjectTimelineParams.builder().build()), RequestOptions.none(), RETRIEVE_SUBJECT_TIMELINE_RESPONSE); + } + + /** + * {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams params, RequestOptions options) { + return client.execute(retrieveEnrichmentMetricsRequest(params), options, RETRIEVE_ENRICHMENT_METRICS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveEnrichmentMetricsAsync(MonitoringEventsRetrieveEnrichmentMetricsParams params, RequestOptions options) { + return client.executeAsync(retrieveEnrichmentMetricsRequest(params), options, RETRIEVE_ENRICHMENT_METRICS_RESPONSE); + } + + /** + * {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams params) { + return client.execute(retrieveEnrichmentMetricsRequest(params), RequestOptions.none(), RETRIEVE_ENRICHMENT_METRICS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> retrieveEnrichmentMetricsAsync(MonitoringEventsRetrieveEnrichmentMetricsParams params) { + return client.executeAsync(retrieveEnrichmentMetricsRequest(params), RequestOptions.none(), RETRIEVE_ENRICHMENT_METRICS_RESPONSE); + } + + /** + * {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse retrieveEnrichmentMetrics(RequestOptions options) { + return client.execute(retrieveEnrichmentMetricsRequest(MonitoringEventsRetrieveEnrichmentMetricsParams.builder().build()), options, RETRIEVE_ENRICHMENT_METRICS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveEnrichmentMetricsAsync(RequestOptions options) { + return client.executeAsync(retrieveEnrichmentMetricsRequest(MonitoringEventsRetrieveEnrichmentMetricsParams.builder().build()), options, RETRIEVE_ENRICHMENT_METRICS_RESPONSE); + } + + /** + * {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse retrieveEnrichmentMetrics() { + return client.execute(retrieveEnrichmentMetricsRequest(MonitoringEventsRetrieveEnrichmentMetricsParams.builder().build()), RequestOptions.none(), RETRIEVE_ENRICHMENT_METRICS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveEnrichmentMetrics(MonitoringEventsRetrieveEnrichmentMetricsParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> retrieveEnrichmentMetricsAsync() { + return client.executeAsync(retrieveEnrichmentMetricsRequest(MonitoringEventsRetrieveEnrichmentMetricsParams.builder().build()), RequestOptions.none(), RETRIEVE_ENRICHMENT_METRICS_RESPONSE); + } + + /** + * {@link #delete(String, RequestOptions)} with the raw response. + * + * @param eventUuid the {@code event_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse<@Nullable Void> delete(String eventUuid, RequestOptions options) { + return client.execute(deleteRequest(eventUuid), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)} with the raw response. + * + * @param eventUuid the {@code event_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String eventUuid, RequestOptions options) { + return client.executeAsync(deleteRequest(eventUuid), options, DELETE_RESPONSE); + } + + /** + * {@link #delete(String, RequestOptions)} with the raw response, with the client's options. + * + * @param eventUuid the {@code event_uuid} + * @return the result + */ + public RawResponse<@Nullable Void> delete(String eventUuid) { + return client.execute(deleteRequest(eventUuid), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)} with the raw response, with the client's options. + * + * @param eventUuid the {@code event_uuid} + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String eventUuid) { + return client.executeAsync(deleteRequest(eventUuid), RequestOptions.none(), DELETE_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/NetworksResource.java b/src/main/java/me/didit/resources/NetworksResource.java index 6908c42..8aa9dde 100644 --- a/src/main/java/me/didit/resources/NetworksResource.java +++ b/src/main/java/me/didit/resources/NetworksResource.java @@ -11,14 +11,16 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; -import me.didit.types.NetworkDetail; +import me.didit.types.NetworkDetailResponse; import me.didit.types.NetworkGraph; import me.didit.types.NetworkListItem; import me.didit.types.NetworkMap; +import me.didit.types.NetworkTagRequest; import me.didit.types.NetworksListMembersResponse; import me.didit.types.NetworksListParams; import me.didit.types.NetworksListSignalsResponse; import me.didit.types.NetworksRetrieveGraphParams; +import me.didit.types.NetworksRetrieveTimelineParams; import me.didit.types.NetworksRetrieveTimelineResponse; /** The {@code networks} resource: {@code client.networks()}. */ @@ -54,7 +56,7 @@ public WithRawResponse withRawResponse() { /** * List networks * - *

List fraud networks detected within this application's organization, newest activity first by default. By default dismissed networks are excluded; pass status to include them. Sandbox applications never have networks - clustering excludes sandbox data entirely. + *

List fraud networks detected within this application's organization, highest risk first by default. By default dismissed networks are excluded; pass status to include them. Sandbox applications never have networks: clustering excludes sandbox data entirely. Every response carries the organization's sharing_mode and whether cross-organization signals apply to it (cross_org_available, cross_org_unavailable_reason). kpis aggregate the networks that match your filters and counts aggregate every network the application can see; pass summary=false to skip both when you only need the page. Requires a full-access API key, or list:networks for a console member. * *

{@code GET /v3/organization/{organization_id}/application/{application_id}/networks/} *

Application scoped: the organization and application ids come from the client options. @@ -140,7 +142,7 @@ public CompletableFuture> listAsync() { /** * Get network * - *

Retrieve a single network's full detail, including stats, fraud tags, and risk signals. + *

Retrieve a single network's full detail: stats, fraud tags and risk signals, plus the organization's sharing_mode and cross-organization availability, your application's tags on the network, member outcomes and pattern scores (patterns). Requires a full-access API key, or read:networks for a console member. * *

{@code GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/} *

Application scoped: the organization and application ids come from the client options. @@ -149,7 +151,7 @@ public CompletableFuture> listAsync() { * @param options per-request options that override the client options * @return the response body */ - public NetworkDetail retrieve(String networkUuid, RequestOptions options) { + public NetworkDetailResponse retrieve(String networkUuid, RequestOptions options) { return client.execute(retrieveRequest(networkUuid), options, RETRIEVE_RESPONSE).data(); } @@ -160,7 +162,7 @@ public NetworkDetail retrieve(String networkUuid, RequestOptions options) { * @param options per-request options * @return a future completed with the result */ - public CompletableFuture retrieveAsync(String networkUuid, RequestOptions options) { + public CompletableFuture retrieveAsync(String networkUuid, RequestOptions options) { return client.executeAsync(retrieveRequest(networkUuid), options, RETRIEVE_RESPONSE).thenApply(RawResponse::data); } @@ -170,7 +172,7 @@ public CompletableFuture retrieveAsync(String networkUuid, Reques * @param networkUuid the {@code network_uuid} * @return the result */ - public NetworkDetail retrieve(String networkUuid) { + public NetworkDetailResponse retrieve(String networkUuid) { return client.execute(retrieveRequest(networkUuid), RequestOptions.none(), RETRIEVE_RESPONSE).data(); } @@ -180,7 +182,7 @@ public NetworkDetail retrieve(String networkUuid) { * @param networkUuid the {@code network_uuid} * @return a future completed with the result */ - public CompletableFuture retrieveAsync(String networkUuid) { + public CompletableFuture retrieveAsync(String networkUuid) { return client.executeAsync(retrieveRequest(networkUuid), RequestOptions.none(), RETRIEVE_RESPONSE).thenApply(RawResponse::data); } @@ -374,48 +376,94 @@ public CompletableFuture listSignalsAsync(String ne /** * Get network timeline * - *

Chronological events for this network: detection, each signal's observation, and lifecycle status changes. + *

Without contract: the network's own events (detection, each signal's latest observation and its review), oldest first and not paginated. limit, cursor and event_type are ignored. With contract=2: the typed timeline, newest first and paginated by cursor. It adds what the member accounts did (verification attempts, transactions, sign-ins you post through the events API and the live rules they hit) and the cases opened for the network. note_added events are returned to console members only, never to an API key. Requires a full-access API key, or read:networks for a console member. * *

{@code GET /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/timeline/} *

Application scoped: the organization and application ids come from the client options. * * @param networkUuid Network UUID. + * @param params the request parameters * @param options per-request options that override the client options * @return the response body */ + public NetworksRetrieveTimelineResponse retrieveTimeline(String networkUuid, NetworksRetrieveTimelineParams params, RequestOptions options) { + return client.execute(retrieveTimelineRequest(networkUuid, params), options, RETRIEVE_TIMELINE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)}. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveTimelineAsync(String networkUuid, NetworksRetrieveTimelineParams params, RequestOptions options) { + return client.executeAsync(retrieveTimelineRequest(networkUuid, params), options, RETRIEVE_TIMELINE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)}, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return the result + */ + public NetworksRetrieveTimelineResponse retrieveTimeline(String networkUuid, NetworksRetrieveTimelineParams params) { + return client.execute(retrieveTimelineRequest(networkUuid, params), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)}, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture retrieveTimelineAsync(String networkUuid, NetworksRetrieveTimelineParams params) { + return client.executeAsync(retrieveTimelineRequest(networkUuid, params), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)}, without parameters. + * + * @param networkUuid the {@code network_uuid} + * @param options per-request options + * @return the result + */ public NetworksRetrieveTimelineResponse retrieveTimeline(String networkUuid, RequestOptions options) { - return client.execute(retrieveTimelineRequest(networkUuid), options, RETRIEVE_TIMELINE_RESPONSE).data(); + return client.execute(retrieveTimelineRequest(networkUuid, NetworksRetrieveTimelineParams.builder().build()), options, RETRIEVE_TIMELINE_RESPONSE).data(); } /** - * Asynchronous {@link #retrieveTimeline(String, RequestOptions)}. + * Asynchronous {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)}, without parameters. * * @param networkUuid the {@code network_uuid} * @param options per-request options * @return a future completed with the result */ public CompletableFuture retrieveTimelineAsync(String networkUuid, RequestOptions options) { - return client.executeAsync(retrieveTimelineRequest(networkUuid), options, RETRIEVE_TIMELINE_RESPONSE).thenApply(RawResponse::data); + return client.executeAsync(retrieveTimelineRequest(networkUuid, NetworksRetrieveTimelineParams.builder().build()), options, RETRIEVE_TIMELINE_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #retrieveTimeline(String, RequestOptions)}, with the client's options. + * Same as {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)}, without parameters, with the client's options. * * @param networkUuid the {@code network_uuid} * @return the result */ public NetworksRetrieveTimelineResponse retrieveTimeline(String networkUuid) { - return client.execute(retrieveTimelineRequest(networkUuid), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE).data(); + return client.execute(retrieveTimelineRequest(networkUuid, NetworksRetrieveTimelineParams.builder().build()), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE).data(); } /** - * Asynchronous {@link #retrieveTimeline(String, RequestOptions)}, with the client's options. + * Asynchronous {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)}, without parameters, with the client's options. * * @param networkUuid the {@code network_uuid} * @return a future completed with the result */ public CompletableFuture retrieveTimelineAsync(String networkUuid) { - return client.executeAsync(retrieveTimelineRequest(networkUuid), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE).thenApply(RawResponse::data); + return client.executeAsync(retrieveTimelineRequest(networkUuid, NetworksRetrieveTimelineParams.builder().build()), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE).thenApply(RawResponse::data); } /** @@ -513,17 +561,119 @@ public CompletableFuture downloadReportAsync(String networkUuid) return client.executeAsync(downloadReportRequest(networkUuid), RequestOptions.none(), DOWNLOAD_REPORT_RESPONSE).thenApply(RawResponse::data); } + /** + * Add a tag to a network + * + *

Put one of your application's tags on the network. Returns the network detail, whose tags lists your application's tags on the network, with 201 when the tag was added and 200 when it was already on the network. A tag that belongs to another application, or does not exist, returns 404 Tag not found.. Requires a full-access API key, or write:networks for a console member. + * + *

{@code POST /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/tags/add/} + *

Application scoped: the organization and application ids come from the client options. + * + * @param networkUuid Network UUID. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public NetworkDetailResponse addTag(String networkUuid, NetworkTagRequest params, RequestOptions options) { + return client.execute(addTagRequest(networkUuid, params), options, ADD_TAG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #addTag(String, NetworkTagRequest, RequestOptions)}. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture addTagAsync(String networkUuid, NetworkTagRequest params, RequestOptions options) { + return client.executeAsync(addTagRequest(networkUuid, params), options, ADD_TAG_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #addTag(String, NetworkTagRequest, RequestOptions)}, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return the result + */ + public NetworkDetailResponse addTag(String networkUuid, NetworkTagRequest params) { + return client.execute(addTagRequest(networkUuid, params), RequestOptions.none(), ADD_TAG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #addTag(String, NetworkTagRequest, RequestOptions)}, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture addTagAsync(String networkUuid, NetworkTagRequest params) { + return client.executeAsync(addTagRequest(networkUuid, params), RequestOptions.none(), ADD_TAG_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Remove a tag from a network + * + *

Remove one of your application's tags from the network. Returns the network detail. 404 when the tag is not on the network, or does not belong to your application. Requires a full-access API key, or write:networks for a console member. + * + *

{@code POST /v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/tags/remove/} + *

Application scoped: the organization and application ids come from the client options. + * + * @param networkUuid Network UUID. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public NetworkDetailResponse removeTag(String networkUuid, NetworkTagRequest params, RequestOptions options) { + return client.execute(removeTagRequest(networkUuid, params), options, REMOVE_TAG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #removeTag(String, NetworkTagRequest, RequestOptions)}. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture removeTagAsync(String networkUuid, NetworkTagRequest params, RequestOptions options) { + return client.executeAsync(removeTagRequest(networkUuid, params), options, REMOVE_TAG_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #removeTag(String, NetworkTagRequest, RequestOptions)}, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return the result + */ + public NetworkDetailResponse removeTag(String networkUuid, NetworkTagRequest params) { + return client.execute(removeTagRequest(networkUuid, params), RequestOptions.none(), REMOVE_TAG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #removeTag(String, NetworkTagRequest, RequestOptions)}, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture removeTagAsync(String networkUuid, NetworkTagRequest params) { + return client.executeAsync(removeTagRequest(networkUuid, params), RequestOptions.none(), REMOVE_TAG_RESPONSE).thenApply(RawResponse::data); + } + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(NetworkListItem::fromJson)); private static ApiRequest listRequest(NetworksListParams params) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/networks/") .applicationScoped() .params(params) - .queryFields("status", "signal_type", "pattern_type", "risk_band", "min_size", "max_size", "date_from", "date_to", "q", "ordering", "limit", "offset") + .queryFields("status", "scope", "signal_type", "pattern_type", "risk_band", "min_size", "max_size", "assignee", "priority", "risk_min", "has_case", "date_from", "date_to", "q", "ordering", "limit", "offset", "summary") .build(); } - private static final ResponseHandler RETRIEVE_RESPONSE = Responses.json(Conv.model(NetworkDetail::fromJson)); + private static final ResponseHandler RETRIEVE_RESPONSE = Responses.json(Conv.model(NetworkDetailResponse::fromJson)); private static ApiRequest retrieveRequest(String networkUuid) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/") @@ -561,12 +711,14 @@ private static ApiRequest listSignalsRequest(String networkUuid) { .build(); } - private static final ResponseHandler RETRIEVE_TIMELINE_RESPONSE = Responses.json(Conv.model(NetworksRetrieveTimelineResponse::fromJson)); + private static final ResponseHandler RETRIEVE_TIMELINE_RESPONSE = Responses.json(Conv.union(NetworksRetrieveTimelineResponse::fromJson)); - private static ApiRequest retrieveTimelineRequest(String networkUuid) { + private static ApiRequest retrieveTimelineRequest(String networkUuid, NetworksRetrieveTimelineParams params) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/timeline/") .applicationScoped() .pathParam("network_uuid", networkUuid) + .params(params) + .queryFields("contract", "limit", "cursor", "event_type") .build(); } @@ -589,6 +741,28 @@ private static ApiRequest downloadReportRequest(String networkUuid) { .build(); } + private static final ResponseHandler ADD_TAG_RESPONSE = Responses.json(Conv.model(NetworkDetailResponse::fromJson)); + + private static ApiRequest addTagRequest(String networkUuid, NetworkTagRequest params) { + return ApiRequest.builder("POST", "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/tags/add/") + .applicationScoped() + .pathParam("network_uuid", networkUuid) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler REMOVE_TAG_RESPONSE = Responses.json(Conv.model(NetworkDetailResponse::fromJson)); + + private static ApiRequest removeTagRequest(String networkUuid, NetworkTagRequest params) { + return ApiRequest.builder("POST", "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/tags/remove/") + .applicationScoped() + .pathParam("network_uuid", networkUuid) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + /** The {@code networks} operations returning {@link RawResponse}. */ public static final class WithRawResponse { private final ApiClient client; @@ -684,7 +858,7 @@ public CompletableFuture>> listAsync() { * @param options per-request options * @return the result */ - public RawResponse retrieve(String networkUuid, RequestOptions options) { + public RawResponse retrieve(String networkUuid, RequestOptions options) { return client.execute(retrieveRequest(networkUuid), options, RETRIEVE_RESPONSE); } @@ -695,7 +869,7 @@ public RawResponse retrieve(String networkUuid, RequestOptions op * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> retrieveAsync(String networkUuid, RequestOptions options) { + public CompletableFuture> retrieveAsync(String networkUuid, RequestOptions options) { return client.executeAsync(retrieveRequest(networkUuid), options, RETRIEVE_RESPONSE); } @@ -705,7 +879,7 @@ public CompletableFuture> retrieveAsync(String networ * @param networkUuid the {@code network_uuid} * @return the result */ - public RawResponse retrieve(String networkUuid) { + public RawResponse retrieve(String networkUuid) { return client.execute(retrieveRequest(networkUuid), RequestOptions.none(), RETRIEVE_RESPONSE); } @@ -715,7 +889,7 @@ public RawResponse retrieve(String networkUuid) { * @param networkUuid the {@code network_uuid} * @return a future completed with the result */ - public CompletableFuture> retrieveAsync(String networkUuid) { + public CompletableFuture> retrieveAsync(String networkUuid) { return client.executeAsync(retrieveRequest(networkUuid), RequestOptions.none(), RETRIEVE_RESPONSE); } @@ -892,45 +1066,91 @@ public CompletableFuture> listSignalsAs } /** - * {@link #retrieveTimeline(String, RequestOptions)} with the raw response. + * {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)} with the raw response. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse retrieveTimeline(String networkUuid, NetworksRetrieveTimelineParams params, RequestOptions options) { + return client.execute(retrieveTimelineRequest(networkUuid, params), options, RETRIEVE_TIMELINE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)} with the raw response. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveTimelineAsync(String networkUuid, NetworksRetrieveTimelineParams params, RequestOptions options) { + return client.executeAsync(retrieveTimelineRequest(networkUuid, params), options, RETRIEVE_TIMELINE_RESPONSE); + } + + /** + * {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)} with the raw response, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return the result + */ + public RawResponse retrieveTimeline(String networkUuid, NetworksRetrieveTimelineParams params) { + return client.execute(retrieveTimelineRequest(networkUuid, params), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)} with the raw response, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> retrieveTimelineAsync(String networkUuid, NetworksRetrieveTimelineParams params) { + return client.executeAsync(retrieveTimelineRequest(networkUuid, params), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE); + } + + /** + * {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)} with the raw response, without parameters. * * @param networkUuid the {@code network_uuid} * @param options per-request options * @return the result */ public RawResponse retrieveTimeline(String networkUuid, RequestOptions options) { - return client.execute(retrieveTimelineRequest(networkUuid), options, RETRIEVE_TIMELINE_RESPONSE); + return client.execute(retrieveTimelineRequest(networkUuid, NetworksRetrieveTimelineParams.builder().build()), options, RETRIEVE_TIMELINE_RESPONSE); } /** - * Asynchronous {@link #retrieveTimeline(String, RequestOptions)} with the raw response. + * Asynchronous {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)} with the raw response, without parameters. * * @param networkUuid the {@code network_uuid} * @param options per-request options * @return a future completed with the result */ public CompletableFuture> retrieveTimelineAsync(String networkUuid, RequestOptions options) { - return client.executeAsync(retrieveTimelineRequest(networkUuid), options, RETRIEVE_TIMELINE_RESPONSE); + return client.executeAsync(retrieveTimelineRequest(networkUuid, NetworksRetrieveTimelineParams.builder().build()), options, RETRIEVE_TIMELINE_RESPONSE); } /** - * {@link #retrieveTimeline(String, RequestOptions)} with the raw response, with the client's options. + * {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param networkUuid the {@code network_uuid} * @return the result */ public RawResponse retrieveTimeline(String networkUuid) { - return client.execute(retrieveTimelineRequest(networkUuid), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE); + return client.execute(retrieveTimelineRequest(networkUuid, NetworksRetrieveTimelineParams.builder().build()), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE); } /** - * Asynchronous {@link #retrieveTimeline(String, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #retrieveTimeline(String, NetworksRetrieveTimelineParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param networkUuid the {@code network_uuid} * @return a future completed with the result */ public CompletableFuture> retrieveTimelineAsync(String networkUuid) { - return client.executeAsync(retrieveTimelineRequest(networkUuid), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE); + return client.executeAsync(retrieveTimelineRequest(networkUuid, NetworksRetrieveTimelineParams.builder().build()), RequestOptions.none(), RETRIEVE_TIMELINE_RESPONSE); } /** @@ -1016,5 +1236,97 @@ public RawResponse downloadReport(String networkUuid) { public CompletableFuture> downloadReportAsync(String networkUuid) { return client.executeAsync(downloadReportRequest(networkUuid), RequestOptions.none(), DOWNLOAD_REPORT_RESPONSE); } + + /** + * {@link #addTag(String, NetworkTagRequest, RequestOptions)} with the raw response. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse addTag(String networkUuid, NetworkTagRequest params, RequestOptions options) { + return client.execute(addTagRequest(networkUuid, params), options, ADD_TAG_RESPONSE); + } + + /** + * Asynchronous {@link #addTag(String, NetworkTagRequest, RequestOptions)} with the raw response. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> addTagAsync(String networkUuid, NetworkTagRequest params, RequestOptions options) { + return client.executeAsync(addTagRequest(networkUuid, params), options, ADD_TAG_RESPONSE); + } + + /** + * {@link #addTag(String, NetworkTagRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return the result + */ + public RawResponse addTag(String networkUuid, NetworkTagRequest params) { + return client.execute(addTagRequest(networkUuid, params), RequestOptions.none(), ADD_TAG_RESPONSE); + } + + /** + * Asynchronous {@link #addTag(String, NetworkTagRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> addTagAsync(String networkUuid, NetworkTagRequest params) { + return client.executeAsync(addTagRequest(networkUuid, params), RequestOptions.none(), ADD_TAG_RESPONSE); + } + + /** + * {@link #removeTag(String, NetworkTagRequest, RequestOptions)} with the raw response. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse removeTag(String networkUuid, NetworkTagRequest params, RequestOptions options) { + return client.execute(removeTagRequest(networkUuid, params), options, REMOVE_TAG_RESPONSE); + } + + /** + * Asynchronous {@link #removeTag(String, NetworkTagRequest, RequestOptions)} with the raw response. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> removeTagAsync(String networkUuid, NetworkTagRequest params, RequestOptions options) { + return client.executeAsync(removeTagRequest(networkUuid, params), options, REMOVE_TAG_RESPONSE); + } + + /** + * {@link #removeTag(String, NetworkTagRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return the result + */ + public RawResponse removeTag(String networkUuid, NetworkTagRequest params) { + return client.execute(removeTagRequest(networkUuid, params), RequestOptions.none(), REMOVE_TAG_RESPONSE); + } + + /** + * Asynchronous {@link #removeTag(String, NetworkTagRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param networkUuid the {@code network_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> removeTagAsync(String networkUuid, NetworkTagRequest params) { + return client.executeAsync(removeTagRequest(networkUuid, params), RequestOptions.none(), REMOVE_TAG_RESPONSE); + } } } diff --git a/src/main/java/me/didit/resources/PhoneResource.java b/src/main/java/me/didit/resources/PhoneResource.java index 290433b..f4d6418 100644 --- a/src/main/java/me/didit/resources/PhoneResource.java +++ b/src/main/java/me/didit/resources/PhoneResource.java @@ -104,7 +104,7 @@ public CompletableFuture assessRiskAsync(PhoneRiskRequest par * *

Delivery channels. options.preferred_channel selects the channel (whatsapp by default; sms, telegram, voice, rcs, viber, and zalo are also supported). When the preferred channel is unavailable for the destination country or number, delivery automatically falls back to SMS — the send still reports Success, and the channel actually used is reported by the check response (phone.verification_method and the lifecycle events). * - *

Branding. SMS codes carry your application's SMS sender name, set in the Business Console, at no extra cost. Pass options.sms_sender_name to sign one code with another name, or options.use_white_label_customization: true to fall back to your white-label public name when no sender name is set. WhatsApp codes use a fixed message and are not branded. + *

Branding. SMS codes carry your application's SMS sender name, set in the Business Console, at no extra cost. Pass options.sms_sender_name to sign one code with another name, or options.use_white_label_customization: true to fall back to your white-label public name when no sender name is set. WhatsApp codes use a fixed message; connect your own WhatsApp Business number in the Business Console to send them from your business name and number, billed at the Didit fee only. * *

Send statuses. Success — OTP sent for a new verification; Retry — OTP re-sent for the pending verification; Blocked — the anti-fraud layer refused to send (see reason, e.g. repeated_attempts, suspicious, spam). A Blocked send immediately finalizes the verification as Declined with a high-risk warning, so a follow-up check returns Expired or Not Found. * diff --git a/src/main/java/me/didit/resources/SessionsResource.java b/src/main/java/me/didit/resources/SessionsResource.java index a823176..ef88f9b 100644 --- a/src/main/java/me/didit/resources/SessionsResource.java +++ b/src/main/java/me/didit/resources/SessionsResource.java @@ -1,7 +1,6 @@ // Code generated by codegen from openapi/*.json. DO NOT EDIT. package me.didit.resources; -import com.fasterxml.jackson.databind.JsonNode; import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; import me.didit.core.ApiRequest; @@ -12,27 +11,47 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.AMLScreeningRun; +import me.didit.types.EntityNetworkMembershipList; +import me.didit.types.FeatureStatusOverride; +import me.didit.types.KYBKeyPeopleParty; +import me.didit.types.SandboxSimulateRequest; +import me.didit.types.SandboxSimulateResponse; +import me.didit.types.SessionDecision; +import me.didit.types.SessionDeletionJob; +import me.didit.types.SessionEvent; +import me.didit.types.SessionsAddImagesParams; +import me.didit.types.SessionsAddImagesResponse; import me.didit.types.SessionsBatchDeleteParams; import me.didit.types.SessionsBatchDeleteResponse; import me.didit.types.SessionsBulkUpdateAmlHitStatusParams; +import me.didit.types.SessionsBulkUpdateAmlHitStatusResponse; import me.didit.types.SessionsCreateParams; import me.didit.types.SessionsCreateResponse; import me.didit.types.SessionsDeleteParams; import me.didit.types.SessionsDeleteResponse; +import me.didit.types.SessionsExportAmlScreeningHistoryParams; import me.didit.types.SessionsImportSharedParams; -import me.didit.types.SessionsListNetworksResponse; +import me.didit.types.SessionsListAmlScreeningHistoryParams; +import me.didit.types.SessionsListEventsParams; +import me.didit.types.SessionsListFeatureOverridesResponse; import me.didit.types.SessionsListParams; import me.didit.types.SessionsListResponse; +import me.didit.types.SessionsPreviewDecisionEmailParams; +import me.didit.types.SessionsPreviewDecisionEmailResponse; import me.didit.types.SessionsPreviewResubmissionParams; import me.didit.types.SessionsPreviewResubmissionResponse; import me.didit.types.SessionsRetrieveDecisionParams; -import me.didit.types.SessionsRetrieveDecisionResponse; -import me.didit.types.SessionsRetrieveDeletionJobResponse; import me.didit.types.SessionsShareParams; import me.didit.types.SessionsShareResponse; import me.didit.types.SessionsUpdateAmlHitStatusParams; +import me.didit.types.SessionsUpdateAmlHitStatusResponse; +import me.didit.types.SessionsUpdateAmlStatusParams; +import me.didit.types.SessionsUpdateAmlStatusResponse; import me.didit.types.SessionsUpdateDataParams; import me.didit.types.SessionsUpdateDataResponse; +import me.didit.types.SessionsUpdateDocumentAiDataParams; +import me.didit.types.SessionsUpdateDocumentAiDataResponse; import me.didit.types.SessionsUpdateFeatureStatusParams; import me.didit.types.SessionsUpdateFeatureStatusResponse; import me.didit.types.SessionsUpdateKybCompanyDataParams; @@ -50,12 +69,16 @@ public final class SessionsResource { private final WithRawResponse withRawResponse; private final SessionsImportsResource imports; private final SessionsReviewsResource reviews; + private final SessionsSignaturesResource signatures; + private final SessionsWebhooksResource webhooks; SessionsResource(ApiClient client) { this.client = client; this.withRawResponse = new WithRawResponse(client); this.imports = new SessionsImportsResource(client); this.reviews = new SessionsReviewsResource(client); + this.signatures = new SessionsSignaturesResource(client); + this.webhooks = new SessionsWebhooksResource(client); } /** @@ -76,6 +99,24 @@ public SessionsReviewsResource reviews() { return reviews; } + /** + * The {@code sessions.signatures} resource. + * + * @return the resource + */ + public SessionsSignaturesResource signatures() { + return signatures; + } + + /** + * The {@code sessions.webhooks} resource. + * + * @return the resource + */ + public SessionsWebhooksResource webhooks() { + return webhooks; + } + /** * The same operations, returning the HTTP status, headers and request id with the parsed body. * @@ -142,7 +183,7 @@ public CompletableFuture createAsync(SessionsCreateParam * *

Retrieve the complete decision report for a verification session — the rolled-up session status plus one report per verification feature the workflow executed. This is the canonical read endpoint for both User Verification (KYC) and Business Verification (KYB) sessions: the same path serves both, and the top-level session_kind discriminator tells you which shape you received ("user" or "business"). * - *

Every feature report is a plural array. V3 workflows are graphs that can run the same feature more than once, so each block (id_verifications, nfc_verifications, liveness_checks, face_matches, poa_verifications, document_ai_documents, phone_verifications, email_verifications, aml_screenings, ip_analyses, database_validations, questionnaire_responses) is a JSON array whose items each carry a node_id identifying the workflow graph node that produced them. Read NFC as response.nfc_verifications[0] — never as a singular nfc field. A block is null until the workflow has run that feature at least once; features the workflow does not include never appear with data. + *

Every feature report is a plural array. V3 workflows are graphs that can run the same feature more than once, so each block (id_verifications, nfc_verifications, liveness_checks, face_matches, poa_verifications, document_ai_documents, phone_verifications, email_verifications, aml_screenings, ip_analyses, database_validations, questionnaire_responses) is a JSON array whose items each carry a node_id identifying the workflow graph node that produced them. Read NFC as response.nfc_verifications[0] — never as a singular nfc field. A block is null until the workflow has run that feature at least once; features the workflow does not include never appear with data. Electronic Signature results are in signatures, which is present only when the workflow has an Electronic Signature step. * *

When to call. The recommended pattern is event-driven: wait for the status.updated webhook that fires when the session reaches a decision (Approved, Declined, In Review), then call this endpoint once to fetch the full report. Polling also works — the endpoint can be called at any point in the session lifecycle and returns the data produced so far — but webhook-then-fetch is cheaper and faster. Media URLs in the response (document images, videos, PDFs) are short-lived presigned links: fetch them promptly or re-request the decision to get fresh ones, and do not persist them as long-term references. * @@ -159,7 +200,7 @@ public CompletableFuture createAsync(SessionsCreateParam * @param options per-request options that override the client options * @return the response body */ - public SessionsRetrieveDecisionResponse retrieveDecision(String sessionId, SessionsRetrieveDecisionParams params, RequestOptions options) { + public SessionDecision retrieveDecision(String sessionId, SessionsRetrieveDecisionParams params, RequestOptions options) { return client.execute(retrieveDecisionRequest(sessionId, params), options, RETRIEVE_DECISION_RESPONSE).data(); } @@ -171,7 +212,7 @@ public SessionsRetrieveDecisionResponse retrieveDecision(String sessionId, Sessi * @param options per-request options * @return a future completed with the result */ - public CompletableFuture retrieveDecisionAsync(String sessionId, SessionsRetrieveDecisionParams params, RequestOptions options) { + public CompletableFuture retrieveDecisionAsync(String sessionId, SessionsRetrieveDecisionParams params, RequestOptions options) { return client.executeAsync(retrieveDecisionRequest(sessionId, params), options, RETRIEVE_DECISION_RESPONSE).thenApply(RawResponse::data); } @@ -182,7 +223,7 @@ public CompletableFuture retrieveDecisionAsync * @param params the request parameters * @return the result */ - public SessionsRetrieveDecisionResponse retrieveDecision(String sessionId, SessionsRetrieveDecisionParams params) { + public SessionDecision retrieveDecision(String sessionId, SessionsRetrieveDecisionParams params) { return client.execute(retrieveDecisionRequest(sessionId, params), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE).data(); } @@ -193,7 +234,7 @@ public SessionsRetrieveDecisionResponse retrieveDecision(String sessionId, Sessi * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture retrieveDecisionAsync(String sessionId, SessionsRetrieveDecisionParams params) { + public CompletableFuture retrieveDecisionAsync(String sessionId, SessionsRetrieveDecisionParams params) { return client.executeAsync(retrieveDecisionRequest(sessionId, params), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE).thenApply(RawResponse::data); } @@ -204,7 +245,7 @@ public CompletableFuture retrieveDecisionAsync * @param options per-request options * @return the result */ - public SessionsRetrieveDecisionResponse retrieveDecision(String sessionId, RequestOptions options) { + public SessionDecision retrieveDecision(String sessionId, RequestOptions options) { return client.execute(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), options, RETRIEVE_DECISION_RESPONSE).data(); } @@ -215,7 +256,7 @@ public SessionsRetrieveDecisionResponse retrieveDecision(String sessionId, Reque * @param options per-request options * @return a future completed with the result */ - public CompletableFuture retrieveDecisionAsync(String sessionId, RequestOptions options) { + public CompletableFuture retrieveDecisionAsync(String sessionId, RequestOptions options) { return client.executeAsync(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), options, RETRIEVE_DECISION_RESPONSE).thenApply(RawResponse::data); } @@ -225,7 +266,7 @@ public CompletableFuture retrieveDecisionAsync * @param sessionId the {@code sessionId} * @return the result */ - public SessionsRetrieveDecisionResponse retrieveDecision(String sessionId) { + public SessionDecision retrieveDecision(String sessionId) { return client.execute(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE).data(); } @@ -235,7 +276,7 @@ public SessionsRetrieveDecisionResponse retrieveDecision(String sessionId) { * @param sessionId the {@code sessionId} * @return a future completed with the result */ - public CompletableFuture retrieveDecisionAsync(String sessionId) { + public CompletableFuture retrieveDecisionAsync(String sessionId) { return client.executeAsync(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE).thenApply(RawResponse::data); } @@ -246,7 +287,7 @@ public CompletableFuture retrieveDecisionAsync * *

What is deleted: * - The session and every verification attempt it holds, including results that a resubmission replaced: the decision, extracted data, all related feature records (ID verification, face, liveness, face match, AML, registry checks, IP analysis, and so on), its reviews and its activity log. - * - All stored files owned by the session: document front/back photos (full, cropped, and privacy-blurred variants), document-capture videos, portrait crops, NFC chip portrait and signature images, face reference images, liveness videos, face-match source/target images, Proof of Address documents, and any extra uploaded files (KYC); uploaded company documents and extra files (KYB). Previously issued media URLs (https://<media-host>/...) stop resolving. + * - All stored files owned by the session: document front/back photos (full, cropped, and privacy-blurred variants), document-capture videos, portrait crops, NFC chip portrait and signature images, face reference images, liveness videos, face-match source/target images, Proof of Address documents, and any extra uploaded files (KYC); uploaded company documents and extra files (KYB). Electronic Signature documents, signed PDFs, evidence PDFs and signature images (KYC and KYB): their download paths stop working. Previously issued media URLs (https://<media-host>/...) stop resolving. * - The session's face data. Its face embedding is deleted too, unless you choose to keep an image-free template (see Face biometric data). * - Webhook deliveries still queued for the session, and their retries. Webhooks already delivered are not affected. * - For a Business Verification (KYB) session: the verifications it created for its key people (KYC) and its corporate owners (KYB), recursively, reported as deleted_child_verifications. A verification it only links to, such as an existing approved KYC it reused, is not deleted. @@ -432,13 +473,13 @@ public CompletableFuture generatePdfAsync(String sessionId) { * *

Manually override the final decision of a KYC or KYB verification session. Set new_status to Approved or Declined to record a manual decision, or to Resubmitted to clear specific verification steps and let the user redo them through the same verification URL. The endpoint accepts both user (KYC) and business (KYB) session IDs. * - *

Call it once a session has reached a reviewable state: the current status must be Approved, Declined, In Review, Kyc Expired, Abandoned, or Resubmitted. Sessions that are still Not Started, In Progress, or Awaiting User, or that ended as Expired, return 400. The new status must differ from the current one, and your API key's organization needs the write:sessions permission. The eligible-state check runs before body validation, so an ineligible session returns the Wrong Current Status error even when the payload is also invalid. + *

Call it once a session has reached a reviewable state: the current status must be Approved, Declined, In Review, Kyc Expired, Abandoned, or Resubmitted. Sessions that are still Not Started, In Progress, or Awaiting User, or that ended as Expired, return 400. Your API key needs the write:sessions permission (a scoped API key needs write access to decisions). The eligible-state check runs before body validation, so an ineligible session returns the Wrong Current Status error even when the payload is also invalid. * - *

For Resubmitted, pass nodes_to_resubmit to choose which workflow steps the user must redo, or omit it to auto-select existing feature attempts in a resubmittable state (Declined, In Review, Not Finished, Expired). Never-attempted features are not auto-selected — list them explicitly in nodes_to_resubmit, or the request returns 400 when no recorded attempt needs resubmission. Captured data for the selected steps is deleted, the steps are reordered to match the workflow graph, the session's expiration window restarts, and the original verification URL becomes usable again. Backend-only steps (AML, DATABASE_VALIDATION, IP_ANALYSIS) at the head of the list run immediately without user interaction; if every resubmitted step is backend-only, the session re-finalizes within the same request. KYB_REGISTRY and KYB_KEY_PEOPLE cannot be resubmitted directly — resubmit the relevant child KYC sessions instead and the parent business session recomputes automatically. + *

For Resubmitted, pass nodes_to_resubmit to choose which workflow steps the user must redo, or omit it to auto-select existing feature attempts in a resubmittable state (Declined, In Review, Not Finished, Expired). Never-attempted features are not auto-selected: list them explicitly in nodes_to_resubmit, or the request returns 400 when no recorded attempt needs resubmission. The selected steps' previous results move to the resubmission's history (returned by Get Decision with include=resubmissions), the steps are reordered to match the workflow graph, the session's expiration window restarts, and the original verification URL becomes usable again. Backend-only steps (AML, DATABASE_VALIDATION, IP_ANALYSIS) at the head of the list run immediately without user interaction; if every resubmitted step is backend-only, the session re-finalizes within the same request. KYB_REGISTRY and KYB_KEY_PEOPLE cannot be resubmitted directly: resubmit the relevant child KYC sessions instead and the parent business session recomputes automatically. * - *

Every successful call fires the status.updated [webhook](/integration/webhooks) once the change commits and appends an activity entry — with actor attribution and your comment — to the session's reviews, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and in the console activity timeline. The user-profile aggregates linked to the session's vendor_data update as well. Declining disables ongoing AML monitoring for the session; re-approving re-enables it (from Resubmitted, Declined, or Kyc Expired) when the workflow has monitoring turned on. Approving or declining a child KYC that belongs to a KYB key-people check recomputes the parent business session's status. Set send_email: true (with email_address) to notify the user: a status notice for Approved/Declined, or a resubmission email containing the verification link and per-step reasons for Resubmitted. + *

Every call that changes the status fires the status.updated [webhook](/integration/webhooks) once the change commits and appends an activity entry (with actor attribution and your comment) to the session's reviews, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and in the console activity timeline. The user-profile aggregates linked to the session's vendor_data update as well. Declining disables ongoing AML monitoring for the session; re-approving re-enables it (from Resubmitted, Declined, or Kyc Expired) when the workflow has monitoring turned on. Approving or declining a child KYC that belongs to a KYB key-people check recomputes the parent business session's status. Set send_email: true (with email_address) to notify the user: a status notice for Approved/Declined, or a resubmission email containing the verification link and per-step reasons for Resubmitted. To check the email first, preview it with [Preview Decision Email](/sessions-api/preview-decision-email) (Approved, Declined) or [Preview Resubmission Email](/sessions-api/preview-resubmission) (Resubmitted) and pass its preview_token as email_preview_token: the update then sends exactly the previewed email, or returns 409 before changing anything when the email or the session changed since the preview or the receipt is older than 15 minutes. * - *

The operation is intentionally not repeatable with the same value: re-sending the current status returns 400 (The new status is the same as the current status.), so accidental double-submissions are harmless. Authentication and permission failures both return 403 — this API never responds 401. + *

Repeating Approved or Declined on a session that already has that status changes nothing and returns 200: no webhook, no activity entry and no email. Repeating Resubmitted returns 400 (The new status is the same as the current status.). Missing or invalid credentials and missing permissions return 403; an expired scoped API key returns 401. * *

{@code PATCH /v3/session/{sessionId}/update-status/} * @@ -650,7 +691,7 @@ public CompletableFuture shareAsync(String sessionId, Ses * @param options per-request options that override the client options * @return the response body */ - public JsonNode importShared(SessionsImportSharedParams params, RequestOptions options) { + public SessionDecision importShared(SessionsImportSharedParams params, RequestOptions options) { return client.execute(importSharedRequest(params), options, IMPORT_SHARED_RESPONSE).data(); } @@ -661,7 +702,7 @@ public JsonNode importShared(SessionsImportSharedParams params, RequestOptions o * @param options per-request options * @return a future completed with the result */ - public CompletableFuture importSharedAsync(SessionsImportSharedParams params, RequestOptions options) { + public CompletableFuture importSharedAsync(SessionsImportSharedParams params, RequestOptions options) { return client.executeAsync(importSharedRequest(params), options, IMPORT_SHARED_RESPONSE).thenApply(RawResponse::data); } @@ -671,7 +712,7 @@ public CompletableFuture importSharedAsync(SessionsImportSharedParams * @param params the request parameters * @return the result */ - public JsonNode importShared(SessionsImportSharedParams params) { + public SessionDecision importShared(SessionsImportSharedParams params) { return client.execute(importSharedRequest(params), RequestOptions.none(), IMPORT_SHARED_RESPONSE).data(); } @@ -681,7 +722,7 @@ public JsonNode importShared(SessionsImportSharedParams params) { * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture importSharedAsync(SessionsImportSharedParams params) { + public CompletableFuture importSharedAsync(SessionsImportSharedParams params) { return client.executeAsync(importSharedRequest(params), RequestOptions.none(), IMPORT_SHARED_RESPONSE).thenApply(RawResponse::data); } @@ -699,6 +740,8 @@ public CompletableFuture importSharedAsync(SessionsImportSharedParams *

Each deleted session gets exactly the same treatment as the single-session delete endpoint: the session, every verification attempt it holds, its decision and extracted data, its related feature records (ID verification, face, liveness, face match, and so on) and all of its stored files are deleted, it disappears from every read endpoint, and previously issued media URLs stop resolving. Webhook deliveries still queued for a deleted session, and their retries, are dropped; webhooks already delivered are not affected. Blocklist entries are not removed, credits already consumed are not refunded, and the parent User entities are not deleted (use POST /v3/users/delete/ for those); no webhook is emitted for the deletion. Deletion is permanent and irreversible. * *

Selection semantics: + * - With delete_all: true you can narrow the selection with the filters of GET /v3/sessions/, each prefixed with filter_ (for example filter_status, filter_workflow_id, filter_date_from). Filters that take several values in List Sessions (status, workflow_id, country, region, document_type, document_name, session_type, warning, organization_name) accept a comma-separated string or an array; the others take one value. An empty value is ignored. Filters apply only together with delete_all, and a deletion job applies the same filters. + * - An unknown filter_* key, or a filter value that cannot be read, rejects the whole request with 400 before anything is deleted, so a mistyped filter never widens the deletion to every session. * - Entries in session_numbers that do not match a live KYC session in your application (unknown numbers, already-deleted sessions, or sessions owned by another application) are silently skipped: they do not appear in results and are not counted in a job's total. * - Validation failures (a non-numeric entry, an empty list, neither session_numbers nor delete_all provided, or a template retention the application cannot honour) reject the whole request with 400 and delete nothing. * - Sessions are deleted in groups. If a group cannot be deleted (for example because a storage service is unavailable), every session in that group is reported as face_retention_outcome: failed_retryable and stays in place; the other groups are unaffected. Repeat the request for those sessions. A job retries such a group by itself. @@ -812,7 +855,7 @@ public CompletableFuture batchDeleteAsync() { * @param options per-request options that override the client options * @return the response body */ - public SessionsRetrieveDeletionJobResponse retrieveDeletionJob(String jobId, RequestOptions options) { + public SessionDeletionJob retrieveDeletionJob(String jobId, RequestOptions options) { return client.execute(retrieveDeletionJobRequest(jobId), options, RETRIEVE_DELETION_JOB_RESPONSE).data(); } @@ -823,7 +866,7 @@ public SessionsRetrieveDeletionJobResponse retrieveDeletionJob(String jobId, Req * @param options per-request options * @return a future completed with the result */ - public CompletableFuture retrieveDeletionJobAsync(String jobId, RequestOptions options) { + public CompletableFuture retrieveDeletionJobAsync(String jobId, RequestOptions options) { return client.executeAsync(retrieveDeletionJobRequest(jobId), options, RETRIEVE_DELETION_JOB_RESPONSE).thenApply(RawResponse::data); } @@ -833,7 +876,7 @@ public CompletableFuture retrieveDeletionJo * @param jobId the {@code job_id} * @return the result */ - public SessionsRetrieveDeletionJobResponse retrieveDeletionJob(String jobId) { + public SessionDeletionJob retrieveDeletionJob(String jobId) { return client.execute(retrieveDeletionJobRequest(jobId), RequestOptions.none(), RETRIEVE_DELETION_JOB_RESPONSE).data(); } @@ -843,7 +886,7 @@ public SessionsRetrieveDeletionJobResponse retrieveDeletionJob(String jobId) { * @param jobId the {@code job_id} * @return a future completed with the result */ - public CompletableFuture retrieveDeletionJobAsync(String jobId) { + public CompletableFuture retrieveDeletionJobAsync(String jobId) { return client.executeAsync(retrieveDeletionJobRequest(jobId), RequestOptions.none(), RETRIEVE_DELETION_JOB_RESPONSE).thenApply(RawResponse::data); } @@ -950,13 +993,19 @@ public CompletableFuture updateDataAsync(String sess /** * Update an individual feature (step) status * - *

Set the status of a single verification feature (step) within a session without changing the overall session decision. Target the feature by its workflow graph nodeId (use default for non-graph workflows where the step has no node). + *

Set the status of a single verification feature (step) within a session. Target the feature by its workflow graph nodeId. On non-graph workflows, whose steps have no node, use default and also pass feature_type to say which step you mean. * - *

Works for both User Verification (KYC) and Business Verification (KYB) sessions and every step type — ID/OCR, Liveness, Face Match, NFC, AML, Proof of Address, Phone, Email, Database Validation, Questionnaire, and the KYB registry / documents / key-people checks. + *

Works for both User Verification (KYC) and Business Verification (KYB) sessions and every step type: ID/OCR, Liveness, Face Match, NFC, AML, Proof of Address, Phone, Email, Database Validation, Questionnaire, and the KYB registry / documents / key-people checks. This endpoint is not available for standalone API sessions (session_type = API). * - *

new_status must be one of Approved, Declined, or In Review, and must differ from the feature's current status. A successful call fires a data.updated webhook and appends an activity entry (with actor attribution) to the session's review trail. This endpoint is not available for standalone API sessions (session_type = API). + *

new_status must be one of Approved, Declined, or In Review, and must differ from the feature's current status. * - *

Authenticate with your API key (x-api-key) or a user authorization header — either must carry the write:sessions privilege. + *

When the override applies. Without defer_until_flow_finishes (or with false) the override applies at once, as it always did; when the person is still in the flow it is recorded as forced. With defer_until_flow_finishes: true while the person is still in the flow (the session is Not Started, In Progress or Resubmitted and has not finished), the override is stored as pending_until_flow_finishes and applies when the flow finishes, after the flow reaches its own decision. On a session whose flow already ended it applies at once either way. A waiting override is cancelled instead when the person redoes its step before finishing, when a newer override on the same check replaces it, when the session leaves the flow unfinished (abandoned, expired, or decided mid-flow), or when you cancel it with POST /v3/session/{sessionId}/features/overrides/{overrideId}/cancel/. List a session's overrides with GET /v3/session/{sessionId}/features/overrides/. + * + *

Effect on the session. A session that already has a decision keeps it: only the check changes. When the session has no decision yet and its workflow is waiting on exactly this step, applying the override completes the step: the workflow moves on, later steps run, and a status node can set the session decision and fire status.updated. + * + *

Webhooks and activity. A data.updated webhook fires when the override applies (at once, or when the flow finishes), never while it waits or when it is cancelled. Every override is recorded in the session's activity as FEATURE_OVERRIDE_PENDING, FEATURE_OVERRIDE_APPLIED or FEATURE_OVERRIDE_CANCELLED, with who made it; the request's entry carries your comment. + * + *

Authenticate with your API key (x-api-key) or a user authorization header carrying the write:sessions privilege; a scoped API key needs write access to decisions. * *

{@code PATCH /v3/session/{sessionId}/features/{nodeId}/update-status/} * @@ -1025,7 +1074,7 @@ public CompletableFuture updateFeatureStatu * @param options per-request options that override the client options * @return the response body */ - public JsonNode updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusParams params, RequestOptions options) { + public SessionsUpdateAmlHitStatusResponse updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusParams params, RequestOptions options) { return client.execute(updateAmlHitStatusRequest(sessionId, params), options, UPDATE_AML_HIT_STATUS_RESPONSE).data(); } @@ -1037,7 +1086,7 @@ public JsonNode updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusP * @param options per-request options * @return a future completed with the result */ - public CompletableFuture updateAmlHitStatusAsync(String sessionId, SessionsUpdateAmlHitStatusParams params, RequestOptions options) { + public CompletableFuture updateAmlHitStatusAsync(String sessionId, SessionsUpdateAmlHitStatusParams params, RequestOptions options) { return client.executeAsync(updateAmlHitStatusRequest(sessionId, params), options, UPDATE_AML_HIT_STATUS_RESPONSE).thenApply(RawResponse::data); } @@ -1048,7 +1097,7 @@ public CompletableFuture updateAmlHitStatusAsync(String sessionId, Ses * @param params the request parameters * @return the result */ - public JsonNode updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusParams params) { + public SessionsUpdateAmlHitStatusResponse updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusParams params) { return client.execute(updateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_HIT_STATUS_RESPONSE).data(); } @@ -1059,7 +1108,7 @@ public JsonNode updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusP * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture updateAmlHitStatusAsync(String sessionId, SessionsUpdateAmlHitStatusParams params) { + public CompletableFuture updateAmlHitStatusAsync(String sessionId, SessionsUpdateAmlHitStatusParams params) { return client.executeAsync(updateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_HIT_STATUS_RESPONSE).thenApply(RawResponse::data); } @@ -1079,7 +1128,7 @@ public CompletableFuture updateAmlHitStatusAsync(String sessionId, Ses * @param options per-request options that override the client options * @return the response body */ - public JsonNode bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHitStatusParams params, RequestOptions options) { + public SessionsBulkUpdateAmlHitStatusResponse bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHitStatusParams params, RequestOptions options) { return client.execute(bulkUpdateAmlHitStatusRequest(sessionId, params), options, BULK_UPDATE_AML_HIT_STATUS_RESPONSE).data(); } @@ -1091,7 +1140,7 @@ public JsonNode bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHi * @param options per-request options * @return a future completed with the result */ - public CompletableFuture bulkUpdateAmlHitStatusAsync(String sessionId, SessionsBulkUpdateAmlHitStatusParams params, RequestOptions options) { + public CompletableFuture bulkUpdateAmlHitStatusAsync(String sessionId, SessionsBulkUpdateAmlHitStatusParams params, RequestOptions options) { return client.executeAsync(bulkUpdateAmlHitStatusRequest(sessionId, params), options, BULK_UPDATE_AML_HIT_STATUS_RESPONSE).thenApply(RawResponse::data); } @@ -1102,7 +1151,7 @@ public CompletableFuture bulkUpdateAmlHitStatusAsync(String sessionId, * @param params the request parameters * @return the result */ - public JsonNode bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { + public SessionsBulkUpdateAmlHitStatusResponse bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { return client.execute(bulkUpdateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), BULK_UPDATE_AML_HIT_STATUS_RESPONSE).data(); } @@ -1113,7 +1162,7 @@ public JsonNode bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHi * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture bulkUpdateAmlHitStatusAsync(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { + public CompletableFuture bulkUpdateAmlHitStatusAsync(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { return client.executeAsync(bulkUpdateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), BULK_UPDATE_AML_HIT_STATUS_RESPONSE).thenApply(RawResponse::data); } @@ -1401,7 +1450,7 @@ public CompletableFuture updateKybPartyAsync(Str * @param options per-request options that override the client options * @return the response body */ - public SessionsListNetworksResponse listNetworks(String sessionId, RequestOptions options) { + public EntityNetworkMembershipList listNetworks(String sessionId, RequestOptions options) { return client.execute(listNetworksRequest(sessionId), options, LIST_NETWORKS_RESPONSE).data(); } @@ -1412,7 +1461,7 @@ public SessionsListNetworksResponse listNetworks(String sessionId, RequestOption * @param options per-request options * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String sessionId, RequestOptions options) { + public CompletableFuture listNetworksAsync(String sessionId, RequestOptions options) { return client.executeAsync(listNetworksRequest(sessionId), options, LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } @@ -1422,7 +1471,7 @@ public CompletableFuture listNetworksAsync(String * @param sessionId the {@code session_id} * @return the result */ - public SessionsListNetworksResponse listNetworks(String sessionId) { + public EntityNetworkMembershipList listNetworks(String sessionId) { return client.execute(listNetworksRequest(sessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE).data(); } @@ -1432,18 +1481,20 @@ public SessionsListNetworksResponse listNetworks(String sessionId) { * @param sessionId the {@code session_id} * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String sessionId) { + public CompletableFuture listNetworksAsync(String sessionId) { return client.executeAsync(listNetworksRequest(sessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } /** * Preview a resubmission email * - *

Render the email that would accompany a KYC or KYB resubmission without changing the session, resetting checks, extending its expiry, running checks or sending mail. Requires write:sessions and an application-scoped session in Approved, Declined, In Review, Kyc Expired or Abandoned status. The server validates the selected workflow steps and renders its fixed translated template with the applicable verified white-label sender and branding. The request cannot set a subject, message body or action URL. + *

Render the email that would accompany a KYC or KYB resubmission without changing the session, resetting checks, extending its expiry, running checks or sending mail. Requires the write:sessions privilege (a scoped API key needs write access to decisions) and a session in Approved, Declined, In Review, Kyc Expired or Abandoned status. The server validates the selected workflow steps and renders its fixed translated template with the applicable verified white-label sender and branding. The request cannot set a subject, message body or action URL. * - *

The response includes the recipient, sender, subject, HTML, resolved steps and a signed preview_token. requires_user_action describes whether the selected steps include an interactive check. Email delivery remains conditional: if automatic checks finalize the verification during the subsequent resubmission, no action-request email is sent. + *

email_address is optional. Without it, the email goes to the person's own address: the one they verified in an email verification step of the flow, else the one given in contact_details when the session was created. An address you pass wins (recipient_source: request). When no address is known and you pass none, recipient and preview_token are empty strings and no email can be sent. * - *

This preview does not reserve session state or authorize delivery. After reviewing it, call Update Session Status with new_status=Resubmitted, the same selection, recipient and language, and email_preview_token set to the returned preview_token. The receipt expires after 15 minutes. When a receipt is supplied, changed inputs, message content, sender or session state return 409 before any mutation or send. Existing integrations may omit the receipt. Regenerate the preview after a conflict. Treat the response as sensitive verification content; responses use Cache-Control: no-store. Render HTML in a sandboxed frame, never directly in your application DOM. + *

The response includes the recipient and where it comes from, the sender, subject, HTML, language, resolved steps, whether confirming sends the email (will_be_sent, not_sent_reason) and a signed preview_token. requires_user_action is false when every selected step runs without the person: the resubmission then finishes on its own and no email goes out (not_sent_reason: no_user_action). + * + *

This preview does not reserve session state or authorize delivery. After reviewing it, call [Update Session Status](/sessions-api/update-status) with new_status: Resubmitted, the same nodes_to_resubmit and email_language you previewed, send_email: true, email_address set to recipient and email_preview_token set to preview_token. The receipt expires after 15 minutes. When a receipt is supplied, changed inputs, message content, sender or session state return 409 before any mutation or send. Existing integrations may omit the receipt. Regenerate the preview after a conflict. Treat the response as sensitive verification content; responses use Cache-Control: no-store. Render HTML in a sandboxed frame, never directly in your application DOM. * *

{@code POST /v3/session/{sessionId}/resubmission-preview/} * @@ -1490,1349 +1541,2965 @@ public CompletableFuture previewResubmissio return client.executeAsync(previewResubmissionRequest(sessionId, params), RequestOptions.none(), PREVIEW_RESUBMISSION_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(SessionsCreateResponse::fromJson)); - - private static ApiRequest createRequest(SessionsCreateParams params) { - return ApiRequest.builder("POST", "/v3/session/") - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return the result + */ + public SessionsPreviewResubmissionResponse previewResubmission(String sessionId, RequestOptions options) { + return client.execute(previewResubmissionRequest(sessionId, SessionsPreviewResubmissionParams.builder().build()), options, PREVIEW_RESUBMISSION_RESPONSE).data(); } - private static final ResponseHandler RETRIEVE_DECISION_RESPONSE = Responses.json(Conv.model(SessionsRetrieveDecisionResponse::fromJson)); - - private static ApiRequest retrieveDecisionRequest(String sessionId, SessionsRetrieveDecisionParams params) { - return ApiRequest.builder("GET", "/v3/session/{sessionId}/decision/") - .pathParam("sessionId", sessionId) - .params(params) - .queryFields("include") - .build(); + /** + * Asynchronous {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture previewResubmissionAsync(String sessionId, RequestOptions options) { + return client.executeAsync(previewResubmissionRequest(sessionId, SessionsPreviewResubmissionParams.builder().build()), options, PREVIEW_RESUBMISSION_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler DELETE_RESPONSE = Responses.json(Conv.model(SessionsDeleteResponse::fromJson)); - - private static ApiRequest deleteRequest(String sessionId, SessionsDeleteParams params) { - return ApiRequest.builder("DELETE", "/v3/session/{sessionId}/delete/") - .pathParam("sessionId", sessionId) - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public SessionsPreviewResubmissionResponse previewResubmission(String sessionId) { + return client.execute(previewResubmissionRequest(sessionId, SessionsPreviewResubmissionParams.builder().build()), RequestOptions.none(), PREVIEW_RESUBMISSION_RESPONSE).data(); } - private static final ResponseHandler GENERATE_PDF_RESPONSE = Responses.binary(); + /** + * Asynchronous {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture previewResubmissionAsync(String sessionId) { + return client.executeAsync(previewResubmissionRequest(sessionId, SessionsPreviewResubmissionParams.builder().build()), RequestOptions.none(), PREVIEW_RESUBMISSION_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest generatePdfRequest(String sessionId) { - return ApiRequest.builder("GET", "/v3/session/{sessionId}/generate-pdf/") - .pathParam("sessionId", sessionId) - .accept("application/pdf") - .build(); + /** + * Update a Document AI document + * + *

Correct the fields Document AI extracted from one uploaded document, set its review status, or both, on a KYC or KYB session. extracted_data must be a JSON object and is merged key by key: a key you send replaces the stored value (a key sent as null is stored as null) and keys you leave out keep their values. The document status feeds the Document AI result of its workflow node in the session decision; the session status itself is not recomputed. Every successful call, including one that changes nothing, sends a data.updated webhook with trigger manual_review. A scoped API key needs decisions: write; see Document AI. + * + *

{@code PATCH /v3/document-ai/{sessionId}/documents/{documentUuid}/} + * + * @param sessionId UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`. + * @param documentUuid UUID of the Document AI document (`document_ai_documents[].items[].uuid` in `GET /v3/session/{sessionId}/decision/`). It must belong to the session in the path. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public SessionsUpdateDocumentAiDataResponse updateDocumentAiData(String sessionId, String documentUuid, SessionsUpdateDocumentAiDataParams params, RequestOptions options) { + return client.execute(updateDocumentAiDataRequest(sessionId, documentUuid, params), options, UPDATE_DOCUMENT_AI_DATA_RESPONSE).data(); } - private static final ResponseHandler UPDATE_STATUS_RESPONSE = Responses.json(Conv.model(SessionsUpdateStatusResponse::fromJson)); + /** + * Asynchronous {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture updateDocumentAiDataAsync(String sessionId, String documentUuid, SessionsUpdateDocumentAiDataParams params, RequestOptions options) { + return client.executeAsync(updateDocumentAiDataRequest(sessionId, documentUuid, params), options, UPDATE_DOCUMENT_AI_DATA_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest updateStatusRequest(String sessionId, SessionsUpdateStatusParams params) { - return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/update-status/") - .pathParam("sessionId", sessionId) - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} + * @param params the request parameters + * @return the result + */ + public SessionsUpdateDocumentAiDataResponse updateDocumentAiData(String sessionId, String documentUuid, SessionsUpdateDocumentAiDataParams params) { + return client.execute(updateDocumentAiDataRequest(sessionId, documentUuid, params), RequestOptions.none(), UPDATE_DOCUMENT_AI_DATA_RESPONSE).data(); } - private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.union(SessionsListResponse.Results::fromJson)); + /** + * Asynchronous {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture updateDocumentAiDataAsync(String sessionId, String documentUuid, SessionsUpdateDocumentAiDataParams params) { + return client.executeAsync(updateDocumentAiDataRequest(sessionId, documentUuid, params), RequestOptions.none(), UPDATE_DOCUMENT_AI_DATA_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest listRequest(SessionsListParams params) { - return ApiRequest.builder("GET", "/v3/sessions/") - .params(params) - .queryFields("session_kind", "limit", "offset", "status", "vendor_data", "search", "workflow_id", "date_from", "date_to", "country", "document_type", "document_name", "region", "didit_internal_id", "session_type", "api_service", "screened_full_name", "warning", "status_initialized_as_in_review", "is_kyc_to_be_reviewed", "euuid", "organization_name") - .build(); + /** + * Same as {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} + * @param options per-request options + * @return the result + */ + public SessionsUpdateDocumentAiDataResponse updateDocumentAiData(String sessionId, String documentUuid, RequestOptions options) { + return client.execute(updateDocumentAiDataRequest(sessionId, documentUuid, SessionsUpdateDocumentAiDataParams.builder().build()), options, UPDATE_DOCUMENT_AI_DATA_RESPONSE).data(); } - private static final ResponseHandler SHARE_RESPONSE = Responses.json(Conv.model(SessionsShareResponse::fromJson)); + /** + * Asynchronous {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture updateDocumentAiDataAsync(String sessionId, String documentUuid, RequestOptions options) { + return client.executeAsync(updateDocumentAiDataRequest(sessionId, documentUuid, SessionsUpdateDocumentAiDataParams.builder().build()), options, UPDATE_DOCUMENT_AI_DATA_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest shareRequest(String sessionId, SessionsShareParams params) { - return ApiRequest.builder("POST", "/v3/session/{sessionId}/share/") - .pathParam("sessionId", sessionId) - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} + * @return the result + */ + public SessionsUpdateDocumentAiDataResponse updateDocumentAiData(String sessionId, String documentUuid) { + return client.execute(updateDocumentAiDataRequest(sessionId, documentUuid, SessionsUpdateDocumentAiDataParams.builder().build()), RequestOptions.none(), UPDATE_DOCUMENT_AI_DATA_RESPONSE).data(); } - private static final ResponseHandler IMPORT_SHARED_RESPONSE = Responses.jsonNode(); + /** + * Asynchronous {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} + * @return a future completed with the result + */ + public CompletableFuture updateDocumentAiDataAsync(String sessionId, String documentUuid) { + return client.executeAsync(updateDocumentAiDataRequest(sessionId, documentUuid, SessionsUpdateDocumentAiDataParams.builder().build()), RequestOptions.none(), UPDATE_DOCUMENT_AI_DATA_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest importSharedRequest(SessionsImportSharedParams params) { - return ApiRequest.builder("POST", "/v3/session/import-shared/") - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Regenerate a Key People party's KYC session + * + *

Give a Key People party of a Business Verification (KYB) session a new individual KYC session after its previous one was deleted, so the Key People check can finish again; send no request body. It works only when the party's KYC session was deleted (kyc_session_deleted: true): a party whose KYC session still exists (in any status), a party that never had one, or a company party returns 409. Didit creates a new hosted KYC session with the workflow the deleted one ran (its latest published version) or, when that is no longer known, the workflow your Key People settings assign to the party's role, emails the link to the party when an email is on file, clears is_skipped, and recomputes the Key People check and the business session status. It then sends a data.updated webhook for the business session with trigger console_key_people_regenerate; to exempt the party instead, use PATCH /v3/session/{sessionId}/kyb/parties/{partyUuid}/. A scoped API key needs decisions: write, and without sessions: write it receives kyc_session_url as null. + * + *

{@code POST /v3/session/{sessionId}/kyb/parties/{partyUuid}/regenerate/} + * + * @param sessionId UUID of the parent Business Verification (KYB) session. + * @param partyUuid UUID of the Key People party (`key_people_checks[].submitted.parties[].uuid` on the business session decision). Must belong to the session referenced by `sessionId`. + * @param options per-request options that override the client options + * @return the response body + */ + public KYBKeyPeopleParty regenerateKybParty(String sessionId, String partyUuid, RequestOptions options) { + return client.execute(regenerateKybPartyRequest(sessionId, partyUuid), options, REGENERATE_KYB_PARTY_RESPONSE).data(); } - private static final ResponseHandler BATCH_DELETE_RESPONSE = Responses.byStatus(SessionsBatchDeleteResponse::fromResponse); + /** + * Asynchronous {@link #regenerateKybParty(String, String, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture regenerateKybPartyAsync(String sessionId, String partyUuid, RequestOptions options) { + return client.executeAsync(regenerateKybPartyRequest(sessionId, partyUuid), options, REGENERATE_KYB_PARTY_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest batchDeleteRequest(SessionsBatchDeleteParams params) { - return ApiRequest.builder("POST", "/v3/sessions/delete/") - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #regenerateKybParty(String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} + * @return the result + */ + public KYBKeyPeopleParty regenerateKybParty(String sessionId, String partyUuid) { + return client.execute(regenerateKybPartyRequest(sessionId, partyUuid), RequestOptions.none(), REGENERATE_KYB_PARTY_RESPONSE).data(); } - private static final ResponseHandler RETRIEVE_DELETION_JOB_RESPONSE = Responses.json(Conv.model(SessionsRetrieveDeletionJobResponse::fromJson)); + /** + * Asynchronous {@link #regenerateKybParty(String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} + * @return a future completed with the result + */ + public CompletableFuture regenerateKybPartyAsync(String sessionId, String partyUuid) { + return client.executeAsync(regenerateKybPartyRequest(sessionId, partyUuid), RequestOptions.none(), REGENERATE_KYB_PARTY_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest retrieveDeletionJobRequest(String jobId) { - return ApiRequest.builder("GET", "/v3/sessions/delete/{job_id}/") - .pathParam("job_id", jobId) - .build(); + /** + * List session events + * + *

Read the event timeline of one verification session, newest first by sent_at: the screens the user saw, buttons and uploads, failed attempts, status and feature-step changes, and the workflow routing trace. Works for User Verification (KYC) and Business Verification (KYB) session IDs. Events are stored asynchronously, so a new event can take a few seconds to appear. Results are paginated with limit and offset; follow next until it is null. A scoped API key needs sessions: read and decisions: read, and without media: read it receives file_url as null. The routing events are described in Retrieve session. + * + *

{@code GET /v3/session/{sessionId}/events/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param sessionId UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page listEvents(String sessionId, SessionsListEventsParams params, RequestOptions options) { + return client.execute(listEventsRequest(sessionId, params), options, LIST_EVENTS_RESPONSE).data(); } - private static final ResponseHandler UPDATE_DATA_RESPONSE = Responses.json(Conv.model(SessionsUpdateDataResponse::fromJson)); + /** + * Asynchronous {@link #listEvents(String, SessionsListEventsParams, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listEventsAsync(String sessionId, SessionsListEventsParams params, RequestOptions options) { + return client.executeAsync(listEventsRequest(sessionId, params), options, LIST_EVENTS_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest updateDataRequest(String sessionId, SessionsUpdateDataParams params) { - return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/update-data/") - .pathParam("sessionId", sessionId) - .params(params) - .queryFields("node_id") - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #listEvents(String, SessionsListEventsParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public Page listEvents(String sessionId, SessionsListEventsParams params) { + return client.execute(listEventsRequest(sessionId, params), RequestOptions.none(), LIST_EVENTS_RESPONSE).data(); } - private static final ResponseHandler UPDATE_FEATURE_STATUS_RESPONSE = Responses.json(Conv.model(SessionsUpdateFeatureStatusResponse::fromJson)); + /** + * Asynchronous {@link #listEvents(String, SessionsListEventsParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listEventsAsync(String sessionId, SessionsListEventsParams params) { + return client.executeAsync(listEventsRequest(sessionId, params), RequestOptions.none(), LIST_EVENTS_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest updateFeatureStatusRequest(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params) { - return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/features/{nodeId}/update-status/") - .pathParam("sessionId", sessionId) - .pathParam("nodeId", nodeId) - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #listEvents(String, SessionsListEventsParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return the result + */ + public Page listEvents(String sessionId, RequestOptions options) { + return client.execute(listEventsRequest(sessionId, SessionsListEventsParams.builder().build()), options, LIST_EVENTS_RESPONSE).data(); } - private static final ResponseHandler UPDATE_AML_HIT_STATUS_RESPONSE = Responses.jsonNode(); + /** + * Asynchronous {@link #listEvents(String, SessionsListEventsParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listEventsAsync(String sessionId, RequestOptions options) { + return client.executeAsync(listEventsRequest(sessionId, SessionsListEventsParams.builder().build()), options, LIST_EVENTS_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest updateAmlHitStatusRequest(String sessionId, SessionsUpdateAmlHitStatusParams params) { - return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/update-aml-hit-status/") - .pathParam("sessionId", sessionId) - .params(params) - .queryField("node_id", "node_id_query") - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #listEvents(String, SessionsListEventsParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public Page listEvents(String sessionId) { + return client.execute(listEventsRequest(sessionId, SessionsListEventsParams.builder().build()), RequestOptions.none(), LIST_EVENTS_RESPONSE).data(); } - private static final ResponseHandler BULK_UPDATE_AML_HIT_STATUS_RESPONSE = Responses.jsonNode(); + /** + * Asynchronous {@link #listEvents(String, SessionsListEventsParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture> listEventsAsync(String sessionId) { + return client.executeAsync(listEventsRequest(sessionId, SessionsListEventsParams.builder().build()), RequestOptions.none(), LIST_EVENTS_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest bulkUpdateAmlHitStatusRequest(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { - return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/bulk-update-aml-hit-status/") - .pathParam("sessionId", sessionId) - .params(params) - .queryField("node_id", "node_id_query") - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * List AML screening history + * + *

List every completed ongoing-monitoring re-screen of a session's AML check, newest first by completed_at, including the runs where nothing changed: alerts, activity entries and webhooks fire only when a result moves, so this history is the evidence that a clean subject was still being screened. Works for KYC and KYB session IDs; pass node_id when the session ran more than one AML step. Only ongoing-monitoring re-screens are listed (the first screening is part of the session decision) and runs from before this history existed are not reconstructed, so a session without ongoing monitoring returns an empty list. count is exact up to 100 and capped at 100 above that, so follow next until it is null. A scoped API key needs decisions: read; see Continuous AML monitoring. + * + *

{@code GET /v3/session/{sessionId}/aml-screening-history/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param sessionId UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page listAmlScreeningHistory(String sessionId, SessionsListAmlScreeningHistoryParams params, RequestOptions options) { + return client.execute(listAmlScreeningHistoryRequest(sessionId, params), options, LIST_AML_SCREENING_HISTORY_RESPONSE).data(); } - private static final ResponseHandler UPDATE_POA_DATA_RESPONSE = Responses.json(Conv.model(SessionsUpdatePoaDataResponse::fromJson)); + /** + * Asynchronous {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAmlScreeningHistoryAsync(String sessionId, SessionsListAmlScreeningHistoryParams params, RequestOptions options) { + return client.executeAsync(listAmlScreeningHistoryRequest(sessionId, params), options, LIST_AML_SCREENING_HISTORY_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest updatePoaDataRequest(String sessionId, SessionsUpdatePoaDataParams params) { - return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/update-poa-data/") - .pathParam("sessionId", sessionId) - .params(params) - .queryFields("node_id") - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public Page listAmlScreeningHistory(String sessionId, SessionsListAmlScreeningHistoryParams params) { + return client.execute(listAmlScreeningHistoryRequest(sessionId, params), RequestOptions.none(), LIST_AML_SCREENING_HISTORY_RESPONSE).data(); } - private static final ResponseHandler UPDATE_KYB_COMPANY_DATA_RESPONSE = Responses.json(Conv.model(SessionsUpdateKybCompanyDataResponse::fromJson)); + /** + * Asynchronous {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listAmlScreeningHistoryAsync(String sessionId, SessionsListAmlScreeningHistoryParams params) { + return client.executeAsync(listAmlScreeningHistoryRequest(sessionId, params), RequestOptions.none(), LIST_AML_SCREENING_HISTORY_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest updateKybCompanyDataRequest(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params) { - return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/kyb/{companyUuid}/update-data/") - .pathParam("sessionId", sessionId) - .pathParam("companyUuid", companyUuid) - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return the result + */ + public Page listAmlScreeningHistory(String sessionId, RequestOptions options) { + return client.execute(listAmlScreeningHistoryRequest(sessionId, SessionsListAmlScreeningHistoryParams.builder().build()), options, LIST_AML_SCREENING_HISTORY_RESPONSE).data(); } - private static final ResponseHandler UPDATE_KYB_PARTY_RESPONSE = Responses.json(Conv.model(SessionsUpdateKybPartyResponse::fromJson)); + /** + * Asynchronous {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAmlScreeningHistoryAsync(String sessionId, RequestOptions options) { + return client.executeAsync(listAmlScreeningHistoryRequest(sessionId, SessionsListAmlScreeningHistoryParams.builder().build()), options, LIST_AML_SCREENING_HISTORY_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest updateKybPartyRequest(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params) { - return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/kyb/parties/{partyUuid}/") - .pathParam("sessionId", sessionId) - .pathParam("partyUuid", partyUuid) - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public Page listAmlScreeningHistory(String sessionId) { + return client.execute(listAmlScreeningHistoryRequest(sessionId, SessionsListAmlScreeningHistoryParams.builder().build()), RequestOptions.none(), LIST_AML_SCREENING_HISTORY_RESPONSE).data(); } - private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(SessionsListNetworksResponse::fromJson)); + /** + * Asynchronous {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture> listAmlScreeningHistoryAsync(String sessionId) { + return client.executeAsync(listAmlScreeningHistoryRequest(sessionId, SessionsListAmlScreeningHistoryParams.builder().build()), RequestOptions.none(), LIST_AML_SCREENING_HISTORY_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest listNetworksRequest(String sessionId) { - return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/sessions/{session_id}/networks/") - .applicationScoped() - .pathParam("session_id", sessionId) - .build(); + /** + * Export AML screening history as CSV + * + *

Download the same ongoing-monitoring history as GET /v3/session/{sessionId}/aml-screening-history/ as a CSV file, one line per completed re-screen, newest first, ready to hand to an auditor. The file is streamed whole (no pagination) and starts with a header line; an empty cell means unknown, dataset dates are never estimated, and a value that would start a spreadsheet formula is prefixed with an apostrophe. Works for KYC and KYB session IDs; pass node_id when the session ran more than one AML step. Errors are returned as JSON, not CSV. A scoped API key needs decisions: read and media: read: the response is a file, so a key without media: read gets 403. + * + *

{@code GET /v3/session/{sessionId}/aml-screening-history/export/} + *

Returns the file; close the {@link me.didit.core.BinaryResponse} when you stream it. + * + * @param sessionId UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public BinaryResponse exportAmlScreeningHistory(String sessionId, SessionsExportAmlScreeningHistoryParams params, RequestOptions options) { + return client.execute(exportAmlScreeningHistoryRequest(sessionId, params), options, EXPORT_AML_SCREENING_HISTORY_RESPONSE).data(); } - private static final ResponseHandler PREVIEW_RESUBMISSION_RESPONSE = Responses.json(Conv.model(SessionsPreviewResubmissionResponse::fromJson)); + /** + * Asynchronous {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture exportAmlScreeningHistoryAsync(String sessionId, SessionsExportAmlScreeningHistoryParams params, RequestOptions options) { + return client.executeAsync(exportAmlScreeningHistoryRequest(sessionId, params), options, EXPORT_AML_SCREENING_HISTORY_RESPONSE).thenApply(RawResponse::data); + } - private static ApiRequest previewResubmissionRequest(String sessionId, SessionsPreviewResubmissionParams params) { - return ApiRequest.builder("POST", "/v3/session/{sessionId}/resubmission-preview/") - .pathParam("sessionId", sessionId) - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Same as {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public BinaryResponse exportAmlScreeningHistory(String sessionId, SessionsExportAmlScreeningHistoryParams params) { + return client.execute(exportAmlScreeningHistoryRequest(sessionId, params), RequestOptions.none(), EXPORT_AML_SCREENING_HISTORY_RESPONSE).data(); } - /** The {@code sessions} operations returning {@link RawResponse}. */ - public static final class WithRawResponse { - private final ApiClient client; + /** + * Asynchronous {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture exportAmlScreeningHistoryAsync(String sessionId, SessionsExportAmlScreeningHistoryParams params) { + return client.executeAsync(exportAmlScreeningHistoryRequest(sessionId, params), RequestOptions.none(), EXPORT_AML_SCREENING_HISTORY_RESPONSE).thenApply(RawResponse::data); + } - WithRawResponse(ApiClient client) { - this.client = client; + /** + * Same as {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return the result + */ + public BinaryResponse exportAmlScreeningHistory(String sessionId, RequestOptions options) { + return client.execute(exportAmlScreeningHistoryRequest(sessionId, SessionsExportAmlScreeningHistoryParams.builder().build()), options, EXPORT_AML_SCREENING_HISTORY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture exportAmlScreeningHistoryAsync(String sessionId, RequestOptions options) { + return client.executeAsync(exportAmlScreeningHistoryRequest(sessionId, SessionsExportAmlScreeningHistoryParams.builder().build()), options, EXPORT_AML_SCREENING_HISTORY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public BinaryResponse exportAmlScreeningHistory(String sessionId) { + return client.execute(exportAmlScreeningHistoryRequest(sessionId, SessionsExportAmlScreeningHistoryParams.builder().build()), RequestOptions.none(), EXPORT_AML_SCREENING_HISTORY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture exportAmlScreeningHistoryAsync(String sessionId) { + return client.executeAsync(exportAmlScreeningHistoryRequest(sessionId, SessionsExportAmlScreeningHistoryParams.builder().build()), RequestOptions.none(), EXPORT_AML_SCREENING_HISTORY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Add an image to a session + * + *

Attach one extra image or PDF to the ID verification (model_type=kyc) or Proof of Address (model_type=poa) result of a finished User Verification (KYC) session, for example evidence collected outside the flow. The session must be Approved, Declined, In Review or Kyc Expired and carry the result named by model_type; Business Verification (KYB) sessions are not supported. Each call appends one file (to the most recent result when the step ran more than once), keeps the existing files and records a FILE_ADDED entry in the session activity log; the upload sends no webhook and does not change the session status. The file is compressed on upload and is returned in extra_files of the session decision. This endpoint is limited to 10 requests per minute per API key. A scoped API key needs sessions: write, and without media: read it receives the extra_files links as null. + * + *

{@code PATCH /v3/session/{sessionId}/add-images/} + * + * @param sessionId UUID of the User Verification (KYC) session to add the file to (the `session_id` returned by `POST /v3/session/`). A Business Verification (KYB) session ID returns `404`. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public SessionsAddImagesResponse addImages(String sessionId, SessionsAddImagesParams params, RequestOptions options) { + return client.execute(addImagesRequest(sessionId, params), options, ADD_IMAGES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #addImages(String, SessionsAddImagesParams, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture addImagesAsync(String sessionId, SessionsAddImagesParams params, RequestOptions options) { + return client.executeAsync(addImagesRequest(sessionId, params), options, ADD_IMAGES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #addImages(String, SessionsAddImagesParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public SessionsAddImagesResponse addImages(String sessionId, SessionsAddImagesParams params) { + return client.execute(addImagesRequest(sessionId, params), RequestOptions.none(), ADD_IMAGES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #addImages(String, SessionsAddImagesParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture addImagesAsync(String sessionId, SessionsAddImagesParams params) { + return client.executeAsync(addImagesRequest(sessionId, params), RequestOptions.none(), ADD_IMAGES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Update an AML check status + * + *

Set the status of a session's AML screening check as a whole (the status of an aml_screenings[] entry in the session decision), for example Approved after your team cleared every hit. Works for KYC and KYB session IDs; pass node_id (query parameter or body field) when the session ran more than one AML step, otherwise the most recent AML check is updated. When the status actually changes, an AML_STATUS_UPDATED activity entry is recorded and a data.updated webhook is sent; sending the current status returns 200 and changes nothing. The session status is not recomputed: use PATCH /v3/session/{sessionId}/update-status/ for the session decision and PATCH /v3/session/{sessionId}/update-aml-hit-status/ for single hits. A scoped API key needs decisions: write. + * + *

{@code PATCH /v3/session/{sessionId}/update-aml-status/} + * + * @param sessionId UUID of the verification session (the `session_id` returned by `POST /v3/session/`). Accepts both User Verification (KYC) and Business Verification (KYB) session IDs: the server resolves the ID across both session types. Must be a well-formed UUID; a malformed value does not match the route and returns an HTML `404`. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public SessionsUpdateAmlStatusResponse updateAmlStatus(String sessionId, SessionsUpdateAmlStatusParams params, RequestOptions options) { + return client.execute(updateAmlStatusRequest(sessionId, params), options, UPDATE_AML_STATUS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #updateAmlStatus(String, SessionsUpdateAmlStatusParams, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture updateAmlStatusAsync(String sessionId, SessionsUpdateAmlStatusParams params, RequestOptions options) { + return client.executeAsync(updateAmlStatusRequest(sessionId, params), options, UPDATE_AML_STATUS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #updateAmlStatus(String, SessionsUpdateAmlStatusParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public SessionsUpdateAmlStatusResponse updateAmlStatus(String sessionId, SessionsUpdateAmlStatusParams params) { + return client.execute(updateAmlStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_STATUS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #updateAmlStatus(String, SessionsUpdateAmlStatusParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture updateAmlStatusAsync(String sessionId, SessionsUpdateAmlStatusParams params) { + return client.executeAsync(updateAmlStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_STATUS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Simulate a sandbox session's status + * + *

Force a terminal status on a sandbox session after the fact, from your backend, without running the flow. Sandbox applications only: a live session returns 404. Works for User Verification (KYC) and Business Verification (KYB) sessions. + * + *

Set new_status, or let scenario or risk supply it. risk (or the first risk the scenario's magic values carry) adds that warning to the session. The change is recorded in the session's activity. + * + *

send_webhooks (default true) sends exactly the webhooks a real status change sends. With false the status still changes and the console still updates live, but no webhook is logged or sent, the missed-webhook recovery never re-sends that decision, and the next status change sends webhooks as usual. The response echoes the value applied. + * + *

Requires the write:sessions privilege; a scoped API key needs write access to sessions. An unknown session_id returns 404 and another application's session returns 403, both before the body is read; a live session returns 404 only after the body is validated. To simulate from the hosted flow with the session's own token, use POST /v3/session/{sessionId}/sandbox/simulate/. See [Sandbox & Test Data](/integration/sandbox-testing) for the scenario catalog. + * + *

{@code POST /v3/session/{sessionId}/simulate/} + * + * @param sessionId UUID of the sandbox verification session. Accepts both user (KYC) and business (KYB) session IDs. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public SandboxSimulateResponse simulate(String sessionId, SandboxSimulateRequest params, RequestOptions options) { + return client.execute(simulateRequest(sessionId, params), options, SIMULATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #simulate(String, SandboxSimulateRequest, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture simulateAsync(String sessionId, SandboxSimulateRequest params, RequestOptions options) { + return client.executeAsync(simulateRequest(sessionId, params), options, SIMULATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #simulate(String, SandboxSimulateRequest, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public SandboxSimulateResponse simulate(String sessionId, SandboxSimulateRequest params) { + return client.execute(simulateRequest(sessionId, params), RequestOptions.none(), SIMULATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #simulate(String, SandboxSimulateRequest, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture simulateAsync(String sessionId, SandboxSimulateRequest params) { + return client.executeAsync(simulateRequest(sessionId, params), RequestOptions.none(), SIMULATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * List a session's check overrides + * + *

Every override of an individual feature (step) status on a User Verification (KYC) or Business Verification (KYB) session, newest first and not paginated: applied ones, ones waiting for the person to finish the flow (pending_until_flow_finishes) and cancelled ones, each with who asked, their reason, and whether it applied while the person was still in the flow (forced). + * + *

person_in_flow tells you whether a new override with defer_until_flow_finishes: true would wait. flow_finished_at is when the person last finished the flow; it is null while they are still in it (a resubmission included) and when the session closed before they finished. + * + *

Requires the read:sessions privilege; a scoped API key needs read access to decisions. + * + *

{@code GET /v3/session/{sessionId}/features/overrides/} + * + * @param sessionId UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs. + * @param options per-request options that override the client options + * @return the response body + */ + public SessionsListFeatureOverridesResponse listFeatureOverrides(String sessionId, RequestOptions options) { + return client.execute(listFeatureOverridesRequest(sessionId), options, LIST_FEATURE_OVERRIDES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listFeatureOverrides(String, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture listFeatureOverridesAsync(String sessionId, RequestOptions options) { + return client.executeAsync(listFeatureOverridesRequest(sessionId), options, LIST_FEATURE_OVERRIDES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listFeatureOverrides(String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public SessionsListFeatureOverridesResponse listFeatureOverrides(String sessionId) { + return client.execute(listFeatureOverridesRequest(sessionId), RequestOptions.none(), LIST_FEATURE_OVERRIDES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listFeatureOverrides(String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture listFeatureOverridesAsync(String sessionId) { + return client.executeAsync(listFeatureOverridesRequest(sessionId), RequestOptions.none(), LIST_FEATURE_OVERRIDES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Cancel a check override that waits for the flow to finish + * + *

Cancel an override of a feature (step) status that is still pending_until_flow_finishes, so it never applies. The check keeps its status, the cancellation is recorded in the session's activity as FEATURE_OVERRIDE_CANCELLED, and no webhook is sent. The response is the override with state: cancelled and cancel_reason: cancelled_by_reviewer. + * + *

An override that already applied or was cancelled returns 409 with the code feature_override_not_pending. Find override IDs with GET /v3/session/{sessionId}/features/overrides/ or in the override returned by the feature status update. + * + *

Requires the write:sessions privilege; a scoped API key needs write access to decisions. + * + *

{@code POST /v3/session/{sessionId}/features/overrides/{overrideId}/cancel/} + * + * @param sessionId UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs. + * @param overrideId The `id` of the override to cancel. + * @param options per-request options that override the client options + * @return the response body + */ + public FeatureStatusOverride cancelFeatureOverride(String sessionId, String overrideId, RequestOptions options) { + return client.execute(cancelFeatureOverrideRequest(sessionId, overrideId), options, CANCEL_FEATURE_OVERRIDE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #cancelFeatureOverride(String, String, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param overrideId the {@code overrideId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture cancelFeatureOverrideAsync(String sessionId, String overrideId, RequestOptions options) { + return client.executeAsync(cancelFeatureOverrideRequest(sessionId, overrideId), options, CANCEL_FEATURE_OVERRIDE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #cancelFeatureOverride(String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param overrideId the {@code overrideId} + * @return the result + */ + public FeatureStatusOverride cancelFeatureOverride(String sessionId, String overrideId) { + return client.execute(cancelFeatureOverrideRequest(sessionId, overrideId), RequestOptions.none(), CANCEL_FEATURE_OVERRIDE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #cancelFeatureOverride(String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param overrideId the {@code overrideId} + * @return a future completed with the result + */ + public CompletableFuture cancelFeatureOverrideAsync(String sessionId, String overrideId) { + return client.executeAsync(cancelFeatureOverrideRequest(sessionId, overrideId), RequestOptions.none(), CANCEL_FEATURE_OVERRIDE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Preview a session's decision email + * + *

Render the email the person receives when you set a session to Approved or Declined with send_email: true, without changing the session or sending anything. The response shows the subject and HTML exactly as they are sent, the recipient, the sender, the language, and whether confirming the change sends the email at all (will_be_sent, not_sent_reason). + * + *

The recipient is the person's own address: the one they verified in an email verification step of the flow, else the one given in contact_details when the session was created. The sender is your application's verified sender domain when white label applies (sender_kind: customer), else Didit's sender (sender_kind: didit). + * + *

To send exactly what you previewed, call [Update Session Status](/sessions-api/update-status) with the same new_status, send_email: true, email_address set to recipient, email_language set to language and email_preview_token set to preview_token. The receipt expires after 15 minutes. When the recipient, language, session status, sender or email changed since the preview, the update returns 409 before anything changes; preview again and confirm with the new receipt. + * + *

Works for User Verification (KYC) and Business Verification (KYB) sessions in Approved, Declined, In Review, Kyc Expired, Abandoned or Resubmitted status. The status check runs before body validation. Requires the write:sessions privilege; a scoped API key needs write access to decisions. Responses use Cache-Control: no-store: treat them as sensitive verification content. + * + *

{@code POST /v3/session/{sessionId}/decision-email-preview/} + * + * @param sessionId UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public SessionsPreviewDecisionEmailResponse previewDecisionEmail(String sessionId, SessionsPreviewDecisionEmailParams params, RequestOptions options) { + return client.execute(previewDecisionEmailRequest(sessionId, params), options, PREVIEW_DECISION_EMAIL_RESPONSE).data(); + } + + /** + * Asynchronous {@link #previewDecisionEmail(String, SessionsPreviewDecisionEmailParams, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture previewDecisionEmailAsync(String sessionId, SessionsPreviewDecisionEmailParams params, RequestOptions options) { + return client.executeAsync(previewDecisionEmailRequest(sessionId, params), options, PREVIEW_DECISION_EMAIL_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #previewDecisionEmail(String, SessionsPreviewDecisionEmailParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public SessionsPreviewDecisionEmailResponse previewDecisionEmail(String sessionId, SessionsPreviewDecisionEmailParams params) { + return client.execute(previewDecisionEmailRequest(sessionId, params), RequestOptions.none(), PREVIEW_DECISION_EMAIL_RESPONSE).data(); + } + + /** + * Asynchronous {@link #previewDecisionEmail(String, SessionsPreviewDecisionEmailParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture previewDecisionEmailAsync(String sessionId, SessionsPreviewDecisionEmailParams params) { + return client.executeAsync(previewDecisionEmailRequest(sessionId, params), RequestOptions.none(), PREVIEW_DECISION_EMAIL_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(SessionsCreateResponse::fromJson)); + + private static ApiRequest createRequest(SessionsCreateParams params) { + return ApiRequest.builder("POST", "/v3/session/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler RETRIEVE_DECISION_RESPONSE = Responses.json(Conv.model(SessionDecision::fromJson)); + + private static ApiRequest retrieveDecisionRequest(String sessionId, SessionsRetrieveDecisionParams params) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/decision/") + .pathParam("sessionId", sessionId) + .params(params) + .queryFields("include") + .build(); + } + + private static final ResponseHandler DELETE_RESPONSE = Responses.json(Conv.model(SessionsDeleteResponse::fromJson)); + + private static ApiRequest deleteRequest(String sessionId, SessionsDeleteParams params) { + return ApiRequest.builder("DELETE", "/v3/session/{sessionId}/delete/") + .pathParam("sessionId", sessionId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler GENERATE_PDF_RESPONSE = Responses.binary(); + + private static ApiRequest generatePdfRequest(String sessionId) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/generate-pdf/") + .pathParam("sessionId", sessionId) + .accept("application/pdf") + .build(); + } + + private static final ResponseHandler UPDATE_STATUS_RESPONSE = Responses.json(Conv.model(SessionsUpdateStatusResponse::fromJson)); + + private static ApiRequest updateStatusRequest(String sessionId, SessionsUpdateStatusParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/update-status/") + .pathParam("sessionId", sessionId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.union(SessionsListResponse.Results::fromJson)); + + private static ApiRequest listRequest(SessionsListParams params) { + return ApiRequest.builder("GET", "/v3/sessions/") + .params(params) + .queryFields("session_kind", "limit", "offset", "status", "vendor_data", "search", "workflow_id", "date_from", "date_to", "country", "document_type", "document_name", "region", "didit_internal_id", "session_type", "api_service", "screened_full_name", "warning", "status_initialized_as_in_review", "is_kyc_to_be_reviewed", "euuid", "organization_name") + .build(); + } + + private static final ResponseHandler SHARE_RESPONSE = Responses.json(Conv.model(SessionsShareResponse::fromJson)); + + private static ApiRequest shareRequest(String sessionId, SessionsShareParams params) { + return ApiRequest.builder("POST", "/v3/session/{sessionId}/share/") + .pathParam("sessionId", sessionId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler IMPORT_SHARED_RESPONSE = Responses.json(Conv.model(SessionDecision::fromJson)); + + private static ApiRequest importSharedRequest(SessionsImportSharedParams params) { + return ApiRequest.builder("POST", "/v3/session/import-shared/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler BATCH_DELETE_RESPONSE = Responses.byStatus(SessionsBatchDeleteResponse::fromResponse); + + private static ApiRequest batchDeleteRequest(SessionsBatchDeleteParams params) { + return ApiRequest.builder("POST", "/v3/sessions/delete/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler RETRIEVE_DELETION_JOB_RESPONSE = Responses.json(Conv.model(SessionDeletionJob::fromJson)); + + private static ApiRequest retrieveDeletionJobRequest(String jobId) { + return ApiRequest.builder("GET", "/v3/sessions/delete/{job_id}/") + .pathParam("job_id", jobId) + .build(); + } + + private static final ResponseHandler UPDATE_DATA_RESPONSE = Responses.json(Conv.model(SessionsUpdateDataResponse::fromJson)); + + private static ApiRequest updateDataRequest(String sessionId, SessionsUpdateDataParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/update-data/") + .pathParam("sessionId", sessionId) + .params(params) + .queryFields("node_id") + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler UPDATE_FEATURE_STATUS_RESPONSE = Responses.json(Conv.model(SessionsUpdateFeatureStatusResponse::fromJson)); + + private static ApiRequest updateFeatureStatusRequest(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/features/{nodeId}/update-status/") + .pathParam("sessionId", sessionId) + .pathParam("nodeId", nodeId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler UPDATE_AML_HIT_STATUS_RESPONSE = Responses.json(Conv.model(SessionsUpdateAmlHitStatusResponse::fromJson)); + + private static ApiRequest updateAmlHitStatusRequest(String sessionId, SessionsUpdateAmlHitStatusParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/update-aml-hit-status/") + .pathParam("sessionId", sessionId) + .params(params) + .queryField("node_id", "node_id_query") + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler BULK_UPDATE_AML_HIT_STATUS_RESPONSE = Responses.json(Conv.model(SessionsBulkUpdateAmlHitStatusResponse::fromJson)); + + private static ApiRequest bulkUpdateAmlHitStatusRequest(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/bulk-update-aml-hit-status/") + .pathParam("sessionId", sessionId) + .params(params) + .queryField("node_id", "node_id_query") + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler UPDATE_POA_DATA_RESPONSE = Responses.json(Conv.model(SessionsUpdatePoaDataResponse::fromJson)); + + private static ApiRequest updatePoaDataRequest(String sessionId, SessionsUpdatePoaDataParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/update-poa-data/") + .pathParam("sessionId", sessionId) + .params(params) + .queryFields("node_id") + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler UPDATE_KYB_COMPANY_DATA_RESPONSE = Responses.json(Conv.model(SessionsUpdateKybCompanyDataResponse::fromJson)); + + private static ApiRequest updateKybCompanyDataRequest(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/kyb/{companyUuid}/update-data/") + .pathParam("sessionId", sessionId) + .pathParam("companyUuid", companyUuid) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler UPDATE_KYB_PARTY_RESPONSE = Responses.json(Conv.model(SessionsUpdateKybPartyResponse::fromJson)); + + private static ApiRequest updateKybPartyRequest(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/kyb/parties/{partyUuid}/") + .pathParam("sessionId", sessionId) + .pathParam("partyUuid", partyUuid) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(EntityNetworkMembershipList::fromJson)); + + private static ApiRequest listNetworksRequest(String sessionId) { + return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/sessions/{session_id}/networks/") + .applicationScoped() + .pathParam("session_id", sessionId) + .build(); + } + + private static final ResponseHandler PREVIEW_RESUBMISSION_RESPONSE = Responses.json(Conv.model(SessionsPreviewResubmissionResponse::fromJson)); + + private static ApiRequest previewResubmissionRequest(String sessionId, SessionsPreviewResubmissionParams params) { + return ApiRequest.builder("POST", "/v3/session/{sessionId}/resubmission-preview/") + .pathParam("sessionId", sessionId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler UPDATE_DOCUMENT_AI_DATA_RESPONSE = Responses.json(Conv.model(SessionsUpdateDocumentAiDataResponse::fromJson)); + + private static ApiRequest updateDocumentAiDataRequest(String sessionId, String documentUuid, SessionsUpdateDocumentAiDataParams params) { + return ApiRequest.builder("PATCH", "/v3/document-ai/{sessionId}/documents/{documentUuid}/") + .pathParam("sessionId", sessionId) + .pathParam("documentUuid", documentUuid) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler REGENERATE_KYB_PARTY_RESPONSE = Responses.json(Conv.model(KYBKeyPeopleParty::fromJson)); + + private static ApiRequest regenerateKybPartyRequest(String sessionId, String partyUuid) { + return ApiRequest.builder("POST", "/v3/session/{sessionId}/kyb/parties/{partyUuid}/regenerate/") + .pathParam("sessionId", sessionId) + .pathParam("partyUuid", partyUuid) + .build(); + } + + private static final ResponseHandler> LIST_EVENTS_RESPONSE = Responses.page(Conv.model(SessionEvent::fromJson)); + + private static ApiRequest listEventsRequest(String sessionId, SessionsListEventsParams params) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/events/") + .pathParam("sessionId", sessionId) + .params(params) + .queryFields("limit", "offset", "ordering") + .build(); + } + + private static final ResponseHandler> LIST_AML_SCREENING_HISTORY_RESPONSE = Responses.page(Conv.model(AMLScreeningRun::fromJson)); + + private static ApiRequest listAmlScreeningHistoryRequest(String sessionId, SessionsListAmlScreeningHistoryParams params) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/aml-screening-history/") + .pathParam("sessionId", sessionId) + .params(params) + .queryFields("node_id", "limit", "offset", "ordering") + .build(); + } + + private static final ResponseHandler EXPORT_AML_SCREENING_HISTORY_RESPONSE = Responses.binary(); + + private static ApiRequest exportAmlScreeningHistoryRequest(String sessionId, SessionsExportAmlScreeningHistoryParams params) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/aml-screening-history/export/") + .pathParam("sessionId", sessionId) + .params(params) + .queryFields("node_id") + .accept("text/csv") + .build(); + } + + private static final ResponseHandler ADD_IMAGES_RESPONSE = Responses.json(Conv.model(SessionsAddImagesResponse::fromJson)); + + private static ApiRequest addImagesRequest(String sessionId, SessionsAddImagesParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/add-images/") + .pathParam("sessionId", sessionId) + .params(params) + .body(ApiRequest.BodyEncoding.MULTIPART) + .build(); + } + + private static final ResponseHandler UPDATE_AML_STATUS_RESPONSE = Responses.json(Conv.model(SessionsUpdateAmlStatusResponse::fromJson)); + + private static ApiRequest updateAmlStatusRequest(String sessionId, SessionsUpdateAmlStatusParams params) { + return ApiRequest.builder("PATCH", "/v3/session/{sessionId}/update-aml-status/") + .pathParam("sessionId", sessionId) + .params(params) + .queryField("node_id", "node_id_query") + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler SIMULATE_RESPONSE = Responses.json(Conv.model(SandboxSimulateResponse::fromJson)); + + private static ApiRequest simulateRequest(String sessionId, SandboxSimulateRequest params) { + return ApiRequest.builder("POST", "/v3/session/{sessionId}/simulate/") + .pathParam("sessionId", sessionId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler LIST_FEATURE_OVERRIDES_RESPONSE = Responses.json(Conv.model(SessionsListFeatureOverridesResponse::fromJson)); + + private static ApiRequest listFeatureOverridesRequest(String sessionId) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/features/overrides/") + .pathParam("sessionId", sessionId) + .build(); + } + + private static final ResponseHandler CANCEL_FEATURE_OVERRIDE_RESPONSE = Responses.json(Conv.model(FeatureStatusOverride::fromJson)); + + private static ApiRequest cancelFeatureOverrideRequest(String sessionId, String overrideId) { + return ApiRequest.builder("POST", "/v3/session/{sessionId}/features/overrides/{overrideId}/cancel/") + .pathParam("sessionId", sessionId) + .pathParam("overrideId", overrideId) + .build(); + } + + private static final ResponseHandler PREVIEW_DECISION_EMAIL_RESPONSE = Responses.json(Conv.model(SessionsPreviewDecisionEmailResponse::fromJson)); + + private static ApiRequest previewDecisionEmailRequest(String sessionId, SessionsPreviewDecisionEmailParams params) { + return ApiRequest.builder("POST", "/v3/session/{sessionId}/decision-email-preview/") + .pathParam("sessionId", sessionId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + /** The {@code sessions} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #create(SessionsCreateParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse create(SessionsCreateParams params, RequestOptions options) { + return client.execute(createRequest(params), options, CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(SessionsCreateParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createAsync(SessionsCreateParams params, RequestOptions options) { + return client.executeAsync(createRequest(params), options, CREATE_RESPONSE); + } + + /** + * {@link #create(SessionsCreateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse create(SessionsCreateParams params) { + return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(SessionsCreateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> createAsync(SessionsCreateParams params) { + return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse retrieveDecision(String sessionId, SessionsRetrieveDecisionParams params, RequestOptions options) { + return client.execute(retrieveDecisionRequest(sessionId, params), options, RETRIEVE_DECISION_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveDecisionAsync(String sessionId, SessionsRetrieveDecisionParams params, RequestOptions options) { + return client.executeAsync(retrieveDecisionRequest(sessionId, params), options, RETRIEVE_DECISION_RESPONSE); + } + + /** + * {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public RawResponse retrieveDecision(String sessionId, SessionsRetrieveDecisionParams params) { + return client.execute(retrieveDecisionRequest(sessionId, params), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> retrieveDecisionAsync(String sessionId, SessionsRetrieveDecisionParams params) { + return client.executeAsync(retrieveDecisionRequest(sessionId, params), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE); + } + + /** + * {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return the result + */ + public RawResponse retrieveDecision(String sessionId, RequestOptions options) { + return client.execute(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), options, RETRIEVE_DECISION_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveDecisionAsync(String sessionId, RequestOptions options) { + return client.executeAsync(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), options, RETRIEVE_DECISION_RESPONSE); + } + + /** + * {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public RawResponse retrieveDecision(String sessionId) { + return client.execute(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture> retrieveDecisionAsync(String sessionId) { + return client.executeAsync(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE); + } + + /** + * {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse delete(String sessionId, SessionsDeleteParams params, RequestOptions options) { + return client.execute(deleteRequest(sessionId, params), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String sessionId, SessionsDeleteParams params, RequestOptions options) { + return client.executeAsync(deleteRequest(sessionId, params), options, DELETE_RESPONSE); + } + + /** + * {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public RawResponse delete(String sessionId, SessionsDeleteParams params) { + return client.execute(deleteRequest(sessionId, params), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String sessionId, SessionsDeleteParams params) { + return client.executeAsync(deleteRequest(sessionId, params), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return the result + */ + public RawResponse delete(String sessionId, RequestOptions options) { + return client.execute(deleteRequest(sessionId, SessionsDeleteParams.builder().build()), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String sessionId, RequestOptions options) { + return client.executeAsync(deleteRequest(sessionId, SessionsDeleteParams.builder().build()), options, DELETE_RESPONSE); + } + + /** + * {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public RawResponse delete(String sessionId) { + return client.execute(deleteRequest(sessionId, SessionsDeleteParams.builder().build()), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String sessionId) { + return client.executeAsync(deleteRequest(sessionId, SessionsDeleteParams.builder().build()), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * {@link #generatePdf(String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return the result + */ + public RawResponse generatePdf(String sessionId, RequestOptions options) { + return client.execute(generatePdfRequest(sessionId), options, GENERATE_PDF_RESPONSE); + } + + /** + * Asynchronous {@link #generatePdf(String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> generatePdfAsync(String sessionId, RequestOptions options) { + return client.executeAsync(generatePdfRequest(sessionId), options, GENERATE_PDF_RESPONSE); + } + + /** + * {@link #generatePdf(String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public RawResponse generatePdf(String sessionId) { + return client.execute(generatePdfRequest(sessionId), RequestOptions.none(), GENERATE_PDF_RESPONSE); + } + + /** + * Asynchronous {@link #generatePdf(String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture> generatePdfAsync(String sessionId) { + return client.executeAsync(generatePdfRequest(sessionId), RequestOptions.none(), GENERATE_PDF_RESPONSE); + } + + /** + * {@link #updateStatus(String, SessionsUpdateStatusParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse updateStatus(String sessionId, SessionsUpdateStatusParams params, RequestOptions options) { + return client.execute(updateStatusRequest(sessionId, params), options, UPDATE_STATUS_RESPONSE); + } + + /** + * Asynchronous {@link #updateStatus(String, SessionsUpdateStatusParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> updateStatusAsync(String sessionId, SessionsUpdateStatusParams params, RequestOptions options) { + return client.executeAsync(updateStatusRequest(sessionId, params), options, UPDATE_STATUS_RESPONSE); + } + + /** + * {@link #updateStatus(String, SessionsUpdateStatusParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public RawResponse updateStatus(String sessionId, SessionsUpdateStatusParams params) { + return client.execute(updateStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_STATUS_RESPONSE); + } + + /** + * Asynchronous {@link #updateStatus(String, SessionsUpdateStatusParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> updateStatusAsync(String sessionId, SessionsUpdateStatusParams params) { + return client.executeAsync(updateStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_STATUS_RESPONSE); + } + + /** + * {@link #list(SessionsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(SessionsListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(SessionsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(SessionsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE); + } + + /** + * {@link #list(SessionsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse> list(SessionsListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(SessionsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(SessionsListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(SessionsListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse> list(RequestOptions options) { + return client.execute(listRequest(SessionsListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(SessionsListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(SessionsListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * {@link #list(SessionsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse> list() { + return client.execute(listRequest(SessionsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(SessionsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>> listAsync() { + return client.executeAsync(listRequest(SessionsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #share(String, SessionsShareParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse share(String sessionId, SessionsShareParams params, RequestOptions options) { + return client.execute(shareRequest(sessionId, params), options, SHARE_RESPONSE); + } + + /** + * Asynchronous {@link #share(String, SessionsShareParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> shareAsync(String sessionId, SessionsShareParams params, RequestOptions options) { + return client.executeAsync(shareRequest(sessionId, params), options, SHARE_RESPONSE); + } + + /** + * {@link #share(String, SessionsShareParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public RawResponse share(String sessionId, SessionsShareParams params) { + return client.execute(shareRequest(sessionId, params), RequestOptions.none(), SHARE_RESPONSE); + } + + /** + * Asynchronous {@link #share(String, SessionsShareParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> shareAsync(String sessionId, SessionsShareParams params) { + return client.executeAsync(shareRequest(sessionId, params), RequestOptions.none(), SHARE_RESPONSE); + } + + /** + * {@link #importShared(SessionsImportSharedParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse importShared(SessionsImportSharedParams params, RequestOptions options) { + return client.execute(importSharedRequest(params), options, IMPORT_SHARED_RESPONSE); + } + + /** + * Asynchronous {@link #importShared(SessionsImportSharedParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> importSharedAsync(SessionsImportSharedParams params, RequestOptions options) { + return client.executeAsync(importSharedRequest(params), options, IMPORT_SHARED_RESPONSE); + } + + /** + * {@link #importShared(SessionsImportSharedParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse importShared(SessionsImportSharedParams params) { + return client.execute(importSharedRequest(params), RequestOptions.none(), IMPORT_SHARED_RESPONSE); + } + + /** + * Asynchronous {@link #importShared(SessionsImportSharedParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> importSharedAsync(SessionsImportSharedParams params) { + return client.executeAsync(importSharedRequest(params), RequestOptions.none(), IMPORT_SHARED_RESPONSE); + } + + /** + * {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse batchDelete(SessionsBatchDeleteParams params, RequestOptions options) { + return client.execute(batchDeleteRequest(params), options, BATCH_DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> batchDeleteAsync(SessionsBatchDeleteParams params, RequestOptions options) { + return client.executeAsync(batchDeleteRequest(params), options, BATCH_DELETE_RESPONSE); + } + + /** + * {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse batchDelete(SessionsBatchDeleteParams params) { + return client.execute(batchDeleteRequest(params), RequestOptions.none(), BATCH_DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> batchDeleteAsync(SessionsBatchDeleteParams params) { + return client.executeAsync(batchDeleteRequest(params), RequestOptions.none(), BATCH_DELETE_RESPONSE); + } + + /** + * {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse batchDelete(RequestOptions options) { + return client.execute(batchDeleteRequest(SessionsBatchDeleteParams.builder().build()), options, BATCH_DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> batchDeleteAsync(RequestOptions options) { + return client.executeAsync(batchDeleteRequest(SessionsBatchDeleteParams.builder().build()), options, BATCH_DELETE_RESPONSE); + } + + /** + * {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse batchDelete() { + return client.execute(batchDeleteRequest(SessionsBatchDeleteParams.builder().build()), RequestOptions.none(), BATCH_DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> batchDeleteAsync() { + return client.executeAsync(batchDeleteRequest(SessionsBatchDeleteParams.builder().build()), RequestOptions.none(), BATCH_DELETE_RESPONSE); + } + + /** + * {@link #retrieveDeletionJob(String, RequestOptions)} with the raw response. + * + * @param jobId the {@code job_id} + * @param options per-request options + * @return the result + */ + public RawResponse retrieveDeletionJob(String jobId, RequestOptions options) { + return client.execute(retrieveDeletionJobRequest(jobId), options, RETRIEVE_DELETION_JOB_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDeletionJob(String, RequestOptions)} with the raw response. + * + * @param jobId the {@code job_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveDeletionJobAsync(String jobId, RequestOptions options) { + return client.executeAsync(retrieveDeletionJobRequest(jobId), options, RETRIEVE_DELETION_JOB_RESPONSE); + } + + /** + * {@link #retrieveDeletionJob(String, RequestOptions)} with the raw response, with the client's options. + * + * @param jobId the {@code job_id} + * @return the result + */ + public RawResponse retrieveDeletionJob(String jobId) { + return client.execute(retrieveDeletionJobRequest(jobId), RequestOptions.none(), RETRIEVE_DELETION_JOB_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDeletionJob(String, RequestOptions)} with the raw response, with the client's options. + * + * @param jobId the {@code job_id} + * @return a future completed with the result + */ + public CompletableFuture> retrieveDeletionJobAsync(String jobId) { + return client.executeAsync(retrieveDeletionJobRequest(jobId), RequestOptions.none(), RETRIEVE_DELETION_JOB_RESPONSE); + } + + /** + * {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse updateData(String sessionId, SessionsUpdateDataParams params, RequestOptions options) { + return client.execute(updateDataRequest(sessionId, params), options, UPDATE_DATA_RESPONSE); + } + + /** + * Asynchronous {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> updateDataAsync(String sessionId, SessionsUpdateDataParams params, RequestOptions options) { + return client.executeAsync(updateDataRequest(sessionId, params), options, UPDATE_DATA_RESPONSE); + } + + /** + * {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return the result + */ + public RawResponse updateData(String sessionId, SessionsUpdateDataParams params) { + return client.execute(updateDataRequest(sessionId, params), RequestOptions.none(), UPDATE_DATA_RESPONSE); + } + + /** + * Asynchronous {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> updateDataAsync(String sessionId, SessionsUpdateDataParams params) { + return client.executeAsync(updateDataRequest(sessionId, params), RequestOptions.none(), UPDATE_DATA_RESPONSE); + } + + /** + * {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return the result + */ + public RawResponse updateData(String sessionId, RequestOptions options) { + return client.execute(updateDataRequest(sessionId, SessionsUpdateDataParams.builder().build()), options, UPDATE_DATA_RESPONSE); + } + + /** + * Asynchronous {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> updateDataAsync(String sessionId, RequestOptions options) { + return client.executeAsync(updateDataRequest(sessionId, SessionsUpdateDataParams.builder().build()), options, UPDATE_DATA_RESPONSE); + } + + /** + * {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public RawResponse updateData(String sessionId) { + return client.execute(updateDataRequest(sessionId, SessionsUpdateDataParams.builder().build()), RequestOptions.none(), UPDATE_DATA_RESPONSE); + } + + /** + * Asynchronous {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture> updateDataAsync(String sessionId) { + return client.executeAsync(updateDataRequest(sessionId, SessionsUpdateDataParams.builder().build()), RequestOptions.none(), UPDATE_DATA_RESPONSE); } /** - * {@link #create(SessionsCreateParams, RequestOptions)} with the raw response. + * {@link #updateFeatureStatus(String, String, SessionsUpdateFeatureStatusParams, RequestOptions)} with the raw response. * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse create(SessionsCreateParams params, RequestOptions options) { - return client.execute(createRequest(params), options, CREATE_RESPONSE); + public RawResponse updateFeatureStatus(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params, RequestOptions options) { + return client.execute(updateFeatureStatusRequest(sessionId, nodeId, params), options, UPDATE_FEATURE_STATUS_RESPONSE); } /** - * Asynchronous {@link #create(SessionsCreateParams, RequestOptions)} with the raw response. + * Asynchronous {@link #updateFeatureStatus(String, String, SessionsUpdateFeatureStatusParams, RequestOptions)} with the raw response. * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> createAsync(SessionsCreateParams params, RequestOptions options) { - return client.executeAsync(createRequest(params), options, CREATE_RESPONSE); + public CompletableFuture> updateFeatureStatusAsync(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params, RequestOptions options) { + return client.executeAsync(updateFeatureStatusRequest(sessionId, nodeId, params), options, UPDATE_FEATURE_STATUS_RESPONSE); } /** - * {@link #create(SessionsCreateParams, RequestOptions)} with the raw response, with the client's options. + * {@link #updateFeatureStatus(String, String, SessionsUpdateFeatureStatusParams, RequestOptions)} with the raw response, with the client's options. * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} * @param params the request parameters * @return the result */ - public RawResponse create(SessionsCreateParams params) { - return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + public RawResponse updateFeatureStatus(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params) { + return client.execute(updateFeatureStatusRequest(sessionId, nodeId, params), RequestOptions.none(), UPDATE_FEATURE_STATUS_RESPONSE); } /** - * Asynchronous {@link #create(SessionsCreateParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #updateFeatureStatus(String, String, SessionsUpdateFeatureStatusParams, RequestOptions)} with the raw response, with the client's options. * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> createAsync(SessionsCreateParams params) { - return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + public CompletableFuture> updateFeatureStatusAsync(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params) { + return client.executeAsync(updateFeatureStatusRequest(sessionId, nodeId, params), RequestOptions.none(), UPDATE_FEATURE_STATUS_RESPONSE); } /** - * {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response. + * {@link #updateAmlHitStatus(String, SessionsUpdateAmlHitStatusParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse retrieveDecision(String sessionId, SessionsRetrieveDecisionParams params, RequestOptions options) { - return client.execute(retrieveDecisionRequest(sessionId, params), options, RETRIEVE_DECISION_RESPONSE); + public RawResponse updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusParams params, RequestOptions options) { + return client.execute(updateAmlHitStatusRequest(sessionId, params), options, UPDATE_AML_HIT_STATUS_RESPONSE); } /** - * Asynchronous {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response. + * Asynchronous {@link #updateAmlHitStatus(String, SessionsUpdateAmlHitStatusParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> retrieveDecisionAsync(String sessionId, SessionsRetrieveDecisionParams params, RequestOptions options) { - return client.executeAsync(retrieveDecisionRequest(sessionId, params), options, RETRIEVE_DECISION_RESPONSE); + public CompletableFuture> updateAmlHitStatusAsync(String sessionId, SessionsUpdateAmlHitStatusParams params, RequestOptions options) { + return client.executeAsync(updateAmlHitStatusRequest(sessionId, params), options, UPDATE_AML_HIT_STATUS_RESPONSE); } /** - * {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, with the client's options. + * {@link #updateAmlHitStatus(String, SessionsUpdateAmlHitStatusParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return the result */ - public RawResponse retrieveDecision(String sessionId, SessionsRetrieveDecisionParams params) { - return client.execute(retrieveDecisionRequest(sessionId, params), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE); + public RawResponse updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusParams params) { + return client.execute(updateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_HIT_STATUS_RESPONSE); } /** - * Asynchronous {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #updateAmlHitStatus(String, SessionsUpdateAmlHitStatusParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> retrieveDecisionAsync(String sessionId, SessionsRetrieveDecisionParams params) { - return client.executeAsync(retrieveDecisionRequest(sessionId, params), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE); + public CompletableFuture> updateAmlHitStatusAsync(String sessionId, SessionsUpdateAmlHitStatusParams params) { + return client.executeAsync(updateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_HIT_STATUS_RESPONSE); } /** - * {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, without parameters. + * {@link #bulkUpdateAmlHitStatus(String, SessionsBulkUpdateAmlHitStatusParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} + * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse retrieveDecision(String sessionId, RequestOptions options) { - return client.execute(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), options, RETRIEVE_DECISION_RESPONSE); + public RawResponse bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHitStatusParams params, RequestOptions options) { + return client.execute(bulkUpdateAmlHitStatusRequest(sessionId, params), options, BULK_UPDATE_AML_HIT_STATUS_RESPONSE); } /** - * Asynchronous {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, without parameters. + * Asynchronous {@link #bulkUpdateAmlHitStatus(String, SessionsBulkUpdateAmlHitStatusParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} + * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> retrieveDecisionAsync(String sessionId, RequestOptions options) { - return client.executeAsync(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), options, RETRIEVE_DECISION_RESPONSE); + public CompletableFuture> bulkUpdateAmlHitStatusAsync(String sessionId, SessionsBulkUpdateAmlHitStatusParams params, RequestOptions options) { + return client.executeAsync(bulkUpdateAmlHitStatusRequest(sessionId, params), options, BULK_UPDATE_AML_HIT_STATUS_RESPONSE); } /** - * {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * {@link #bulkUpdateAmlHitStatus(String, SessionsBulkUpdateAmlHitStatusParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} + * @param params the request parameters * @return the result */ - public RawResponse retrieveDecision(String sessionId) { - return client.execute(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE); + public RawResponse bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { + return client.execute(bulkUpdateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), BULK_UPDATE_AML_HIT_STATUS_RESPONSE); } /** - * Asynchronous {@link #retrieveDecision(String, SessionsRetrieveDecisionParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * Asynchronous {@link #bulkUpdateAmlHitStatus(String, SessionsBulkUpdateAmlHitStatusParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} + * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> retrieveDecisionAsync(String sessionId) { - return client.executeAsync(retrieveDecisionRequest(sessionId, SessionsRetrieveDecisionParams.builder().build()), RequestOptions.none(), RETRIEVE_DECISION_RESPONSE); + public CompletableFuture> bulkUpdateAmlHitStatusAsync(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { + return client.executeAsync(bulkUpdateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), BULK_UPDATE_AML_HIT_STATUS_RESPONSE); } /** - * {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response. + * {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse delete(String sessionId, SessionsDeleteParams params, RequestOptions options) { - return client.execute(deleteRequest(sessionId, params), options, DELETE_RESPONSE); + public RawResponse updatePoaData(String sessionId, SessionsUpdatePoaDataParams params, RequestOptions options) { + return client.execute(updatePoaDataRequest(sessionId, params), options, UPDATE_POA_DATA_RESPONSE); } /** - * Asynchronous {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response. + * Asynchronous {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> deleteAsync(String sessionId, SessionsDeleteParams params, RequestOptions options) { - return client.executeAsync(deleteRequest(sessionId, params), options, DELETE_RESPONSE); + public CompletableFuture> updatePoaDataAsync(String sessionId, SessionsUpdatePoaDataParams params, RequestOptions options) { + return client.executeAsync(updatePoaDataRequest(sessionId, params), options, UPDATE_POA_DATA_RESPONSE); } /** - * {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, with the client's options. + * {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return the result */ - public RawResponse delete(String sessionId, SessionsDeleteParams params) { - return client.execute(deleteRequest(sessionId, params), RequestOptions.none(), DELETE_RESPONSE); + public RawResponse updatePoaData(String sessionId, SessionsUpdatePoaDataParams params) { + return client.execute(updatePoaDataRequest(sessionId, params), RequestOptions.none(), UPDATE_POA_DATA_RESPONSE); } /** - * Asynchronous {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> deleteAsync(String sessionId, SessionsDeleteParams params) { - return client.executeAsync(deleteRequest(sessionId, params), RequestOptions.none(), DELETE_RESPONSE); + public CompletableFuture> updatePoaDataAsync(String sessionId, SessionsUpdatePoaDataParams params) { + return client.executeAsync(updatePoaDataRequest(sessionId, params), RequestOptions.none(), UPDATE_POA_DATA_RESPONSE); } /** - * {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, without parameters. + * {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} * @param options per-request options * @return the result */ - public RawResponse delete(String sessionId, RequestOptions options) { - return client.execute(deleteRequest(sessionId, SessionsDeleteParams.builder().build()), options, DELETE_RESPONSE); + public RawResponse updatePoaData(String sessionId, RequestOptions options) { + return client.execute(updatePoaDataRequest(sessionId, SessionsUpdatePoaDataParams.builder().build()), options, UPDATE_POA_DATA_RESPONSE); } /** - * Asynchronous {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, without parameters. + * Asynchronous {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> deleteAsync(String sessionId, RequestOptions options) { - return client.executeAsync(deleteRequest(sessionId, SessionsDeleteParams.builder().build()), options, DELETE_RESPONSE); + public CompletableFuture> updatePoaDataAsync(String sessionId, RequestOptions options) { + return client.executeAsync(updatePoaDataRequest(sessionId, SessionsUpdatePoaDataParams.builder().build()), options, UPDATE_POA_DATA_RESPONSE); } /** - * {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} * @return the result */ - public RawResponse delete(String sessionId) { - return client.execute(deleteRequest(sessionId, SessionsDeleteParams.builder().build()), RequestOptions.none(), DELETE_RESPONSE); + public RawResponse updatePoaData(String sessionId) { + return client.execute(updatePoaDataRequest(sessionId, SessionsUpdatePoaDataParams.builder().build()), RequestOptions.none(), UPDATE_POA_DATA_RESPONSE); } /** - * Asynchronous {@link #delete(String, SessionsDeleteParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * Asynchronous {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} * @return a future completed with the result */ - public CompletableFuture> deleteAsync(String sessionId) { - return client.executeAsync(deleteRequest(sessionId, SessionsDeleteParams.builder().build()), RequestOptions.none(), DELETE_RESPONSE); + public CompletableFuture> updatePoaDataAsync(String sessionId) { + return client.executeAsync(updatePoaDataRequest(sessionId, SessionsUpdatePoaDataParams.builder().build()), RequestOptions.none(), UPDATE_POA_DATA_RESPONSE); } /** - * {@link #generatePdf(String, RequestOptions)} with the raw response. + * {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} + * @param companyUuid the {@code companyUuid} + * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse generatePdf(String sessionId, RequestOptions options) { - return client.execute(generatePdfRequest(sessionId), options, GENERATE_PDF_RESPONSE); + public RawResponse updateKybCompanyData(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params, RequestOptions options) { + return client.execute(updateKybCompanyDataRequest(sessionId, companyUuid, params), options, UPDATE_KYB_COMPANY_DATA_RESPONSE); } /** - * Asynchronous {@link #generatePdf(String, RequestOptions)} with the raw response. + * Asynchronous {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} + * @param companyUuid the {@code companyUuid} + * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> generatePdfAsync(String sessionId, RequestOptions options) { - return client.executeAsync(generatePdfRequest(sessionId), options, GENERATE_PDF_RESPONSE); + public CompletableFuture> updateKybCompanyDataAsync(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params, RequestOptions options) { + return client.executeAsync(updateKybCompanyDataRequest(sessionId, companyUuid, params), options, UPDATE_KYB_COMPANY_DATA_RESPONSE); } /** - * {@link #generatePdf(String, RequestOptions)} with the raw response, with the client's options. + * {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} + * @param companyUuid the {@code companyUuid} + * @param params the request parameters * @return the result */ - public RawResponse generatePdf(String sessionId) { - return client.execute(generatePdfRequest(sessionId), RequestOptions.none(), GENERATE_PDF_RESPONSE); + public RawResponse updateKybCompanyData(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params) { + return client.execute(updateKybCompanyDataRequest(sessionId, companyUuid, params), RequestOptions.none(), UPDATE_KYB_COMPANY_DATA_RESPONSE); } /** - * Asynchronous {@link #generatePdf(String, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} + * @param companyUuid the {@code companyUuid} + * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> generatePdfAsync(String sessionId) { - return client.executeAsync(generatePdfRequest(sessionId), RequestOptions.none(), GENERATE_PDF_RESPONSE); + public CompletableFuture> updateKybCompanyDataAsync(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params) { + return client.executeAsync(updateKybCompanyDataRequest(sessionId, companyUuid, params), RequestOptions.none(), UPDATE_KYB_COMPANY_DATA_RESPONSE); } /** - * {@link #updateStatus(String, SessionsUpdateStatusParams, RequestOptions)} with the raw response. + * {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} - * @param params the request parameters + * @param companyUuid the {@code companyUuid} * @param options per-request options * @return the result */ - public RawResponse updateStatus(String sessionId, SessionsUpdateStatusParams params, RequestOptions options) { - return client.execute(updateStatusRequest(sessionId, params), options, UPDATE_STATUS_RESPONSE); + public RawResponse updateKybCompanyData(String sessionId, String companyUuid, RequestOptions options) { + return client.execute(updateKybCompanyDataRequest(sessionId, companyUuid, SessionsUpdateKybCompanyDataParams.builder().build()), options, UPDATE_KYB_COMPANY_DATA_RESPONSE); } /** - * Asynchronous {@link #updateStatus(String, SessionsUpdateStatusParams, RequestOptions)} with the raw response. + * Asynchronous {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} - * @param params the request parameters + * @param companyUuid the {@code companyUuid} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateStatusAsync(String sessionId, SessionsUpdateStatusParams params, RequestOptions options) { - return client.executeAsync(updateStatusRequest(sessionId, params), options, UPDATE_STATUS_RESPONSE); + public CompletableFuture> updateKybCompanyDataAsync(String sessionId, String companyUuid, RequestOptions options) { + return client.executeAsync(updateKybCompanyDataRequest(sessionId, companyUuid, SessionsUpdateKybCompanyDataParams.builder().build()), options, UPDATE_KYB_COMPANY_DATA_RESPONSE); } /** - * {@link #updateStatus(String, SessionsUpdateStatusParams, RequestOptions)} with the raw response, with the client's options. + * {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} - * @param params the request parameters + * @param companyUuid the {@code companyUuid} * @return the result */ - public RawResponse updateStatus(String sessionId, SessionsUpdateStatusParams params) { - return client.execute(updateStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_STATUS_RESPONSE); + public RawResponse updateKybCompanyData(String sessionId, String companyUuid) { + return client.execute(updateKybCompanyDataRequest(sessionId, companyUuid, SessionsUpdateKybCompanyDataParams.builder().build()), RequestOptions.none(), UPDATE_KYB_COMPANY_DATA_RESPONSE); } /** - * Asynchronous {@link #updateStatus(String, SessionsUpdateStatusParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} - * @param params the request parameters + * @param companyUuid the {@code companyUuid} * @return a future completed with the result */ - public CompletableFuture> updateStatusAsync(String sessionId, SessionsUpdateStatusParams params) { - return client.executeAsync(updateStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_STATUS_RESPONSE); + public CompletableFuture> updateKybCompanyDataAsync(String sessionId, String companyUuid) { + return client.executeAsync(updateKybCompanyDataRequest(sessionId, companyUuid, SessionsUpdateKybCompanyDataParams.builder().build()), RequestOptions.none(), UPDATE_KYB_COMPANY_DATA_RESPONSE); } /** - * {@link #list(SessionsListParams, RequestOptions)} with the raw response. + * {@link #updateKybParty(String, String, SessionsUpdateKybPartyParams, RequestOptions)} with the raw response. * + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse> list(SessionsListParams params, RequestOptions options) { - return client.execute(listRequest(params), options, LIST_RESPONSE); + public RawResponse updateKybParty(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params, RequestOptions options) { + return client.execute(updateKybPartyRequest(sessionId, partyUuid, params), options, UPDATE_KYB_PARTY_RESPONSE); } /** - * Asynchronous {@link #list(SessionsListParams, RequestOptions)} with the raw response. + * Asynchronous {@link #updateKybParty(String, String, SessionsUpdateKybPartyParams, RequestOptions)} with the raw response. * + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture>> listAsync(SessionsListParams params, RequestOptions options) { - return client.executeAsync(listRequest(params), options, LIST_RESPONSE); + public CompletableFuture> updateKybPartyAsync(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params, RequestOptions options) { + return client.executeAsync(updateKybPartyRequest(sessionId, partyUuid, params), options, UPDATE_KYB_PARTY_RESPONSE); } /** - * {@link #list(SessionsListParams, RequestOptions)} with the raw response, with the client's options. + * {@link #updateKybParty(String, String, SessionsUpdateKybPartyParams, RequestOptions)} with the raw response, with the client's options. * + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} * @param params the request parameters * @return the result */ - public RawResponse> list(SessionsListParams params) { - return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + public RawResponse updateKybParty(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params) { + return client.execute(updateKybPartyRequest(sessionId, partyUuid, params), RequestOptions.none(), UPDATE_KYB_PARTY_RESPONSE); } /** - * Asynchronous {@link #list(SessionsListParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #updateKybParty(String, String, SessionsUpdateKybPartyParams, RequestOptions)} with the raw response, with the client's options. * + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture>> listAsync(SessionsListParams params) { - return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + public CompletableFuture> updateKybPartyAsync(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params) { + return client.executeAsync(updateKybPartyRequest(sessionId, partyUuid, params), RequestOptions.none(), UPDATE_KYB_PARTY_RESPONSE); } /** - * {@link #list(SessionsListParams, RequestOptions)} with the raw response, without parameters. + * {@link #listNetworks(String, RequestOptions)} with the raw response. * + * @param sessionId the {@code session_id} * @param options per-request options * @return the result */ - public RawResponse> list(RequestOptions options) { - return client.execute(listRequest(SessionsListParams.builder().build()), options, LIST_RESPONSE); + public RawResponse listNetworks(String sessionId, RequestOptions options) { + return client.execute(listNetworksRequest(sessionId), options, LIST_NETWORKS_RESPONSE); } /** - * Asynchronous {@link #list(SessionsListParams, RequestOptions)} with the raw response, without parameters. + * Asynchronous {@link #listNetworks(String, RequestOptions)} with the raw response. * + * @param sessionId the {@code session_id} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture>> listAsync(RequestOptions options) { - return client.executeAsync(listRequest(SessionsListParams.builder().build()), options, LIST_RESPONSE); + public CompletableFuture> listNetworksAsync(String sessionId, RequestOptions options) { + return client.executeAsync(listNetworksRequest(sessionId), options, LIST_NETWORKS_RESPONSE); } /** - * {@link #list(SessionsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * {@link #listNetworks(String, RequestOptions)} with the raw response, with the client's options. * + * @param sessionId the {@code session_id} * @return the result */ - public RawResponse> list() { - return client.execute(listRequest(SessionsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + public RawResponse listNetworks(String sessionId) { + return client.execute(listNetworksRequest(sessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } /** - * Asynchronous {@link #list(SessionsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * Asynchronous {@link #listNetworks(String, RequestOptions)} with the raw response, with the client's options. * + * @param sessionId the {@code session_id} * @return a future completed with the result */ - public CompletableFuture>> listAsync() { - return client.executeAsync(listRequest(SessionsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + public CompletableFuture> listNetworksAsync(String sessionId) { + return client.executeAsync(listNetworksRequest(sessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } /** - * {@link #share(String, SessionsShareParams, RequestOptions)} with the raw response. + * {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse share(String sessionId, SessionsShareParams params, RequestOptions options) { - return client.execute(shareRequest(sessionId, params), options, SHARE_RESPONSE); + public RawResponse previewResubmission(String sessionId, SessionsPreviewResubmissionParams params, RequestOptions options) { + return client.execute(previewResubmissionRequest(sessionId, params), options, PREVIEW_RESUBMISSION_RESPONSE); } /** - * Asynchronous {@link #share(String, SessionsShareParams, RequestOptions)} with the raw response. + * Asynchronous {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> shareAsync(String sessionId, SessionsShareParams params, RequestOptions options) { - return client.executeAsync(shareRequest(sessionId, params), options, SHARE_RESPONSE); + public CompletableFuture> previewResubmissionAsync(String sessionId, SessionsPreviewResubmissionParams params, RequestOptions options) { + return client.executeAsync(previewResubmissionRequest(sessionId, params), options, PREVIEW_RESUBMISSION_RESPONSE); } /** - * {@link #share(String, SessionsShareParams, RequestOptions)} with the raw response, with the client's options. + * {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return the result */ - public RawResponse share(String sessionId, SessionsShareParams params) { - return client.execute(shareRequest(sessionId, params), RequestOptions.none(), SHARE_RESPONSE); + public RawResponse previewResubmission(String sessionId, SessionsPreviewResubmissionParams params) { + return client.execute(previewResubmissionRequest(sessionId, params), RequestOptions.none(), PREVIEW_RESUBMISSION_RESPONSE); } /** - * Asynchronous {@link #share(String, SessionsShareParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> shareAsync(String sessionId, SessionsShareParams params) { - return client.executeAsync(shareRequest(sessionId, params), RequestOptions.none(), SHARE_RESPONSE); + public CompletableFuture> previewResubmissionAsync(String sessionId, SessionsPreviewResubmissionParams params) { + return client.executeAsync(previewResubmissionRequest(sessionId, params), RequestOptions.none(), PREVIEW_RESUBMISSION_RESPONSE); } /** - * {@link #importShared(SessionsImportSharedParams, RequestOptions)} with the raw response. + * {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response, without parameters. * - * @param params the request parameters + * @param sessionId the {@code sessionId} * @param options per-request options * @return the result */ - public RawResponse importShared(SessionsImportSharedParams params, RequestOptions options) { - return client.execute(importSharedRequest(params), options, IMPORT_SHARED_RESPONSE); + public RawResponse previewResubmission(String sessionId, RequestOptions options) { + return client.execute(previewResubmissionRequest(sessionId, SessionsPreviewResubmissionParams.builder().build()), options, PREVIEW_RESUBMISSION_RESPONSE); } /** - * Asynchronous {@link #importShared(SessionsImportSharedParams, RequestOptions)} with the raw response. + * Asynchronous {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response, without parameters. * - * @param params the request parameters + * @param sessionId the {@code sessionId} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> importSharedAsync(SessionsImportSharedParams params, RequestOptions options) { - return client.executeAsync(importSharedRequest(params), options, IMPORT_SHARED_RESPONSE); + public CompletableFuture> previewResubmissionAsync(String sessionId, RequestOptions options) { + return client.executeAsync(previewResubmissionRequest(sessionId, SessionsPreviewResubmissionParams.builder().build()), options, PREVIEW_RESUBMISSION_RESPONSE); } /** - * {@link #importShared(SessionsImportSharedParams, RequestOptions)} with the raw response, with the client's options. + * {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response, without parameters, with the client's options. * - * @param params the request parameters + * @param sessionId the {@code sessionId} * @return the result */ - public RawResponse importShared(SessionsImportSharedParams params) { - return client.execute(importSharedRequest(params), RequestOptions.none(), IMPORT_SHARED_RESPONSE); + public RawResponse previewResubmission(String sessionId) { + return client.execute(previewResubmissionRequest(sessionId, SessionsPreviewResubmissionParams.builder().build()), RequestOptions.none(), PREVIEW_RESUBMISSION_RESPONSE); } /** - * Asynchronous {@link #importShared(SessionsImportSharedParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response, without parameters, with the client's options. * - * @param params the request parameters + * @param sessionId the {@code sessionId} * @return a future completed with the result */ - public CompletableFuture> importSharedAsync(SessionsImportSharedParams params) { - return client.executeAsync(importSharedRequest(params), RequestOptions.none(), IMPORT_SHARED_RESPONSE); + public CompletableFuture> previewResubmissionAsync(String sessionId) { + return client.executeAsync(previewResubmissionRequest(sessionId, SessionsPreviewResubmissionParams.builder().build()), RequestOptions.none(), PREVIEW_RESUBMISSION_RESPONSE); } /** - * {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response. + * {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)} with the raw response. * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse batchDelete(SessionsBatchDeleteParams params, RequestOptions options) { - return client.execute(batchDeleteRequest(params), options, BATCH_DELETE_RESPONSE); + public RawResponse updateDocumentAiData(String sessionId, String documentUuid, SessionsUpdateDocumentAiDataParams params, RequestOptions options) { + return client.execute(updateDocumentAiDataRequest(sessionId, documentUuid, params), options, UPDATE_DOCUMENT_AI_DATA_RESPONSE); } /** - * Asynchronous {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response. + * Asynchronous {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)} with the raw response. * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> batchDeleteAsync(SessionsBatchDeleteParams params, RequestOptions options) { - return client.executeAsync(batchDeleteRequest(params), options, BATCH_DELETE_RESPONSE); + public CompletableFuture> updateDocumentAiDataAsync(String sessionId, String documentUuid, SessionsUpdateDocumentAiDataParams params, RequestOptions options) { + return client.executeAsync(updateDocumentAiDataRequest(sessionId, documentUuid, params), options, UPDATE_DOCUMENT_AI_DATA_RESPONSE); } /** - * {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, with the client's options. + * {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)} with the raw response, with the client's options. * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} * @param params the request parameters * @return the result */ - public RawResponse batchDelete(SessionsBatchDeleteParams params) { - return client.execute(batchDeleteRequest(params), RequestOptions.none(), BATCH_DELETE_RESPONSE); + public RawResponse updateDocumentAiData(String sessionId, String documentUuid, SessionsUpdateDocumentAiDataParams params) { + return client.execute(updateDocumentAiDataRequest(sessionId, documentUuid, params), RequestOptions.none(), UPDATE_DOCUMENT_AI_DATA_RESPONSE); } /** - * Asynchronous {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)} with the raw response, with the client's options. * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> batchDeleteAsync(SessionsBatchDeleteParams params) { - return client.executeAsync(batchDeleteRequest(params), RequestOptions.none(), BATCH_DELETE_RESPONSE); + public CompletableFuture> updateDocumentAiDataAsync(String sessionId, String documentUuid, SessionsUpdateDocumentAiDataParams params) { + return client.executeAsync(updateDocumentAiDataRequest(sessionId, documentUuid, params), RequestOptions.none(), UPDATE_DOCUMENT_AI_DATA_RESPONSE); } /** - * {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, without parameters. + * {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)} with the raw response, without parameters. * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} * @param options per-request options * @return the result */ - public RawResponse batchDelete(RequestOptions options) { - return client.execute(batchDeleteRequest(SessionsBatchDeleteParams.builder().build()), options, BATCH_DELETE_RESPONSE); + public RawResponse updateDocumentAiData(String sessionId, String documentUuid, RequestOptions options) { + return client.execute(updateDocumentAiDataRequest(sessionId, documentUuid, SessionsUpdateDocumentAiDataParams.builder().build()), options, UPDATE_DOCUMENT_AI_DATA_RESPONSE); } /** - * Asynchronous {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, without parameters. + * Asynchronous {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)} with the raw response, without parameters. * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> batchDeleteAsync(RequestOptions options) { - return client.executeAsync(batchDeleteRequest(SessionsBatchDeleteParams.builder().build()), options, BATCH_DELETE_RESPONSE); + public CompletableFuture> updateDocumentAiDataAsync(String sessionId, String documentUuid, RequestOptions options) { + return client.executeAsync(updateDocumentAiDataRequest(sessionId, documentUuid, SessionsUpdateDocumentAiDataParams.builder().build()), options, UPDATE_DOCUMENT_AI_DATA_RESPONSE); } /** - * {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} * @return the result */ - public RawResponse batchDelete() { - return client.execute(batchDeleteRequest(SessionsBatchDeleteParams.builder().build()), RequestOptions.none(), BATCH_DELETE_RESPONSE); + public RawResponse updateDocumentAiData(String sessionId, String documentUuid) { + return client.execute(updateDocumentAiDataRequest(sessionId, documentUuid, SessionsUpdateDocumentAiDataParams.builder().build()), RequestOptions.none(), UPDATE_DOCUMENT_AI_DATA_RESPONSE); } /** - * Asynchronous {@link #batchDelete(SessionsBatchDeleteParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * Asynchronous {@link #updateDocumentAiData(String, String, SessionsUpdateDocumentAiDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. * + * @param sessionId the {@code sessionId} + * @param documentUuid the {@code documentUuid} * @return a future completed with the result */ - public CompletableFuture> batchDeleteAsync() { - return client.executeAsync(batchDeleteRequest(SessionsBatchDeleteParams.builder().build()), RequestOptions.none(), BATCH_DELETE_RESPONSE); + public CompletableFuture> updateDocumentAiDataAsync(String sessionId, String documentUuid) { + return client.executeAsync(updateDocumentAiDataRequest(sessionId, documentUuid, SessionsUpdateDocumentAiDataParams.builder().build()), RequestOptions.none(), UPDATE_DOCUMENT_AI_DATA_RESPONSE); } /** - * {@link #retrieveDeletionJob(String, RequestOptions)} with the raw response. + * {@link #regenerateKybParty(String, String, RequestOptions)} with the raw response. * - * @param jobId the {@code job_id} + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} * @param options per-request options * @return the result */ - public RawResponse retrieveDeletionJob(String jobId, RequestOptions options) { - return client.execute(retrieveDeletionJobRequest(jobId), options, RETRIEVE_DELETION_JOB_RESPONSE); + public RawResponse regenerateKybParty(String sessionId, String partyUuid, RequestOptions options) { + return client.execute(regenerateKybPartyRequest(sessionId, partyUuid), options, REGENERATE_KYB_PARTY_RESPONSE); } /** - * Asynchronous {@link #retrieveDeletionJob(String, RequestOptions)} with the raw response. + * Asynchronous {@link #regenerateKybParty(String, String, RequestOptions)} with the raw response. * - * @param jobId the {@code job_id} + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> retrieveDeletionJobAsync(String jobId, RequestOptions options) { - return client.executeAsync(retrieveDeletionJobRequest(jobId), options, RETRIEVE_DELETION_JOB_RESPONSE); + public CompletableFuture> regenerateKybPartyAsync(String sessionId, String partyUuid, RequestOptions options) { + return client.executeAsync(regenerateKybPartyRequest(sessionId, partyUuid), options, REGENERATE_KYB_PARTY_RESPONSE); } /** - * {@link #retrieveDeletionJob(String, RequestOptions)} with the raw response, with the client's options. + * {@link #regenerateKybParty(String, String, RequestOptions)} with the raw response, with the client's options. * - * @param jobId the {@code job_id} + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} * @return the result */ - public RawResponse retrieveDeletionJob(String jobId) { - return client.execute(retrieveDeletionJobRequest(jobId), RequestOptions.none(), RETRIEVE_DELETION_JOB_RESPONSE); + public RawResponse regenerateKybParty(String sessionId, String partyUuid) { + return client.execute(regenerateKybPartyRequest(sessionId, partyUuid), RequestOptions.none(), REGENERATE_KYB_PARTY_RESPONSE); } /** - * Asynchronous {@link #retrieveDeletionJob(String, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #regenerateKybParty(String, String, RequestOptions)} with the raw response, with the client's options. * - * @param jobId the {@code job_id} + * @param sessionId the {@code sessionId} + * @param partyUuid the {@code partyUuid} * @return a future completed with the result */ - public CompletableFuture> retrieveDeletionJobAsync(String jobId) { - return client.executeAsync(retrieveDeletionJobRequest(jobId), RequestOptions.none(), RETRIEVE_DELETION_JOB_RESPONSE); + public CompletableFuture> regenerateKybPartyAsync(String sessionId, String partyUuid) { + return client.executeAsync(regenerateKybPartyRequest(sessionId, partyUuid), RequestOptions.none(), REGENERATE_KYB_PARTY_RESPONSE); } /** - * {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response. + * {@link #listEvents(String, SessionsListEventsParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse updateData(String sessionId, SessionsUpdateDataParams params, RequestOptions options) { - return client.execute(updateDataRequest(sessionId, params), options, UPDATE_DATA_RESPONSE); + public RawResponse> listEvents(String sessionId, SessionsListEventsParams params, RequestOptions options) { + return client.execute(listEventsRequest(sessionId, params), options, LIST_EVENTS_RESPONSE); } /** - * Asynchronous {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response. + * Asynchronous {@link #listEvents(String, SessionsListEventsParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateDataAsync(String sessionId, SessionsUpdateDataParams params, RequestOptions options) { - return client.executeAsync(updateDataRequest(sessionId, params), options, UPDATE_DATA_RESPONSE); + public CompletableFuture>> listEventsAsync(String sessionId, SessionsListEventsParams params, RequestOptions options) { + return client.executeAsync(listEventsRequest(sessionId, params), options, LIST_EVENTS_RESPONSE); } /** - * {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, with the client's options. + * {@link #listEvents(String, SessionsListEventsParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return the result */ - public RawResponse updateData(String sessionId, SessionsUpdateDataParams params) { - return client.execute(updateDataRequest(sessionId, params), RequestOptions.none(), UPDATE_DATA_RESPONSE); + public RawResponse> listEvents(String sessionId, SessionsListEventsParams params) { + return client.execute(listEventsRequest(sessionId, params), RequestOptions.none(), LIST_EVENTS_RESPONSE); } /** - * Asynchronous {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #listEvents(String, SessionsListEventsParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updateDataAsync(String sessionId, SessionsUpdateDataParams params) { - return client.executeAsync(updateDataRequest(sessionId, params), RequestOptions.none(), UPDATE_DATA_RESPONSE); + public CompletableFuture>> listEventsAsync(String sessionId, SessionsListEventsParams params) { + return client.executeAsync(listEventsRequest(sessionId, params), RequestOptions.none(), LIST_EVENTS_RESPONSE); } /** - * {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, without parameters. + * {@link #listEvents(String, SessionsListEventsParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} * @param options per-request options * @return the result */ - public RawResponse updateData(String sessionId, RequestOptions options) { - return client.execute(updateDataRequest(sessionId, SessionsUpdateDataParams.builder().build()), options, UPDATE_DATA_RESPONSE); + public RawResponse> listEvents(String sessionId, RequestOptions options) { + return client.execute(listEventsRequest(sessionId, SessionsListEventsParams.builder().build()), options, LIST_EVENTS_RESPONSE); } /** - * Asynchronous {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, without parameters. + * Asynchronous {@link #listEvents(String, SessionsListEventsParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateDataAsync(String sessionId, RequestOptions options) { - return client.executeAsync(updateDataRequest(sessionId, SessionsUpdateDataParams.builder().build()), options, UPDATE_DATA_RESPONSE); + public CompletableFuture>> listEventsAsync(String sessionId, RequestOptions options) { + return client.executeAsync(listEventsRequest(sessionId, SessionsListEventsParams.builder().build()), options, LIST_EVENTS_RESPONSE); } /** - * {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * {@link #listEvents(String, SessionsListEventsParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} * @return the result */ - public RawResponse updateData(String sessionId) { - return client.execute(updateDataRequest(sessionId, SessionsUpdateDataParams.builder().build()), RequestOptions.none(), UPDATE_DATA_RESPONSE); + public RawResponse> listEvents(String sessionId) { + return client.execute(listEventsRequest(sessionId, SessionsListEventsParams.builder().build()), RequestOptions.none(), LIST_EVENTS_RESPONSE); } /** - * Asynchronous {@link #updateData(String, SessionsUpdateDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * Asynchronous {@link #listEvents(String, SessionsListEventsParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} * @return a future completed with the result */ - public CompletableFuture> updateDataAsync(String sessionId) { - return client.executeAsync(updateDataRequest(sessionId, SessionsUpdateDataParams.builder().build()), RequestOptions.none(), UPDATE_DATA_RESPONSE); + public CompletableFuture>> listEventsAsync(String sessionId) { + return client.executeAsync(listEventsRequest(sessionId, SessionsListEventsParams.builder().build()), RequestOptions.none(), LIST_EVENTS_RESPONSE); } /** - * {@link #updateFeatureStatus(String, String, SessionsUpdateFeatureStatusParams, RequestOptions)} with the raw response. + * {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} - * @param nodeId the {@code nodeId} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse updateFeatureStatus(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params, RequestOptions options) { - return client.execute(updateFeatureStatusRequest(sessionId, nodeId, params), options, UPDATE_FEATURE_STATUS_RESPONSE); + public RawResponse> listAmlScreeningHistory(String sessionId, SessionsListAmlScreeningHistoryParams params, RequestOptions options) { + return client.execute(listAmlScreeningHistoryRequest(sessionId, params), options, LIST_AML_SCREENING_HISTORY_RESPONSE); } /** - * Asynchronous {@link #updateFeatureStatus(String, String, SessionsUpdateFeatureStatusParams, RequestOptions)} with the raw response. + * Asynchronous {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} - * @param nodeId the {@code nodeId} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateFeatureStatusAsync(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params, RequestOptions options) { - return client.executeAsync(updateFeatureStatusRequest(sessionId, nodeId, params), options, UPDATE_FEATURE_STATUS_RESPONSE); + public CompletableFuture>> listAmlScreeningHistoryAsync(String sessionId, SessionsListAmlScreeningHistoryParams params, RequestOptions options) { + return client.executeAsync(listAmlScreeningHistoryRequest(sessionId, params), options, LIST_AML_SCREENING_HISTORY_RESPONSE); } /** - * {@link #updateFeatureStatus(String, String, SessionsUpdateFeatureStatusParams, RequestOptions)} with the raw response, with the client's options. + * {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} - * @param nodeId the {@code nodeId} * @param params the request parameters * @return the result */ - public RawResponse updateFeatureStatus(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params) { - return client.execute(updateFeatureStatusRequest(sessionId, nodeId, params), RequestOptions.none(), UPDATE_FEATURE_STATUS_RESPONSE); + public RawResponse> listAmlScreeningHistory(String sessionId, SessionsListAmlScreeningHistoryParams params) { + return client.execute(listAmlScreeningHistoryRequest(sessionId, params), RequestOptions.none(), LIST_AML_SCREENING_HISTORY_RESPONSE); } /** - * Asynchronous {@link #updateFeatureStatus(String, String, SessionsUpdateFeatureStatusParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} - * @param nodeId the {@code nodeId} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updateFeatureStatusAsync(String sessionId, String nodeId, SessionsUpdateFeatureStatusParams params) { - return client.executeAsync(updateFeatureStatusRequest(sessionId, nodeId, params), RequestOptions.none(), UPDATE_FEATURE_STATUS_RESPONSE); + public CompletableFuture>> listAmlScreeningHistoryAsync(String sessionId, SessionsListAmlScreeningHistoryParams params) { + return client.executeAsync(listAmlScreeningHistoryRequest(sessionId, params), RequestOptions.none(), LIST_AML_SCREENING_HISTORY_RESPONSE); } /** - * {@link #updateAmlHitStatus(String, SessionsUpdateAmlHitStatusParams, RequestOptions)} with the raw response. + * {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} - * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusParams params, RequestOptions options) { - return client.execute(updateAmlHitStatusRequest(sessionId, params), options, UPDATE_AML_HIT_STATUS_RESPONSE); + public RawResponse> listAmlScreeningHistory(String sessionId, RequestOptions options) { + return client.execute(listAmlScreeningHistoryRequest(sessionId, SessionsListAmlScreeningHistoryParams.builder().build()), options, LIST_AML_SCREENING_HISTORY_RESPONSE); } /** - * Asynchronous {@link #updateAmlHitStatus(String, SessionsUpdateAmlHitStatusParams, RequestOptions)} with the raw response. + * Asynchronous {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} - * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateAmlHitStatusAsync(String sessionId, SessionsUpdateAmlHitStatusParams params, RequestOptions options) { - return client.executeAsync(updateAmlHitStatusRequest(sessionId, params), options, UPDATE_AML_HIT_STATUS_RESPONSE); + public CompletableFuture>> listAmlScreeningHistoryAsync(String sessionId, RequestOptions options) { + return client.executeAsync(listAmlScreeningHistoryRequest(sessionId, SessionsListAmlScreeningHistoryParams.builder().build()), options, LIST_AML_SCREENING_HISTORY_RESPONSE); } /** - * {@link #updateAmlHitStatus(String, SessionsUpdateAmlHitStatusParams, RequestOptions)} with the raw response, with the client's options. + * {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} - * @param params the request parameters * @return the result */ - public RawResponse updateAmlHitStatus(String sessionId, SessionsUpdateAmlHitStatusParams params) { - return client.execute(updateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_HIT_STATUS_RESPONSE); + public RawResponse> listAmlScreeningHistory(String sessionId) { + return client.execute(listAmlScreeningHistoryRequest(sessionId, SessionsListAmlScreeningHistoryParams.builder().build()), RequestOptions.none(), LIST_AML_SCREENING_HISTORY_RESPONSE); } /** - * Asynchronous {@link #updateAmlHitStatus(String, SessionsUpdateAmlHitStatusParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #listAmlScreeningHistory(String, SessionsListAmlScreeningHistoryParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} - * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updateAmlHitStatusAsync(String sessionId, SessionsUpdateAmlHitStatusParams params) { - return client.executeAsync(updateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_HIT_STATUS_RESPONSE); + public CompletableFuture>> listAmlScreeningHistoryAsync(String sessionId) { + return client.executeAsync(listAmlScreeningHistoryRequest(sessionId, SessionsListAmlScreeningHistoryParams.builder().build()), RequestOptions.none(), LIST_AML_SCREENING_HISTORY_RESPONSE); } /** - * {@link #bulkUpdateAmlHitStatus(String, SessionsBulkUpdateAmlHitStatusParams, RequestOptions)} with the raw response. + * {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHitStatusParams params, RequestOptions options) { - return client.execute(bulkUpdateAmlHitStatusRequest(sessionId, params), options, BULK_UPDATE_AML_HIT_STATUS_RESPONSE); + public RawResponse exportAmlScreeningHistory(String sessionId, SessionsExportAmlScreeningHistoryParams params, RequestOptions options) { + return client.execute(exportAmlScreeningHistoryRequest(sessionId, params), options, EXPORT_AML_SCREENING_HISTORY_RESPONSE); } /** - * Asynchronous {@link #bulkUpdateAmlHitStatus(String, SessionsBulkUpdateAmlHitStatusParams, RequestOptions)} with the raw response. + * Asynchronous {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> bulkUpdateAmlHitStatusAsync(String sessionId, SessionsBulkUpdateAmlHitStatusParams params, RequestOptions options) { - return client.executeAsync(bulkUpdateAmlHitStatusRequest(sessionId, params), options, BULK_UPDATE_AML_HIT_STATUS_RESPONSE); + public CompletableFuture> exportAmlScreeningHistoryAsync(String sessionId, SessionsExportAmlScreeningHistoryParams params, RequestOptions options) { + return client.executeAsync(exportAmlScreeningHistoryRequest(sessionId, params), options, EXPORT_AML_SCREENING_HISTORY_RESPONSE); } /** - * {@link #bulkUpdateAmlHitStatus(String, SessionsBulkUpdateAmlHitStatusParams, RequestOptions)} with the raw response, with the client's options. + * {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return the result */ - public RawResponse bulkUpdateAmlHitStatus(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { - return client.execute(bulkUpdateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), BULK_UPDATE_AML_HIT_STATUS_RESPONSE); + public RawResponse exportAmlScreeningHistory(String sessionId, SessionsExportAmlScreeningHistoryParams params) { + return client.execute(exportAmlScreeningHistoryRequest(sessionId, params), RequestOptions.none(), EXPORT_AML_SCREENING_HISTORY_RESPONSE); } /** - * Asynchronous {@link #bulkUpdateAmlHitStatus(String, SessionsBulkUpdateAmlHitStatusParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> bulkUpdateAmlHitStatusAsync(String sessionId, SessionsBulkUpdateAmlHitStatusParams params) { - return client.executeAsync(bulkUpdateAmlHitStatusRequest(sessionId, params), RequestOptions.none(), BULK_UPDATE_AML_HIT_STATUS_RESPONSE); + public CompletableFuture> exportAmlScreeningHistoryAsync(String sessionId, SessionsExportAmlScreeningHistoryParams params) { + return client.executeAsync(exportAmlScreeningHistoryRequest(sessionId, params), RequestOptions.none(), EXPORT_AML_SCREENING_HISTORY_RESPONSE); } /** - * {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response. + * {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} - * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse updatePoaData(String sessionId, SessionsUpdatePoaDataParams params, RequestOptions options) { - return client.execute(updatePoaDataRequest(sessionId, params), options, UPDATE_POA_DATA_RESPONSE); + public RawResponse exportAmlScreeningHistory(String sessionId, RequestOptions options) { + return client.execute(exportAmlScreeningHistoryRequest(sessionId, SessionsExportAmlScreeningHistoryParams.builder().build()), options, EXPORT_AML_SCREENING_HISTORY_RESPONSE); } /** - * Asynchronous {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response. + * Asynchronous {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)} with the raw response, without parameters. * * @param sessionId the {@code sessionId} - * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updatePoaDataAsync(String sessionId, SessionsUpdatePoaDataParams params, RequestOptions options) { - return client.executeAsync(updatePoaDataRequest(sessionId, params), options, UPDATE_POA_DATA_RESPONSE); + public CompletableFuture> exportAmlScreeningHistoryAsync(String sessionId, RequestOptions options) { + return client.executeAsync(exportAmlScreeningHistoryRequest(sessionId, SessionsExportAmlScreeningHistoryParams.builder().build()), options, EXPORT_AML_SCREENING_HISTORY_RESPONSE); } /** - * {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, with the client's options. + * {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} - * @param params the request parameters * @return the result */ - public RawResponse updatePoaData(String sessionId, SessionsUpdatePoaDataParams params) { - return client.execute(updatePoaDataRequest(sessionId, params), RequestOptions.none(), UPDATE_POA_DATA_RESPONSE); + public RawResponse exportAmlScreeningHistory(String sessionId) { + return client.execute(exportAmlScreeningHistoryRequest(sessionId, SessionsExportAmlScreeningHistoryParams.builder().build()), RequestOptions.none(), EXPORT_AML_SCREENING_HISTORY_RESPONSE); } /** - * Asynchronous {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #exportAmlScreeningHistory(String, SessionsExportAmlScreeningHistoryParams, RequestOptions)} with the raw response, without parameters, with the client's options. * * @param sessionId the {@code sessionId} - * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updatePoaDataAsync(String sessionId, SessionsUpdatePoaDataParams params) { - return client.executeAsync(updatePoaDataRequest(sessionId, params), RequestOptions.none(), UPDATE_POA_DATA_RESPONSE); + public CompletableFuture> exportAmlScreeningHistoryAsync(String sessionId) { + return client.executeAsync(exportAmlScreeningHistoryRequest(sessionId, SessionsExportAmlScreeningHistoryParams.builder().build()), RequestOptions.none(), EXPORT_AML_SCREENING_HISTORY_RESPONSE); } /** - * {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, without parameters. + * {@link #addImages(String, SessionsAddImagesParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} + * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse updatePoaData(String sessionId, RequestOptions options) { - return client.execute(updatePoaDataRequest(sessionId, SessionsUpdatePoaDataParams.builder().build()), options, UPDATE_POA_DATA_RESPONSE); + public RawResponse addImages(String sessionId, SessionsAddImagesParams params, RequestOptions options) { + return client.execute(addImagesRequest(sessionId, params), options, ADD_IMAGES_RESPONSE); } /** - * Asynchronous {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, without parameters. + * Asynchronous {@link #addImages(String, SessionsAddImagesParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} + * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updatePoaDataAsync(String sessionId, RequestOptions options) { - return client.executeAsync(updatePoaDataRequest(sessionId, SessionsUpdatePoaDataParams.builder().build()), options, UPDATE_POA_DATA_RESPONSE); + public CompletableFuture> addImagesAsync(String sessionId, SessionsAddImagesParams params, RequestOptions options) { + return client.executeAsync(addImagesRequest(sessionId, params), options, ADD_IMAGES_RESPONSE); } /** - * {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * {@link #addImages(String, SessionsAddImagesParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} + * @param params the request parameters * @return the result */ - public RawResponse updatePoaData(String sessionId) { - return client.execute(updatePoaDataRequest(sessionId, SessionsUpdatePoaDataParams.builder().build()), RequestOptions.none(), UPDATE_POA_DATA_RESPONSE); + public RawResponse addImages(String sessionId, SessionsAddImagesParams params) { + return client.execute(addImagesRequest(sessionId, params), RequestOptions.none(), ADD_IMAGES_RESPONSE); } /** - * Asynchronous {@link #updatePoaData(String, SessionsUpdatePoaDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * Asynchronous {@link #addImages(String, SessionsAddImagesParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} + * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updatePoaDataAsync(String sessionId) { - return client.executeAsync(updatePoaDataRequest(sessionId, SessionsUpdatePoaDataParams.builder().build()), RequestOptions.none(), UPDATE_POA_DATA_RESPONSE); + public CompletableFuture> addImagesAsync(String sessionId, SessionsAddImagesParams params) { + return client.executeAsync(addImagesRequest(sessionId, params), RequestOptions.none(), ADD_IMAGES_RESPONSE); } /** - * {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response. + * {@link #updateAmlStatus(String, SessionsUpdateAmlStatusParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} - * @param companyUuid the {@code companyUuid} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse updateKybCompanyData(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params, RequestOptions options) { - return client.execute(updateKybCompanyDataRequest(sessionId, companyUuid, params), options, UPDATE_KYB_COMPANY_DATA_RESPONSE); + public RawResponse updateAmlStatus(String sessionId, SessionsUpdateAmlStatusParams params, RequestOptions options) { + return client.execute(updateAmlStatusRequest(sessionId, params), options, UPDATE_AML_STATUS_RESPONSE); } /** - * Asynchronous {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response. + * Asynchronous {@link #updateAmlStatus(String, SessionsUpdateAmlStatusParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} - * @param companyUuid the {@code companyUuid} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateKybCompanyDataAsync(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params, RequestOptions options) { - return client.executeAsync(updateKybCompanyDataRequest(sessionId, companyUuid, params), options, UPDATE_KYB_COMPANY_DATA_RESPONSE); + public CompletableFuture> updateAmlStatusAsync(String sessionId, SessionsUpdateAmlStatusParams params, RequestOptions options) { + return client.executeAsync(updateAmlStatusRequest(sessionId, params), options, UPDATE_AML_STATUS_RESPONSE); } /** - * {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, with the client's options. + * {@link #updateAmlStatus(String, SessionsUpdateAmlStatusParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} - * @param companyUuid the {@code companyUuid} * @param params the request parameters * @return the result */ - public RawResponse updateKybCompanyData(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params) { - return client.execute(updateKybCompanyDataRequest(sessionId, companyUuid, params), RequestOptions.none(), UPDATE_KYB_COMPANY_DATA_RESPONSE); + public RawResponse updateAmlStatus(String sessionId, SessionsUpdateAmlStatusParams params) { + return client.execute(updateAmlStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_STATUS_RESPONSE); } /** - * Asynchronous {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #updateAmlStatus(String, SessionsUpdateAmlStatusParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} - * @param companyUuid the {@code companyUuid} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updateKybCompanyDataAsync(String sessionId, String companyUuid, SessionsUpdateKybCompanyDataParams params) { - return client.executeAsync(updateKybCompanyDataRequest(sessionId, companyUuid, params), RequestOptions.none(), UPDATE_KYB_COMPANY_DATA_RESPONSE); + public CompletableFuture> updateAmlStatusAsync(String sessionId, SessionsUpdateAmlStatusParams params) { + return client.executeAsync(updateAmlStatusRequest(sessionId, params), RequestOptions.none(), UPDATE_AML_STATUS_RESPONSE); } /** - * {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, without parameters. + * {@link #simulate(String, SandboxSimulateRequest, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} - * @param companyUuid the {@code companyUuid} + * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse updateKybCompanyData(String sessionId, String companyUuid, RequestOptions options) { - return client.execute(updateKybCompanyDataRequest(sessionId, companyUuid, SessionsUpdateKybCompanyDataParams.builder().build()), options, UPDATE_KYB_COMPANY_DATA_RESPONSE); + public RawResponse simulate(String sessionId, SandboxSimulateRequest params, RequestOptions options) { + return client.execute(simulateRequest(sessionId, params), options, SIMULATE_RESPONSE); } /** - * Asynchronous {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, without parameters. + * Asynchronous {@link #simulate(String, SandboxSimulateRequest, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} - * @param companyUuid the {@code companyUuid} + * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateKybCompanyDataAsync(String sessionId, String companyUuid, RequestOptions options) { - return client.executeAsync(updateKybCompanyDataRequest(sessionId, companyUuid, SessionsUpdateKybCompanyDataParams.builder().build()), options, UPDATE_KYB_COMPANY_DATA_RESPONSE); + public CompletableFuture> simulateAsync(String sessionId, SandboxSimulateRequest params, RequestOptions options) { + return client.executeAsync(simulateRequest(sessionId, params), options, SIMULATE_RESPONSE); } /** - * {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * {@link #simulate(String, SandboxSimulateRequest, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} - * @param companyUuid the {@code companyUuid} + * @param params the request parameters * @return the result */ - public RawResponse updateKybCompanyData(String sessionId, String companyUuid) { - return client.execute(updateKybCompanyDataRequest(sessionId, companyUuid, SessionsUpdateKybCompanyDataParams.builder().build()), RequestOptions.none(), UPDATE_KYB_COMPANY_DATA_RESPONSE); + public RawResponse simulate(String sessionId, SandboxSimulateRequest params) { + return client.execute(simulateRequest(sessionId, params), RequestOptions.none(), SIMULATE_RESPONSE); } /** - * Asynchronous {@link #updateKybCompanyData(String, String, SessionsUpdateKybCompanyDataParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * Asynchronous {@link #simulate(String, SandboxSimulateRequest, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} - * @param companyUuid the {@code companyUuid} + * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updateKybCompanyDataAsync(String sessionId, String companyUuid) { - return client.executeAsync(updateKybCompanyDataRequest(sessionId, companyUuid, SessionsUpdateKybCompanyDataParams.builder().build()), RequestOptions.none(), UPDATE_KYB_COMPANY_DATA_RESPONSE); + public CompletableFuture> simulateAsync(String sessionId, SandboxSimulateRequest params) { + return client.executeAsync(simulateRequest(sessionId, params), RequestOptions.none(), SIMULATE_RESPONSE); } /** - * {@link #updateKybParty(String, String, SessionsUpdateKybPartyParams, RequestOptions)} with the raw response. + * {@link #listFeatureOverrides(String, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} - * @param partyUuid the {@code partyUuid} - * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse updateKybParty(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params, RequestOptions options) { - return client.execute(updateKybPartyRequest(sessionId, partyUuid, params), options, UPDATE_KYB_PARTY_RESPONSE); + public RawResponse listFeatureOverrides(String sessionId, RequestOptions options) { + return client.execute(listFeatureOverridesRequest(sessionId), options, LIST_FEATURE_OVERRIDES_RESPONSE); } /** - * Asynchronous {@link #updateKybParty(String, String, SessionsUpdateKybPartyParams, RequestOptions)} with the raw response. + * Asynchronous {@link #listFeatureOverrides(String, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} - * @param partyUuid the {@code partyUuid} - * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateKybPartyAsync(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params, RequestOptions options) { - return client.executeAsync(updateKybPartyRequest(sessionId, partyUuid, params), options, UPDATE_KYB_PARTY_RESPONSE); + public CompletableFuture> listFeatureOverridesAsync(String sessionId, RequestOptions options) { + return client.executeAsync(listFeatureOverridesRequest(sessionId), options, LIST_FEATURE_OVERRIDES_RESPONSE); } /** - * {@link #updateKybParty(String, String, SessionsUpdateKybPartyParams, RequestOptions)} with the raw response, with the client's options. + * {@link #listFeatureOverrides(String, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} - * @param partyUuid the {@code partyUuid} - * @param params the request parameters * @return the result */ - public RawResponse updateKybParty(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params) { - return client.execute(updateKybPartyRequest(sessionId, partyUuid, params), RequestOptions.none(), UPDATE_KYB_PARTY_RESPONSE); + public RawResponse listFeatureOverrides(String sessionId) { + return client.execute(listFeatureOverridesRequest(sessionId), RequestOptions.none(), LIST_FEATURE_OVERRIDES_RESPONSE); } /** - * Asynchronous {@link #updateKybParty(String, String, SessionsUpdateKybPartyParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #listFeatureOverrides(String, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} - * @param partyUuid the {@code partyUuid} - * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updateKybPartyAsync(String sessionId, String partyUuid, SessionsUpdateKybPartyParams params) { - return client.executeAsync(updateKybPartyRequest(sessionId, partyUuid, params), RequestOptions.none(), UPDATE_KYB_PARTY_RESPONSE); + public CompletableFuture> listFeatureOverridesAsync(String sessionId) { + return client.executeAsync(listFeatureOverridesRequest(sessionId), RequestOptions.none(), LIST_FEATURE_OVERRIDES_RESPONSE); } /** - * {@link #listNetworks(String, RequestOptions)} with the raw response. + * {@link #cancelFeatureOverride(String, String, RequestOptions)} with the raw response. * - * @param sessionId the {@code session_id} + * @param sessionId the {@code sessionId} + * @param overrideId the {@code overrideId} * @param options per-request options * @return the result */ - public RawResponse listNetworks(String sessionId, RequestOptions options) { - return client.execute(listNetworksRequest(sessionId), options, LIST_NETWORKS_RESPONSE); + public RawResponse cancelFeatureOverride(String sessionId, String overrideId, RequestOptions options) { + return client.execute(cancelFeatureOverrideRequest(sessionId, overrideId), options, CANCEL_FEATURE_OVERRIDE_RESPONSE); } /** - * Asynchronous {@link #listNetworks(String, RequestOptions)} with the raw response. + * Asynchronous {@link #cancelFeatureOverride(String, String, RequestOptions)} with the raw response. * - * @param sessionId the {@code session_id} + * @param sessionId the {@code sessionId} + * @param overrideId the {@code overrideId} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String sessionId, RequestOptions options) { - return client.executeAsync(listNetworksRequest(sessionId), options, LIST_NETWORKS_RESPONSE); + public CompletableFuture> cancelFeatureOverrideAsync(String sessionId, String overrideId, RequestOptions options) { + return client.executeAsync(cancelFeatureOverrideRequest(sessionId, overrideId), options, CANCEL_FEATURE_OVERRIDE_RESPONSE); } /** - * {@link #listNetworks(String, RequestOptions)} with the raw response, with the client's options. + * {@link #cancelFeatureOverride(String, String, RequestOptions)} with the raw response, with the client's options. * - * @param sessionId the {@code session_id} + * @param sessionId the {@code sessionId} + * @param overrideId the {@code overrideId} * @return the result */ - public RawResponse listNetworks(String sessionId) { - return client.execute(listNetworksRequest(sessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE); + public RawResponse cancelFeatureOverride(String sessionId, String overrideId) { + return client.execute(cancelFeatureOverrideRequest(sessionId, overrideId), RequestOptions.none(), CANCEL_FEATURE_OVERRIDE_RESPONSE); } /** - * Asynchronous {@link #listNetworks(String, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #cancelFeatureOverride(String, String, RequestOptions)} with the raw response, with the client's options. * - * @param sessionId the {@code session_id} + * @param sessionId the {@code sessionId} + * @param overrideId the {@code overrideId} * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String sessionId) { - return client.executeAsync(listNetworksRequest(sessionId), RequestOptions.none(), LIST_NETWORKS_RESPONSE); + public CompletableFuture> cancelFeatureOverrideAsync(String sessionId, String overrideId) { + return client.executeAsync(cancelFeatureOverrideRequest(sessionId, overrideId), RequestOptions.none(), CANCEL_FEATURE_OVERRIDE_RESPONSE); } /** - * {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response. + * {@link #previewDecisionEmail(String, SessionsPreviewDecisionEmailParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse previewResubmission(String sessionId, SessionsPreviewResubmissionParams params, RequestOptions options) { - return client.execute(previewResubmissionRequest(sessionId, params), options, PREVIEW_RESUBMISSION_RESPONSE); + public RawResponse previewDecisionEmail(String sessionId, SessionsPreviewDecisionEmailParams params, RequestOptions options) { + return client.execute(previewDecisionEmailRequest(sessionId, params), options, PREVIEW_DECISION_EMAIL_RESPONSE); } /** - * Asynchronous {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response. + * Asynchronous {@link #previewDecisionEmail(String, SessionsPreviewDecisionEmailParams, RequestOptions)} with the raw response. * * @param sessionId the {@code sessionId} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> previewResubmissionAsync(String sessionId, SessionsPreviewResubmissionParams params, RequestOptions options) { - return client.executeAsync(previewResubmissionRequest(sessionId, params), options, PREVIEW_RESUBMISSION_RESPONSE); + public CompletableFuture> previewDecisionEmailAsync(String sessionId, SessionsPreviewDecisionEmailParams params, RequestOptions options) { + return client.executeAsync(previewDecisionEmailRequest(sessionId, params), options, PREVIEW_DECISION_EMAIL_RESPONSE); } /** - * {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response, with the client's options. + * {@link #previewDecisionEmail(String, SessionsPreviewDecisionEmailParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return the result */ - public RawResponse previewResubmission(String sessionId, SessionsPreviewResubmissionParams params) { - return client.execute(previewResubmissionRequest(sessionId, params), RequestOptions.none(), PREVIEW_RESUBMISSION_RESPONSE); + public RawResponse previewDecisionEmail(String sessionId, SessionsPreviewDecisionEmailParams params) { + return client.execute(previewDecisionEmailRequest(sessionId, params), RequestOptions.none(), PREVIEW_DECISION_EMAIL_RESPONSE); } /** - * Asynchronous {@link #previewResubmission(String, SessionsPreviewResubmissionParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #previewDecisionEmail(String, SessionsPreviewDecisionEmailParams, RequestOptions)} with the raw response, with the client's options. * * @param sessionId the {@code sessionId} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> previewResubmissionAsync(String sessionId, SessionsPreviewResubmissionParams params) { - return client.executeAsync(previewResubmissionRequest(sessionId, params), RequestOptions.none(), PREVIEW_RESUBMISSION_RESPONSE); + public CompletableFuture> previewDecisionEmailAsync(String sessionId, SessionsPreviewDecisionEmailParams params) { + return client.executeAsync(previewDecisionEmailRequest(sessionId, params), RequestOptions.none(), PREVIEW_DECISION_EMAIL_RESPONSE); } } } diff --git a/src/main/java/me/didit/resources/SessionsSignaturesResource.java b/src/main/java/me/didit/resources/SessionsSignaturesResource.java new file mode 100644 index 0000000..1920331 --- /dev/null +++ b/src/main/java/me/didit/resources/SessionsSignaturesResource.java @@ -0,0 +1,698 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.BinaryResponse; +import me.didit.core.Conv; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.SessionsSignaturesDownloadEvidenceParams; +import me.didit.types.SessionsSignaturesValidateParams; +import me.didit.types.SessionsSignaturesValidateResponse; + +/** The {@code sessions.signatures} resource: {@code client.sessions().signatures()}. */ +public final class SessionsSignaturesResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + SessionsSignaturesResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * Download a signed document + * + *

Download the signed PDF of one document of an Electronic Signature step. Use the signed_file path of signatures[].documents[] in the decision. The PDF holds the document the signer was shown with their field values stamped, the evidence page (unless signature_evidence_page is off) and the PAdES signature with its timestamp. Didit checks the file against sha256_signed before returning it. + * + *

Works for User Verification (KYC) and Business Verification (KYB) sessions. Needs read:sessions; your API key works. See the [Electronic Signature report](/core-technology/electronic-signature/report-electronic-signature#download-the-signed-documents). + * + *

{@code GET /v3/session/{sessionId}/signatures/{nodeId}/documents/{documentId}/file/} + *

Returns the file; close the {@link me.didit.core.BinaryResponse} when you stream it. + * + * @param sessionId The User Verification (KYC) or Business Verification (KYB) session. + * @param nodeId The Electronic Signature step: `node_id` of the item in `signatures[]`. + * @param documentId `id` of the document in `signatures[].documents[]`. + * @param options per-request options that override the client options + * @return the response body + */ + public BinaryResponse downloadDocument(String sessionId, String nodeId, String documentId, RequestOptions options) { + return client.execute(downloadDocumentRequest(sessionId, nodeId, documentId), options, DOWNLOAD_DOCUMENT_RESPONSE).data(); + } + + /** + * Asynchronous {@link #downloadDocument(String, String, String, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param documentId the {@code documentId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture downloadDocumentAsync(String sessionId, String nodeId, String documentId, RequestOptions options) { + return client.executeAsync(downloadDocumentRequest(sessionId, nodeId, documentId), options, DOWNLOAD_DOCUMENT_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #downloadDocument(String, String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param documentId the {@code documentId} + * @return the result + */ + public BinaryResponse downloadDocument(String sessionId, String nodeId, String documentId) { + return client.execute(downloadDocumentRequest(sessionId, nodeId, documentId), RequestOptions.none(), DOWNLOAD_DOCUMENT_RESPONSE).data(); + } + + /** + * Asynchronous {@link #downloadDocument(String, String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param documentId the {@code documentId} + * @return a future completed with the result + */ + public CompletableFuture downloadDocumentAsync(String sessionId, String nodeId, String documentId) { + return client.executeAsync(downloadDocumentRequest(sessionId, nodeId, documentId), RequestOptions.none(), DOWNLOAD_DOCUMENT_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Download the signature evidence + * + *

Download a signer's evidence PDF: the level and format, the signer and their verified identity session, the code confirmation, the IP address, each document with its original SHA-256 and field values, the certificate, the timestamp and the SHA-256 of every signed file. Use the evidence_file path of the signer's item in signatures[]. + * + *

Always pass signature_id when a Business Verification step has several signers: without it, the route picks one of the step's signers. Needs read:sessions; your API key works. + * + *

{@code GET /v3/session/{sessionId}/signatures/{nodeId}/evidence/} + *

Returns the file; close the {@link me.didit.core.BinaryResponse} when you stream it. + * + * @param sessionId The User Verification (KYC) or Business Verification (KYB) session. + * @param nodeId The Electronic Signature step: `node_id` of the item in `signatures[]`. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public BinaryResponse downloadEvidence(String sessionId, String nodeId, SessionsSignaturesDownloadEvidenceParams params, RequestOptions options) { + return client.execute(downloadEvidenceRequest(sessionId, nodeId, params), options, DOWNLOAD_EVIDENCE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture downloadEvidenceAsync(String sessionId, String nodeId, SessionsSignaturesDownloadEvidenceParams params, RequestOptions options) { + return client.executeAsync(downloadEvidenceRequest(sessionId, nodeId, params), options, DOWNLOAD_EVIDENCE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @return the result + */ + public BinaryResponse downloadEvidence(String sessionId, String nodeId, SessionsSignaturesDownloadEvidenceParams params) { + return client.execute(downloadEvidenceRequest(sessionId, nodeId, params), RequestOptions.none(), DOWNLOAD_EVIDENCE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture downloadEvidenceAsync(String sessionId, String nodeId, SessionsSignaturesDownloadEvidenceParams params) { + return client.executeAsync(downloadEvidenceRequest(sessionId, nodeId, params), RequestOptions.none(), DOWNLOAD_EVIDENCE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param options per-request options + * @return the result + */ + public BinaryResponse downloadEvidence(String sessionId, String nodeId, RequestOptions options) { + return client.execute(downloadEvidenceRequest(sessionId, nodeId, SessionsSignaturesDownloadEvidenceParams.builder().build()), options, DOWNLOAD_EVIDENCE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture downloadEvidenceAsync(String sessionId, String nodeId, RequestOptions options) { + return client.executeAsync(downloadEvidenceRequest(sessionId, nodeId, SessionsSignaturesDownloadEvidenceParams.builder().build()), options, DOWNLOAD_EVIDENCE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @return the result + */ + public BinaryResponse downloadEvidence(String sessionId, String nodeId) { + return client.execute(downloadEvidenceRequest(sessionId, nodeId, SessionsSignaturesDownloadEvidenceParams.builder().build()), RequestOptions.none(), DOWNLOAD_EVIDENCE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @return a future completed with the result + */ + public CompletableFuture downloadEvidenceAsync(String sessionId, String nodeId) { + return client.executeAsync(downloadEvidenceRequest(sessionId, nodeId, SessionsSignaturesDownloadEvidenceParams.builder().build()), RequestOptions.none(), DOWNLOAD_EVIDENCE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Download a signature image + * + *

Download the signature or initials a signer applied to a field, as PNG. Use the image_url of the field in signatures[].documents[].fields[]. Needs read:sessions; your API key works. + * + *

{@code GET /v3/session/{sessionId}/signatures/{nodeId}/fields/{fieldId}/image/} + *

Returns the file; close the {@link me.didit.core.BinaryResponse} when you stream it. + * + * @param sessionId The User Verification (KYC) or Business Verification (KYB) session. + * @param nodeId The Electronic Signature step: `node_id` of the item in `signatures[]`. + * @param fieldId The field on this signer's copy, as in the field's `image_url`. + * @param options per-request options that override the client options + * @return the response body + */ + public BinaryResponse downloadFieldImage(String sessionId, String nodeId, String fieldId, RequestOptions options) { + return client.execute(downloadFieldImageRequest(sessionId, nodeId, fieldId), options, DOWNLOAD_FIELD_IMAGE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #downloadFieldImage(String, String, String, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param fieldId the {@code fieldId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture downloadFieldImageAsync(String sessionId, String nodeId, String fieldId, RequestOptions options) { + return client.executeAsync(downloadFieldImageRequest(sessionId, nodeId, fieldId), options, DOWNLOAD_FIELD_IMAGE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #downloadFieldImage(String, String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param fieldId the {@code fieldId} + * @return the result + */ + public BinaryResponse downloadFieldImage(String sessionId, String nodeId, String fieldId) { + return client.execute(downloadFieldImageRequest(sessionId, nodeId, fieldId), RequestOptions.none(), DOWNLOAD_FIELD_IMAGE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #downloadFieldImage(String, String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param fieldId the {@code fieldId} + * @return a future completed with the result + */ + public CompletableFuture downloadFieldImageAsync(String sessionId, String nodeId, String fieldId) { + return client.executeAsync(downloadFieldImageRequest(sessionId, nodeId, fieldId), RequestOptions.none(), DOWNLOAD_FIELD_IMAGE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Validate signatures again + * + *

Re-check the signed documents of an Electronic Signature step. Didit re-reads every signed PDF, checks its SHA-256 and validates the PAdES signature, the certificate, the revocation data and the timestamp, then saves the result in each signer's validation and returns the updated items. Nothing is signed again. Send signature_id to check one signer; without it, every signer of the step is checked, and the call fails if any of them hasn't signed. + * + *

Needs write:sessions; your API key works. In the console it's the Validate again button. + * + *

{@code POST /v3/session/{sessionId}/signatures/{nodeId}/validate/} + * + * @param sessionId The User Verification (KYC) or Business Verification (KYB) session. + * @param nodeId The Electronic Signature step: `node_id` of the item in `signatures[]`. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public SessionsSignaturesValidateResponse validate(String sessionId, String nodeId, SessionsSignaturesValidateParams params, RequestOptions options) { + return client.execute(validateRequest(sessionId, nodeId, params), options, VALIDATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture validateAsync(String sessionId, String nodeId, SessionsSignaturesValidateParams params, RequestOptions options) { + return client.executeAsync(validateRequest(sessionId, nodeId, params), options, VALIDATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @return the result + */ + public SessionsSignaturesValidateResponse validate(String sessionId, String nodeId, SessionsSignaturesValidateParams params) { + return client.execute(validateRequest(sessionId, nodeId, params), RequestOptions.none(), VALIDATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture validateAsync(String sessionId, String nodeId, SessionsSignaturesValidateParams params) { + return client.executeAsync(validateRequest(sessionId, nodeId, params), RequestOptions.none(), VALIDATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param options per-request options + * @return the result + */ + public SessionsSignaturesValidateResponse validate(String sessionId, String nodeId, RequestOptions options) { + return client.execute(validateRequest(sessionId, nodeId, SessionsSignaturesValidateParams.builder().build()), options, VALIDATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)}, without parameters. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture validateAsync(String sessionId, String nodeId, RequestOptions options) { + return client.executeAsync(validateRequest(sessionId, nodeId, SessionsSignaturesValidateParams.builder().build()), options, VALIDATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @return the result + */ + public SessionsSignaturesValidateResponse validate(String sessionId, String nodeId) { + return client.execute(validateRequest(sessionId, nodeId, SessionsSignaturesValidateParams.builder().build()), RequestOptions.none(), VALIDATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)}, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @return a future completed with the result + */ + public CompletableFuture validateAsync(String sessionId, String nodeId) { + return client.executeAsync(validateRequest(sessionId, nodeId, SessionsSignaturesValidateParams.builder().build()), RequestOptions.none(), VALIDATE_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler DOWNLOAD_DOCUMENT_RESPONSE = Responses.binary(); + + private static ApiRequest downloadDocumentRequest(String sessionId, String nodeId, String documentId) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/signatures/{nodeId}/documents/{documentId}/file/") + .pathParam("sessionId", sessionId) + .pathParam("nodeId", nodeId) + .pathParam("documentId", documentId) + .accept("application/pdf") + .build(); + } + + private static final ResponseHandler DOWNLOAD_EVIDENCE_RESPONSE = Responses.binary(); + + private static ApiRequest downloadEvidenceRequest(String sessionId, String nodeId, SessionsSignaturesDownloadEvidenceParams params) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/signatures/{nodeId}/evidence/") + .pathParam("sessionId", sessionId) + .pathParam("nodeId", nodeId) + .params(params) + .queryFields("signature_id") + .accept("application/pdf") + .build(); + } + + private static final ResponseHandler DOWNLOAD_FIELD_IMAGE_RESPONSE = Responses.binary(); + + private static ApiRequest downloadFieldImageRequest(String sessionId, String nodeId, String fieldId) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/signatures/{nodeId}/fields/{fieldId}/image/") + .pathParam("sessionId", sessionId) + .pathParam("nodeId", nodeId) + .pathParam("fieldId", fieldId) + .accept("image/png") + .build(); + } + + private static final ResponseHandler VALIDATE_RESPONSE = Responses.json(Conv.model(SessionsSignaturesValidateResponse::fromJson)); + + private static ApiRequest validateRequest(String sessionId, String nodeId, SessionsSignaturesValidateParams params) { + return ApiRequest.builder("POST", "/v3/session/{sessionId}/signatures/{nodeId}/validate/") + .pathParam("sessionId", sessionId) + .pathParam("nodeId", nodeId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + /** The {@code sessions.signatures} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #downloadDocument(String, String, String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param documentId the {@code documentId} + * @param options per-request options + * @return the result + */ + public RawResponse downloadDocument(String sessionId, String nodeId, String documentId, RequestOptions options) { + return client.execute(downloadDocumentRequest(sessionId, nodeId, documentId), options, DOWNLOAD_DOCUMENT_RESPONSE); + } + + /** + * Asynchronous {@link #downloadDocument(String, String, String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param documentId the {@code documentId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> downloadDocumentAsync(String sessionId, String nodeId, String documentId, RequestOptions options) { + return client.executeAsync(downloadDocumentRequest(sessionId, nodeId, documentId), options, DOWNLOAD_DOCUMENT_RESPONSE); + } + + /** + * {@link #downloadDocument(String, String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param documentId the {@code documentId} + * @return the result + */ + public RawResponse downloadDocument(String sessionId, String nodeId, String documentId) { + return client.execute(downloadDocumentRequest(sessionId, nodeId, documentId), RequestOptions.none(), DOWNLOAD_DOCUMENT_RESPONSE); + } + + /** + * Asynchronous {@link #downloadDocument(String, String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param documentId the {@code documentId} + * @return a future completed with the result + */ + public CompletableFuture> downloadDocumentAsync(String sessionId, String nodeId, String documentId) { + return client.executeAsync(downloadDocumentRequest(sessionId, nodeId, documentId), RequestOptions.none(), DOWNLOAD_DOCUMENT_RESPONSE); + } + + /** + * {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse downloadEvidence(String sessionId, String nodeId, SessionsSignaturesDownloadEvidenceParams params, RequestOptions options) { + return client.execute(downloadEvidenceRequest(sessionId, nodeId, params), options, DOWNLOAD_EVIDENCE_RESPONSE); + } + + /** + * Asynchronous {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> downloadEvidenceAsync(String sessionId, String nodeId, SessionsSignaturesDownloadEvidenceParams params, RequestOptions options) { + return client.executeAsync(downloadEvidenceRequest(sessionId, nodeId, params), options, DOWNLOAD_EVIDENCE_RESPONSE); + } + + /** + * {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @return the result + */ + public RawResponse downloadEvidence(String sessionId, String nodeId, SessionsSignaturesDownloadEvidenceParams params) { + return client.execute(downloadEvidenceRequest(sessionId, nodeId, params), RequestOptions.none(), DOWNLOAD_EVIDENCE_RESPONSE); + } + + /** + * Asynchronous {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> downloadEvidenceAsync(String sessionId, String nodeId, SessionsSignaturesDownloadEvidenceParams params) { + return client.executeAsync(downloadEvidenceRequest(sessionId, nodeId, params), RequestOptions.none(), DOWNLOAD_EVIDENCE_RESPONSE); + } + + /** + * {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param options per-request options + * @return the result + */ + public RawResponse downloadEvidence(String sessionId, String nodeId, RequestOptions options) { + return client.execute(downloadEvidenceRequest(sessionId, nodeId, SessionsSignaturesDownloadEvidenceParams.builder().build()), options, DOWNLOAD_EVIDENCE_RESPONSE); + } + + /** + * Asynchronous {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> downloadEvidenceAsync(String sessionId, String nodeId, RequestOptions options) { + return client.executeAsync(downloadEvidenceRequest(sessionId, nodeId, SessionsSignaturesDownloadEvidenceParams.builder().build()), options, DOWNLOAD_EVIDENCE_RESPONSE); + } + + /** + * {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @return the result + */ + public RawResponse downloadEvidence(String sessionId, String nodeId) { + return client.execute(downloadEvidenceRequest(sessionId, nodeId, SessionsSignaturesDownloadEvidenceParams.builder().build()), RequestOptions.none(), DOWNLOAD_EVIDENCE_RESPONSE); + } + + /** + * Asynchronous {@link #downloadEvidence(String, String, SessionsSignaturesDownloadEvidenceParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @return a future completed with the result + */ + public CompletableFuture> downloadEvidenceAsync(String sessionId, String nodeId) { + return client.executeAsync(downloadEvidenceRequest(sessionId, nodeId, SessionsSignaturesDownloadEvidenceParams.builder().build()), RequestOptions.none(), DOWNLOAD_EVIDENCE_RESPONSE); + } + + /** + * {@link #downloadFieldImage(String, String, String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param fieldId the {@code fieldId} + * @param options per-request options + * @return the result + */ + public RawResponse downloadFieldImage(String sessionId, String nodeId, String fieldId, RequestOptions options) { + return client.execute(downloadFieldImageRequest(sessionId, nodeId, fieldId), options, DOWNLOAD_FIELD_IMAGE_RESPONSE); + } + + /** + * Asynchronous {@link #downloadFieldImage(String, String, String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param fieldId the {@code fieldId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> downloadFieldImageAsync(String sessionId, String nodeId, String fieldId, RequestOptions options) { + return client.executeAsync(downloadFieldImageRequest(sessionId, nodeId, fieldId), options, DOWNLOAD_FIELD_IMAGE_RESPONSE); + } + + /** + * {@link #downloadFieldImage(String, String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param fieldId the {@code fieldId} + * @return the result + */ + public RawResponse downloadFieldImage(String sessionId, String nodeId, String fieldId) { + return client.execute(downloadFieldImageRequest(sessionId, nodeId, fieldId), RequestOptions.none(), DOWNLOAD_FIELD_IMAGE_RESPONSE); + } + + /** + * Asynchronous {@link #downloadFieldImage(String, String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param fieldId the {@code fieldId} + * @return a future completed with the result + */ + public CompletableFuture> downloadFieldImageAsync(String sessionId, String nodeId, String fieldId) { + return client.executeAsync(downloadFieldImageRequest(sessionId, nodeId, fieldId), RequestOptions.none(), DOWNLOAD_FIELD_IMAGE_RESPONSE); + } + + /** + * {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse validate(String sessionId, String nodeId, SessionsSignaturesValidateParams params, RequestOptions options) { + return client.execute(validateRequest(sessionId, nodeId, params), options, VALIDATE_RESPONSE); + } + + /** + * Asynchronous {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> validateAsync(String sessionId, String nodeId, SessionsSignaturesValidateParams params, RequestOptions options) { + return client.executeAsync(validateRequest(sessionId, nodeId, params), options, VALIDATE_RESPONSE); + } + + /** + * {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @return the result + */ + public RawResponse validate(String sessionId, String nodeId, SessionsSignaturesValidateParams params) { + return client.execute(validateRequest(sessionId, nodeId, params), RequestOptions.none(), VALIDATE_RESPONSE); + } + + /** + * Asynchronous {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> validateAsync(String sessionId, String nodeId, SessionsSignaturesValidateParams params) { + return client.executeAsync(validateRequest(sessionId, nodeId, params), RequestOptions.none(), VALIDATE_RESPONSE); + } + + /** + * {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param options per-request options + * @return the result + */ + public RawResponse validate(String sessionId, String nodeId, RequestOptions options) { + return client.execute(validateRequest(sessionId, nodeId, SessionsSignaturesValidateParams.builder().build()), options, VALIDATE_RESPONSE); + } + + /** + * Asynchronous {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)} with the raw response, without parameters. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> validateAsync(String sessionId, String nodeId, RequestOptions options) { + return client.executeAsync(validateRequest(sessionId, nodeId, SessionsSignaturesValidateParams.builder().build()), options, VALIDATE_RESPONSE); + } + + /** + * {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @return the result + */ + public RawResponse validate(String sessionId, String nodeId) { + return client.execute(validateRequest(sessionId, nodeId, SessionsSignaturesValidateParams.builder().build()), RequestOptions.none(), VALIDATE_RESPONSE); + } + + /** + * Asynchronous {@link #validate(String, String, SessionsSignaturesValidateParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param nodeId the {@code nodeId} + * @return a future completed with the result + */ + public CompletableFuture> validateAsync(String sessionId, String nodeId) { + return client.executeAsync(validateRequest(sessionId, nodeId, SessionsSignaturesValidateParams.builder().build()), RequestOptions.none(), VALIDATE_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/SessionsWebhooksResource.java b/src/main/java/me/didit/resources/SessionsWebhooksResource.java new file mode 100644 index 0000000..ab0ca87 --- /dev/null +++ b/src/main/java/me/didit/resources/SessionsWebhooksResource.java @@ -0,0 +1,348 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.SessionWebhookDelivery; + +/** The {@code sessions.webhooks} resource: {@code client.sessions().webhooks()}. */ +public final class SessionsWebhooksResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + SessionsWebhooksResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * List session webhook deliveries + * + *

List every webhook delivery Didit recorded for one verification session, user (KYC) or business (KYB), newest first, as a plain JSON array (not paginated). Each entry is one attempt to one destination: the first send, each automatic retry and each resend are separate entries with their own uuid, and retries and resends keep the payload's event_id. request_header and request_body are JSON text; parse them to read the signatures and the payload. Delivery records are available for about 30 days, so an empty array means no webhook has been sent for the session yet or every delivery is older than that. A scoped API key needs sessions: read and decisions: read. + * + *

{@code GET /v3/session/{sessionId}/webhooks/} + * + * @param sessionId UUID of the verification session, user (KYC) or business (KYB): the `session_id` returned by `POST /v3/session/`. The server resolves it against both session types. + * @param options per-request options that override the client options + * @return the response body + */ + public List list(String sessionId, RequestOptions options) { + return client.execute(listRequest(sessionId), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String sessionId, RequestOptions options) { + return client.executeAsync(listRequest(sessionId), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public List list(String sessionId) { + return client.execute(listRequest(sessionId), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String sessionId) { + return client.executeAsync(listRequest(sessionId), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Get session webhook delivery + * + *

Retrieve one webhook delivery of a verification session: the URL it went to, the HTTP status your endpoint returned, and the exact headers and JSON body Didit sent. request_header and request_body are JSON text; to check your signature verification, recompute X-Signature-V2 from the parsed body with the secret of the destination it was sent to (or the application-level secret for a delivery to the legacy webhook URL). The path has no trailing slash: with one it returns 404. A scoped API key needs sessions: read and decisions: read. + * + *

{@code GET /v3/session/{sessionId}/webhook/{webhook_uuid}} + * + * @param sessionId UUID of the verification session, user (KYC) or business (KYB): the `session_id` returned by `POST /v3/session/`. The server resolves it against both session types. + * @param webhookUuid UUID of the delivery: the `uuid` of an entry returned by `GET /v3/session/{sessionId}/webhooks/`. It must belong to the session in the path. + * @param options per-request options that override the client options + * @return the response body + */ + public SessionWebhookDelivery retrieve(String sessionId, String webhookUuid, RequestOptions options) { + return client.execute(retrieveRequest(sessionId, webhookUuid), options, RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(String, String, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync(String sessionId, String webhookUuid, RequestOptions options) { + return client.executeAsync(retrieveRequest(sessionId, webhookUuid), options, RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieve(String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @return the result + */ + public SessionWebhookDelivery retrieve(String sessionId, String webhookUuid) { + return client.execute(retrieveRequest(sessionId, webhookUuid), RequestOptions.none(), RETRIEVE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieve(String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @return a future completed with the result + */ + public CompletableFuture retrieveAsync(String sessionId, String webhookUuid) { + return client.executeAsync(retrieveRequest(sessionId, webhookUuid), RequestOptions.none(), RETRIEVE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Resend session webhook delivery + * + *

Send a recorded session webhook again to the URL it first went to, with the same payload and event_id and a fresh timestamp, X-Timestamp header and signatures. Didit signs it with the current secret of the destination the delivery belongs to (or the application-level secret for a delivery to the legacy webhook URL), waits up to 5 seconds for your endpoint, records the attempt as a new delivery and returns it with 200 whatever your endpoint answered: read response_status. A resend is not retried automatically, and every call sends again, so make your endpoint idempotent on event_id. The path has no trailing slash and the request has no body. A scoped API key needs webhooks: write and decisions: read. + * + *

{@code POST /v3/session/{sessionId}/webhook/{webhook_uuid}/resend} + * + * @param sessionId UUID of the verification session, user (KYC) or business (KYB): the `session_id` returned by `POST /v3/session/`. The server resolves it against both session types. + * @param webhookUuid UUID of the delivery to resend: the `uuid` of an entry returned by `GET /v3/session/{sessionId}/webhooks/`. It must belong to the session in the path. + * @param options per-request options that override the client options + * @return the response body + */ + public SessionWebhookDelivery resend(String sessionId, String webhookUuid, RequestOptions options) { + return client.execute(resendRequest(sessionId, webhookUuid), options, RESEND_RESPONSE).data(); + } + + /** + * Asynchronous {@link #resend(String, String, RequestOptions)}. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture resendAsync(String sessionId, String webhookUuid, RequestOptions options) { + return client.executeAsync(resendRequest(sessionId, webhookUuid), options, RESEND_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #resend(String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @return the result + */ + public SessionWebhookDelivery resend(String sessionId, String webhookUuid) { + return client.execute(resendRequest(sessionId, webhookUuid), RequestOptions.none(), RESEND_RESPONSE).data(); + } + + /** + * Asynchronous {@link #resend(String, String, RequestOptions)}, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @return a future completed with the result + */ + public CompletableFuture resendAsync(String sessionId, String webhookUuid) { + return client.executeAsync(resendRequest(sessionId, webhookUuid), RequestOptions.none(), RESEND_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.json(Conv.list(Conv.model(SessionWebhookDelivery::fromJson))); + + private static ApiRequest listRequest(String sessionId) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/webhooks/") + .pathParam("sessionId", sessionId) + .build(); + } + + private static final ResponseHandler RETRIEVE_RESPONSE = Responses.json(Conv.model(SessionWebhookDelivery::fromJson)); + + private static ApiRequest retrieveRequest(String sessionId, String webhookUuid) { + return ApiRequest.builder("GET", "/v3/session/{sessionId}/webhook/{webhook_uuid}") + .pathParam("sessionId", sessionId) + .pathParam("webhook_uuid", webhookUuid) + .build(); + } + + private static final ResponseHandler RESEND_RESPONSE = Responses.json(Conv.model(SessionWebhookDelivery::fromJson)); + + private static ApiRequest resendRequest(String sessionId, String webhookUuid) { + return ApiRequest.builder("POST", "/v3/session/{sessionId}/webhook/{webhook_uuid}/resend") + .pathParam("sessionId", sessionId) + .pathParam("webhook_uuid", webhookUuid) + .build(); + } + + /** The {@code sessions.webhooks} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #list(String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return the result + */ + public RawResponse> list(String sessionId, RequestOptions options) { + return client.execute(listRequest(sessionId), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String sessionId, RequestOptions options) { + return client.executeAsync(listRequest(sessionId), options, LIST_RESPONSE); + } + + /** + * {@link #list(String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return the result + */ + public RawResponse> list(String sessionId) { + return client.execute(listRequest(sessionId), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String sessionId) { + return client.executeAsync(listRequest(sessionId), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #retrieve(String, String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse retrieve(String sessionId, String webhookUuid, RequestOptions options) { + return client.execute(retrieveRequest(sessionId, webhookUuid), options, RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(String, String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync(String sessionId, String webhookUuid, RequestOptions options) { + return client.executeAsync(retrieveRequest(sessionId, webhookUuid), options, RETRIEVE_RESPONSE); + } + + /** + * {@link #retrieve(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @return the result + */ + public RawResponse retrieve(String sessionId, String webhookUuid) { + return client.execute(retrieveRequest(sessionId, webhookUuid), RequestOptions.none(), RETRIEVE_RESPONSE); + } + + /** + * Asynchronous {@link #retrieve(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @return a future completed with the result + */ + public CompletableFuture> retrieveAsync(String sessionId, String webhookUuid) { + return client.executeAsync(retrieveRequest(sessionId, webhookUuid), RequestOptions.none(), RETRIEVE_RESPONSE); + } + + /** + * {@link #resend(String, String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse resend(String sessionId, String webhookUuid, RequestOptions options) { + return client.execute(resendRequest(sessionId, webhookUuid), options, RESEND_RESPONSE); + } + + /** + * Asynchronous {@link #resend(String, String, RequestOptions)} with the raw response. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> resendAsync(String sessionId, String webhookUuid, RequestOptions options) { + return client.executeAsync(resendRequest(sessionId, webhookUuid), options, RESEND_RESPONSE); + } + + /** + * {@link #resend(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @return the result + */ + public RawResponse resend(String sessionId, String webhookUuid) { + return client.execute(resendRequest(sessionId, webhookUuid), RequestOptions.none(), RESEND_RESPONSE); + } + + /** + * Asynchronous {@link #resend(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param sessionId the {@code sessionId} + * @param webhookUuid the {@code webhook_uuid} + * @return a future completed with the result + */ + public CompletableFuture> resendAsync(String sessionId, String webhookUuid) { + return client.executeAsync(resendRequest(sessionId, webhookUuid), RequestOptions.none(), RESEND_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/TransactionsNotesResource.java b/src/main/java/me/didit/resources/TransactionsNotesResource.java new file mode 100644 index 0000000..3939c30 --- /dev/null +++ b/src/main/java/me/didit/resources/TransactionsNotesResource.java @@ -0,0 +1,349 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.Page; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.TransactionNote; +import me.didit.types.TransactionsNotesCreateParams; +import me.didit.types.TransactionsNotesListParams; + +/** The {@code transactions.notes} resource: {@code client.transactions().notes()}. */ +public final class TransactionsNotesResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + TransactionsNotesResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * List transaction notes + * + *

List the notes on one monitored transaction, newest first (created_at descending): notes you add through the API, notes and status changes reviewers make in the Console, and notes Didit adds automatically (a rule's add_note action, or a problem creating the user-action verification session), which carry actor_name Rule Engine. metadata tells the note types apart. + * + *

Paginated with limit and offset. count is exact up to 100 and capped at 100 above that, so follow next until it is null instead of computing pages from count. This is a read with no side effects. + * + *

A scoped API key needs transactions: read; a key limited to some workflows or to approved sessions cannot call this endpoint. Reviewers see the same list under Notes on the transaction detail. + * + *

{@code GET /v3/transactions/{transaction_id}/notes/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param transactionId Didit transaction UUID: the `uuid` returned by `POST /v3/transactions/` and `GET /v3/transactions/`. Your own `txn_id` is not accepted. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page list(String transactionId, TransactionsNotesListParams params, RequestOptions options) { + return client.execute(listRequest(transactionId, params), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, TransactionsNotesListParams, RequestOptions)}. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String transactionId, TransactionsNotesListParams params, RequestOptions options) { + return client.executeAsync(listRequest(transactionId, params), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(String, TransactionsNotesListParams, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return the result + */ + public Page list(String transactionId, TransactionsNotesListParams params) { + return client.execute(listRequest(transactionId, params), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, TransactionsNotesListParams, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String transactionId, TransactionsNotesListParams params) { + return client.executeAsync(listRequest(transactionId, params), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(String, TransactionsNotesListParams, RequestOptions)}, without parameters. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return the result + */ + public Page list(String transactionId, RequestOptions options) { + return client.execute(listRequest(transactionId, TransactionsNotesListParams.builder().build()), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, TransactionsNotesListParams, RequestOptions)}, without parameters. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String transactionId, RequestOptions options) { + return client.executeAsync(listRequest(transactionId, TransactionsNotesListParams.builder().build()), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(String, TransactionsNotesListParams, RequestOptions)}, without parameters, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return the result + */ + public Page list(String transactionId) { + return client.execute(listRequest(transactionId, TransactionsNotesListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(String, TransactionsNotesListParams, RequestOptions)}, without parameters, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return a future completed with the result + */ + public CompletableFuture> listAsync(String transactionId) { + return client.executeAsync(listRequest(transactionId, TransactionsNotesListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Create transaction note + * + *

Add a plain-text note to a monitored transaction's review timeline, where Console reviewers see it. A note is context, not a decision: the transaction's status, score and alerts do not change, no webhook is sent, and nothing is billed. The transaction's activities gain a TRANSACTION_NOTE_ADDED entry. + * + *

Notes created with an API key have no author: actor_email and actor_name are null, so put the author in the text if you need it. Every address in mentioned_emails must belong to a member of your organization, otherwise the whole request fails with 400; mentions are stored lower-cased and de-duplicated, and this endpoint does not notify the mentioned members. + * + *

Each call creates a new note, so retrying a timed-out request can add it twice: list the notes before retrying. There is no API endpoint to edit or remove a note. A scoped API key needs transactions: write; a key limited to some workflows or to approved sessions cannot call this endpoint. + * + *

{@code POST /v3/transactions/{transaction_id}/notes/} + * + * @param transactionId Didit transaction UUID: the `uuid` returned by `POST /v3/transactions/` and `GET /v3/transactions/`. Your own `txn_id` is not accepted. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TransactionNote create(String transactionId, TransactionsNotesCreateParams params, RequestOptions options) { + return client.execute(createRequest(transactionId, params), options, CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(String, TransactionsNotesCreateParams, RequestOptions)}. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createAsync(String transactionId, TransactionsNotesCreateParams params, RequestOptions options) { + return client.executeAsync(createRequest(transactionId, params), options, CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #create(String, TransactionsNotesCreateParams, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return the result + */ + public TransactionNote create(String transactionId, TransactionsNotesCreateParams params) { + return client.execute(createRequest(transactionId, params), RequestOptions.none(), CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(String, TransactionsNotesCreateParams, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture createAsync(String transactionId, TransactionsNotesCreateParams params) { + return client.executeAsync(createRequest(transactionId, params), RequestOptions.none(), CREATE_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(TransactionNote::fromJson)); + + private static ApiRequest listRequest(String transactionId, TransactionsNotesListParams params) { + return ApiRequest.builder("GET", "/v3/transactions/{transaction_id}/notes/") + .pathParam("transaction_id", transactionId) + .params(params) + .queryFields("limit", "offset") + .build(); + } + + private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(TransactionNote::fromJson)); + + private static ApiRequest createRequest(String transactionId, TransactionsNotesCreateParams params) { + return ApiRequest.builder("POST", "/v3/transactions/{transaction_id}/notes/") + .pathParam("transaction_id", transactionId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + /** The {@code transactions.notes} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #list(String, TransactionsNotesListParams, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(String transactionId, TransactionsNotesListParams params, RequestOptions options) { + return client.execute(listRequest(transactionId, params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, TransactionsNotesListParams, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String transactionId, TransactionsNotesListParams params, RequestOptions options) { + return client.executeAsync(listRequest(transactionId, params), options, LIST_RESPONSE); + } + + /** + * {@link #list(String, TransactionsNotesListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return the result + */ + public RawResponse> list(String transactionId, TransactionsNotesListParams params) { + return client.execute(listRequest(transactionId, params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, TransactionsNotesListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String transactionId, TransactionsNotesListParams params) { + return client.executeAsync(listRequest(transactionId, params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(String, TransactionsNotesListParams, RequestOptions)} with the raw response, without parameters. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return the result + */ + public RawResponse> list(String transactionId, RequestOptions options) { + return client.execute(listRequest(transactionId, TransactionsNotesListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, TransactionsNotesListParams, RequestOptions)} with the raw response, without parameters. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String transactionId, RequestOptions options) { + return client.executeAsync(listRequest(transactionId, TransactionsNotesListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * {@link #list(String, TransactionsNotesListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return the result + */ + public RawResponse> list(String transactionId) { + return client.execute(listRequest(transactionId, TransactionsNotesListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(String, TransactionsNotesListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(String transactionId) { + return client.executeAsync(listRequest(transactionId, TransactionsNotesListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #create(String, TransactionsNotesCreateParams, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse create(String transactionId, TransactionsNotesCreateParams params, RequestOptions options) { + return client.execute(createRequest(transactionId, params), options, CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(String, TransactionsNotesCreateParams, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createAsync(String transactionId, TransactionsNotesCreateParams params, RequestOptions options) { + return client.executeAsync(createRequest(transactionId, params), options, CREATE_RESPONSE); + } + + /** + * {@link #create(String, TransactionsNotesCreateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return the result + */ + public RawResponse create(String transactionId, TransactionsNotesCreateParams params) { + return client.execute(createRequest(transactionId, params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(String, TransactionsNotesCreateParams, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> createAsync(String transactionId, TransactionsNotesCreateParams params) { + return client.executeAsync(createRequest(transactionId, params), RequestOptions.none(), CREATE_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/TransactionsResource.java b/src/main/java/me/didit/resources/TransactionsResource.java index 666acb6..40dd239 100644 --- a/src/main/java/me/didit/resources/TransactionsResource.java +++ b/src/main/java/me/didit/resources/TransactionsResource.java @@ -4,35 +4,48 @@ import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; import me.didit.core.ApiRequest; +import me.didit.core.BinaryResponse; import me.didit.core.Conv; import me.didit.core.Page; import me.didit.core.RawResponse; import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.EntityNetworkMembershipList; import me.didit.types.TransactionDetail; +import me.didit.types.TransactionSummary; import me.didit.types.TransactionsCreateParams; import me.didit.types.TransactionsCreateSdkTokenParams; import me.didit.types.TransactionsCreateSdkTokenResponse; -import me.didit.types.TransactionsListNetworksResponse; import me.didit.types.TransactionsListParams; -import me.didit.types.TransactionsListPartyNetworksResponse; +import me.didit.types.TransactionsListRelatedParams; import me.didit.types.TransactionsListResponse; /** The {@code transactions} resource: {@code client.transactions()}. */ public final class TransactionsResource { private final ApiClient client; private final WithRawResponse withRawResponse; + private final TransactionsNotesResource notes; private final TransactionsRulesResource rules; private final TransactionsTravelRuleResource travelRule; TransactionsResource(ApiClient client) { this.client = client; this.withRawResponse = new WithRawResponse(client); + this.notes = new TransactionsNotesResource(client); this.rules = new TransactionsRulesResource(client); this.travelRule = new TransactionsTravelRuleResource(client); } + /** + * The {@code transactions.notes} resource. + * + * @return the resource + */ + public TransactionsNotesResource notes() { + return notes; + } + /** * The {@code transactions.rules} resource. * @@ -148,7 +161,7 @@ public CompletableFuture> listAsync() { /** * Create transaction * - *

Submit a transaction for synchronous AML/risk monitoring. The transaction is created APPROVED, then your rules (and crypto screening, when applicable) run inline and may flip the returned status to IN_REVIEW or DECLINED. **transaction_id is your idempotency key** (max 128 chars, unique per app); reusing it returns 400. A transaction.created webhook is dispatched on success (sandbox applications are not billed and do not emit webhooks). + *

Submit a transaction for synchronous AML/risk monitoring. The transaction is created APPROVED, then your rules (and crypto screening, when applicable) run inline and may flip the returned status to IN_REVIEW or DECLINED. **transaction_id is your idempotency key** (max 128 chars, unique per app); reusing it returns 400. A transaction.created webhook is dispatched on success for live applications. Sandbox applications are not billed and do not emit transaction.created or transaction.status.updated webhooks. Sandbox Travel Rule exchanges do emit travel_rule.status.updated webhooks with environment: sandbox. A Travel Rule transfer is stored and returned before any counterparty is contacted: travel_rule.status comes back AWAITING_COUNTERPARTY (or NOT_APPLICABLE below your threshold, UNCONFIRMED_OWNERSHIP for a declared self-hosted wallet, COMPLETED when the other wallet is your own verified wallet) and travel_rule.protocol is null. Didit then chooses the rail and sends the exchange, and every later status change of the transfer, including COUNTERPARTY_VASP_NOT_FOUND when no rail reaches the counterparty, arrives as a travel_rule.status.updated webhook. Your rules run again when the rail or the status changes. * *

{@code POST /v3/transactions/} * @@ -295,7 +308,7 @@ public CompletableFuture createSdkTokenAsync * @param options per-request options that override the client options * @return the response body */ - public TransactionsListNetworksResponse listNetworks(String transactionUuid, RequestOptions options) { + public EntityNetworkMembershipList listNetworks(String transactionUuid, RequestOptions options) { return client.execute(listNetworksRequest(transactionUuid), options, LIST_NETWORKS_RESPONSE).data(); } @@ -306,7 +319,7 @@ public TransactionsListNetworksResponse listNetworks(String transactionUuid, Req * @param options per-request options * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String transactionUuid, RequestOptions options) { + public CompletableFuture listNetworksAsync(String transactionUuid, RequestOptions options) { return client.executeAsync(listNetworksRequest(transactionUuid), options, LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } @@ -316,7 +329,7 @@ public CompletableFuture listNetworksAsync(Str * @param transactionUuid the {@code transaction_uuid} * @return the result */ - public TransactionsListNetworksResponse listNetworks(String transactionUuid) { + public EntityNetworkMembershipList listNetworks(String transactionUuid) { return client.execute(listNetworksRequest(transactionUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE).data(); } @@ -326,7 +339,7 @@ public TransactionsListNetworksResponse listNetworks(String transactionUuid) { * @param transactionUuid the {@code transaction_uuid} * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String transactionUuid) { + public CompletableFuture listNetworksAsync(String transactionUuid) { return client.executeAsync(listNetworksRequest(transactionUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } @@ -343,7 +356,7 @@ public CompletableFuture listNetworksAsync(Str * @param options per-request options that override the client options * @return the response body */ - public TransactionsListPartyNetworksResponse listPartyNetworks(String transactionUuid, String transactionPartyUuid, RequestOptions options) { + public EntityNetworkMembershipList listPartyNetworks(String transactionUuid, String transactionPartyUuid, RequestOptions options) { return client.execute(listPartyNetworksRequest(transactionUuid, transactionPartyUuid), options, LIST_PARTY_NETWORKS_RESPONSE).data(); } @@ -355,7 +368,7 @@ public TransactionsListPartyNetworksResponse listPartyNetworks(String transactio * @param options per-request options * @return a future completed with the result */ - public CompletableFuture listPartyNetworksAsync(String transactionUuid, String transactionPartyUuid, RequestOptions options) { + public CompletableFuture listPartyNetworksAsync(String transactionUuid, String transactionPartyUuid, RequestOptions options) { return client.executeAsync(listPartyNetworksRequest(transactionUuid, transactionPartyUuid), options, LIST_PARTY_NETWORKS_RESPONSE).thenApply(RawResponse::data); } @@ -366,7 +379,7 @@ public CompletableFuture listPartyNetwork * @param transactionPartyUuid the {@code transaction_party_uuid} * @return the result */ - public TransactionsListPartyNetworksResponse listPartyNetworks(String transactionUuid, String transactionPartyUuid) { + public EntityNetworkMembershipList listPartyNetworks(String transactionUuid, String transactionPartyUuid) { return client.execute(listPartyNetworksRequest(transactionUuid, transactionPartyUuid), RequestOptions.none(), LIST_PARTY_NETWORKS_RESPONSE).data(); } @@ -377,16 +390,165 @@ public TransactionsListPartyNetworksResponse listPartyNetworks(String transactio * @param transactionPartyUuid the {@code transaction_party_uuid} * @return a future completed with the result */ - public CompletableFuture listPartyNetworksAsync(String transactionUuid, String transactionPartyUuid) { + public CompletableFuture listPartyNetworksAsync(String transactionUuid, String transactionPartyUuid) { return client.executeAsync(listPartyNetworksRequest(transactionUuid, transactionPartyUuid), RequestOptions.none(), LIST_PARTY_NETWORKS_RESPONSE).thenApply(RawResponse::data); } + /** + * List related transactions + * + *

List the other transactions in your application that involve the same people or companies as this one. A transaction is related when any of its parties, in any role, has the same vendor_data as one of this transaction's parties. The transaction itself is never returned, and parties without vendor_data are ignored: when none of this transaction's parties has one, the result is always empty. + * + *

Results use the summary shape of GET /v3/transactions/, newest first (txn_date, then created_at). Paginated with limit and offset; count is exact up to 100 and capped at 100 above that, so follow next until it is null. + * + *

A scoped API key needs transactions: read; a key limited to some workflows or to approved sessions cannot call this endpoint. Send the same vendor_data for the same person or company on every transaction to make this list complete. + * + *

{@code GET /v3/transactions/{transaction_id}/related/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param transactionId Didit transaction UUID: the `uuid` returned by `POST /v3/transactions/` and `GET /v3/transactions/`. Your own `txn_id` is not accepted. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page listRelated(String transactionId, TransactionsListRelatedParams params, RequestOptions options) { + return client.execute(listRelatedRequest(transactionId, params), options, LIST_RELATED_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)}. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listRelatedAsync(String transactionId, TransactionsListRelatedParams params, RequestOptions options) { + return client.executeAsync(listRelatedRequest(transactionId, params), options, LIST_RELATED_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return the result + */ + public Page listRelated(String transactionId, TransactionsListRelatedParams params) { + return client.execute(listRelatedRequest(transactionId, params), RequestOptions.none(), LIST_RELATED_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listRelatedAsync(String transactionId, TransactionsListRelatedParams params) { + return client.executeAsync(listRelatedRequest(transactionId, params), RequestOptions.none(), LIST_RELATED_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)}, without parameters. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return the result + */ + public Page listRelated(String transactionId, RequestOptions options) { + return client.execute(listRelatedRequest(transactionId, TransactionsListRelatedParams.builder().build()), options, LIST_RELATED_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)}, without parameters. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listRelatedAsync(String transactionId, RequestOptions options) { + return client.executeAsync(listRelatedRequest(transactionId, TransactionsListRelatedParams.builder().build()), options, LIST_RELATED_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)}, without parameters, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return the result + */ + public Page listRelated(String transactionId) { + return client.execute(listRelatedRequest(transactionId, TransactionsListRelatedParams.builder().build()), RequestOptions.none(), LIST_RELATED_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)}, without parameters, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return a future completed with the result + */ + public CompletableFuture> listRelatedAsync(String transactionId) { + return client.executeAsync(listRelatedRequest(transactionId, TransactionsListRelatedParams.builder().build()), RequestOptions.none(), LIST_RELATED_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Download a transaction PDF report + * + *

Render one monitored transaction as a PDF report: overview and verdict, parties and payment methods, custom properties, alerts and rule runs, screening and Travel Rule results, the network summary, the cost breakdown, up to 50 related transactions and the 20 most recent activity entries. The body is the PDF itself (application/pdf), not JSON. + * + *

The report is rendered from the transaction's current data on every call, so request it again after a status change. Nothing is stored and nothing is billed. Rendering can take a few seconds, so use a generous client timeout. + * + *

This endpoint lives on an application-scoped path: put your organization ID and the ID of the application your API key belongs to in the URL (find your organization ID). A PDF report counts as media: a scoped API key needs media: read and transactions: read, and a key limited to some workflows or to approved sessions cannot call it. + * + *

{@code GET /v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/pdf/} + *

Application scoped: the organization and application ids come from the client options. + *

Returns the file; close the {@link me.didit.core.BinaryResponse} when you stream it. + * + * @param transactionUuid Didit transaction UUID: the `uuid` returned by `POST /v3/transactions/` and `GET /v3/transactions/`. Your own `txn_id` is not accepted. + * @param options per-request options that override the client options + * @return the response body + */ + public BinaryResponse generatePdf(String transactionUuid, RequestOptions options) { + return client.execute(generatePdfRequest(transactionUuid), options, GENERATE_PDF_RESPONSE).data(); + } + + /** + * Asynchronous {@link #generatePdf(String, RequestOptions)}. + * + * @param transactionUuid the {@code transaction_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture generatePdfAsync(String transactionUuid, RequestOptions options) { + return client.executeAsync(generatePdfRequest(transactionUuid), options, GENERATE_PDF_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #generatePdf(String, RequestOptions)}, with the client's options. + * + * @param transactionUuid the {@code transaction_uuid} + * @return the result + */ + public BinaryResponse generatePdf(String transactionUuid) { + return client.execute(generatePdfRequest(transactionUuid), RequestOptions.none(), GENERATE_PDF_RESPONSE).data(); + } + + /** + * Asynchronous {@link #generatePdf(String, RequestOptions)}, with the client's options. + * + * @param transactionUuid the {@code transaction_uuid} + * @return a future completed with the result + */ + public CompletableFuture generatePdfAsync(String transactionUuid) { + return client.executeAsync(generatePdfRequest(transactionUuid), RequestOptions.none(), GENERATE_PDF_RESPONSE).thenApply(RawResponse::data); + } + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(TransactionsListResponse.Results::fromJson)); private static ApiRequest listRequest(TransactionsListParams params) { return ApiRequest.builder("GET", "/v3/transactions/") .params(params) - .queryFields("limit", "offset") + .queryFields("limit", "offset", "travel_rule_status") .build(); } @@ -416,7 +578,7 @@ private static ApiRequest createSdkTokenRequest(TransactionsCreateSdkTokenParams .build(); } - private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(TransactionsListNetworksResponse::fromJson)); + private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(EntityNetworkMembershipList::fromJson)); private static ApiRequest listNetworksRequest(String transactionUuid) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/networks/") @@ -425,7 +587,7 @@ private static ApiRequest listNetworksRequest(String transactionUuid) { .build(); } - private static final ResponseHandler LIST_PARTY_NETWORKS_RESPONSE = Responses.json(Conv.model(TransactionsListPartyNetworksResponse::fromJson)); + private static final ResponseHandler LIST_PARTY_NETWORKS_RESPONSE = Responses.json(Conv.model(EntityNetworkMembershipList::fromJson)); private static ApiRequest listPartyNetworksRequest(String transactionUuid, String transactionPartyUuid) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/parties/{transaction_party_uuid}/networks/") @@ -435,6 +597,26 @@ private static ApiRequest listPartyNetworksRequest(String transactionUuid, Strin .build(); } + private static final ResponseHandler> LIST_RELATED_RESPONSE = Responses.page(Conv.model(TransactionSummary::fromJson)); + + private static ApiRequest listRelatedRequest(String transactionId, TransactionsListRelatedParams params) { + return ApiRequest.builder("GET", "/v3/transactions/{transaction_id}/related/") + .pathParam("transaction_id", transactionId) + .params(params) + .queryFields("limit", "offset") + .build(); + } + + private static final ResponseHandler GENERATE_PDF_RESPONSE = Responses.binary(); + + private static ApiRequest generatePdfRequest(String transactionUuid) { + return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/transactions/{transaction_uuid}/pdf/") + .applicationScoped() + .pathParam("transaction_uuid", transactionUuid) + .accept("application/pdf") + .build(); + } + /** The {@code transactions} operations returning {@link RawResponse}. */ public static final class WithRawResponse { private final ApiClient client; @@ -656,7 +838,7 @@ public CompletableFuture> create * @param options per-request options * @return the result */ - public RawResponse listNetworks(String transactionUuid, RequestOptions options) { + public RawResponse listNetworks(String transactionUuid, RequestOptions options) { return client.execute(listNetworksRequest(transactionUuid), options, LIST_NETWORKS_RESPONSE); } @@ -667,7 +849,7 @@ public RawResponse listNetworks(String transac * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String transactionUuid, RequestOptions options) { + public CompletableFuture> listNetworksAsync(String transactionUuid, RequestOptions options) { return client.executeAsync(listNetworksRequest(transactionUuid), options, LIST_NETWORKS_RESPONSE); } @@ -677,7 +859,7 @@ public CompletableFuture> listNetw * @param transactionUuid the {@code transaction_uuid} * @return the result */ - public RawResponse listNetworks(String transactionUuid) { + public RawResponse listNetworks(String transactionUuid) { return client.execute(listNetworksRequest(transactionUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } @@ -687,7 +869,7 @@ public RawResponse listNetworks(String transac * @param transactionUuid the {@code transaction_uuid} * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String transactionUuid) { + public CompletableFuture> listNetworksAsync(String transactionUuid) { return client.executeAsync(listNetworksRequest(transactionUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } @@ -699,7 +881,7 @@ public CompletableFuture> listNetw * @param options per-request options * @return the result */ - public RawResponse listPartyNetworks(String transactionUuid, String transactionPartyUuid, RequestOptions options) { + public RawResponse listPartyNetworks(String transactionUuid, String transactionPartyUuid, RequestOptions options) { return client.execute(listPartyNetworksRequest(transactionUuid, transactionPartyUuid), options, LIST_PARTY_NETWORKS_RESPONSE); } @@ -711,7 +893,7 @@ public RawResponse listPartyNetworks(Stri * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listPartyNetworksAsync(String transactionUuid, String transactionPartyUuid, RequestOptions options) { + public CompletableFuture> listPartyNetworksAsync(String transactionUuid, String transactionPartyUuid, RequestOptions options) { return client.executeAsync(listPartyNetworksRequest(transactionUuid, transactionPartyUuid), options, LIST_PARTY_NETWORKS_RESPONSE); } @@ -722,7 +904,7 @@ public CompletableFuture> lis * @param transactionPartyUuid the {@code transaction_party_uuid} * @return the result */ - public RawResponse listPartyNetworks(String transactionUuid, String transactionPartyUuid) { + public RawResponse listPartyNetworks(String transactionUuid, String transactionPartyUuid) { return client.execute(listPartyNetworksRequest(transactionUuid, transactionPartyUuid), RequestOptions.none(), LIST_PARTY_NETWORKS_RESPONSE); } @@ -733,8 +915,138 @@ public RawResponse listPartyNetworks(Stri * @param transactionPartyUuid the {@code transaction_party_uuid} * @return a future completed with the result */ - public CompletableFuture> listPartyNetworksAsync(String transactionUuid, String transactionPartyUuid) { + public CompletableFuture> listPartyNetworksAsync(String transactionUuid, String transactionPartyUuid) { return client.executeAsync(listPartyNetworksRequest(transactionUuid, transactionPartyUuid), RequestOptions.none(), LIST_PARTY_NETWORKS_RESPONSE); } + + /** + * {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> listRelated(String transactionId, TransactionsListRelatedParams params, RequestOptions options) { + return client.execute(listRelatedRequest(transactionId, params), options, LIST_RELATED_RESPONSE); + } + + /** + * Asynchronous {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listRelatedAsync(String transactionId, TransactionsListRelatedParams params, RequestOptions options) { + return client.executeAsync(listRelatedRequest(transactionId, params), options, LIST_RELATED_RESPONSE); + } + + /** + * {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return the result + */ + public RawResponse> listRelated(String transactionId, TransactionsListRelatedParams params) { + return client.execute(listRelatedRequest(transactionId, params), RequestOptions.none(), LIST_RELATED_RESPONSE); + } + + /** + * Asynchronous {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listRelatedAsync(String transactionId, TransactionsListRelatedParams params) { + return client.executeAsync(listRelatedRequest(transactionId, params), RequestOptions.none(), LIST_RELATED_RESPONSE); + } + + /** + * {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)} with the raw response, without parameters. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return the result + */ + public RawResponse> listRelated(String transactionId, RequestOptions options) { + return client.execute(listRelatedRequest(transactionId, TransactionsListRelatedParams.builder().build()), options, LIST_RELATED_RESPONSE); + } + + /** + * Asynchronous {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)} with the raw response, without parameters. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listRelatedAsync(String transactionId, RequestOptions options) { + return client.executeAsync(listRelatedRequest(transactionId, TransactionsListRelatedParams.builder().build()), options, LIST_RELATED_RESPONSE); + } + + /** + * {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return the result + */ + public RawResponse> listRelated(String transactionId) { + return client.execute(listRelatedRequest(transactionId, TransactionsListRelatedParams.builder().build()), RequestOptions.none(), LIST_RELATED_RESPONSE); + } + + /** + * Asynchronous {@link #listRelated(String, TransactionsListRelatedParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return a future completed with the result + */ + public CompletableFuture>> listRelatedAsync(String transactionId) { + return client.executeAsync(listRelatedRequest(transactionId, TransactionsListRelatedParams.builder().build()), RequestOptions.none(), LIST_RELATED_RESPONSE); + } + + /** + * {@link #generatePdf(String, RequestOptions)} with the raw response. + * + * @param transactionUuid the {@code transaction_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse generatePdf(String transactionUuid, RequestOptions options) { + return client.execute(generatePdfRequest(transactionUuid), options, GENERATE_PDF_RESPONSE); + } + + /** + * Asynchronous {@link #generatePdf(String, RequestOptions)} with the raw response. + * + * @param transactionUuid the {@code transaction_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> generatePdfAsync(String transactionUuid, RequestOptions options) { + return client.executeAsync(generatePdfRequest(transactionUuid), options, GENERATE_PDF_RESPONSE); + } + + /** + * {@link #generatePdf(String, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionUuid the {@code transaction_uuid} + * @return the result + */ + public RawResponse generatePdf(String transactionUuid) { + return client.execute(generatePdfRequest(transactionUuid), RequestOptions.none(), GENERATE_PDF_RESPONSE); + } + + /** + * Asynchronous {@link #generatePdf(String, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionUuid the {@code transaction_uuid} + * @return a future completed with the result + */ + public CompletableFuture> generatePdfAsync(String transactionUuid) { + return client.executeAsync(generatePdfRequest(transactionUuid), RequestOptions.none(), GENERATE_PDF_RESPONSE); + } } } diff --git a/src/main/java/me/didit/resources/TransactionsRulesResource.java b/src/main/java/me/didit/resources/TransactionsRulesResource.java index 5d03bc4..e48c999 100644 --- a/src/main/java/me/didit/resources/TransactionsRulesResource.java +++ b/src/main/java/me/didit/resources/TransactionsRulesResource.java @@ -221,7 +221,7 @@ public CompletableFuture retrieveAsync(String ruleUuid) { /** * Update rule * - *

Partially update a rule. Custom rules accept every configuration field. Library (PRESET) rules accept only mode - use it to activate, disable, or switch a preset to TEST; any other field returns 400. + *

Partially update a rule. Custom rules accept every configuration field. Library (PRESET) rules accept only mode - use it to activate, disable, or switch a preset to TEST; any other field returns 400. An installed copy of a withheld library rule is switched off: setting its mode to anything but DISABLED answers 400 ("This library rule cannot run yet: its conditions read data the rules engine cannot evaluate."). * *

{@code PATCH /v3/transactions/rules/{rule_uuid}/} * @@ -361,7 +361,7 @@ public CompletableFuture backtestAsync(Transactio /** * List rule library * - *

Browse the full preset rule library (150+ rules) with each entry's complete configuration and an is_installed flag for this application. Filter by bundle, category, or free-text search. + *

Browse the full preset rule library (150+ rules) with each entry's complete configuration and an is_installed flag for this application. Filter by bundle, category, or free-text search. A library rule whose conditions read data the rules engine cannot evaluate yet is withheld until it can run. Withheld rules are not listed. * *

{@code GET /v3/transactions/rules/library/} *

Returns one page; iterate it to fetch every following page automatically. @@ -446,7 +446,7 @@ public CompletableFuture> listLibraryAsync() { /** * Install library rules * - *

Install one or more presets onto the application, by library_keys or a whole bundle. Installed presets evaluate immediately in their default mode; already-installed keys are skipped. + *

Install one or more presets onto the application, by library_keys or a whole bundle. An installed preset is active at once while transaction monitoring is on, and disabled otherwise; already-installed keys are skipped. A library rule whose conditions read data the rules engine cannot evaluate yet is withheld until it can run. A request that names one in library_keys answers 400 and installs nothing ("These library rules cannot run yet: their conditions read data the rules engine cannot evaluate." followed by the keys), and a bundle install skips it. A withheld rule comes back on its own once its conditions can be evaluated. A library_keys entry that is not in the library is skipped, not refused. * *

{@code POST /v3/transactions/rules/install/} * diff --git a/src/main/java/me/didit/resources/TransactionsTravelRuleResource.java b/src/main/java/me/didit/resources/TransactionsTravelRuleResource.java index 3a503b1..fe4f448 100644 --- a/src/main/java/me/didit/resources/TransactionsTravelRuleResource.java +++ b/src/main/java/me/didit/resources/TransactionsTravelRuleResource.java @@ -1,6 +1,7 @@ // Code generated by codegen from openapi/*.json. DO NOT EDIT. package me.didit.resources; +import java.util.List; import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; import me.didit.core.ApiRequest; @@ -9,7 +10,11 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.TravelRuleMessage; +import me.didit.types.TravelRuleMessageCreateRequest; import me.didit.types.TravelRuleOwnershipConfirmRequest; +import me.didit.types.TravelRuleTestVaspInboundRequest; +import me.didit.types.TravelRuleTestVaspInboundResponse; import me.didit.types.TravelRuleTransferDetail; import me.didit.types.TravelRuleTransferPatchRequest; @@ -33,9 +38,9 @@ public WithRawResponse withRawResponse() { } /** - * Finish, cancel, or resend a Travel Rule transfer + * Finish, cancel, resend, or update the originator wallet of a Travel Rule transfer * - *

Provide payment_txn_id to report the on-chain hash and move an outbound COMPLETED transfer to FINISHED (its INTERNAL-rail counterpart also moves to FINISHED), {"action": "cancel"} to cancel a non-terminal transfer, or {"action": "resend"} to re-run routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA (improve the counterparty data first, e.g. add a travel_address to the destination wallet entry). + *

Provide payment_txn_id to report the on-chain hash and move an outbound COMPLETED transfer to FINISHED (its INTERNAL-rail counterpart also moves to FINISHED), {"action": "cancel"} to cancel a non-terminal transfer, or {"action": "resend"} to route an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA again (improve the counterparty data first, e.g. add a travel_address to the destination wallet entry). resend applies to outbound transfers only. {"action": "update_compliance_email", "compliance_email": "desk@example.com"} replaces the compliance email of an outbound transfer in one of those three statuses, the fix for reason: email_undeliverable: the status does not change, and the next resend sends the request to that address. A resend is queued, not run inside the request: the response shows the transfer back in AWAITING_COUNTERPARTY with a resend queued timeline entry and a queued delivery, and where it went arrives as a travel_rule.status.updated webhook. When the transfer went over GTR, VerifyVASP or Sygna Bridge, the network learns the on-chain hash or the cancellation after the response, with retries; delivery shows how that message stands. {"action": "update_originator_wallet", "wallet_address": "..."} records the wallet the funds actually left from on an outbound transfer, for example the address a UTXO withdrawal was broadcast from rather than the main wallet you declared. It changes the record only: nothing is sent to the counterparty again and the status stays. The originator becomes chain_details.originator with source: updated, the change is added to the timeline with the previous and the new wallet, and the INTERNAL-rail counterpart of the transfer is updated the same way. * *

{@code PATCH /v3/transactions/{transaction_id}/travel-rule/} * @@ -85,11 +90,11 @@ public CompletableFuture updateTransferAsync(String tr /** * Confirm or deny wallet ownership * - *

Called by the beneficiary's application when its INTERNAL-rail transfer is UNCONFIRMED_OWNERSHIP. Confirming marks the matching wallet address book entry as ownership-verified and runs the beneficiary name match on both sides of the transfer; denying declines both sides. + *

Resolve a transfer that waits on wallet ownership: call it on your own side of the transfer when it is UNCONFIRMED_OWNERSHIP, or when it is an inbound transfer in ON_HOLD. Confirming marks the matching wallet address book entry as ownership-verified (for an inbound ON_HOLD transfer with no entry, Didit creates one from beneficiary_data.wallet_address) and runs the beneficiary name match on both sides of the transfer; denying declines both sides. An inbound transfer whose deposit already settled moves on to FINISHED when the match succeeds. When the counterparty wallet was declared self-hosted (counterparty.payment_method.method_type: unhosted_wallet), confirming records that the customer verified who controls that wallet and completes the transfer (COMPLETED) in both directions, or FINISHED for an inbound deposit whose on-chain hash is already known. The wallet verified is the counterparty's: the destination of an outbound withdrawal, or the source (originator's wallet) of an inbound deposit. If it is not in your wallet address book yet, Didit adds it as ownership-verified under the counterparty's name; if the book already holds it under a different holder name, the transfer moves to COUNTERPARTY_MISMATCHED_DATA. For an inbound TRP transfer in UNCONFIRMED_OWNERSHIP, ON_HOLD or COUNTERPARTY_MISMATCHED_DATA, Didit sends your decision to the originating VASP instead: approving returns the payment address (wallet_address, or the matched entry's address) and moves the transfer to COMPLETED, and denying declines it. The same call also decides an inbound transfer that a GTR or Sygna Bridge counterparty sent and that waits for your review (UNCONFIRMED_OWNERSHIP, ON_HOLD or COUNTERPARTY_MISMATCHED_DATA): confirmed: true approves it (pass wallet_address when the transfer names none) and confirmed: false declines it, and the network is answered after the response, with retries. VerifyVASP takes its answer when it asks, so a VerifyVASP transfer that would need your review is declined at once and cannot be decided later. * *

{@code POST /v3/transactions/{transaction_id}/travel-rule/ownership/} * - * @param transactionId Didit-stable transaction UUID that owns the transfer (the beneficiary side's transaction). + * @param transactionId Didit-stable UUID of the transaction your application holds for this transfer: the beneficiary side's transaction on an INTERNAL-rail transfer, or the transaction you created (outbound or inbound) when the counterparty wallet was declared self-hosted. * @param params the request parameters * @param options per-request options that override the client options * @return the response body @@ -132,6 +137,148 @@ public CompletableFuture confirmOwnershipAsync(String return client.executeAsync(confirmOwnershipRequest(transactionId, params), RequestOptions.none(), CONFIRM_OWNERSHIP_RESPONSE).thenApply(RawResponse::data); } + /** + * List Travel Rule messages + * + *

Messages and document requests exchanged with the counterparty VASP about this transfer. + * + *

{@code GET /v3/transactions/{transaction_id}/travel-rule/messages/} + * + * @param transactionId Didit-stable transaction UUID that owns the transfer. + * @param options per-request options that override the client options + * @return the response body + */ + public List listMessages(String transactionId, RequestOptions options) { + return client.execute(listMessagesRequest(transactionId), options, LIST_MESSAGES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listMessages(String, RequestOptions)}. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listMessagesAsync(String transactionId, RequestOptions options) { + return client.executeAsync(listMessagesRequest(transactionId), options, LIST_MESSAGES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listMessages(String, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return the result + */ + public List listMessages(String transactionId) { + return client.execute(listMessagesRequest(transactionId), RequestOptions.none(), LIST_MESSAGES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listMessages(String, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return a future completed with the result + */ + public CompletableFuture> listMessagesAsync(String transactionId) { + return client.executeAsync(listMessagesRequest(transactionId), RequestOptions.none(), LIST_MESSAGES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Send a Travel Rule message or document request + * + *

Sends a message or a document request to the counterparty VASP. Works on INTERNAL transfers (the other Didit customer sees it on its side of the pair) and EMAIL transfers (the counterparty is emailed and answers in the pickup portal). A transfer over TRP, GTR, TRUST, VerifyVASP or Sygna carries no messages, because those protocols have none. Limits: 100 messages per transfer, 5000 characters per body, 10 requested documents, 5 files of 10 MB each. + * + *

{@code POST /v3/transactions/{transaction_id}/travel-rule/messages/} + * + * @param transactionId Didit-stable transaction UUID that owns the transfer. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleMessage createMessage(String transactionId, TravelRuleMessageCreateRequest params, RequestOptions options) { + return client.execute(createMessageRequest(transactionId, params), options, CREATE_MESSAGE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #createMessage(String, TravelRuleMessageCreateRequest, RequestOptions)}. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createMessageAsync(String transactionId, TravelRuleMessageCreateRequest params, RequestOptions options) { + return client.executeAsync(createMessageRequest(transactionId, params), options, CREATE_MESSAGE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #createMessage(String, TravelRuleMessageCreateRequest, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return the result + */ + public TravelRuleMessage createMessage(String transactionId, TravelRuleMessageCreateRequest params) { + return client.execute(createMessageRequest(transactionId, params), RequestOptions.none(), CREATE_MESSAGE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #createMessage(String, TravelRuleMessageCreateRequest, RequestOptions)}, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture createMessageAsync(String transactionId, TravelRuleMessageCreateRequest params) { + return client.executeAsync(createMessageRequest(transactionId, params), RequestOptions.none(), CREATE_MESSAGE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Trigger an inbound transfer from the Didit Test VASP (sandbox) + * + *

Sandbox applications only. The Didit Test VASP sends your application an inbound Travel Rule transfer, so you can exercise the receiving side of an exchange end to end before going live. The transfer is booked and decided by the same logic a real inbound transfer from a network runs against your wallet address book, so the inbox, the decision and the wallet ownership flow behave as they do live. Nothing leaves Didit. A live application receives 400. Send the same reference again to get the transfer the first call booked instead of a second one. + * + *

{@code POST /v3/travel-rule/test-vasp/inbound/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleTestVaspInboundResponse triggerTestVaspInbound(TravelRuleTestVaspInboundRequest params, RequestOptions options) { + return client.execute(triggerTestVaspInboundRequest(params), options, TRIGGER_TEST_VASP_INBOUND_RESPONSE).data(); + } + + /** + * Asynchronous {@link #triggerTestVaspInbound(TravelRuleTestVaspInboundRequest, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture triggerTestVaspInboundAsync(TravelRuleTestVaspInboundRequest params, RequestOptions options) { + return client.executeAsync(triggerTestVaspInboundRequest(params), options, TRIGGER_TEST_VASP_INBOUND_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #triggerTestVaspInbound(TravelRuleTestVaspInboundRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public TravelRuleTestVaspInboundResponse triggerTestVaspInbound(TravelRuleTestVaspInboundRequest params) { + return client.execute(triggerTestVaspInboundRequest(params), RequestOptions.none(), TRIGGER_TEST_VASP_INBOUND_RESPONSE).data(); + } + + /** + * Asynchronous {@link #triggerTestVaspInbound(TravelRuleTestVaspInboundRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture triggerTestVaspInboundAsync(TravelRuleTestVaspInboundRequest params) { + return client.executeAsync(triggerTestVaspInboundRequest(params), RequestOptions.none(), TRIGGER_TEST_VASP_INBOUND_RESPONSE).thenApply(RawResponse::data); + } + private static final ResponseHandler UPDATE_TRANSFER_RESPONSE = Responses.json(Conv.model(TravelRuleTransferDetail::fromJson)); private static ApiRequest updateTransferRequest(String transactionId, TravelRuleTransferPatchRequest params) { @@ -152,6 +299,33 @@ private static ApiRequest confirmOwnershipRequest(String transactionId, TravelRu .build(); } + private static final ResponseHandler> LIST_MESSAGES_RESPONSE = Responses.json(Conv.list(Conv.model(TravelRuleMessage::fromJson))); + + private static ApiRequest listMessagesRequest(String transactionId) { + return ApiRequest.builder("GET", "/v3/transactions/{transaction_id}/travel-rule/messages/") + .pathParam("transaction_id", transactionId) + .build(); + } + + private static final ResponseHandler CREATE_MESSAGE_RESPONSE = Responses.json(Conv.model(TravelRuleMessage::fromJson)); + + private static ApiRequest createMessageRequest(String transactionId, TravelRuleMessageCreateRequest params) { + return ApiRequest.builder("POST", "/v3/transactions/{transaction_id}/travel-rule/messages/") + .pathParam("transaction_id", transactionId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON_OR_MULTIPART) + .build(); + } + + private static final ResponseHandler TRIGGER_TEST_VASP_INBOUND_RESPONSE = Responses.json(Conv.model(TravelRuleTestVaspInboundResponse::fromJson)); + + private static ApiRequest triggerTestVaspInboundRequest(TravelRuleTestVaspInboundRequest params) { + return ApiRequest.builder("POST", "/v3/travel-rule/test-vasp/inbound/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + /** The {@code transactions.travel_rule} operations returning {@link RawResponse}. */ public static final class WithRawResponse { private final ApiClient client; @@ -251,5 +425,135 @@ public RawResponse confirmOwnership(String transaction public CompletableFuture> confirmOwnershipAsync(String transactionId, TravelRuleOwnershipConfirmRequest params) { return client.executeAsync(confirmOwnershipRequest(transactionId, params), RequestOptions.none(), CONFIRM_OWNERSHIP_RESPONSE); } + + /** + * {@link #listMessages(String, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return the result + */ + public RawResponse> listMessages(String transactionId, RequestOptions options) { + return client.execute(listMessagesRequest(transactionId), options, LIST_MESSAGES_RESPONSE); + } + + /** + * Asynchronous {@link #listMessages(String, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listMessagesAsync(String transactionId, RequestOptions options) { + return client.executeAsync(listMessagesRequest(transactionId), options, LIST_MESSAGES_RESPONSE); + } + + /** + * {@link #listMessages(String, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return the result + */ + public RawResponse> listMessages(String transactionId) { + return client.execute(listMessagesRequest(transactionId), RequestOptions.none(), LIST_MESSAGES_RESPONSE); + } + + /** + * Asynchronous {@link #listMessages(String, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @return a future completed with the result + */ + public CompletableFuture>> listMessagesAsync(String transactionId) { + return client.executeAsync(listMessagesRequest(transactionId), RequestOptions.none(), LIST_MESSAGES_RESPONSE); + } + + /** + * {@link #createMessage(String, TravelRuleMessageCreateRequest, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse createMessage(String transactionId, TravelRuleMessageCreateRequest params, RequestOptions options) { + return client.execute(createMessageRequest(transactionId, params), options, CREATE_MESSAGE_RESPONSE); + } + + /** + * Asynchronous {@link #createMessage(String, TravelRuleMessageCreateRequest, RequestOptions)} with the raw response. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createMessageAsync(String transactionId, TravelRuleMessageCreateRequest params, RequestOptions options) { + return client.executeAsync(createMessageRequest(transactionId, params), options, CREATE_MESSAGE_RESPONSE); + } + + /** + * {@link #createMessage(String, TravelRuleMessageCreateRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return the result + */ + public RawResponse createMessage(String transactionId, TravelRuleMessageCreateRequest params) { + return client.execute(createMessageRequest(transactionId, params), RequestOptions.none(), CREATE_MESSAGE_RESPONSE); + } + + /** + * Asynchronous {@link #createMessage(String, TravelRuleMessageCreateRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param transactionId the {@code transaction_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> createMessageAsync(String transactionId, TravelRuleMessageCreateRequest params) { + return client.executeAsync(createMessageRequest(transactionId, params), RequestOptions.none(), CREATE_MESSAGE_RESPONSE); + } + + /** + * {@link #triggerTestVaspInbound(TravelRuleTestVaspInboundRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse triggerTestVaspInbound(TravelRuleTestVaspInboundRequest params, RequestOptions options) { + return client.execute(triggerTestVaspInboundRequest(params), options, TRIGGER_TEST_VASP_INBOUND_RESPONSE); + } + + /** + * Asynchronous {@link #triggerTestVaspInbound(TravelRuleTestVaspInboundRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> triggerTestVaspInboundAsync(TravelRuleTestVaspInboundRequest params, RequestOptions options) { + return client.executeAsync(triggerTestVaspInboundRequest(params), options, TRIGGER_TEST_VASP_INBOUND_RESPONSE); + } + + /** + * {@link #triggerTestVaspInbound(TravelRuleTestVaspInboundRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse triggerTestVaspInbound(TravelRuleTestVaspInboundRequest params) { + return client.execute(triggerTestVaspInboundRequest(params), RequestOptions.none(), TRIGGER_TEST_VASP_INBOUND_RESPONSE); + } + + /** + * Asynchronous {@link #triggerTestVaspInbound(TravelRuleTestVaspInboundRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> triggerTestVaspInboundAsync(TravelRuleTestVaspInboundRequest params) { + return client.executeAsync(triggerTestVaspInboundRequest(params), RequestOptions.none(), TRIGGER_TEST_VASP_INBOUND_RESPONSE); + } } } diff --git a/src/main/java/me/didit/resources/TravelRuleCounterpartyContactsResource.java b/src/main/java/me/didit/resources/TravelRuleCounterpartyContactsResource.java new file mode 100644 index 0000000..c854096 --- /dev/null +++ b/src/main/java/me/didit/resources/TravelRuleCounterpartyContactsResource.java @@ -0,0 +1,410 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.Page; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.TravelRuleCounterpartyContact; +import me.didit.types.TravelRuleCounterpartyContactCreate; +import me.didit.types.TravelRuleCounterpartyContactsListParams; +import org.jspecify.annotations.Nullable; + +/** The {@code travel_rule.counterparty_contacts} resource: {@code client.travelRule().counterpartyContacts()}. */ +public final class TravelRuleCounterpartyContactsResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + TravelRuleCounterpartyContactsResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * List counterparty contacts + * + *

The compliance addresses the EMAIL rail may write to for your application: Didit's platform contacts plus your application's own private ones, bounced ones included, newest first. Paginated with limit (default 50) and offset; count stops at 100, so 100 means 100 or more. + * + *

{@code GET /v3/travel-rule/counterparty-contacts/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page list(TravelRuleCounterpartyContactsListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(TravelRuleCounterpartyContactsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public Page list(TravelRuleCounterpartyContactsListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listAsync(TravelRuleCounterpartyContactsListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public Page list(RequestOptions options) { + return client.execute(listRequest(TravelRuleCounterpartyContactsListParams.builder().build()), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(TravelRuleCounterpartyContactsListParams.builder().build()), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public Page list() { + return client.execute(listRequest(TravelRuleCounterpartyContactsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> listAsync() { + return client.executeAsync(listRequest(TravelRuleCounterpartyContactsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Add a counterparty contact + * + *

Adds a private compliance address for one directory entry. Only your application sees and uses it, and the EMAIL rail writes to it before any platform contact. Adding an address the entry already has among your own contacts returns that contact. + * + *

{@code POST /v3/travel-rule/counterparty-contacts/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleCounterpartyContact create(TravelRuleCounterpartyContactCreate params, RequestOptions options) { + return client.execute(createRequest(params), options, CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(TravelRuleCounterpartyContactCreate, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createAsync(TravelRuleCounterpartyContactCreate params, RequestOptions options) { + return client.executeAsync(createRequest(params), options, CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #create(TravelRuleCounterpartyContactCreate, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public TravelRuleCounterpartyContact create(TravelRuleCounterpartyContactCreate params) { + return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #create(TravelRuleCounterpartyContactCreate, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture createAsync(TravelRuleCounterpartyContactCreate params) { + return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Delete a counterparty contact + * + *

Deletes one of your application's own contacts. The EMAIL rail stops writing to it at once. Platform contacts cannot be deleted. + * + *

{@code DELETE /v3/travel-rule/counterparty-contacts/{contact_uuid}/} + * + * @param contactUuid the {@code contact_uuid} + * @param options per-request options that override the client options + */ + public void delete(String contactUuid, RequestOptions options) { + client.execute(deleteRequest(contactUuid), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)}. + * + * @param contactUuid the {@code contact_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture deleteAsync(String contactUuid, RequestOptions options) { + return client.executeAsync(deleteRequest(contactUuid), options, DELETE_RESPONSE).thenAccept(response -> {}); + } + + /** + * Same as {@link #delete(String, RequestOptions)}, with the client's options. + * + * @param contactUuid the {@code contact_uuid} + */ + public void delete(String contactUuid) { + client.execute(deleteRequest(contactUuid), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)}, with the client's options. + * + * @param contactUuid the {@code contact_uuid} + * @return a future completed with the result + */ + public CompletableFuture deleteAsync(String contactUuid) { + return client.executeAsync(deleteRequest(contactUuid), RequestOptions.none(), DELETE_RESPONSE).thenAccept(response -> {}); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(TravelRuleCounterpartyContact::fromJson)); + + private static ApiRequest listRequest(TravelRuleCounterpartyContactsListParams params) { + return ApiRequest.builder("GET", "/v3/travel-rule/counterparty-contacts/") + .params(params) + .queryFields("counterparty", "limit", "offset") + .build(); + } + + private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(TravelRuleCounterpartyContact::fromJson)); + + private static ApiRequest createRequest(TravelRuleCounterpartyContactCreate params) { + return ApiRequest.builder("POST", "/v3/travel-rule/counterparty-contacts/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler<@Nullable Void> DELETE_RESPONSE = Responses.none(); + + private static ApiRequest deleteRequest(String contactUuid) { + return ApiRequest.builder("DELETE", "/v3/travel-rule/counterparty-contacts/{contact_uuid}/") + .pathParam("contact_uuid", contactUuid) + .build(); + } + + /** The {@code travel_rule.counterparty_contacts} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(TravelRuleCounterpartyContactsListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(TravelRuleCounterpartyContactsListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE); + } + + /** + * {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse> list(TravelRuleCounterpartyContactsListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(TravelRuleCounterpartyContactsListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse> list(RequestOptions options) { + return client.execute(listRequest(TravelRuleCounterpartyContactsListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(TravelRuleCounterpartyContactsListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse> list() { + return client.execute(listRequest(TravelRuleCounterpartyContactsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(TravelRuleCounterpartyContactsListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>> listAsync() { + return client.executeAsync(listRequest(TravelRuleCounterpartyContactsListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #create(TravelRuleCounterpartyContactCreate, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse create(TravelRuleCounterpartyContactCreate params, RequestOptions options) { + return client.execute(createRequest(params), options, CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(TravelRuleCounterpartyContactCreate, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> createAsync(TravelRuleCounterpartyContactCreate params, RequestOptions options) { + return client.executeAsync(createRequest(params), options, CREATE_RESPONSE); + } + + /** + * {@link #create(TravelRuleCounterpartyContactCreate, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse create(TravelRuleCounterpartyContactCreate params) { + return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * Asynchronous {@link #create(TravelRuleCounterpartyContactCreate, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> createAsync(TravelRuleCounterpartyContactCreate params) { + return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); + } + + /** + * {@link #delete(String, RequestOptions)} with the raw response. + * + * @param contactUuid the {@code contact_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse<@Nullable Void> delete(String contactUuid, RequestOptions options) { + return client.execute(deleteRequest(contactUuid), options, DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)} with the raw response. + * + * @param contactUuid the {@code contact_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String contactUuid, RequestOptions options) { + return client.executeAsync(deleteRequest(contactUuid), options, DELETE_RESPONSE); + } + + /** + * {@link #delete(String, RequestOptions)} with the raw response, with the client's options. + * + * @param contactUuid the {@code contact_uuid} + * @return the result + */ + public RawResponse<@Nullable Void> delete(String contactUuid) { + return client.execute(deleteRequest(contactUuid), RequestOptions.none(), DELETE_RESPONSE); + } + + /** + * Asynchronous {@link #delete(String, RequestOptions)} with the raw response, with the client's options. + * + * @param contactUuid the {@code contact_uuid} + * @return a future completed with the result + */ + public CompletableFuture> deleteAsync(String contactUuid) { + return client.executeAsync(deleteRequest(contactUuid), RequestOptions.none(), DELETE_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/TravelRuleResource.java b/src/main/java/me/didit/resources/TravelRuleResource.java index 8842e94..6d1943f 100644 --- a/src/main/java/me/didit/resources/TravelRuleResource.java +++ b/src/main/java/me/didit/resources/TravelRuleResource.java @@ -10,9 +10,18 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.TravelRuleCounterpartyDecisionRequest; +import me.didit.types.TravelRuleCounterpartyDecisionResponse; +import me.didit.types.TravelRuleCounterpartyQuestionnaire; +import me.didit.types.TravelRuleCounterpartyQuestionnaireList; +import me.didit.types.TravelRuleCounterpartyQuestionnaireRequest; +import me.didit.types.TravelRuleCounterpartyScreeningRequest; +import me.didit.types.TravelRuleCounterpartyScreeningResult; +import me.didit.types.TravelRuleCounterpartyScreeningStatus; import me.didit.types.TravelRuleInboundRequest; import me.didit.types.TravelRuleInboundResponse; import me.didit.types.TravelRuleListVaspsParams; +import me.didit.types.TravelRuleSendComplianceEmailConfirmationResponse; import me.didit.types.TravelRuleSettingsDetail; import me.didit.types.TravelRuleSettingsUpdate; import me.didit.types.TravelRuleWidgetSessionCreate; @@ -23,12 +32,36 @@ public final class TravelRuleResource { private final ApiClient client; private final WithRawResponse withRawResponse; + private final TravelRuleCounterpartyContactsResource counterpartyContacts; + private final TravelRuleTransfersResource transfers; private final TravelRuleWalletAddressesResource walletAddresses; + private final TravelRuleWorkspaceResource workspace; TravelRuleResource(ApiClient client) { this.client = client; this.withRawResponse = new WithRawResponse(client); + this.counterpartyContacts = new TravelRuleCounterpartyContactsResource(client); + this.transfers = new TravelRuleTransfersResource(client); this.walletAddresses = new TravelRuleWalletAddressesResource(client); + this.workspace = new TravelRuleWorkspaceResource(client); + } + + /** + * The {@code travel_rule.counterparty_contacts} resource. + * + * @return the resource + */ + public TravelRuleCounterpartyContactsResource counterpartyContacts() { + return counterpartyContacts; + } + + /** + * The {@code travel_rule.transfers} resource. + * + * @return the resource + */ + public TravelRuleTransfersResource transfers() { + return transfers; } /** @@ -40,6 +73,15 @@ public TravelRuleWalletAddressesResource walletAddresses() { return walletAddresses; } + /** + * The {@code travel_rule.workspace} resource. + * + * @return the resource + */ + public TravelRuleWorkspaceResource workspace() { + return workspace; + } + /** * The same operations, returning the HTTP status, headers and request id with the parsed body. * @@ -137,10 +179,52 @@ public CompletableFuture updateSettingsAsync(TravelRul return client.executeAsync(updateSettingsRequest(params), RequestOptions.none(), UPDATE_SETTINGS_RESPONSE).thenApply(RawResponse::data); } + /** + * Send the compliance email confirmation + * + *

Emails a single-use confirmation link to your VASP profile's compliance_email. Opening the link and pressing its confirm button confirms the address, which the Didit network needs before it lists your profile. A new request replaces the previous link; the link stops working when the compliance email changes. One email per minute per address. + * + *

{@code POST /v3/travel-rule/settings/compliance-email/confirmation/} + * + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleSendComplianceEmailConfirmationResponse sendComplianceEmailConfirmation(RequestOptions options) { + return client.execute(sendComplianceEmailConfirmationRequest(), options, SEND_COMPLIANCE_EMAIL_CONFIRMATION_RESPONSE).data(); + } + + /** + * Asynchronous {@link #sendComplianceEmailConfirmation(RequestOptions)}. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture sendComplianceEmailConfirmationAsync(RequestOptions options) { + return client.executeAsync(sendComplianceEmailConfirmationRequest(), options, SEND_COMPLIANCE_EMAIL_CONFIRMATION_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #sendComplianceEmailConfirmation(RequestOptions)}, with the client's options. + * + * @return the result + */ + public TravelRuleSendComplianceEmailConfirmationResponse sendComplianceEmailConfirmation() { + return client.execute(sendComplianceEmailConfirmationRequest(), RequestOptions.none(), SEND_COMPLIANCE_EMAIL_CONFIRMATION_RESPONSE).data(); + } + + /** + * Asynchronous {@link #sendComplianceEmailConfirmation(RequestOptions)}, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture sendComplianceEmailConfirmationAsync() { + return client.executeAsync(sendComplianceEmailConfirmationRequest(), RequestOptions.none(), SEND_COMPLIANCE_EMAIL_CONFIRMATION_RESPONSE).thenApply(RawResponse::data); + } + /** * Search the VASP directory * - *

Paginated search across discoverable Didit customers with Travel Rule enabled, plus VASPs catalogued in the internal counterparty registry. Excludes your own application's profile. + *

Paginated, name-sorted search of the VASP directory: the shared directory entries, which Didit builds from the ESMA interim MiCA register, the public TRISA directory, the global LEI register and its own curation, plus the members of the Didit network. Each row carries the routes your application has to it today. Your own application's profile is never listed. Rows also carry the shared due-diligence assessment and your application's own decision, entity screening and questionnaires. * *

{@code GET /v3/travel-rule/vasps/} *

Returns one page; iterate it to fetch every following page automatically. @@ -223,306 +307,1317 @@ public CompletableFuture> listVaspsAsync() { } /** - * Register an after-deposit (sunrise) inbound transfer + * Get a VASP directory entry * - *

Register Travel Rule data for a crypto deposit that already settled on-chain before any exchange took place. Creates an inbound travelRule transaction (or reuses an unclaimed deposit) and resolves the transfer against your wallet address book. A transaction.created webhook fires for newly-created transactions. + *

One directory entry or Didit network member by the id the search returned, with the same fields and the routes your application has to it. * - *

{@code POST /v3/travel-rule/inbound/} + *

{@code GET /v3/travel-rule/vasps/{vasp_id}/} * - * @param params the request parameters + * @param vaspId The id of a row of GET /v3/travel-rule/vasps/. * @param options per-request options that override the client options * @return the response body */ - public TravelRuleInboundResponse registerInbound(TravelRuleInboundRequest params, RequestOptions options) { - return client.execute(registerInboundRequest(params), options, REGISTER_INBOUND_RESPONSE).data(); + public VaspDirectoryEntry retrieveVasp(String vaspId, RequestOptions options) { + return client.execute(retrieveVaspRequest(vaspId), options, RETRIEVE_VASP_RESPONSE).data(); } /** - * Asynchronous {@link #registerInbound(TravelRuleInboundRequest, RequestOptions)}. + * Asynchronous {@link #retrieveVasp(String, RequestOptions)}. * - * @param params the request parameters + * @param vaspId the {@code vasp_id} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture registerInboundAsync(TravelRuleInboundRequest params, RequestOptions options) { - return client.executeAsync(registerInboundRequest(params), options, REGISTER_INBOUND_RESPONSE).thenApply(RawResponse::data); + public CompletableFuture retrieveVaspAsync(String vaspId, RequestOptions options) { + return client.executeAsync(retrieveVaspRequest(vaspId), options, RETRIEVE_VASP_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #registerInbound(TravelRuleInboundRequest, RequestOptions)}, with the client's options. + * Same as {@link #retrieveVasp(String, RequestOptions)}, with the client's options. * - * @param params the request parameters + * @param vaspId the {@code vasp_id} * @return the result */ - public TravelRuleInboundResponse registerInbound(TravelRuleInboundRequest params) { - return client.execute(registerInboundRequest(params), RequestOptions.none(), REGISTER_INBOUND_RESPONSE).data(); + public VaspDirectoryEntry retrieveVasp(String vaspId) { + return client.execute(retrieveVaspRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_RESPONSE).data(); } /** - * Asynchronous {@link #registerInbound(TravelRuleInboundRequest, RequestOptions)}, with the client's options. + * Asynchronous {@link #retrieveVasp(String, RequestOptions)}, with the client's options. * - * @param params the request parameters + * @param vaspId the {@code vasp_id} * @return a future completed with the result */ - public CompletableFuture registerInboundAsync(TravelRuleInboundRequest params) { - return client.executeAsync(registerInboundRequest(params), RequestOptions.none(), REGISTER_INBOUND_RESPONSE).thenApply(RawResponse::data); + public CompletableFuture retrieveVaspAsync(String vaspId) { + return client.executeAsync(retrieveVaspRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_RESPONSE).thenApply(RawResponse::data); } /** - * Mint a wallet-ownership widget session + * Get your decision on a VASP * - *

Mint a hosted wallet-ownership widget URL/token. Send the customer to the returned url to prove control of a wallet (message signing, Satoshi test, screenshot, or self-declaration). The widget is web-only in this release - open the url in the system browser or a custom tab, never inside a mobile SDK webview. + *

Your application's decision on a counterparty (a directory entry or a Didit network member): approved, restricted or blocked. An entity of a brand with no decision of its own follows the brand's decision, named in inherited_from. Private to your application. * - *

{@code POST /v3/travel-rule/widget-session/} + *

{@code GET /v3/travel-rule/vasps/{vasp_id}/decision/} + * + * @param vaspId The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member. + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleCounterpartyDecisionResponse retrieveVaspDecision(String vaspId, RequestOptions options) { + return client.execute(retrieveVaspDecisionRequest(vaspId), options, RETRIEVE_VASP_DECISION_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveVaspDecision(String, RequestOptions)}. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveVaspDecisionAsync(String vaspId, RequestOptions options) { + return client.executeAsync(retrieveVaspDecisionRequest(vaspId), options, RETRIEVE_VASP_DECISION_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveVaspDecision(String, RequestOptions)}, with the client's options. * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public TravelRuleCounterpartyDecisionResponse retrieveVaspDecision(String vaspId) { + return client.execute(retrieveVaspDecisionRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_DECISION_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveVaspDecision(String, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture retrieveVaspDecisionAsync(String vaspId) { + return client.executeAsync(retrieveVaspDecisionRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_DECISION_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Set your decision on a VASP + * + *

Make or change your application's decision on a counterparty: APPROVED, RESTRICTED or BLOCKED, with an optional reason. It never changes the shared directory entry and acts only through your transaction rules (counterparty_dd_status). A decision on a brand also covers each of its entities that has no decision of its own. + * + *

{@code PUT /v3/travel-rule/vasps/{vasp_id}/decision/} + * + * @param vaspId The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member. * @param params the request parameters * @param options per-request options that override the client options * @return the response body */ - public TravelRuleWidgetSessionResponse createWidgetSession(TravelRuleWidgetSessionCreate params, RequestOptions options) { - return client.execute(createWidgetSessionRequest(params), options, CREATE_WIDGET_SESSION_RESPONSE).data(); + public TravelRuleCounterpartyDecisionResponse updateVaspDecision(String vaspId, TravelRuleCounterpartyDecisionRequest params, RequestOptions options) { + return client.execute(updateVaspDecisionRequest(vaspId, params), options, UPDATE_VASP_DECISION_RESPONSE).data(); } /** - * Asynchronous {@link #createWidgetSession(TravelRuleWidgetSessionCreate, RequestOptions)}. + * Asynchronous {@link #updateVaspDecision(String, TravelRuleCounterpartyDecisionRequest, RequestOptions)}. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture createWidgetSessionAsync(TravelRuleWidgetSessionCreate params, RequestOptions options) { - return client.executeAsync(createWidgetSessionRequest(params), options, CREATE_WIDGET_SESSION_RESPONSE).thenApply(RawResponse::data); + public CompletableFuture updateVaspDecisionAsync(String vaspId, TravelRuleCounterpartyDecisionRequest params, RequestOptions options) { + return client.executeAsync(updateVaspDecisionRequest(vaspId, params), options, UPDATE_VASP_DECISION_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #createWidgetSession(TravelRuleWidgetSessionCreate, RequestOptions)}, with the client's options. + * Same as {@link #updateVaspDecision(String, TravelRuleCounterpartyDecisionRequest, RequestOptions)}, with the client's options. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @return the result */ - public TravelRuleWidgetSessionResponse createWidgetSession(TravelRuleWidgetSessionCreate params) { - return client.execute(createWidgetSessionRequest(params), RequestOptions.none(), CREATE_WIDGET_SESSION_RESPONSE).data(); + public TravelRuleCounterpartyDecisionResponse updateVaspDecision(String vaspId, TravelRuleCounterpartyDecisionRequest params) { + return client.execute(updateVaspDecisionRequest(vaspId, params), RequestOptions.none(), UPDATE_VASP_DECISION_RESPONSE).data(); } /** - * Asynchronous {@link #createWidgetSession(TravelRuleWidgetSessionCreate, RequestOptions)}, with the client's options. + * Asynchronous {@link #updateVaspDecision(String, TravelRuleCounterpartyDecisionRequest, RequestOptions)}, with the client's options. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture createWidgetSessionAsync(TravelRuleWidgetSessionCreate params) { - return client.executeAsync(createWidgetSessionRequest(params), RequestOptions.none(), CREATE_WIDGET_SESSION_RESPONSE).thenApply(RawResponse::data); + public CompletableFuture updateVaspDecisionAsync(String vaspId, TravelRuleCounterpartyDecisionRequest params) { + return client.executeAsync(updateVaspDecisionRequest(vaspId, params), RequestOptions.none(), UPDATE_VASP_DECISION_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler RETRIEVE_SETTINGS_RESPONSE = Responses.json(Conv.model(TravelRuleSettingsDetail::fromJson)); + /** + * Clear your decision on a VASP + * + *

Clear your application's own decision on a counterparty. The response shows the decision that applies afterwards: null, or for an entity of a brand the brand's decision, which the entity follows again. + * + *

{@code DELETE /v3/travel-rule/vasps/{vasp_id}/decision/} + * + * @param vaspId The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member. + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleCounterpartyDecisionResponse clearVaspDecision(String vaspId, RequestOptions options) { + return client.execute(clearVaspDecisionRequest(vaspId), options, CLEAR_VASP_DECISION_RESPONSE).data(); + } - private static ApiRequest retrieveSettingsRequest() { - return ApiRequest.builder("GET", "/v3/travel-rule/settings/") - .build(); + /** + * Asynchronous {@link #clearVaspDecision(String, RequestOptions)}. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture clearVaspDecisionAsync(String vaspId, RequestOptions options) { + return client.executeAsync(clearVaspDecisionRequest(vaspId), options, CLEAR_VASP_DECISION_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler UPDATE_SETTINGS_RESPONSE = Responses.json(Conv.model(TravelRuleSettingsDetail::fromJson)); + /** + * Same as {@link #clearVaspDecision(String, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public TravelRuleCounterpartyDecisionResponse clearVaspDecision(String vaspId) { + return client.execute(clearVaspDecisionRequest(vaspId), RequestOptions.none(), CLEAR_VASP_DECISION_RESPONSE).data(); + } - private static ApiRequest updateSettingsRequest(TravelRuleSettingsUpdate params) { - return ApiRequest.builder("PUT", "/v3/travel-rule/settings/") - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Asynchronous {@link #clearVaspDecision(String, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture clearVaspDecisionAsync(String vaspId) { + return client.executeAsync(clearVaspDecisionRequest(vaspId), RequestOptions.none(), CLEAR_VASP_DECISION_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler> LIST_VASPS_RESPONSE = Responses.page(Conv.model(VaspDirectoryEntry::fromJson)); + /** + * Get a VASP's entity screening + * + *

Whether counterparty screening is on for your application, what one screening costs your organization, how long a result stays fresh, and your latest sanctions and adverse-media screening of the counterparty. Reading it is free. + * + *

{@code GET /v3/travel-rule/vasps/{vasp_id}/screening/} + * + * @param vaspId The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member. + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleCounterpartyScreeningStatus retrieveVaspScreening(String vaspId, RequestOptions options) { + return client.execute(retrieveVaspScreeningRequest(vaspId), options, RETRIEVE_VASP_SCREENING_RESPONSE).data(); + } - private static ApiRequest listVaspsRequest(TravelRuleListVaspsParams params) { - return ApiRequest.builder("GET", "/v3/travel-rule/vasps/") - .params(params) - .queryFields("search", "limit", "offset") - .build(); + /** + * Asynchronous {@link #retrieveVaspScreening(String, RequestOptions)}. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveVaspScreeningAsync(String vaspId, RequestOptions options) { + return client.executeAsync(retrieveVaspScreeningRequest(vaspId), options, RETRIEVE_VASP_SCREENING_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler REGISTER_INBOUND_RESPONSE = Responses.json(Conv.model(TravelRuleInboundResponse::fromJson)); + /** + * Same as {@link #retrieveVaspScreening(String, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public TravelRuleCounterpartyScreeningStatus retrieveVaspScreening(String vaspId) { + return client.execute(retrieveVaspScreeningRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_SCREENING_RESPONSE).data(); + } - private static ApiRequest registerInboundRequest(TravelRuleInboundRequest params) { - return ApiRequest.builder("POST", "/v3/travel-rule/inbound/") - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Asynchronous {@link #retrieveVaspScreening(String, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture retrieveVaspScreeningAsync(String vaspId) { + return client.executeAsync(retrieveVaspScreeningRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_SCREENING_RESPONSE).thenApply(RawResponse::data); } - private static final ResponseHandler CREATE_WIDGET_SESSION_RESPONSE = Responses.json(Conv.model(TravelRuleWidgetSessionResponse::fromJson)); + /** + * Screen a VASP + * + *

Screen the counterparty entity against sanctions and adverse media for your application. Opt-in: counterparty_screening_enabled must be on in your Travel Rule settings. Each new screening is billed at your organization's Company AML Screening price; a screening younger than 30 days is returned instead (200, not billed) unless you send refresh: true. One screening of a counterparty runs at a time per application. The result is private to your application. Sandbox applications are never screened. + * + *

{@code POST /v3/travel-rule/vasps/{vasp_id}/screening/} + * + * @param vaspId The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleCounterpartyScreeningResult screenVasp(String vaspId, TravelRuleCounterpartyScreeningRequest params, RequestOptions options) { + return client.execute(screenVaspRequest(vaspId, params), options, SCREEN_VASP_RESPONSE).data(); + } - private static ApiRequest createWidgetSessionRequest(TravelRuleWidgetSessionCreate params) { - return ApiRequest.builder("POST", "/v3/travel-rule/widget-session/") - .params(params) - .body(ApiRequest.BodyEncoding.JSON) - .build(); + /** + * Asynchronous {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)}. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture screenVaspAsync(String vaspId, TravelRuleCounterpartyScreeningRequest params, RequestOptions options) { + return client.executeAsync(screenVaspRequest(vaspId, params), options, SCREEN_VASP_RESPONSE).thenApply(RawResponse::data); } - /** The {@code travel_rule} operations returning {@link RawResponse}. */ - public static final class WithRawResponse { - private final ApiClient client; + /** + * Same as {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @return the result + */ + public TravelRuleCounterpartyScreeningResult screenVasp(String vaspId, TravelRuleCounterpartyScreeningRequest params) { + return client.execute(screenVaspRequest(vaspId, params), RequestOptions.none(), SCREEN_VASP_RESPONSE).data(); + } - WithRawResponse(ApiClient client) { - this.client = client; - } + /** + * Asynchronous {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture screenVaspAsync(String vaspId, TravelRuleCounterpartyScreeningRequest params) { + return client.executeAsync(screenVaspRequest(vaspId, params), RequestOptions.none(), SCREEN_VASP_RESPONSE).thenApply(RawResponse::data); + } - /** - * {@link #retrieveSettings(RequestOptions)} with the raw response. - * - * @param options per-request options - * @return the result - */ - public RawResponse retrieveSettings(RequestOptions options) { - return client.execute(retrieveSettingsRequest(), options, RETRIEVE_SETTINGS_RESPONSE); - } + /** + * Same as {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)}, without parameters. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return the result + */ + public TravelRuleCounterpartyScreeningResult screenVasp(String vaspId, RequestOptions options) { + return client.execute(screenVaspRequest(vaspId, TravelRuleCounterpartyScreeningRequest.builder().build()), options, SCREEN_VASP_RESPONSE).data(); + } - /** - * Asynchronous {@link #retrieveSettings(RequestOptions)} with the raw response. - * - * @param options per-request options - * @return a future completed with the result - */ - public CompletableFuture> retrieveSettingsAsync(RequestOptions options) { - return client.executeAsync(retrieveSettingsRequest(), options, RETRIEVE_SETTINGS_RESPONSE); - } + /** + * Asynchronous {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)}, without parameters. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture screenVaspAsync(String vaspId, RequestOptions options) { + return client.executeAsync(screenVaspRequest(vaspId, TravelRuleCounterpartyScreeningRequest.builder().build()), options, SCREEN_VASP_RESPONSE).thenApply(RawResponse::data); + } - /** - * {@link #retrieveSettings(RequestOptions)} with the raw response, with the client's options. - * - * @return the result - */ - public RawResponse retrieveSettings() { - return client.execute(retrieveSettingsRequest(), RequestOptions.none(), RETRIEVE_SETTINGS_RESPONSE); - } + /** + * Same as {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)}, without parameters, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public TravelRuleCounterpartyScreeningResult screenVasp(String vaspId) { + return client.execute(screenVaspRequest(vaspId, TravelRuleCounterpartyScreeningRequest.builder().build()), RequestOptions.none(), SCREEN_VASP_RESPONSE).data(); + } - /** - * Asynchronous {@link #retrieveSettings(RequestOptions)} with the raw response, with the client's options. - * - * @return a future completed with the result + /** + * Asynchronous {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)}, without parameters, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture screenVaspAsync(String vaspId) { + return client.executeAsync(screenVaspRequest(vaspId, TravelRuleCounterpartyScreeningRequest.builder().build()), RequestOptions.none(), SCREEN_VASP_RESPONSE).thenApply(RawResponse::data); + } + + /** + * List due-diligence questionnaires with a VASP + * + *

Every due-diligence questionnaire your application sent the counterparty, newest first, with its status and, once answered, the answers. Private to your application. + * + *

{@code GET /v3/travel-rule/vasps/{vasp_id}/questionnaires/} + * + * @param vaspId The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member. + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleCounterpartyQuestionnaireList listVaspQuestionnaires(String vaspId, RequestOptions options) { + return client.execute(listVaspQuestionnairesRequest(vaspId), options, LIST_VASP_QUESTIONNAIRES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listVaspQuestionnaires(String, RequestOptions)}. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture listVaspQuestionnairesAsync(String vaspId, RequestOptions options) { + return client.executeAsync(listVaspQuestionnairesRequest(vaspId), options, LIST_VASP_QUESTIONNAIRES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listVaspQuestionnaires(String, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public TravelRuleCounterpartyQuestionnaireList listVaspQuestionnaires(String vaspId) { + return client.execute(listVaspQuestionnairesRequest(vaspId), RequestOptions.none(), LIST_VASP_QUESTIONNAIRES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listVaspQuestionnaires(String, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture listVaspQuestionnairesAsync(String vaspId) { + return client.executeAsync(listVaspQuestionnairesRequest(vaspId), RequestOptions.none(), LIST_VASP_QUESTIONNAIRES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Send a due-diligence questionnaire to a VASP + * + *

Email Didit's standard due-diligence questionnaire to a compliance address on record for the counterparty, with a single-use answer link. The counterparty has 30 days to answer every question; the answers stay current for 365 days. A new send replaces your earlier unanswered questionnaire to the same counterparty, whose link stops working. One email per minute per address. + * + *

{@code POST /v3/travel-rule/vasps/{vasp_id}/questionnaires/} + * + * @param vaspId The id of a row of GET /v3/travel-rule/vasps/: a directory entry or a Didit network member. + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleCounterpartyQuestionnaire sendVaspQuestionnaire(String vaspId, TravelRuleCounterpartyQuestionnaireRequest params, RequestOptions options) { + return client.execute(sendVaspQuestionnaireRequest(vaspId, params), options, SEND_VASP_QUESTIONNAIRE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)}. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture sendVaspQuestionnaireAsync(String vaspId, TravelRuleCounterpartyQuestionnaireRequest params, RequestOptions options) { + return client.executeAsync(sendVaspQuestionnaireRequest(vaspId, params), options, SEND_VASP_QUESTIONNAIRE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @return the result + */ + public TravelRuleCounterpartyQuestionnaire sendVaspQuestionnaire(String vaspId, TravelRuleCounterpartyQuestionnaireRequest params) { + return client.execute(sendVaspQuestionnaireRequest(vaspId, params), RequestOptions.none(), SEND_VASP_QUESTIONNAIRE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)}, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture sendVaspQuestionnaireAsync(String vaspId, TravelRuleCounterpartyQuestionnaireRequest params) { + return client.executeAsync(sendVaspQuestionnaireRequest(vaspId, params), RequestOptions.none(), SEND_VASP_QUESTIONNAIRE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)}, without parameters. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return the result + */ + public TravelRuleCounterpartyQuestionnaire sendVaspQuestionnaire(String vaspId, RequestOptions options) { + return client.execute(sendVaspQuestionnaireRequest(vaspId, TravelRuleCounterpartyQuestionnaireRequest.builder().build()), options, SEND_VASP_QUESTIONNAIRE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)}, without parameters. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture sendVaspQuestionnaireAsync(String vaspId, RequestOptions options) { + return client.executeAsync(sendVaspQuestionnaireRequest(vaspId, TravelRuleCounterpartyQuestionnaireRequest.builder().build()), options, SEND_VASP_QUESTIONNAIRE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)}, without parameters, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public TravelRuleCounterpartyQuestionnaire sendVaspQuestionnaire(String vaspId) { + return client.execute(sendVaspQuestionnaireRequest(vaspId, TravelRuleCounterpartyQuestionnaireRequest.builder().build()), RequestOptions.none(), SEND_VASP_QUESTIONNAIRE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)}, without parameters, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture sendVaspQuestionnaireAsync(String vaspId) { + return client.executeAsync(sendVaspQuestionnaireRequest(vaspId, TravelRuleCounterpartyQuestionnaireRequest.builder().build()), RequestOptions.none(), SEND_VASP_QUESTIONNAIRE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Register an after-deposit (sunrise) inbound transfer + * + *

Register Travel Rule data for a crypto deposit that already settled on-chain before any exchange took place. Creates an inbound travelRule transaction (or reuses an unclaimed deposit) and resolves the transfer against your wallet address book. For a newly created transaction, Didit sends transaction.created first and the travel_rule.status.updated webhooks after it. + * + *

{@code POST /v3/travel-rule/inbound/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleInboundResponse registerInbound(TravelRuleInboundRequest params, RequestOptions options) { + return client.execute(registerInboundRequest(params), options, REGISTER_INBOUND_RESPONSE).data(); + } + + /** + * Asynchronous {@link #registerInbound(TravelRuleInboundRequest, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture registerInboundAsync(TravelRuleInboundRequest params, RequestOptions options) { + return client.executeAsync(registerInboundRequest(params), options, REGISTER_INBOUND_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #registerInbound(TravelRuleInboundRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public TravelRuleInboundResponse registerInbound(TravelRuleInboundRequest params) { + return client.execute(registerInboundRequest(params), RequestOptions.none(), REGISTER_INBOUND_RESPONSE).data(); + } + + /** + * Asynchronous {@link #registerInbound(TravelRuleInboundRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture registerInboundAsync(TravelRuleInboundRequest params) { + return client.executeAsync(registerInboundRequest(params), RequestOptions.none(), REGISTER_INBOUND_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Mint a wallet-ownership widget session + * + *

Mint a hosted wallet-ownership widget URL/token. Send the customer to the returned url to prove control of a wallet (message signing, Satoshi test, screenshot, or self-declaration). The widget is web-only in this release - open the url in the system browser or a custom tab, never inside a mobile SDK webview. + * + *

{@code POST /v3/travel-rule/widget-session/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleWidgetSessionResponse createWidgetSession(TravelRuleWidgetSessionCreate params, RequestOptions options) { + return client.execute(createWidgetSessionRequest(params), options, CREATE_WIDGET_SESSION_RESPONSE).data(); + } + + /** + * Asynchronous {@link #createWidgetSession(TravelRuleWidgetSessionCreate, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture createWidgetSessionAsync(TravelRuleWidgetSessionCreate params, RequestOptions options) { + return client.executeAsync(createWidgetSessionRequest(params), options, CREATE_WIDGET_SESSION_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #createWidgetSession(TravelRuleWidgetSessionCreate, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public TravelRuleWidgetSessionResponse createWidgetSession(TravelRuleWidgetSessionCreate params) { + return client.execute(createWidgetSessionRequest(params), RequestOptions.none(), CREATE_WIDGET_SESSION_RESPONSE).data(); + } + + /** + * Asynchronous {@link #createWidgetSession(TravelRuleWidgetSessionCreate, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture createWidgetSessionAsync(TravelRuleWidgetSessionCreate params) { + return client.executeAsync(createWidgetSessionRequest(params), RequestOptions.none(), CREATE_WIDGET_SESSION_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler RETRIEVE_SETTINGS_RESPONSE = Responses.json(Conv.model(TravelRuleSettingsDetail::fromJson)); + + private static ApiRequest retrieveSettingsRequest() { + return ApiRequest.builder("GET", "/v3/travel-rule/settings/") + .build(); + } + + private static final ResponseHandler UPDATE_SETTINGS_RESPONSE = Responses.json(Conv.model(TravelRuleSettingsDetail::fromJson)); + + private static ApiRequest updateSettingsRequest(TravelRuleSettingsUpdate params) { + return ApiRequest.builder("PUT", "/v3/travel-rule/settings/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler SEND_COMPLIANCE_EMAIL_CONFIRMATION_RESPONSE = Responses.json(Conv.model(TravelRuleSendComplianceEmailConfirmationResponse::fromJson)); + + private static ApiRequest sendComplianceEmailConfirmationRequest() { + return ApiRequest.builder("POST", "/v3/travel-rule/settings/compliance-email/confirmation/") + .build(); + } + + private static final ResponseHandler> LIST_VASPS_RESPONSE = Responses.page(Conv.model(VaspDirectoryEntry::fromJson)); + + private static ApiRequest listVaspsRequest(TravelRuleListVaspsParams params) { + return ApiRequest.builder("GET", "/v3/travel-rule/vasps/") + .params(params) + .queryFields("search", "network", "jurisdiction", "reachable", "kind", "risk", "responsiveness", "limit", "offset") + .build(); + } + + private static final ResponseHandler RETRIEVE_VASP_RESPONSE = Responses.json(Conv.model(VaspDirectoryEntry::fromJson)); + + private static ApiRequest retrieveVaspRequest(String vaspId) { + return ApiRequest.builder("GET", "/v3/travel-rule/vasps/{vasp_id}/") + .pathParam("vasp_id", vaspId) + .build(); + } + + private static final ResponseHandler RETRIEVE_VASP_DECISION_RESPONSE = Responses.json(Conv.model(TravelRuleCounterpartyDecisionResponse::fromJson)); + + private static ApiRequest retrieveVaspDecisionRequest(String vaspId) { + return ApiRequest.builder("GET", "/v3/travel-rule/vasps/{vasp_id}/decision/") + .pathParam("vasp_id", vaspId) + .build(); + } + + private static final ResponseHandler UPDATE_VASP_DECISION_RESPONSE = Responses.json(Conv.model(TravelRuleCounterpartyDecisionResponse::fromJson)); + + private static ApiRequest updateVaspDecisionRequest(String vaspId, TravelRuleCounterpartyDecisionRequest params) { + return ApiRequest.builder("PUT", "/v3/travel-rule/vasps/{vasp_id}/decision/") + .pathParam("vasp_id", vaspId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler CLEAR_VASP_DECISION_RESPONSE = Responses.json(Conv.model(TravelRuleCounterpartyDecisionResponse::fromJson)); + + private static ApiRequest clearVaspDecisionRequest(String vaspId) { + return ApiRequest.builder("DELETE", "/v3/travel-rule/vasps/{vasp_id}/decision/") + .pathParam("vasp_id", vaspId) + .build(); + } + + private static final ResponseHandler RETRIEVE_VASP_SCREENING_RESPONSE = Responses.json(Conv.model(TravelRuleCounterpartyScreeningStatus::fromJson)); + + private static ApiRequest retrieveVaspScreeningRequest(String vaspId) { + return ApiRequest.builder("GET", "/v3/travel-rule/vasps/{vasp_id}/screening/") + .pathParam("vasp_id", vaspId) + .build(); + } + + private static final ResponseHandler SCREEN_VASP_RESPONSE = Responses.json(Conv.model(TravelRuleCounterpartyScreeningResult::fromJson)); + + private static ApiRequest screenVaspRequest(String vaspId, TravelRuleCounterpartyScreeningRequest params) { + return ApiRequest.builder("POST", "/v3/travel-rule/vasps/{vasp_id}/screening/") + .pathParam("vasp_id", vaspId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler LIST_VASP_QUESTIONNAIRES_RESPONSE = Responses.json(Conv.model(TravelRuleCounterpartyQuestionnaireList::fromJson)); + + private static ApiRequest listVaspQuestionnairesRequest(String vaspId) { + return ApiRequest.builder("GET", "/v3/travel-rule/vasps/{vasp_id}/questionnaires/") + .pathParam("vasp_id", vaspId) + .build(); + } + + private static final ResponseHandler SEND_VASP_QUESTIONNAIRE_RESPONSE = Responses.json(Conv.model(TravelRuleCounterpartyQuestionnaire::fromJson)); + + private static ApiRequest sendVaspQuestionnaireRequest(String vaspId, TravelRuleCounterpartyQuestionnaireRequest params) { + return ApiRequest.builder("POST", "/v3/travel-rule/vasps/{vasp_id}/questionnaires/") + .pathParam("vasp_id", vaspId) + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler REGISTER_INBOUND_RESPONSE = Responses.json(Conv.model(TravelRuleInboundResponse::fromJson)); + + private static ApiRequest registerInboundRequest(TravelRuleInboundRequest params) { + return ApiRequest.builder("POST", "/v3/travel-rule/inbound/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler CREATE_WIDGET_SESSION_RESPONSE = Responses.json(Conv.model(TravelRuleWidgetSessionResponse::fromJson)); + + private static ApiRequest createWidgetSessionRequest(TravelRuleWidgetSessionCreate params) { + return ApiRequest.builder("POST", "/v3/travel-rule/widget-session/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + /** The {@code travel_rule} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #retrieveSettings(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return the result + */ + public RawResponse retrieveSettings(RequestOptions options) { + return client.execute(retrieveSettingsRequest(), options, RETRIEVE_SETTINGS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSettings(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveSettingsAsync(RequestOptions options) { + return client.executeAsync(retrieveSettingsRequest(), options, RETRIEVE_SETTINGS_RESPONSE); + } + + /** + * {@link #retrieveSettings(RequestOptions)} with the raw response, with the client's options. + * + * @return the result + */ + public RawResponse retrieveSettings() { + return client.execute(retrieveSettingsRequest(), RequestOptions.none(), RETRIEVE_SETTINGS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveSettings(RequestOptions)} with the raw response, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> retrieveSettingsAsync() { + return client.executeAsync(retrieveSettingsRequest(), RequestOptions.none(), RETRIEVE_SETTINGS_RESPONSE); + } + + /** + * {@link #updateSettings(TravelRuleSettingsUpdate, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse updateSettings(TravelRuleSettingsUpdate params, RequestOptions options) { + return client.execute(updateSettingsRequest(params), options, UPDATE_SETTINGS_RESPONSE); + } + + /** + * Asynchronous {@link #updateSettings(TravelRuleSettingsUpdate, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> updateSettingsAsync(TravelRuleSettingsUpdate params, RequestOptions options) { + return client.executeAsync(updateSettingsRequest(params), options, UPDATE_SETTINGS_RESPONSE); + } + + /** + * {@link #updateSettings(TravelRuleSettingsUpdate, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse updateSettings(TravelRuleSettingsUpdate params) { + return client.execute(updateSettingsRequest(params), RequestOptions.none(), UPDATE_SETTINGS_RESPONSE); + } + + /** + * Asynchronous {@link #updateSettings(TravelRuleSettingsUpdate, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> updateSettingsAsync(TravelRuleSettingsUpdate params) { + return client.executeAsync(updateSettingsRequest(params), RequestOptions.none(), UPDATE_SETTINGS_RESPONSE); + } + + /** + * {@link #sendComplianceEmailConfirmation(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return the result + */ + public RawResponse sendComplianceEmailConfirmation(RequestOptions options) { + return client.execute(sendComplianceEmailConfirmationRequest(), options, SEND_COMPLIANCE_EMAIL_CONFIRMATION_RESPONSE); + } + + /** + * Asynchronous {@link #sendComplianceEmailConfirmation(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> sendComplianceEmailConfirmationAsync(RequestOptions options) { + return client.executeAsync(sendComplianceEmailConfirmationRequest(), options, SEND_COMPLIANCE_EMAIL_CONFIRMATION_RESPONSE); + } + + /** + * {@link #sendComplianceEmailConfirmation(RequestOptions)} with the raw response, with the client's options. + * + * @return the result + */ + public RawResponse sendComplianceEmailConfirmation() { + return client.execute(sendComplianceEmailConfirmationRequest(), RequestOptions.none(), SEND_COMPLIANCE_EMAIL_CONFIRMATION_RESPONSE); + } + + /** + * Asynchronous {@link #sendComplianceEmailConfirmation(RequestOptions)} with the raw response, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> sendComplianceEmailConfirmationAsync() { + return client.executeAsync(sendComplianceEmailConfirmationRequest(), RequestOptions.none(), SEND_COMPLIANCE_EMAIL_CONFIRMATION_RESPONSE); + } + + /** + * {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> listVasps(TravelRuleListVaspsParams params, RequestOptions options) { + return client.execute(listVaspsRequest(params), options, LIST_VASPS_RESPONSE); + } + + /** + * Asynchronous {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listVaspsAsync(TravelRuleListVaspsParams params, RequestOptions options) { + return client.executeAsync(listVaspsRequest(params), options, LIST_VASPS_RESPONSE); + } + + /** + * {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse> listVasps(TravelRuleListVaspsParams params) { + return client.execute(listVaspsRequest(params), RequestOptions.none(), LIST_VASPS_RESPONSE); + } + + /** + * Asynchronous {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listVaspsAsync(TravelRuleListVaspsParams params) { + return client.executeAsync(listVaspsRequest(params), RequestOptions.none(), LIST_VASPS_RESPONSE); + } + + /** + * {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result */ - public CompletableFuture> retrieveSettingsAsync() { - return client.executeAsync(retrieveSettingsRequest(), RequestOptions.none(), RETRIEVE_SETTINGS_RESPONSE); + public RawResponse> listVasps(RequestOptions options) { + return client.execute(listVaspsRequest(TravelRuleListVaspsParams.builder().build()), options, LIST_VASPS_RESPONSE); } /** - * {@link #updateSettings(TravelRuleSettingsUpdate, RequestOptions)} with the raw response. + * Asynchronous {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listVaspsAsync(RequestOptions options) { + return client.executeAsync(listVaspsRequest(TravelRuleListVaspsParams.builder().build()), options, LIST_VASPS_RESPONSE); + } + + /** + * {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse> listVasps() { + return client.execute(listVaspsRequest(TravelRuleListVaspsParams.builder().build()), RequestOptions.none(), LIST_VASPS_RESPONSE); + } + + /** + * Asynchronous {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>> listVaspsAsync() { + return client.executeAsync(listVaspsRequest(TravelRuleListVaspsParams.builder().build()), RequestOptions.none(), LIST_VASPS_RESPONSE); + } + + /** + * {@link #retrieveVasp(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return the result + */ + public RawResponse retrieveVasp(String vaspId, RequestOptions options) { + return client.execute(retrieveVaspRequest(vaspId), options, RETRIEVE_VASP_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveVasp(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveVaspAsync(String vaspId, RequestOptions options) { + return client.executeAsync(retrieveVaspRequest(vaspId), options, RETRIEVE_VASP_RESPONSE); + } + + /** + * {@link #retrieveVasp(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public RawResponse retrieveVasp(String vaspId) { + return client.execute(retrieveVaspRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveVasp(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture> retrieveVaspAsync(String vaspId) { + return client.executeAsync(retrieveVaspRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_RESPONSE); + } + + /** + * {@link #retrieveVaspDecision(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return the result + */ + public RawResponse retrieveVaspDecision(String vaspId, RequestOptions options) { + return client.execute(retrieveVaspDecisionRequest(vaspId), options, RETRIEVE_VASP_DECISION_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveVaspDecision(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveVaspDecisionAsync(String vaspId, RequestOptions options) { + return client.executeAsync(retrieveVaspDecisionRequest(vaspId), options, RETRIEVE_VASP_DECISION_RESPONSE); + } + + /** + * {@link #retrieveVaspDecision(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public RawResponse retrieveVaspDecision(String vaspId) { + return client.execute(retrieveVaspDecisionRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_DECISION_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveVaspDecision(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture> retrieveVaspDecisionAsync(String vaspId) { + return client.executeAsync(retrieveVaspDecisionRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_DECISION_RESPONSE); + } + + /** + * {@link #updateVaspDecision(String, TravelRuleCounterpartyDecisionRequest, RequestOptions)} with the raw response. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse updateSettings(TravelRuleSettingsUpdate params, RequestOptions options) { - return client.execute(updateSettingsRequest(params), options, UPDATE_SETTINGS_RESPONSE); + public RawResponse updateVaspDecision(String vaspId, TravelRuleCounterpartyDecisionRequest params, RequestOptions options) { + return client.execute(updateVaspDecisionRequest(vaspId, params), options, UPDATE_VASP_DECISION_RESPONSE); } /** - * Asynchronous {@link #updateSettings(TravelRuleSettingsUpdate, RequestOptions)} with the raw response. + * Asynchronous {@link #updateVaspDecision(String, TravelRuleCounterpartyDecisionRequest, RequestOptions)} with the raw response. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateSettingsAsync(TravelRuleSettingsUpdate params, RequestOptions options) { - return client.executeAsync(updateSettingsRequest(params), options, UPDATE_SETTINGS_RESPONSE); + public CompletableFuture> updateVaspDecisionAsync(String vaspId, TravelRuleCounterpartyDecisionRequest params, RequestOptions options) { + return client.executeAsync(updateVaspDecisionRequest(vaspId, params), options, UPDATE_VASP_DECISION_RESPONSE); } /** - * {@link #updateSettings(TravelRuleSettingsUpdate, RequestOptions)} with the raw response, with the client's options. + * {@link #updateVaspDecision(String, TravelRuleCounterpartyDecisionRequest, RequestOptions)} with the raw response, with the client's options. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @return the result */ - public RawResponse updateSettings(TravelRuleSettingsUpdate params) { - return client.execute(updateSettingsRequest(params), RequestOptions.none(), UPDATE_SETTINGS_RESPONSE); + public RawResponse updateVaspDecision(String vaspId, TravelRuleCounterpartyDecisionRequest params) { + return client.execute(updateVaspDecisionRequest(vaspId, params), RequestOptions.none(), UPDATE_VASP_DECISION_RESPONSE); } /** - * Asynchronous {@link #updateSettings(TravelRuleSettingsUpdate, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #updateVaspDecision(String, TravelRuleCounterpartyDecisionRequest, RequestOptions)} with the raw response, with the client's options. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updateSettingsAsync(TravelRuleSettingsUpdate params) { - return client.executeAsync(updateSettingsRequest(params), RequestOptions.none(), UPDATE_SETTINGS_RESPONSE); + public CompletableFuture> updateVaspDecisionAsync(String vaspId, TravelRuleCounterpartyDecisionRequest params) { + return client.executeAsync(updateVaspDecisionRequest(vaspId, params), RequestOptions.none(), UPDATE_VASP_DECISION_RESPONSE); } /** - * {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response. + * {@link #clearVaspDecision(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return the result + */ + public RawResponse clearVaspDecision(String vaspId, RequestOptions options) { + return client.execute(clearVaspDecisionRequest(vaspId), options, CLEAR_VASP_DECISION_RESPONSE); + } + + /** + * Asynchronous {@link #clearVaspDecision(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> clearVaspDecisionAsync(String vaspId, RequestOptions options) { + return client.executeAsync(clearVaspDecisionRequest(vaspId), options, CLEAR_VASP_DECISION_RESPONSE); + } + + /** + * {@link #clearVaspDecision(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public RawResponse clearVaspDecision(String vaspId) { + return client.execute(clearVaspDecisionRequest(vaspId), RequestOptions.none(), CLEAR_VASP_DECISION_RESPONSE); + } + + /** + * Asynchronous {@link #clearVaspDecision(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture> clearVaspDecisionAsync(String vaspId) { + return client.executeAsync(clearVaspDecisionRequest(vaspId), RequestOptions.none(), CLEAR_VASP_DECISION_RESPONSE); + } + + /** + * {@link #retrieveVaspScreening(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return the result + */ + public RawResponse retrieveVaspScreening(String vaspId, RequestOptions options) { + return client.execute(retrieveVaspScreeningRequest(vaspId), options, RETRIEVE_VASP_SCREENING_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveVaspScreening(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveVaspScreeningAsync(String vaspId, RequestOptions options) { + return client.executeAsync(retrieveVaspScreeningRequest(vaspId), options, RETRIEVE_VASP_SCREENING_RESPONSE); + } + + /** + * {@link #retrieveVaspScreening(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public RawResponse retrieveVaspScreening(String vaspId) { + return client.execute(retrieveVaspScreeningRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_SCREENING_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveVaspScreening(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture> retrieveVaspScreeningAsync(String vaspId) { + return client.executeAsync(retrieveVaspScreeningRequest(vaspId), RequestOptions.none(), RETRIEVE_VASP_SCREENING_RESPONSE); + } + + /** + * {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)} with the raw response. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse> listVasps(TravelRuleListVaspsParams params, RequestOptions options) { - return client.execute(listVaspsRequest(params), options, LIST_VASPS_RESPONSE); + public RawResponse screenVasp(String vaspId, TravelRuleCounterpartyScreeningRequest params, RequestOptions options) { + return client.execute(screenVaspRequest(vaspId, params), options, SCREEN_VASP_RESPONSE); } /** - * Asynchronous {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response. + * Asynchronous {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)} with the raw response. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture>> listVaspsAsync(TravelRuleListVaspsParams params, RequestOptions options) { - return client.executeAsync(listVaspsRequest(params), options, LIST_VASPS_RESPONSE); + public CompletableFuture> screenVaspAsync(String vaspId, TravelRuleCounterpartyScreeningRequest params, RequestOptions options) { + return client.executeAsync(screenVaspRequest(vaspId, params), options, SCREEN_VASP_RESPONSE); } /** - * {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, with the client's options. + * {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)} with the raw response, with the client's options. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @return the result */ - public RawResponse> listVasps(TravelRuleListVaspsParams params) { - return client.execute(listVaspsRequest(params), RequestOptions.none(), LIST_VASPS_RESPONSE); + public RawResponse screenVasp(String vaspId, TravelRuleCounterpartyScreeningRequest params) { + return client.execute(screenVaspRequest(vaspId, params), RequestOptions.none(), SCREEN_VASP_RESPONSE); } /** - * Asynchronous {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)} with the raw response, with the client's options. * + * @param vaspId the {@code vasp_id} * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture>> listVaspsAsync(TravelRuleListVaspsParams params) { - return client.executeAsync(listVaspsRequest(params), RequestOptions.none(), LIST_VASPS_RESPONSE); + public CompletableFuture> screenVaspAsync(String vaspId, TravelRuleCounterpartyScreeningRequest params) { + return client.executeAsync(screenVaspRequest(vaspId, params), RequestOptions.none(), SCREEN_VASP_RESPONSE); } /** - * {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, without parameters. + * {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)} with the raw response, without parameters. * + * @param vaspId the {@code vasp_id} * @param options per-request options * @return the result */ - public RawResponse> listVasps(RequestOptions options) { - return client.execute(listVaspsRequest(TravelRuleListVaspsParams.builder().build()), options, LIST_VASPS_RESPONSE); + public RawResponse screenVasp(String vaspId, RequestOptions options) { + return client.execute(screenVaspRequest(vaspId, TravelRuleCounterpartyScreeningRequest.builder().build()), options, SCREEN_VASP_RESPONSE); } /** - * Asynchronous {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, without parameters. + * Asynchronous {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)} with the raw response, without parameters. * + * @param vaspId the {@code vasp_id} * @param options per-request options * @return a future completed with the result */ - public CompletableFuture>> listVaspsAsync(RequestOptions options) { - return client.executeAsync(listVaspsRequest(TravelRuleListVaspsParams.builder().build()), options, LIST_VASPS_RESPONSE); + public CompletableFuture> screenVaspAsync(String vaspId, RequestOptions options) { + return client.executeAsync(screenVaspRequest(vaspId, TravelRuleCounterpartyScreeningRequest.builder().build()), options, SCREEN_VASP_RESPONSE); } /** - * {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)} with the raw response, without parameters, with the client's options. * + * @param vaspId the {@code vasp_id} * @return the result */ - public RawResponse> listVasps() { - return client.execute(listVaspsRequest(TravelRuleListVaspsParams.builder().build()), RequestOptions.none(), LIST_VASPS_RESPONSE); + public RawResponse screenVasp(String vaspId) { + return client.execute(screenVaspRequest(vaspId, TravelRuleCounterpartyScreeningRequest.builder().build()), RequestOptions.none(), SCREEN_VASP_RESPONSE); } /** - * Asynchronous {@link #listVasps(TravelRuleListVaspsParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * Asynchronous {@link #screenVasp(String, TravelRuleCounterpartyScreeningRequest, RequestOptions)} with the raw response, without parameters, with the client's options. * + * @param vaspId the {@code vasp_id} * @return a future completed with the result */ - public CompletableFuture>> listVaspsAsync() { - return client.executeAsync(listVaspsRequest(TravelRuleListVaspsParams.builder().build()), RequestOptions.none(), LIST_VASPS_RESPONSE); + public CompletableFuture> screenVaspAsync(String vaspId) { + return client.executeAsync(screenVaspRequest(vaspId, TravelRuleCounterpartyScreeningRequest.builder().build()), RequestOptions.none(), SCREEN_VASP_RESPONSE); + } + + /** + * {@link #listVaspQuestionnaires(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return the result + */ + public RawResponse listVaspQuestionnaires(String vaspId, RequestOptions options) { + return client.execute(listVaspQuestionnairesRequest(vaspId), options, LIST_VASP_QUESTIONNAIRES_RESPONSE); + } + + /** + * Asynchronous {@link #listVaspQuestionnaires(String, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listVaspQuestionnairesAsync(String vaspId, RequestOptions options) { + return client.executeAsync(listVaspQuestionnairesRequest(vaspId), options, LIST_VASP_QUESTIONNAIRES_RESPONSE); + } + + /** + * {@link #listVaspQuestionnaires(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public RawResponse listVaspQuestionnaires(String vaspId) { + return client.execute(listVaspQuestionnairesRequest(vaspId), RequestOptions.none(), LIST_VASP_QUESTIONNAIRES_RESPONSE); + } + + /** + * Asynchronous {@link #listVaspQuestionnaires(String, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture> listVaspQuestionnairesAsync(String vaspId) { + return client.executeAsync(listVaspQuestionnairesRequest(vaspId), RequestOptions.none(), LIST_VASP_QUESTIONNAIRES_RESPONSE); + } + + /** + * {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse sendVaspQuestionnaire(String vaspId, TravelRuleCounterpartyQuestionnaireRequest params, RequestOptions options) { + return client.execute(sendVaspQuestionnaireRequest(vaspId, params), options, SEND_VASP_QUESTIONNAIRE_RESPONSE); + } + + /** + * Asynchronous {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)} with the raw response. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> sendVaspQuestionnaireAsync(String vaspId, TravelRuleCounterpartyQuestionnaireRequest params, RequestOptions options) { + return client.executeAsync(sendVaspQuestionnaireRequest(vaspId, params), options, SEND_VASP_QUESTIONNAIRE_RESPONSE); + } + + /** + * {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @return the result + */ + public RawResponse sendVaspQuestionnaire(String vaspId, TravelRuleCounterpartyQuestionnaireRequest params) { + return client.execute(sendVaspQuestionnaireRequest(vaspId, params), RequestOptions.none(), SEND_VASP_QUESTIONNAIRE_RESPONSE); + } + + /** + * Asynchronous {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> sendVaspQuestionnaireAsync(String vaspId, TravelRuleCounterpartyQuestionnaireRequest params) { + return client.executeAsync(sendVaspQuestionnaireRequest(vaspId, params), RequestOptions.none(), SEND_VASP_QUESTIONNAIRE_RESPONSE); + } + + /** + * {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)} with the raw response, without parameters. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return the result + */ + public RawResponse sendVaspQuestionnaire(String vaspId, RequestOptions options) { + return client.execute(sendVaspQuestionnaireRequest(vaspId, TravelRuleCounterpartyQuestionnaireRequest.builder().build()), options, SEND_VASP_QUESTIONNAIRE_RESPONSE); + } + + /** + * Asynchronous {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)} with the raw response, without parameters. + * + * @param vaspId the {@code vasp_id} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> sendVaspQuestionnaireAsync(String vaspId, RequestOptions options) { + return client.executeAsync(sendVaspQuestionnaireRequest(vaspId, TravelRuleCounterpartyQuestionnaireRequest.builder().build()), options, SEND_VASP_QUESTIONNAIRE_RESPONSE); + } + + /** + * {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return the result + */ + public RawResponse sendVaspQuestionnaire(String vaspId) { + return client.execute(sendVaspQuestionnaireRequest(vaspId, TravelRuleCounterpartyQuestionnaireRequest.builder().build()), RequestOptions.none(), SEND_VASP_QUESTIONNAIRE_RESPONSE); + } + + /** + * Asynchronous {@link #sendVaspQuestionnaire(String, TravelRuleCounterpartyQuestionnaireRequest, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param vaspId the {@code vasp_id} + * @return a future completed with the result + */ + public CompletableFuture> sendVaspQuestionnaireAsync(String vaspId) { + return client.executeAsync(sendVaspQuestionnaireRequest(vaspId, TravelRuleCounterpartyQuestionnaireRequest.builder().build()), RequestOptions.none(), SEND_VASP_QUESTIONNAIRE_RESPONSE); } /** diff --git a/src/main/java/me/didit/resources/TravelRuleTransfersResource.java b/src/main/java/me/didit/resources/TravelRuleTransfersResource.java new file mode 100644 index 0000000..a03d400 --- /dev/null +++ b/src/main/java/me/didit/resources/TravelRuleTransfersResource.java @@ -0,0 +1,217 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.Page; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.TravelRuleTransferRow; +import me.didit.types.TravelRuleTransfersListParams; + +/** The {@code travel_rule.transfers} resource: {@code client.travelRule().transfers()}. */ +public final class TravelRuleTransfersResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + TravelRuleTransfersResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * List Travel Rule transfers + * + *

Your application's Travel Rule transfers, newest first, paginated with limit (default 50) and offset. Rows carry VASP names, wallets and memos only, never person data. Comma-separated values select several. An unknown value answers 400 naming the parameter, for example {"direction": ["Unknown value(s): inbound."]}. + * + *

{@code GET /v3/travel-rule/transfers/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page list(TravelRuleTransfersListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(TravelRuleTransfersListParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(TravelRuleTransfersListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(TravelRuleTransfersListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public Page list(TravelRuleTransfersListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(TravelRuleTransfersListParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listAsync(TravelRuleTransfersListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(TravelRuleTransfersListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public Page list(RequestOptions options) { + return client.execute(listRequest(TravelRuleTransfersListParams.builder().build()), options, LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(TravelRuleTransfersListParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(TravelRuleTransfersListParams.builder().build()), options, LIST_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #list(TravelRuleTransfersListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public Page list() { + return client.execute(listRequest(TravelRuleTransfersListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).data(); + } + + /** + * Asynchronous {@link #list(TravelRuleTransfersListParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> listAsync() { + return client.executeAsync(listRequest(TravelRuleTransfersListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(TravelRuleTransferRow::fromJson)); + + private static ApiRequest listRequest(TravelRuleTransfersListParams params) { + return ApiRequest.builder("GET", "/v3/travel-rule/transfers/") + .params(params) + .queryFields("direction", "status", "rail", "kind", "currency", "start_date", "end_date", "counterparty", "limit", "offset") + .build(); + } + + /** The {@code travel_rule.transfers} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #list(TravelRuleTransfersListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> list(TravelRuleTransfersListParams params, RequestOptions options) { + return client.execute(listRequest(params), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(TravelRuleTransfersListParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(TravelRuleTransfersListParams params, RequestOptions options) { + return client.executeAsync(listRequest(params), options, LIST_RESPONSE); + } + + /** + * {@link #list(TravelRuleTransfersListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse> list(TravelRuleTransfersListParams params) { + return client.execute(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(TravelRuleTransfersListParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(TravelRuleTransfersListParams params) { + return client.executeAsync(listRequest(params), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * {@link #list(TravelRuleTransfersListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse> list(RequestOptions options) { + return client.execute(listRequest(TravelRuleTransfersListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(TravelRuleTransfersListParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listAsync(RequestOptions options) { + return client.executeAsync(listRequest(TravelRuleTransfersListParams.builder().build()), options, LIST_RESPONSE); + } + + /** + * {@link #list(TravelRuleTransfersListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse> list() { + return client.execute(listRequest(TravelRuleTransfersListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + + /** + * Asynchronous {@link #list(TravelRuleTransfersListParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture>> listAsync() { + return client.executeAsync(listRequest(TravelRuleTransfersListParams.builder().build()), RequestOptions.none(), LIST_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/TravelRuleWalletAddressesResource.java b/src/main/java/me/didit/resources/TravelRuleWalletAddressesResource.java index fddb7d6..5769bba 100644 --- a/src/main/java/me/didit/resources/TravelRuleWalletAddressesResource.java +++ b/src/main/java/me/didit/resources/TravelRuleWalletAddressesResource.java @@ -10,10 +10,12 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.TravelRuleWalletAddressesBulkImportParams; import me.didit.types.TravelRuleWalletAddressesListParams; import me.didit.types.WalletAddressEntry; import me.didit.types.WalletAddressEntryCreate; import me.didit.types.WalletAddressEntryUpdate; +import me.didit.types.WalletAddressImportReport; import org.jspecify.annotations.Nullable; /** The {@code travel_rule.wallet_addresses} resource: {@code client.travelRule().walletAddresses()}. */ @@ -219,7 +221,7 @@ public CompletableFuture updateAsync(String entryUuid, /** * Delete a wallet address * - *

Deletes the entry. It stops matching new transfers immediately and the address is freed up for re-registration. + *

Deletes the entry together with its ownership proofs and the screenshots they stored. It stops matching new transfers immediately and the address is freed up for re-registration. Transfers that already used the wallet keep their own record of it and read the same. Deletion is irreversible. * *

{@code DELETE /v3/travel-rule/wallet-addresses/{entry_uuid}/} * @@ -260,6 +262,52 @@ public CompletableFuture deleteAsync(String entryUuid) { return client.executeAsync(deleteRequest(entryUuid), RequestOptions.none(), DELETE_RESPONSE).thenAccept(response -> {}); } + /** + * Import wallet addresses in bulk + * + *

Add up to 10,000 wallets to your Travel Rule address book in one call, as JSON or as a CSV file. A wallet can be sent by its address, by the SHA-256 of its lowercased address (address_hash) to keep the address private, or both. The import is idempotent: a wallet already in the book is reported as existing and left untouched, so the same file can be sent again. Each row is validated on its own, so one bad row never fails the others. A wallet imported by its hash only matches transfers to that address and can be proven with the wallet-ownership widget like any other entry. + * + *

{@code POST /v3/travel-rule/wallet-addresses/import/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public WalletAddressImportReport bulkImport(TravelRuleWalletAddressesBulkImportParams params, RequestOptions options) { + return client.execute(bulkImportRequest(params), options, BULK_IMPORT_RESPONSE).data(); + } + + /** + * Asynchronous {@link #bulkImport(TravelRuleWalletAddressesBulkImportParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture bulkImportAsync(TravelRuleWalletAddressesBulkImportParams params, RequestOptions options) { + return client.executeAsync(bulkImportRequest(params), options, BULK_IMPORT_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #bulkImport(TravelRuleWalletAddressesBulkImportParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public WalletAddressImportReport bulkImport(TravelRuleWalletAddressesBulkImportParams params) { + return client.execute(bulkImportRequest(params), RequestOptions.none(), BULK_IMPORT_RESPONSE).data(); + } + + /** + * Asynchronous {@link #bulkImport(TravelRuleWalletAddressesBulkImportParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture bulkImportAsync(TravelRuleWalletAddressesBulkImportParams params) { + return client.executeAsync(bulkImportRequest(params), RequestOptions.none(), BULK_IMPORT_RESPONSE).thenApply(RawResponse::data); + } + private static final ResponseHandler> LIST_RESPONSE = Responses.page(Conv.model(WalletAddressEntry::fromJson)); private static ApiRequest listRequest(TravelRuleWalletAddressesListParams params) { @@ -296,6 +344,15 @@ private static ApiRequest deleteRequest(String entryUuid) { .build(); } + private static final ResponseHandler BULK_IMPORT_RESPONSE = Responses.json(Conv.model(WalletAddressImportReport::fromJson)); + + private static ApiRequest bulkImportRequest(TravelRuleWalletAddressesBulkImportParams params) { + return ApiRequest.builder("POST", "/v3/travel-rule/wallet-addresses/import/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON_OR_MULTIPART) + .build(); + } + /** The {@code travel_rule.wallet_addresses} operations returning {@link RawResponse}. */ public static final class WithRawResponse { private final ApiClient client; @@ -513,5 +570,47 @@ public CompletableFuture> updateAsync(Stri public CompletableFuture> deleteAsync(String entryUuid) { return client.executeAsync(deleteRequest(entryUuid), RequestOptions.none(), DELETE_RESPONSE); } + + /** + * {@link #bulkImport(TravelRuleWalletAddressesBulkImportParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse bulkImport(TravelRuleWalletAddressesBulkImportParams params, RequestOptions options) { + return client.execute(bulkImportRequest(params), options, BULK_IMPORT_RESPONSE); + } + + /** + * Asynchronous {@link #bulkImport(TravelRuleWalletAddressesBulkImportParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> bulkImportAsync(TravelRuleWalletAddressesBulkImportParams params, RequestOptions options) { + return client.executeAsync(bulkImportRequest(params), options, BULK_IMPORT_RESPONSE); + } + + /** + * {@link #bulkImport(TravelRuleWalletAddressesBulkImportParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse bulkImport(TravelRuleWalletAddressesBulkImportParams params) { + return client.execute(bulkImportRequest(params), RequestOptions.none(), BULK_IMPORT_RESPONSE); + } + + /** + * Asynchronous {@link #bulkImport(TravelRuleWalletAddressesBulkImportParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> bulkImportAsync(TravelRuleWalletAddressesBulkImportParams params) { + return client.executeAsync(bulkImportRequest(params), RequestOptions.none(), BULK_IMPORT_RESPONSE); + } } } diff --git a/src/main/java/me/didit/resources/TravelRuleWorkspaceResource.java b/src/main/java/me/didit/resources/TravelRuleWorkspaceResource.java new file mode 100644 index 0000000..e52e31e --- /dev/null +++ b/src/main/java/me/didit/resources/TravelRuleWorkspaceResource.java @@ -0,0 +1,597 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import java.util.concurrent.CompletableFuture; +import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.TravelRuleWorkspace; +import me.didit.types.TravelRuleWorkspaceBillingRequest; +import me.didit.types.TravelRuleWorkspaceClaimRequest; +import me.didit.types.TravelRuleWorkspaceDomainCodeResponse; +import me.didit.types.TravelRuleWorkspaceDomainConfirmRequest; + +/** The {@code travel_rule.workspace} resource: {@code client.travelRule().workspace()}. */ +public final class TravelRuleWorkspaceResource { + private final ApiClient client; + private final WithRawResponse withRawResponse; + + TravelRuleWorkspaceResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); + } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * Get the free Travel Rule workspace + * + *

Reads the free workspace of an application that joined free: directory claim, domain proof, billing and Didit network listing. + * + *

{@code GET /v3/travel-rule/workspace/} + * + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleWorkspace get(RequestOptions options) { + return client.execute(getRequest(), options, GET_RESPONSE).data(); + } + + /** + * Asynchronous {@link #get(RequestOptions)}. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture getAsync(RequestOptions options) { + return client.executeAsync(getRequest(), options, GET_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #get(RequestOptions)}, with the client's options. + * + * @return the result + */ + public TravelRuleWorkspace get() { + return client.execute(getRequest(), RequestOptions.none(), GET_RESPONSE).data(); + } + + /** + * Asynchronous {@link #get(RequestOptions)}, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture getAsync() { + return client.executeAsync(getRequest(), RequestOptions.none(), GET_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Claim a directory entry + * + *

Claims the directory entry this workspace is. When the work email is at a domain the entry lists, the claim is proved and applies at once: the VASP profile and Travel Rule settings are filled from the entry and Travel Rule is turned on, so the next transfer to it routes over the Didit network. Otherwise the claim waits for review (pending_review). + * + *

{@code POST /v3/travel-rule/workspace/claim/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleWorkspace claim(TravelRuleWorkspaceClaimRequest params, RequestOptions options) { + return client.execute(claimRequest(params), options, CLAIM_RESPONSE).data(); + } + + /** + * Asynchronous {@link #claim(TravelRuleWorkspaceClaimRequest, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture claimAsync(TravelRuleWorkspaceClaimRequest params, RequestOptions options) { + return client.executeAsync(claimRequest(params), options, CLAIM_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #claim(TravelRuleWorkspaceClaimRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public TravelRuleWorkspace claim(TravelRuleWorkspaceClaimRequest params) { + return client.execute(claimRequest(params), RequestOptions.none(), CLAIM_RESPONSE).data(); + } + + /** + * Asynchronous {@link #claim(TravelRuleWorkspaceClaimRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture claimAsync(TravelRuleWorkspaceClaimRequest params) { + return client.executeAsync(claimRequest(params), RequestOptions.none(), CLAIM_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Email a domain verification code + * + *

Emails a single-use code to the workspace's work address to prove it controls the email domain. Throttled, and a limited number of wrong attempts is accepted per code. Also returns the TXT record for the DNS alternative. + * + *

{@code POST /v3/travel-rule/workspace/domain/code/} + * + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleWorkspaceDomainCodeResponse sendDomainCode(RequestOptions options) { + return client.execute(sendDomainCodeRequest(), options, SEND_DOMAIN_CODE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #sendDomainCode(RequestOptions)}. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture sendDomainCodeAsync(RequestOptions options) { + return client.executeAsync(sendDomainCodeRequest(), options, SEND_DOMAIN_CODE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #sendDomainCode(RequestOptions)}, with the client's options. + * + * @return the result + */ + public TravelRuleWorkspaceDomainCodeResponse sendDomainCode() { + return client.execute(sendDomainCodeRequest(), RequestOptions.none(), SEND_DOMAIN_CODE_RESPONSE).data(); + } + + /** + * Asynchronous {@link #sendDomainCode(RequestOptions)}, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture sendDomainCodeAsync() { + return client.executeAsync(sendDomainCodeRequest(), RequestOptions.none(), SEND_DOMAIN_CODE_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Confirm the domain verification code + * + *

Spends the emailed code and marks the email domain verified. + * + *

{@code POST /v3/travel-rule/workspace/domain/confirm/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleWorkspace confirmDomain(TravelRuleWorkspaceDomainConfirmRequest params, RequestOptions options) { + return client.execute(confirmDomainRequest(params), options, CONFIRM_DOMAIN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #confirmDomain(TravelRuleWorkspaceDomainConfirmRequest, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture confirmDomainAsync(TravelRuleWorkspaceDomainConfirmRequest params, RequestOptions options) { + return client.executeAsync(confirmDomainRequest(params), options, CONFIRM_DOMAIN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #confirmDomain(TravelRuleWorkspaceDomainConfirmRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public TravelRuleWorkspace confirmDomain(TravelRuleWorkspaceDomainConfirmRequest params) { + return client.execute(confirmDomainRequest(params), RequestOptions.none(), CONFIRM_DOMAIN_RESPONSE).data(); + } + + /** + * Asynchronous {@link #confirmDomain(TravelRuleWorkspaceDomainConfirmRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture confirmDomainAsync(TravelRuleWorkspaceDomainConfirmRequest params) { + return client.executeAsync(confirmDomainRequest(params), RequestOptions.none(), CONFIRM_DOMAIN_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Verify the domain from its TXT record + * + *

Checks for the didit-tr-verify.<domain> TXT record returned by the code endpoint and marks the domain verified when it matches. One of the two proofs, the emailed code or the TXT record, is enough. + * + *

{@code POST /v3/travel-rule/workspace/domain/dns/} + * + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleWorkspace verifyDomainDns(RequestOptions options) { + return client.execute(verifyDomainDnsRequest(), options, VERIFY_DOMAIN_DNS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #verifyDomainDns(RequestOptions)}. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture verifyDomainDnsAsync(RequestOptions options) { + return client.executeAsync(verifyDomainDnsRequest(), options, VERIFY_DOMAIN_DNS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #verifyDomainDns(RequestOptions)}, with the client's options. + * + * @return the result + */ + public TravelRuleWorkspace verifyDomainDns() { + return client.execute(verifyDomainDnsRequest(), RequestOptions.none(), VERIFY_DOMAIN_DNS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #verifyDomainDns(RequestOptions)}, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture verifyDomainDnsAsync() { + return client.executeAsync(verifyDomainDnsRequest(), RequestOptions.none(), VERIFY_DOMAIN_DNS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Enable outbound billing + * + *

Turns outbound Travel Rule billing on for the workspace. Receiving stays free. This cannot be turned back off through the API: contact Didit support. + * + *

{@code POST /v3/travel-rule/workspace/billing/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public TravelRuleWorkspace enableBilling(TravelRuleWorkspaceBillingRequest params, RequestOptions options) { + return client.execute(enableBillingRequest(params), options, ENABLE_BILLING_RESPONSE).data(); + } + + /** + * Asynchronous {@link #enableBilling(TravelRuleWorkspaceBillingRequest, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture enableBillingAsync(TravelRuleWorkspaceBillingRequest params, RequestOptions options) { + return client.executeAsync(enableBillingRequest(params), options, ENABLE_BILLING_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #enableBilling(TravelRuleWorkspaceBillingRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public TravelRuleWorkspace enableBilling(TravelRuleWorkspaceBillingRequest params) { + return client.execute(enableBillingRequest(params), RequestOptions.none(), ENABLE_BILLING_RESPONSE).data(); + } + + /** + * Asynchronous {@link #enableBilling(TravelRuleWorkspaceBillingRequest, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture enableBillingAsync(TravelRuleWorkspaceBillingRequest params) { + return client.executeAsync(enableBillingRequest(params), RequestOptions.none(), ENABLE_BILLING_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler GET_RESPONSE = Responses.json(Conv.model(TravelRuleWorkspace::fromJson)); + + private static ApiRequest getRequest() { + return ApiRequest.builder("GET", "/v3/travel-rule/workspace/") + .build(); + } + + private static final ResponseHandler CLAIM_RESPONSE = Responses.json(Conv.model(TravelRuleWorkspace::fromJson)); + + private static ApiRequest claimRequest(TravelRuleWorkspaceClaimRequest params) { + return ApiRequest.builder("POST", "/v3/travel-rule/workspace/claim/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler SEND_DOMAIN_CODE_RESPONSE = Responses.json(Conv.model(TravelRuleWorkspaceDomainCodeResponse::fromJson)); + + private static ApiRequest sendDomainCodeRequest() { + return ApiRequest.builder("POST", "/v3/travel-rule/workspace/domain/code/") + .build(); + } + + private static final ResponseHandler CONFIRM_DOMAIN_RESPONSE = Responses.json(Conv.model(TravelRuleWorkspace::fromJson)); + + private static ApiRequest confirmDomainRequest(TravelRuleWorkspaceDomainConfirmRequest params) { + return ApiRequest.builder("POST", "/v3/travel-rule/workspace/domain/confirm/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + private static final ResponseHandler VERIFY_DOMAIN_DNS_RESPONSE = Responses.json(Conv.model(TravelRuleWorkspace::fromJson)); + + private static ApiRequest verifyDomainDnsRequest() { + return ApiRequest.builder("POST", "/v3/travel-rule/workspace/domain/dns/") + .build(); + } + + private static final ResponseHandler ENABLE_BILLING_RESPONSE = Responses.json(Conv.model(TravelRuleWorkspace::fromJson)); + + private static ApiRequest enableBillingRequest(TravelRuleWorkspaceBillingRequest params) { + return ApiRequest.builder("POST", "/v3/travel-rule/workspace/billing/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + /** The {@code travel_rule.workspace} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #get(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return the result + */ + public RawResponse get(RequestOptions options) { + return client.execute(getRequest(), options, GET_RESPONSE); + } + + /** + * Asynchronous {@link #get(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> getAsync(RequestOptions options) { + return client.executeAsync(getRequest(), options, GET_RESPONSE); + } + + /** + * {@link #get(RequestOptions)} with the raw response, with the client's options. + * + * @return the result + */ + public RawResponse get() { + return client.execute(getRequest(), RequestOptions.none(), GET_RESPONSE); + } + + /** + * Asynchronous {@link #get(RequestOptions)} with the raw response, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> getAsync() { + return client.executeAsync(getRequest(), RequestOptions.none(), GET_RESPONSE); + } + + /** + * {@link #claim(TravelRuleWorkspaceClaimRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse claim(TravelRuleWorkspaceClaimRequest params, RequestOptions options) { + return client.execute(claimRequest(params), options, CLAIM_RESPONSE); + } + + /** + * Asynchronous {@link #claim(TravelRuleWorkspaceClaimRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> claimAsync(TravelRuleWorkspaceClaimRequest params, RequestOptions options) { + return client.executeAsync(claimRequest(params), options, CLAIM_RESPONSE); + } + + /** + * {@link #claim(TravelRuleWorkspaceClaimRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse claim(TravelRuleWorkspaceClaimRequest params) { + return client.execute(claimRequest(params), RequestOptions.none(), CLAIM_RESPONSE); + } + + /** + * Asynchronous {@link #claim(TravelRuleWorkspaceClaimRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> claimAsync(TravelRuleWorkspaceClaimRequest params) { + return client.executeAsync(claimRequest(params), RequestOptions.none(), CLAIM_RESPONSE); + } + + /** + * {@link #sendDomainCode(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return the result + */ + public RawResponse sendDomainCode(RequestOptions options) { + return client.execute(sendDomainCodeRequest(), options, SEND_DOMAIN_CODE_RESPONSE); + } + + /** + * Asynchronous {@link #sendDomainCode(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> sendDomainCodeAsync(RequestOptions options) { + return client.executeAsync(sendDomainCodeRequest(), options, SEND_DOMAIN_CODE_RESPONSE); + } + + /** + * {@link #sendDomainCode(RequestOptions)} with the raw response, with the client's options. + * + * @return the result + */ + public RawResponse sendDomainCode() { + return client.execute(sendDomainCodeRequest(), RequestOptions.none(), SEND_DOMAIN_CODE_RESPONSE); + } + + /** + * Asynchronous {@link #sendDomainCode(RequestOptions)} with the raw response, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> sendDomainCodeAsync() { + return client.executeAsync(sendDomainCodeRequest(), RequestOptions.none(), SEND_DOMAIN_CODE_RESPONSE); + } + + /** + * {@link #confirmDomain(TravelRuleWorkspaceDomainConfirmRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse confirmDomain(TravelRuleWorkspaceDomainConfirmRequest params, RequestOptions options) { + return client.execute(confirmDomainRequest(params), options, CONFIRM_DOMAIN_RESPONSE); + } + + /** + * Asynchronous {@link #confirmDomain(TravelRuleWorkspaceDomainConfirmRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> confirmDomainAsync(TravelRuleWorkspaceDomainConfirmRequest params, RequestOptions options) { + return client.executeAsync(confirmDomainRequest(params), options, CONFIRM_DOMAIN_RESPONSE); + } + + /** + * {@link #confirmDomain(TravelRuleWorkspaceDomainConfirmRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse confirmDomain(TravelRuleWorkspaceDomainConfirmRequest params) { + return client.execute(confirmDomainRequest(params), RequestOptions.none(), CONFIRM_DOMAIN_RESPONSE); + } + + /** + * Asynchronous {@link #confirmDomain(TravelRuleWorkspaceDomainConfirmRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> confirmDomainAsync(TravelRuleWorkspaceDomainConfirmRequest params) { + return client.executeAsync(confirmDomainRequest(params), RequestOptions.none(), CONFIRM_DOMAIN_RESPONSE); + } + + /** + * {@link #verifyDomainDns(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return the result + */ + public RawResponse verifyDomainDns(RequestOptions options) { + return client.execute(verifyDomainDnsRequest(), options, VERIFY_DOMAIN_DNS_RESPONSE); + } + + /** + * Asynchronous {@link #verifyDomainDns(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> verifyDomainDnsAsync(RequestOptions options) { + return client.executeAsync(verifyDomainDnsRequest(), options, VERIFY_DOMAIN_DNS_RESPONSE); + } + + /** + * {@link #verifyDomainDns(RequestOptions)} with the raw response, with the client's options. + * + * @return the result + */ + public RawResponse verifyDomainDns() { + return client.execute(verifyDomainDnsRequest(), RequestOptions.none(), VERIFY_DOMAIN_DNS_RESPONSE); + } + + /** + * Asynchronous {@link #verifyDomainDns(RequestOptions)} with the raw response, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> verifyDomainDnsAsync() { + return client.executeAsync(verifyDomainDnsRequest(), RequestOptions.none(), VERIFY_DOMAIN_DNS_RESPONSE); + } + + /** + * {@link #enableBilling(TravelRuleWorkspaceBillingRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse enableBilling(TravelRuleWorkspaceBillingRequest params, RequestOptions options) { + return client.execute(enableBillingRequest(params), options, ENABLE_BILLING_RESPONSE); + } + + /** + * Asynchronous {@link #enableBilling(TravelRuleWorkspaceBillingRequest, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> enableBillingAsync(TravelRuleWorkspaceBillingRequest params, RequestOptions options) { + return client.executeAsync(enableBillingRequest(params), options, ENABLE_BILLING_RESPONSE); + } + + /** + * {@link #enableBilling(TravelRuleWorkspaceBillingRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse enableBilling(TravelRuleWorkspaceBillingRequest params) { + return client.execute(enableBillingRequest(params), RequestOptions.none(), ENABLE_BILLING_RESPONSE); + } + + /** + * Asynchronous {@link #enableBilling(TravelRuleWorkspaceBillingRequest, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> enableBillingAsync(TravelRuleWorkspaceBillingRequest params) { + return client.executeAsync(enableBillingRequest(params), RequestOptions.none(), ENABLE_BILLING_RESPONSE); + } + } +} diff --git a/src/main/java/me/didit/resources/UsersResource.java b/src/main/java/me/didit/resources/UsersResource.java index f8021de..557d0d3 100644 --- a/src/main/java/me/didit/resources/UsersResource.java +++ b/src/main/java/me/didit/resources/UsersResource.java @@ -11,17 +11,18 @@ import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.EntityNetworkMembershipList; +import me.didit.types.OngoingMonitoringJob; +import me.didit.types.OngoingMonitoringToggleRequest; import me.didit.types.UserDetailItem; import me.didit.types.UserListItem; import me.didit.types.UsersBatchDeleteParams; import me.didit.types.UsersBatchDeleteResponse; import me.didit.types.UsersCreateParams; -import me.didit.types.UsersListNetworksResponse; +import me.didit.types.UsersCreateResponse; import me.didit.types.UsersListParams; -import me.didit.types.UsersRetrieveOngoingMonitoringJobResponse; import me.didit.types.UsersSetAmlMonitoringParams; import me.didit.types.UsersSetAmlMonitoringResponse; -import me.didit.types.UsersSetOngoingMonitoringParams; import me.didit.types.UsersSetOngoingMonitoringResponse; import me.didit.types.UsersUpdateParams; import me.didit.types.UsersUpdateStatusParams; @@ -181,7 +182,7 @@ public CompletableFuture retrieveAsync(String vendorData) { /** * Update user * - *

Partial update of a user: display_name, full_name, date_of_birth, status, metadata, approved_emails/approved_phones/issuing_states. metadata fully replaces. Note: unlike [Update User Status](#patch-/v3/users/-vendor_data-/update-status/), changing status here does NOT record a status-change activity and does NOT sync the system blocklist — use the dedicated update-status endpoint for BLOCKED/unblock flows. + *

Partial update of a user: display_name, full_name, date_of_birth, status, metadata, approved_emails/approved_phones/issuing_states. metadata fully replaces. Changing status here works like [Update User Status](#patch-/v3/users/-vendor_data-/update-status/): it records a STATUS_CHANGED activity with the optional reason, and BLOCKED adds the vendor_data to the system blocklist (moving away from BLOCKED removes that entry). Changes to display_name, full_name, date_of_birth or metadata are recorded as a PROFILE_UPDATED activity. * *

{@code PATCH /v3/users/{vendor_data}/} * @@ -336,7 +337,7 @@ public CompletableFuture generatePdfAsync(String vendorData) { /** * Batch delete users * - *

Permanently delete users — the user records are removed permanently. For everyday blocking prefer [Update User Status](#patch-/v3/users/-vendor_data-/update-status/) with BLOCKED. Select users with vendor_data_list, with didit_internal_id_list, or with both at once; or pass delete_all: true. The Console's filter_* batch params are rejected with a 400 rather than ignored, so a narrowed intent can never turn into a full wipe. + *

Permanently delete users: the user records are removed permanently. For everyday blocking prefer [Update User Status](#patch-/v3/users/-vendor_data-/update-status/) with BLOCKED. Select users with vendor_data_list, with didit_internal_id_list, or with both at once; or pass delete_all: true. This endpoint never narrows delete_all with filters. The Console's People filters are refused with a 400 (see the 400 response), so a narrowed intent does not turn into a full wipe. Two exceptions: filter_has_approved_sessions and filter_has_declined_sessions are not refused and not applied, so with delete_all: true they still delete every user; do not send them. * *

Biometric templates: every image-free biometric template retained for these users after session deletion (see /v3/biometric-templates/) is purged before the user is deleted, so the person stops matching in duplicate detection and Face Search. If a template cannot be purged because a biometric store is unavailable, the call returns 503, no user in the request is deleted, and repeating the request finishes the job. * @@ -470,7 +471,7 @@ public CompletableFuture setAmlMonitoringAsync(Us /** * Update user status * - *

Flip only the lifecycle status of a user — ACTIVE/FLAGGED/BLOCKED (NOT session statuses). BLOCKED adds the vendor_data to the system blocklist; moving away from BLOCKED removes that entry. Each change is recorded as a STATUS_CHANGED activity on the user. + *

Flip only the lifecycle status of a user: ACTIVE, FLAGGED or BLOCKED (not session statuses). BLOCKED adds the vendor_data to the system blocklist; moving away from BLOCKED removes that entry. Each change is recorded as a STATUS_CHANGED activity on the user, with the optional reason as its comment. Sending the current status changes nothing and records no reason. The user.status.updated webhook does not carry reason. * *

{@code PATCH /v3/users/{vendor_data}/update-status/} * @@ -520,7 +521,13 @@ public CompletableFuture updateStatusAsync(String vendorData, Us /** * Create user * - *

Pre-create a [user](/entities/users) by vendor_data *without* a verification session. vendor_data must be unique among non-deleted users for the application (exact match; conflicts return 400). Not idempotent. + *

Create a [user](/entities/users) by vendor_data, before or without a verification session. vendor_data must be unique among the application's users (a duplicate returns 400). Not idempotent. + * + *

Contact details: email and phone_number are stored in metadata.contact, and country in metadata.address. When you send first_name or last_name without full_name, Didit joins them into full_name. + * + *

Verification link: with send_verification_link: true, Didit also creates a verification session for the person on workflow_id and emails them the link at email. The session runs the same checks as [Create Session](/sessions-api/create-session), such as the blocklist and your credit balance. The person and the session are created together or not at all: if any check fails, nothing is written and no email is sent. The response's verification_link describes the session, and is null when you don't ask for a link. + * + *

API key access: a scoped API key needs users write. With send_verification_link: true it also needs sessions write, or the call answers 403 before anything is written. Full-access keys are unaffected. See [API keys](/console/api-keys). * *

{@code POST /v3/users/create/} * @@ -528,7 +535,7 @@ public CompletableFuture updateStatusAsync(String vendorData, Us * @param options per-request options that override the client options * @return the response body */ - public UserDetailItem create(UsersCreateParams params, RequestOptions options) { + public UsersCreateResponse create(UsersCreateParams params, RequestOptions options) { return client.execute(createRequest(params), options, CREATE_RESPONSE).data(); } @@ -539,7 +546,7 @@ public UserDetailItem create(UsersCreateParams params, RequestOptions options) { * @param options per-request options * @return a future completed with the result */ - public CompletableFuture createAsync(UsersCreateParams params, RequestOptions options) { + public CompletableFuture createAsync(UsersCreateParams params, RequestOptions options) { return client.executeAsync(createRequest(params), options, CREATE_RESPONSE).thenApply(RawResponse::data); } @@ -549,7 +556,7 @@ public CompletableFuture createAsync(UsersCreateParams params, R * @param params the request parameters * @return the result */ - public UserDetailItem create(UsersCreateParams params) { + public UsersCreateResponse create(UsersCreateParams params) { return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).data(); } @@ -559,7 +566,7 @@ public UserDetailItem create(UsersCreateParams params) { * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture createAsync(UsersCreateParams params) { + public CompletableFuture createAsync(UsersCreateParams params) { return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).thenApply(RawResponse::data); } @@ -626,38 +633,38 @@ public CompletableFuture uploadFaceAsync(String diditIn * @param options per-request options that override the client options * @return the response body */ - public UsersSetOngoingMonitoringResponse setOngoingMonitoring(UsersSetOngoingMonitoringParams params, RequestOptions options) { + public UsersSetOngoingMonitoringResponse setOngoingMonitoring(OngoingMonitoringToggleRequest params, RequestOptions options) { return client.execute(setOngoingMonitoringRequest(params), options, SET_ONGOING_MONITORING_RESPONSE).data(); } /** - * Asynchronous {@link #setOngoingMonitoring(UsersSetOngoingMonitoringParams, RequestOptions)}. + * Asynchronous {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)}. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture setOngoingMonitoringAsync(UsersSetOngoingMonitoringParams params, RequestOptions options) { + public CompletableFuture setOngoingMonitoringAsync(OngoingMonitoringToggleRequest params, RequestOptions options) { return client.executeAsync(setOngoingMonitoringRequest(params), options, SET_ONGOING_MONITORING_RESPONSE).thenApply(RawResponse::data); } /** - * Same as {@link #setOngoingMonitoring(UsersSetOngoingMonitoringParams, RequestOptions)}, with the client's options. + * Same as {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return the result */ - public UsersSetOngoingMonitoringResponse setOngoingMonitoring(UsersSetOngoingMonitoringParams params) { + public UsersSetOngoingMonitoringResponse setOngoingMonitoring(OngoingMonitoringToggleRequest params) { return client.execute(setOngoingMonitoringRequest(params), RequestOptions.none(), SET_ONGOING_MONITORING_RESPONSE).data(); } /** - * Asynchronous {@link #setOngoingMonitoring(UsersSetOngoingMonitoringParams, RequestOptions)}, with the client's options. + * Asynchronous {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)}, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture setOngoingMonitoringAsync(UsersSetOngoingMonitoringParams params) { + public CompletableFuture setOngoingMonitoringAsync(OngoingMonitoringToggleRequest params) { return client.executeAsync(setOngoingMonitoringRequest(params), RequestOptions.none(), SET_ONGOING_MONITORING_RESPONSE).thenApply(RawResponse::data); } @@ -673,7 +680,7 @@ public CompletableFuture setOngoingMonitoring * @param options per-request options that override the client options * @return the response body */ - public UsersRetrieveOngoingMonitoringJobResponse retrieveOngoingMonitoringJob(String jobId, RequestOptions options) { + public OngoingMonitoringJob retrieveOngoingMonitoringJob(String jobId, RequestOptions options) { return client.execute(retrieveOngoingMonitoringJobRequest(jobId), options, RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE).data(); } @@ -684,7 +691,7 @@ public UsersRetrieveOngoingMonitoringJobResponse retrieveOngoingMonitoringJob(St * @param options per-request options * @return a future completed with the result */ - public CompletableFuture retrieveOngoingMonitoringJobAsync(String jobId, RequestOptions options) { + public CompletableFuture retrieveOngoingMonitoringJobAsync(String jobId, RequestOptions options) { return client.executeAsync(retrieveOngoingMonitoringJobRequest(jobId), options, RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE).thenApply(RawResponse::data); } @@ -694,7 +701,7 @@ public CompletableFuture retrieveOngo * @param jobId the {@code job_id} * @return the result */ - public UsersRetrieveOngoingMonitoringJobResponse retrieveOngoingMonitoringJob(String jobId) { + public OngoingMonitoringJob retrieveOngoingMonitoringJob(String jobId) { return client.execute(retrieveOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE).data(); } @@ -704,7 +711,7 @@ public UsersRetrieveOngoingMonitoringJobResponse retrieveOngoingMonitoringJob(St * @param jobId the {@code job_id} * @return a future completed with the result */ - public CompletableFuture retrieveOngoingMonitoringJobAsync(String jobId) { + public CompletableFuture retrieveOngoingMonitoringJobAsync(String jobId) { return client.executeAsync(retrieveOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE).thenApply(RawResponse::data); } @@ -720,7 +727,7 @@ public CompletableFuture retrieveOngo * @param options per-request options that override the client options * @return the response body */ - public UsersListNetworksResponse listNetworks(String vendorUserUuid, RequestOptions options) { + public EntityNetworkMembershipList listNetworks(String vendorUserUuid, RequestOptions options) { return client.execute(listNetworksRequest(vendorUserUuid), options, LIST_NETWORKS_RESPONSE).data(); } @@ -731,7 +738,7 @@ public UsersListNetworksResponse listNetworks(String vendorUserUuid, RequestOpti * @param options per-request options * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String vendorUserUuid, RequestOptions options) { + public CompletableFuture listNetworksAsync(String vendorUserUuid, RequestOptions options) { return client.executeAsync(listNetworksRequest(vendorUserUuid), options, LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } @@ -741,7 +748,7 @@ public CompletableFuture listNetworksAsync(String ven * @param vendorUserUuid the {@code vendor_user_uuid} * @return the result */ - public UsersListNetworksResponse listNetworks(String vendorUserUuid) { + public EntityNetworkMembershipList listNetworks(String vendorUserUuid) { return client.execute(listNetworksRequest(vendorUserUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE).data(); } @@ -751,7 +758,7 @@ public UsersListNetworksResponse listNetworks(String vendorUserUuid) { * @param vendorUserUuid the {@code vendor_user_uuid} * @return a future completed with the result */ - public CompletableFuture listNetworksAsync(String vendorUserUuid) { + public CompletableFuture listNetworksAsync(String vendorUserUuid) { return client.executeAsync(listNetworksRequest(vendorUserUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE).thenApply(RawResponse::data); } @@ -819,7 +826,7 @@ private static ApiRequest updateStatusRequest(String vendorData, UsersUpdateStat .build(); } - private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(UserDetailItem::fromJson)); + private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(UsersCreateResponse::fromJson)); private static ApiRequest createRequest(UsersCreateParams params) { return ApiRequest.builder("POST", "/v3/users/create/") @@ -841,7 +848,7 @@ private static ApiRequest uploadFaceRequest(String diditInternalId, UsersUploadF private static final ResponseHandler SET_ONGOING_MONITORING_RESPONSE = Responses.byStatus(UsersSetOngoingMonitoringResponse::fromResponse); - private static ApiRequest setOngoingMonitoringRequest(UsersSetOngoingMonitoringParams params) { + private static ApiRequest setOngoingMonitoringRequest(OngoingMonitoringToggleRequest params) { return ApiRequest.builder("PATCH", "/v3/organization/{organization_id}/application/{application_id}/vendor-users/toggle-ongoing-monitoring/") .applicationScoped() .params(params) @@ -849,7 +856,7 @@ private static ApiRequest setOngoingMonitoringRequest(UsersSetOngoingMonitoringP .build(); } - private static final ResponseHandler RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE = Responses.json(Conv.model(UsersRetrieveOngoingMonitoringJobResponse::fromJson)); + private static final ResponseHandler RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE = Responses.json(Conv.model(OngoingMonitoringJob::fromJson)); private static ApiRequest retrieveOngoingMonitoringJobRequest(String jobId) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/vendor-users/toggle-ongoing-monitoring/{job_id}/") @@ -858,7 +865,7 @@ private static ApiRequest retrieveOngoingMonitoringJobRequest(String jobId) { .build(); } - private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(UsersListNetworksResponse::fromJson)); + private static final ResponseHandler LIST_NETWORKS_RESPONSE = Responses.json(Conv.model(EntityNetworkMembershipList::fromJson)); private static ApiRequest listNetworksRequest(String vendorUserUuid) { return ApiRequest.builder("GET", "/v3/organization/{organization_id}/application/{application_id}/users/{vendor_user_uuid}/networks/") @@ -1302,7 +1309,7 @@ public CompletableFuture> updateStatusAsync(String v * @param options per-request options * @return the result */ - public RawResponse create(UsersCreateParams params, RequestOptions options) { + public RawResponse create(UsersCreateParams params, RequestOptions options) { return client.execute(createRequest(params), options, CREATE_RESPONSE); } @@ -1313,7 +1320,7 @@ public RawResponse create(UsersCreateParams params, RequestOptio * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> createAsync(UsersCreateParams params, RequestOptions options) { + public CompletableFuture> createAsync(UsersCreateParams params, RequestOptions options) { return client.executeAsync(createRequest(params), options, CREATE_RESPONSE); } @@ -1323,7 +1330,7 @@ public CompletableFuture> createAsync(UsersCreatePar * @param params the request parameters * @return the result */ - public RawResponse create(UsersCreateParams params) { + public RawResponse create(UsersCreateParams params) { return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); } @@ -1333,7 +1340,7 @@ public RawResponse create(UsersCreateParams params) { * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> createAsync(UsersCreateParams params) { + public CompletableFuture> createAsync(UsersCreateParams params) { return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); } @@ -1384,44 +1391,44 @@ public CompletableFuture> uploadFaceAsync(S } /** - * {@link #setOngoingMonitoring(UsersSetOngoingMonitoringParams, RequestOptions)} with the raw response. + * {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return the result */ - public RawResponse setOngoingMonitoring(UsersSetOngoingMonitoringParams params, RequestOptions options) { + public RawResponse setOngoingMonitoring(OngoingMonitoringToggleRequest params, RequestOptions options) { return client.execute(setOngoingMonitoringRequest(params), options, SET_ONGOING_MONITORING_RESPONSE); } /** - * Asynchronous {@link #setOngoingMonitoring(UsersSetOngoingMonitoringParams, RequestOptions)} with the raw response. + * Asynchronous {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)} with the raw response. * * @param params the request parameters * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> setOngoingMonitoringAsync(UsersSetOngoingMonitoringParams params, RequestOptions options) { + public CompletableFuture> setOngoingMonitoringAsync(OngoingMonitoringToggleRequest params, RequestOptions options) { return client.executeAsync(setOngoingMonitoringRequest(params), options, SET_ONGOING_MONITORING_RESPONSE); } /** - * {@link #setOngoingMonitoring(UsersSetOngoingMonitoringParams, RequestOptions)} with the raw response, with the client's options. + * {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return the result */ - public RawResponse setOngoingMonitoring(UsersSetOngoingMonitoringParams params) { + public RawResponse setOngoingMonitoring(OngoingMonitoringToggleRequest params) { return client.execute(setOngoingMonitoringRequest(params), RequestOptions.none(), SET_ONGOING_MONITORING_RESPONSE); } /** - * Asynchronous {@link #setOngoingMonitoring(UsersSetOngoingMonitoringParams, RequestOptions)} with the raw response, with the client's options. + * Asynchronous {@link #setOngoingMonitoring(OngoingMonitoringToggleRequest, RequestOptions)} with the raw response, with the client's options. * * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> setOngoingMonitoringAsync(UsersSetOngoingMonitoringParams params) { + public CompletableFuture> setOngoingMonitoringAsync(OngoingMonitoringToggleRequest params) { return client.executeAsync(setOngoingMonitoringRequest(params), RequestOptions.none(), SET_ONGOING_MONITORING_RESPONSE); } @@ -1432,7 +1439,7 @@ public CompletableFuture> setOngo * @param options per-request options * @return the result */ - public RawResponse retrieveOngoingMonitoringJob(String jobId, RequestOptions options) { + public RawResponse retrieveOngoingMonitoringJob(String jobId, RequestOptions options) { return client.execute(retrieveOngoingMonitoringJobRequest(jobId), options, RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1443,7 +1450,7 @@ public RawResponse retrieveOngoingMon * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> retrieveOngoingMonitoringJobAsync(String jobId, RequestOptions options) { + public CompletableFuture> retrieveOngoingMonitoringJobAsync(String jobId, RequestOptions options) { return client.executeAsync(retrieveOngoingMonitoringJobRequest(jobId), options, RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1453,7 +1460,7 @@ public CompletableFuture> * @param jobId the {@code job_id} * @return the result */ - public RawResponse retrieveOngoingMonitoringJob(String jobId) { + public RawResponse retrieveOngoingMonitoringJob(String jobId) { return client.execute(retrieveOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1463,7 +1470,7 @@ public RawResponse retrieveOngoingMon * @param jobId the {@code job_id} * @return a future completed with the result */ - public CompletableFuture> retrieveOngoingMonitoringJobAsync(String jobId) { + public CompletableFuture> retrieveOngoingMonitoringJobAsync(String jobId) { return client.executeAsync(retrieveOngoingMonitoringJobRequest(jobId), RequestOptions.none(), RETRIEVE_ONGOING_MONITORING_JOB_RESPONSE); } @@ -1474,7 +1481,7 @@ public CompletableFuture> * @param options per-request options * @return the result */ - public RawResponse listNetworks(String vendorUserUuid, RequestOptions options) { + public RawResponse listNetworks(String vendorUserUuid, RequestOptions options) { return client.execute(listNetworksRequest(vendorUserUuid), options, LIST_NETWORKS_RESPONSE); } @@ -1485,7 +1492,7 @@ public RawResponse listNetworks(String vendorUserUuid * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String vendorUserUuid, RequestOptions options) { + public CompletableFuture> listNetworksAsync(String vendorUserUuid, RequestOptions options) { return client.executeAsync(listNetworksRequest(vendorUserUuid), options, LIST_NETWORKS_RESPONSE); } @@ -1495,7 +1502,7 @@ public CompletableFuture> listNetworksAsy * @param vendorUserUuid the {@code vendor_user_uuid} * @return the result */ - public RawResponse listNetworks(String vendorUserUuid) { + public RawResponse listNetworks(String vendorUserUuid) { return client.execute(listNetworksRequest(vendorUserUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } @@ -1505,7 +1512,7 @@ public RawResponse listNetworks(String vendorUserUuid * @param vendorUserUuid the {@code vendor_user_uuid} * @return a future completed with the result */ - public CompletableFuture> listNetworksAsync(String vendorUserUuid) { + public CompletableFuture> listNetworksAsync(String vendorUserUuid) { return client.executeAsync(listNetworksRequest(vendorUserUuid), RequestOptions.none(), LIST_NETWORKS_RESPONSE); } } diff --git a/src/main/java/me/didit/resources/WebhooksDestinationsResource.java b/src/main/java/me/didit/resources/WebhooksDestinationsResource.java index 241a67b..ec091f1 100644 --- a/src/main/java/me/didit/resources/WebhooksDestinationsResource.java +++ b/src/main/java/me/didit/resources/WebhooksDestinationsResource.java @@ -6,13 +6,19 @@ import me.didit.core.ApiClient; import me.didit.core.ApiRequest; import me.didit.core.Conv; +import me.didit.core.Page; import me.didit.core.RawResponse; import me.didit.core.RequestOptions; import me.didit.core.ResponseHandler; import me.didit.core.Responses; +import me.didit.types.WebhookDestinationDelivery; +import me.didit.types.WebhookDestinationDeliveryDetail; import me.didit.types.WebhooksDestinationsCreateParams; import me.didit.types.WebhooksDestinationsCreateResponse; +import me.didit.types.WebhooksDestinationsListDeliveriesParams; import me.didit.types.WebhooksDestinationsListResponse; +import me.didit.types.WebhooksDestinationsRetrieveAnalyticsParams; +import me.didit.types.WebhooksDestinationsRetrieveAnalyticsResponse; import me.didit.types.WebhooksDestinationsRetrieveResponse; import me.didit.types.WebhooksDestinationsUpdateParams; import me.didit.types.WebhooksDestinationsUpdateResponse; @@ -40,7 +46,7 @@ public WithRawResponse withRawResponse() { /** * List webhook destinations * - *

List all webhook destinations (unpaginated array, oldest first). Each row includes URL, subscribed events, and delivery health metrics. The signing secret is NOT included in the list — fetch a single destination (GET /v3/webhook/destinations/{destination_uuid}/) to read secret_shared_key. + *

List all webhook destinations (unpaginated array, oldest first). Each item carries the destination's configuration, its delivery metrics for the last 30 days (metrics_window_days) with one daily_points entry per day, and its health (health_status, failing_streak, failing_since, last_success_at). The list has no summary object and never includes the signing secret: each item shows only secret_last4. Fetch a single destination (GET /v3/webhook/destinations/{destination_uuid}/) to read secret_shared_key. * *

{@code GET /v3/webhook/destinations/} * @@ -82,7 +88,7 @@ public CompletableFuture> listAsync() { /** * Create webhook destination * - *

Create a webhook destination. The response includes the auto-generated secret_shared_key HMAC signing secret (also readable later via GET /v3/webhook/destinations/{destination_uuid}/). Always send subscribed_events with at least one event (no wildcards): an explicit empty list is rejected with 400, and omitting the field creates a destination subscribed to nothing, which never receives a webhook. The (application, url) pair must be unique among non-deleted destinations. + *

Create a webhook destination. The response includes the auto-generated secret_shared_key HMAC signing secret (also readable later via GET /v3/webhook/destinations/{destination_uuid}/). Always send subscribed_events with at least one event (no wildcards): an explicit empty list is rejected with 400, and omitting the field creates a destination subscribed to nothing, which never receives a webhook. The (application, url) pair must be unique among the application's destinations. * *

{@code POST /v3/webhook/destinations/} * @@ -128,7 +134,7 @@ public CompletableFuture createAsync(Webhook /** * Get webhook destination * - *

Retrieve one webhook destination with its secret_shared_key signing secret and delivery health summary. API-key callers always receive the secret in full; Console users without read:webhooks see an empty string. + *

Retrieve one webhook destination with its secret_shared_key signing secret, its health (health_status, failing_streak, failing_since, last_success_at) and a delivery summary for the last 30 days. API-key callers always receive the secret in full; Console users without read:webhooks see an empty string. * *

{@code GET /v3/webhook/destinations/{destination_uuid}/} * @@ -174,7 +180,7 @@ public CompletableFuture retrieveAsync(Str /** * Update webhook destination * - *

Update a webhook destination. **subscribed_events is REPLACED wholesale, not merged** — fetch first, append, resend. Secret is not rotated. + *

Update a webhook destination. **subscribed_events is REPLACED wholesale, not merged**: fetch first, append, resend. Secret is not rotated. A uuid in the body is ignored: the path names the destination. * *

{@code PATCH /v3/webhook/destinations/{destination_uuid}/} * @@ -266,7 +272,7 @@ public CompletableFuture updateAsync(String /** * Delete webhook destination * - *

Delete a webhook destination. Future events stop being pushed to its URL. Past delivery records are not affected and stay queryable from the Business Console, so deleting a destination is not a way to remove your delivery history. Calling again returns 404. + *

Permanently delete a webhook destination, its signing secret and its entire delivery log. This cannot be undone. No new webhook is sent to the destination's URL; other destinations subscribed to the same events keep receiving them. Afterwards the destination no longer appears in GET /v3/webhook/destinations/, and every route under /v3/webhook/destinations/{destination_uuid}/, including its deliveries, a delivery's detail and its resend, answers 404. The application's legacy webhook_url is set to the URL of the oldest remaining enabled destination, or cleared when no enabled destination is left. Calling DELETE again returns 404. * *

{@code DELETE /v3/webhook/destinations/{destination_uuid}/} * @@ -307,6 +313,291 @@ public CompletableFuture deleteAsync(String destinationUuid) { return client.executeAsync(deleteRequest(destinationUuid), RequestOptions.none(), DELETE_RESPONSE).thenAccept(response -> {}); } + /** + * Get webhook destination analytics + * + *

Delivery totals, failure rate and response times for one webhook destination, over the whole range (summary) and per UTC day (points). Every attempt counts as a delivery: the first send of an event, each automatic retry and each resend. A delivery is failed when its recorded HTTP status is 400 or above, so timeouts (recorded as 504) and connection failures (503) count as failed; a delivery still queued counts in total_deliveries only. Delivery records are available for about 30 days. A scoped API key needs webhooks: read. + * + *

{@code GET /v3/webhook/destinations/{destination_uuid}/analytics/} + * + * @param destinationUuid UUID of the destination (the `uuid` returned by `GET /v3/webhook/destinations/`). + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public WebhooksDestinationsRetrieveAnalyticsResponse retrieveAnalytics(String destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams params, RequestOptions options) { + return client.execute(retrieveAnalyticsRequest(destinationUuid, params), options, RETRIEVE_ANALYTICS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)}. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveAnalyticsAsync(String destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams params, RequestOptions options) { + return client.executeAsync(retrieveAnalyticsRequest(destinationUuid, params), options, RETRIEVE_ANALYTICS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)}, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @return the result + */ + public WebhooksDestinationsRetrieveAnalyticsResponse retrieveAnalytics(String destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams params) { + return client.execute(retrieveAnalyticsRequest(destinationUuid, params), RequestOptions.none(), RETRIEVE_ANALYTICS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)}, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture retrieveAnalyticsAsync(String destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams params) { + return client.executeAsync(retrieveAnalyticsRequest(destinationUuid, params), RequestOptions.none(), RETRIEVE_ANALYTICS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)}, without parameters. + * + * @param destinationUuid the {@code destination_uuid} + * @param options per-request options + * @return the result + */ + public WebhooksDestinationsRetrieveAnalyticsResponse retrieveAnalytics(String destinationUuid, RequestOptions options) { + return client.execute(retrieveAnalyticsRequest(destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams.builder().build()), options, RETRIEVE_ANALYTICS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)}, without parameters. + * + * @param destinationUuid the {@code destination_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveAnalyticsAsync(String destinationUuid, RequestOptions options) { + return client.executeAsync(retrieveAnalyticsRequest(destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams.builder().build()), options, RETRIEVE_ANALYTICS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)}, without parameters, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @return the result + */ + public WebhooksDestinationsRetrieveAnalyticsResponse retrieveAnalytics(String destinationUuid) { + return client.execute(retrieveAnalyticsRequest(destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams.builder().build()), RequestOptions.none(), RETRIEVE_ANALYTICS_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)}, without parameters, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @return a future completed with the result + */ + public CompletableFuture retrieveAnalyticsAsync(String destinationUuid) { + return client.executeAsync(retrieveAnalyticsRequest(destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams.builder().build()), RequestOptions.none(), RETRIEVE_ANALYTICS_RESPONSE).thenApply(RawResponse::data); + } + + /** + * List webhook destination deliveries + * + *

Page through every delivery attempt to one webhook destination, newest first. The first send of each event, each automatic retry and each resend are separate entries. Entries carry the HTTP status and response time but not the payload: fetch one delivery to read its headers and body. count is exact up to 100 and reads 100 above that, so follow next until it is null. Delivery records are available for about 30 days. A scoped API key needs webhooks: read and decisions: read; a key limited to some workflows or to approved sessions cannot use this endpoint. + * + *

{@code GET /v3/webhook/destinations/{destination_uuid}/deliveries/} + *

Returns one page; iterate it to fetch every following page automatically. + * + * @param destinationUuid UUID of the destination (the `uuid` returned by `GET /v3/webhook/destinations/`). + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public Page listDeliveries(String destinationUuid, WebhooksDestinationsListDeliveriesParams params, RequestOptions options) { + return client.execute(listDeliveriesRequest(destinationUuid, params), options, LIST_DELIVERIES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)}. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listDeliveriesAsync(String destinationUuid, WebhooksDestinationsListDeliveriesParams params, RequestOptions options) { + return client.executeAsync(listDeliveriesRequest(destinationUuid, params), options, LIST_DELIVERIES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)}, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @return the result + */ + public Page listDeliveries(String destinationUuid, WebhooksDestinationsListDeliveriesParams params) { + return client.execute(listDeliveriesRequest(destinationUuid, params), RequestOptions.none(), LIST_DELIVERIES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)}, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> listDeliveriesAsync(String destinationUuid, WebhooksDestinationsListDeliveriesParams params) { + return client.executeAsync(listDeliveriesRequest(destinationUuid, params), RequestOptions.none(), LIST_DELIVERIES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)}, without parameters. + * + * @param destinationUuid the {@code destination_uuid} + * @param options per-request options + * @return the result + */ + public Page listDeliveries(String destinationUuid, RequestOptions options) { + return client.execute(listDeliveriesRequest(destinationUuid, WebhooksDestinationsListDeliveriesParams.builder().build()), options, LIST_DELIVERIES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)}, without parameters. + * + * @param destinationUuid the {@code destination_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> listDeliveriesAsync(String destinationUuid, RequestOptions options) { + return client.executeAsync(listDeliveriesRequest(destinationUuid, WebhooksDestinationsListDeliveriesParams.builder().build()), options, LIST_DELIVERIES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)}, without parameters, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @return the result + */ + public Page listDeliveries(String destinationUuid) { + return client.execute(listDeliveriesRequest(destinationUuid, WebhooksDestinationsListDeliveriesParams.builder().build()), RequestOptions.none(), LIST_DELIVERIES_RESPONSE).data(); + } + + /** + * Asynchronous {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)}, without parameters, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @return a future completed with the result + */ + public CompletableFuture> listDeliveriesAsync(String destinationUuid) { + return client.executeAsync(listDeliveriesRequest(destinationUuid, WebhooksDestinationsListDeliveriesParams.builder().build()), RequestOptions.none(), LIST_DELIVERIES_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Get webhook destination delivery + * + *

Retrieve one delivery attempt to a webhook destination with the exact request headers and JSON body Didit sent, the HTTP status your endpoint returned and the response time. request_header and request_body are JSON text: parse them to read the signature headers and the payload, for example to check your signature verification against the destination's secret_shared_key. A delivery never contains the signing secret, and Didit does not record your endpoint's response body. A scoped API key needs webhooks: read and decisions: read; a key limited to some workflows or to approved sessions cannot use this endpoint. + * + *

{@code GET /v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/} + * + * @param destinationUuid UUID of the destination the delivery was sent to. + * @param deliveryUuid UUID of the delivery: the `uuid` of an entry returned by `GET /v3/webhook/destinations/{destination_uuid}/deliveries/`. + * @param options per-request options that override the client options + * @return the response body + */ + public WebhookDestinationDeliveryDetail retrieveDelivery(String destinationUuid, String deliveryUuid, RequestOptions options) { + return client.execute(retrieveDeliveryRequest(destinationUuid, deliveryUuid), options, RETRIEVE_DELIVERY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveDelivery(String, String, RequestOptions)}. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveDeliveryAsync(String destinationUuid, String deliveryUuid, RequestOptions options) { + return client.executeAsync(retrieveDeliveryRequest(destinationUuid, deliveryUuid), options, RETRIEVE_DELIVERY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveDelivery(String, String, RequestOptions)}, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @return the result + */ + public WebhookDestinationDeliveryDetail retrieveDelivery(String destinationUuid, String deliveryUuid) { + return client.execute(retrieveDeliveryRequest(destinationUuid, deliveryUuid), RequestOptions.none(), RETRIEVE_DELIVERY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveDelivery(String, String, RequestOptions)}, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @return a future completed with the result + */ + public CompletableFuture retrieveDeliveryAsync(String destinationUuid, String deliveryUuid) { + return client.executeAsync(retrieveDeliveryRequest(destinationUuid, deliveryUuid), RequestOptions.none(), RETRIEVE_DELIVERY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Resend webhook destination delivery + * + *

Send a past delivery again, for example after your endpoint was down. Didit takes the stored payload, sets a fresh timestamp, signs it again with the destination's current secret_shared_key (X-Signature, X-Signature-V2, X-Signature-Simple and X-Timestamp) and posts it to the delivery's request_url, even if the destination's URL has changed since or the destination is disabled. event_id and the rest of the payload are unchanged, so deduplicate on event_id. The call waits up to 5 seconds for your endpoint and returns the new delivery with 200 whatever your endpoint answered: read response_status. A resend is not retried automatically, every call sends again and creates a new delivery, and the request has no body. A scoped API key needs webhooks: write and decisions: read; a key limited to some workflows or to approved sessions cannot use this endpoint. + * + *

{@code POST /v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/resend/} + * + * @param destinationUuid UUID of the destination the delivery was sent to. + * @param deliveryUuid UUID of the delivery to resend: the `uuid` of an entry returned by `GET /v3/webhook/destinations/{destination_uuid}/deliveries/`. + * @param options per-request options that override the client options + * @return the response body + */ + public WebhookDestinationDeliveryDetail resendDelivery(String destinationUuid, String deliveryUuid, RequestOptions options) { + return client.execute(resendDeliveryRequest(destinationUuid, deliveryUuid), options, RESEND_DELIVERY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #resendDelivery(String, String, RequestOptions)}. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture resendDeliveryAsync(String destinationUuid, String deliveryUuid, RequestOptions options) { + return client.executeAsync(resendDeliveryRequest(destinationUuid, deliveryUuid), options, RESEND_DELIVERY_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #resendDelivery(String, String, RequestOptions)}, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @return the result + */ + public WebhookDestinationDeliveryDetail resendDelivery(String destinationUuid, String deliveryUuid) { + return client.execute(resendDeliveryRequest(destinationUuid, deliveryUuid), RequestOptions.none(), RESEND_DELIVERY_RESPONSE).data(); + } + + /** + * Asynchronous {@link #resendDelivery(String, String, RequestOptions)}, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @return a future completed with the result + */ + public CompletableFuture resendDeliveryAsync(String destinationUuid, String deliveryUuid) { + return client.executeAsync(resendDeliveryRequest(destinationUuid, deliveryUuid), RequestOptions.none(), RESEND_DELIVERY_RESPONSE).thenApply(RawResponse::data); + } + private static final ResponseHandler> LIST_RESPONSE = Responses.json(Conv.list(Conv.model(WebhooksDestinationsListResponse::fromJson))); private static ApiRequest listRequest() { @@ -349,6 +640,44 @@ private static ApiRequest deleteRequest(String destinationUuid) { .build(); } + private static final ResponseHandler RETRIEVE_ANALYTICS_RESPONSE = Responses.json(Conv.model(WebhooksDestinationsRetrieveAnalyticsResponse::fromJson)); + + private static ApiRequest retrieveAnalyticsRequest(String destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams params) { + return ApiRequest.builder("GET", "/v3/webhook/destinations/{destination_uuid}/analytics/") + .pathParam("destination_uuid", destinationUuid) + .params(params) + .queryFields("date_from", "date_to") + .build(); + } + + private static final ResponseHandler> LIST_DELIVERIES_RESPONSE = Responses.page(Conv.model(WebhookDestinationDelivery::fromJson)); + + private static ApiRequest listDeliveriesRequest(String destinationUuid, WebhooksDestinationsListDeliveriesParams params) { + return ApiRequest.builder("GET", "/v3/webhook/destinations/{destination_uuid}/deliveries/") + .pathParam("destination_uuid", destinationUuid) + .params(params) + .queryFields("limit", "offset", "date_from", "date_to") + .build(); + } + + private static final ResponseHandler RETRIEVE_DELIVERY_RESPONSE = Responses.json(Conv.model(WebhookDestinationDeliveryDetail::fromJson)); + + private static ApiRequest retrieveDeliveryRequest(String destinationUuid, String deliveryUuid) { + return ApiRequest.builder("GET", "/v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/") + .pathParam("destination_uuid", destinationUuid) + .pathParam("delivery_uuid", deliveryUuid) + .build(); + } + + private static final ResponseHandler RESEND_DELIVERY_RESPONSE = Responses.json(Conv.model(WebhookDestinationDeliveryDetail::fromJson)); + + private static ApiRequest resendDeliveryRequest(String destinationUuid, String deliveryUuid) { + return ApiRequest.builder("POST", "/v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/resend/") + .pathParam("destination_uuid", destinationUuid) + .pathParam("delivery_uuid", deliveryUuid) + .build(); + } + /** The {@code webhooks.destinations} operations returning {@link RawResponse}. */ public static final class WithRawResponse { private final ApiClient client; @@ -608,5 +937,273 @@ public CompletableFuture> update public CompletableFuture> deleteAsync(String destinationUuid) { return client.executeAsync(deleteRequest(destinationUuid), RequestOptions.none(), DELETE_RESPONSE); } + + /** + * {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)} with the raw response. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse retrieveAnalytics(String destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams params, RequestOptions options) { + return client.execute(retrieveAnalyticsRequest(destinationUuid, params), options, RETRIEVE_ANALYTICS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)} with the raw response. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveAnalyticsAsync(String destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams params, RequestOptions options) { + return client.executeAsync(retrieveAnalyticsRequest(destinationUuid, params), options, RETRIEVE_ANALYTICS_RESPONSE); + } + + /** + * {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)} with the raw response, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @return the result + */ + public RawResponse retrieveAnalytics(String destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams params) { + return client.execute(retrieveAnalyticsRequest(destinationUuid, params), RequestOptions.none(), RETRIEVE_ANALYTICS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)} with the raw response, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> retrieveAnalyticsAsync(String destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams params) { + return client.executeAsync(retrieveAnalyticsRequest(destinationUuid, params), RequestOptions.none(), RETRIEVE_ANALYTICS_RESPONSE); + } + + /** + * {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)} with the raw response, without parameters. + * + * @param destinationUuid the {@code destination_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse retrieveAnalytics(String destinationUuid, RequestOptions options) { + return client.execute(retrieveAnalyticsRequest(destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams.builder().build()), options, RETRIEVE_ANALYTICS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)} with the raw response, without parameters. + * + * @param destinationUuid the {@code destination_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveAnalyticsAsync(String destinationUuid, RequestOptions options) { + return client.executeAsync(retrieveAnalyticsRequest(destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams.builder().build()), options, RETRIEVE_ANALYTICS_RESPONSE); + } + + /** + * {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @return the result + */ + public RawResponse retrieveAnalytics(String destinationUuid) { + return client.execute(retrieveAnalyticsRequest(destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams.builder().build()), RequestOptions.none(), RETRIEVE_ANALYTICS_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveAnalytics(String, WebhooksDestinationsRetrieveAnalyticsParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @return a future completed with the result + */ + public CompletableFuture> retrieveAnalyticsAsync(String destinationUuid) { + return client.executeAsync(retrieveAnalyticsRequest(destinationUuid, WebhooksDestinationsRetrieveAnalyticsParams.builder().build()), RequestOptions.none(), RETRIEVE_ANALYTICS_RESPONSE); + } + + /** + * {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)} with the raw response. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse> listDeliveries(String destinationUuid, WebhooksDestinationsListDeliveriesParams params, RequestOptions options) { + return client.execute(listDeliveriesRequest(destinationUuid, params), options, LIST_DELIVERIES_RESPONSE); + } + + /** + * Asynchronous {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)} with the raw response. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listDeliveriesAsync(String destinationUuid, WebhooksDestinationsListDeliveriesParams params, RequestOptions options) { + return client.executeAsync(listDeliveriesRequest(destinationUuid, params), options, LIST_DELIVERIES_RESPONSE); + } + + /** + * {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)} with the raw response, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @return the result + */ + public RawResponse> listDeliveries(String destinationUuid, WebhooksDestinationsListDeliveriesParams params) { + return client.execute(listDeliveriesRequest(destinationUuid, params), RequestOptions.none(), LIST_DELIVERIES_RESPONSE); + } + + /** + * Asynchronous {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)} with the raw response, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture>> listDeliveriesAsync(String destinationUuid, WebhooksDestinationsListDeliveriesParams params) { + return client.executeAsync(listDeliveriesRequest(destinationUuid, params), RequestOptions.none(), LIST_DELIVERIES_RESPONSE); + } + + /** + * {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)} with the raw response, without parameters. + * + * @param destinationUuid the {@code destination_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse> listDeliveries(String destinationUuid, RequestOptions options) { + return client.execute(listDeliveriesRequest(destinationUuid, WebhooksDestinationsListDeliveriesParams.builder().build()), options, LIST_DELIVERIES_RESPONSE); + } + + /** + * Asynchronous {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)} with the raw response, without parameters. + * + * @param destinationUuid the {@code destination_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture>> listDeliveriesAsync(String destinationUuid, RequestOptions options) { + return client.executeAsync(listDeliveriesRequest(destinationUuid, WebhooksDestinationsListDeliveriesParams.builder().build()), options, LIST_DELIVERIES_RESPONSE); + } + + /** + * {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @return the result + */ + public RawResponse> listDeliveries(String destinationUuid) { + return client.execute(listDeliveriesRequest(destinationUuid, WebhooksDestinationsListDeliveriesParams.builder().build()), RequestOptions.none(), LIST_DELIVERIES_RESPONSE); + } + + /** + * Asynchronous {@link #listDeliveries(String, WebhooksDestinationsListDeliveriesParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @return a future completed with the result + */ + public CompletableFuture>> listDeliveriesAsync(String destinationUuid) { + return client.executeAsync(listDeliveriesRequest(destinationUuid, WebhooksDestinationsListDeliveriesParams.builder().build()), RequestOptions.none(), LIST_DELIVERIES_RESPONSE); + } + + /** + * {@link #retrieveDelivery(String, String, RequestOptions)} with the raw response. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse retrieveDelivery(String destinationUuid, String deliveryUuid, RequestOptions options) { + return client.execute(retrieveDeliveryRequest(destinationUuid, deliveryUuid), options, RETRIEVE_DELIVERY_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDelivery(String, String, RequestOptions)} with the raw response. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveDeliveryAsync(String destinationUuid, String deliveryUuid, RequestOptions options) { + return client.executeAsync(retrieveDeliveryRequest(destinationUuid, deliveryUuid), options, RETRIEVE_DELIVERY_RESPONSE); + } + + /** + * {@link #retrieveDelivery(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @return the result + */ + public RawResponse retrieveDelivery(String destinationUuid, String deliveryUuid) { + return client.execute(retrieveDeliveryRequest(destinationUuid, deliveryUuid), RequestOptions.none(), RETRIEVE_DELIVERY_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveDelivery(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @return a future completed with the result + */ + public CompletableFuture> retrieveDeliveryAsync(String destinationUuid, String deliveryUuid) { + return client.executeAsync(retrieveDeliveryRequest(destinationUuid, deliveryUuid), RequestOptions.none(), RETRIEVE_DELIVERY_RESPONSE); + } + + /** + * {@link #resendDelivery(String, String, RequestOptions)} with the raw response. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @param options per-request options + * @return the result + */ + public RawResponse resendDelivery(String destinationUuid, String deliveryUuid, RequestOptions options) { + return client.execute(resendDeliveryRequest(destinationUuid, deliveryUuid), options, RESEND_DELIVERY_RESPONSE); + } + + /** + * Asynchronous {@link #resendDelivery(String, String, RequestOptions)} with the raw response. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> resendDeliveryAsync(String destinationUuid, String deliveryUuid, RequestOptions options) { + return client.executeAsync(resendDeliveryRequest(destinationUuid, deliveryUuid), options, RESEND_DELIVERY_RESPONSE); + } + + /** + * {@link #resendDelivery(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @return the result + */ + public RawResponse resendDelivery(String destinationUuid, String deliveryUuid) { + return client.execute(resendDeliveryRequest(destinationUuid, deliveryUuid), RequestOptions.none(), RESEND_DELIVERY_RESPONSE); + } + + /** + * Asynchronous {@link #resendDelivery(String, String, RequestOptions)} with the raw response, with the client's options. + * + * @param destinationUuid the {@code destination_uuid} + * @param deliveryUuid the {@code delivery_uuid} + * @return a future completed with the result + */ + public CompletableFuture> resendDeliveryAsync(String destinationUuid, String deliveryUuid) { + return client.executeAsync(resendDeliveryRequest(destinationUuid, deliveryUuid), RequestOptions.none(), RESEND_DELIVERY_RESPONSE); + } } } diff --git a/src/main/java/me/didit/resources/WebhooksResource.java b/src/main/java/me/didit/resources/WebhooksResource.java index 7bb35e7..bd4c22f 100644 --- a/src/main/java/me/didit/resources/WebhooksResource.java +++ b/src/main/java/me/didit/resources/WebhooksResource.java @@ -1,16 +1,28 @@ // Code generated by codegen from openapi/*.json. DO NOT EDIT. package me.didit.resources; +import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; +import me.didit.core.ApiRequest; +import me.didit.core.Conv; +import me.didit.core.RawResponse; +import me.didit.core.RequestOptions; +import me.didit.core.ResponseHandler; +import me.didit.core.Responses; +import me.didit.types.WebhookConfig; +import me.didit.types.WebhooksUpdateConfigParams; import me.didit.webhooks.WebhookMethods; /** The {@code webhooks} resource: {@code client.webhooks()}. */ public final class WebhooksResource extends WebhookMethods { + private final ApiClient client; + private final WithRawResponse withRawResponse; private final WebhooksDestinationsResource destinations; WebhooksResource(ApiClient client) { + this.client = client; + this.withRawResponse = new WithRawResponse(client); this.destinations = new WebhooksDestinationsResource(client); - java.util.Objects.requireNonNull(client, "client"); } /** @@ -21,4 +33,282 @@ public final class WebhooksResource extends WebhookMethods { public WebhooksDestinationsResource destinations() { return destinations; } + + /** + * The same operations, returning the HTTP status, headers and request id with the parsed body. + * + * @return the raw response view + */ + public WithRawResponse withRawResponse() { + return withRawResponse; + } + + /** + * Get webhook configuration + * + *

Return the application's webhook settings in one call: every webhook destination with its signing secret, the event types a destination can subscribe to, the application-level (legacy) webhook URL, version and secret, the data and biometric-template retention policy, and the Digital ID wallet return hosts. Didit delivers webhooks to destinations, each signed with its own secret_shared_key; the top-level webhook_url, webhook_version and secret_shared_key are used only while no destination is enabled, and then only for status.updated and data.updated. The response contains signing secrets, so keep it out of logs and client-side code. A scoped API key needs webhooks: read. + * + *

{@code GET /v3/webhook/} + * + * @param options per-request options that override the client options + * @return the response body + */ + public WebhookConfig retrieveConfig(RequestOptions options) { + return client.execute(retrieveConfigRequest(), options, RETRIEVE_CONFIG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveConfig(RequestOptions)}. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture retrieveConfigAsync(RequestOptions options) { + return client.executeAsync(retrieveConfigRequest(), options, RETRIEVE_CONFIG_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #retrieveConfig(RequestOptions)}, with the client's options. + * + * @return the result + */ + public WebhookConfig retrieveConfig() { + return client.execute(retrieveConfigRequest(), RequestOptions.none(), RETRIEVE_CONFIG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #retrieveConfig(RequestOptions)}, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture retrieveConfigAsync() { + return client.executeAsync(retrieveConfigRequest(), RequestOptions.none(), RETRIEVE_CONFIG_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Update webhook configuration + * + *

Partially update application-level settings: the legacy webhook URL, version and secret, the data and biometric-template retention policy, and the Digital ID wallet return hosts. Send only the fields to change; the response is the full configuration, the same shape as GET /v3/webhook/. Destinations are not managed here: create, update and delete them with the /v3/webhook/destinations/ endpoints, and do not send an endpoints field to this endpoint. A data_retention_months change also applies to existing sessions, and sessions it deletes cannot be restored; read Data retention first. A scoped API key needs webhooks: write. + * + *

{@code PATCH /v3/webhook/} + * + * @param params the request parameters + * @param options per-request options that override the client options + * @return the response body + */ + public WebhookConfig updateConfig(WebhooksUpdateConfigParams params, RequestOptions options) { + return client.execute(updateConfigRequest(params), options, UPDATE_CONFIG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)}. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture updateConfigAsync(WebhooksUpdateConfigParams params, RequestOptions options) { + return client.executeAsync(updateConfigRequest(params), options, UPDATE_CONFIG_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public WebhookConfig updateConfig(WebhooksUpdateConfigParams params) { + return client.execute(updateConfigRequest(params), RequestOptions.none(), UPDATE_CONFIG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)}, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture updateConfigAsync(WebhooksUpdateConfigParams params) { + return client.executeAsync(updateConfigRequest(params), RequestOptions.none(), UPDATE_CONFIG_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return the result + */ + public WebhookConfig updateConfig(RequestOptions options) { + return client.execute(updateConfigRequest(WebhooksUpdateConfigParams.builder().build()), options, UPDATE_CONFIG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)}, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture updateConfigAsync(RequestOptions options) { + return client.executeAsync(updateConfigRequest(WebhooksUpdateConfigParams.builder().build()), options, UPDATE_CONFIG_RESPONSE).thenApply(RawResponse::data); + } + + /** + * Same as {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)}, without parameters, with the client's options. + * + * @return the result + */ + public WebhookConfig updateConfig() { + return client.execute(updateConfigRequest(WebhooksUpdateConfigParams.builder().build()), RequestOptions.none(), UPDATE_CONFIG_RESPONSE).data(); + } + + /** + * Asynchronous {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)}, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture updateConfigAsync() { + return client.executeAsync(updateConfigRequest(WebhooksUpdateConfigParams.builder().build()), RequestOptions.none(), UPDATE_CONFIG_RESPONSE).thenApply(RawResponse::data); + } + + private static final ResponseHandler RETRIEVE_CONFIG_RESPONSE = Responses.json(Conv.model(WebhookConfig::fromJson)); + + private static ApiRequest retrieveConfigRequest() { + return ApiRequest.builder("GET", "/v3/webhook/") + .build(); + } + + private static final ResponseHandler UPDATE_CONFIG_RESPONSE = Responses.json(Conv.model(WebhookConfig::fromJson)); + + private static ApiRequest updateConfigRequest(WebhooksUpdateConfigParams params) { + return ApiRequest.builder("PATCH", "/v3/webhook/") + .params(params) + .body(ApiRequest.BodyEncoding.JSON) + .build(); + } + + /** The {@code webhooks} operations returning {@link RawResponse}. */ + public static final class WithRawResponse { + private final ApiClient client; + + WithRawResponse(ApiClient client) { + this.client = client; + } + + /** + * {@link #retrieveConfig(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return the result + */ + public RawResponse retrieveConfig(RequestOptions options) { + return client.execute(retrieveConfigRequest(), options, RETRIEVE_CONFIG_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveConfig(RequestOptions)} with the raw response. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> retrieveConfigAsync(RequestOptions options) { + return client.executeAsync(retrieveConfigRequest(), options, RETRIEVE_CONFIG_RESPONSE); + } + + /** + * {@link #retrieveConfig(RequestOptions)} with the raw response, with the client's options. + * + * @return the result + */ + public RawResponse retrieveConfig() { + return client.execute(retrieveConfigRequest(), RequestOptions.none(), RETRIEVE_CONFIG_RESPONSE); + } + + /** + * Asynchronous {@link #retrieveConfig(RequestOptions)} with the raw response, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> retrieveConfigAsync() { + return client.executeAsync(retrieveConfigRequest(), RequestOptions.none(), RETRIEVE_CONFIG_RESPONSE); + } + + /** + * {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return the result + */ + public RawResponse updateConfig(WebhooksUpdateConfigParams params, RequestOptions options) { + return client.execute(updateConfigRequest(params), options, UPDATE_CONFIG_RESPONSE); + } + + /** + * Asynchronous {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)} with the raw response. + * + * @param params the request parameters + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> updateConfigAsync(WebhooksUpdateConfigParams params, RequestOptions options) { + return client.executeAsync(updateConfigRequest(params), options, UPDATE_CONFIG_RESPONSE); + } + + /** + * {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return the result + */ + public RawResponse updateConfig(WebhooksUpdateConfigParams params) { + return client.execute(updateConfigRequest(params), RequestOptions.none(), UPDATE_CONFIG_RESPONSE); + } + + /** + * Asynchronous {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)} with the raw response, with the client's options. + * + * @param params the request parameters + * @return a future completed with the result + */ + public CompletableFuture> updateConfigAsync(WebhooksUpdateConfigParams params) { + return client.executeAsync(updateConfigRequest(params), RequestOptions.none(), UPDATE_CONFIG_RESPONSE); + } + + /** + * {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return the result + */ + public RawResponse updateConfig(RequestOptions options) { + return client.execute(updateConfigRequest(WebhooksUpdateConfigParams.builder().build()), options, UPDATE_CONFIG_RESPONSE); + } + + /** + * Asynchronous {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)} with the raw response, without parameters. + * + * @param options per-request options + * @return a future completed with the result + */ + public CompletableFuture> updateConfigAsync(RequestOptions options) { + return client.executeAsync(updateConfigRequest(WebhooksUpdateConfigParams.builder().build()), options, UPDATE_CONFIG_RESPONSE); + } + + /** + * {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return the result + */ + public RawResponse updateConfig() { + return client.execute(updateConfigRequest(WebhooksUpdateConfigParams.builder().build()), RequestOptions.none(), UPDATE_CONFIG_RESPONSE); + } + + /** + * Asynchronous {@link #updateConfig(WebhooksUpdateConfigParams, RequestOptions)} with the raw response, without parameters, with the client's options. + * + * @return a future completed with the result + */ + public CompletableFuture> updateConfigAsync() { + return client.executeAsync(updateConfigRequest(WebhooksUpdateConfigParams.builder().build()), RequestOptions.none(), UPDATE_CONFIG_RESPONSE); + } + } } diff --git a/src/main/java/me/didit/resources/WorkflowsResource.java b/src/main/java/me/didit/resources/WorkflowsResource.java index 1d272ab..0866a2e 100644 --- a/src/main/java/me/didit/resources/WorkflowsResource.java +++ b/src/main/java/me/didit/resources/WorkflowsResource.java @@ -1,8 +1,6 @@ // Code generated by codegen from openapi/*.json. DO NOT EDIT. package me.didit.resources; -import com.fasterxml.jackson.databind.JsonNode; -import java.util.Map; import java.util.concurrent.CompletableFuture; import me.didit.core.ApiClient; import me.didit.core.ApiRequest; @@ -14,6 +12,7 @@ import me.didit.core.Responses; import me.didit.types.SimpleWorkflowRequest; import me.didit.types.SimpleWorkflowUpdateRequest; +import me.didit.types.WorkflowDetail; import me.didit.types.WorkflowListItem; import me.didit.types.WorkflowsListParams; import org.jspecify.annotations.Nullable; @@ -133,7 +132,7 @@ public CompletableFuture> listAsync() { * @param options per-request options that override the client options * @return the response body */ - public Map create(SimpleWorkflowRequest params, RequestOptions options) { + public WorkflowDetail create(SimpleWorkflowRequest params, RequestOptions options) { return client.execute(createRequest(params), options, CREATE_RESPONSE).data(); } @@ -144,7 +143,7 @@ public Map create(SimpleWorkflowRequest params, RequestOptions * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> createAsync(SimpleWorkflowRequest params, RequestOptions options) { + public CompletableFuture createAsync(SimpleWorkflowRequest params, RequestOptions options) { return client.executeAsync(createRequest(params), options, CREATE_RESPONSE).thenApply(RawResponse::data); } @@ -154,7 +153,7 @@ public CompletableFuture> createAsync(SimpleWorkflowReques * @param params the request parameters * @return the result */ - public Map create(SimpleWorkflowRequest params) { + public WorkflowDetail create(SimpleWorkflowRequest params) { return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).data(); } @@ -164,7 +163,7 @@ public Map create(SimpleWorkflowRequest params) { * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> createAsync(SimpleWorkflowRequest params) { + public CompletableFuture createAsync(SimpleWorkflowRequest params) { return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE).thenApply(RawResponse::data); } @@ -179,7 +178,7 @@ public CompletableFuture> createAsync(SimpleWorkflowReques * @param options per-request options that override the client options * @return the response body */ - public JsonNode retrieve(String settingsUuid, RequestOptions options) { + public WorkflowDetail retrieve(String settingsUuid, RequestOptions options) { return client.execute(retrieveRequest(settingsUuid), options, RETRIEVE_RESPONSE).data(); } @@ -190,7 +189,7 @@ public JsonNode retrieve(String settingsUuid, RequestOptions options) { * @param options per-request options * @return a future completed with the result */ - public CompletableFuture retrieveAsync(String settingsUuid, RequestOptions options) { + public CompletableFuture retrieveAsync(String settingsUuid, RequestOptions options) { return client.executeAsync(retrieveRequest(settingsUuid), options, RETRIEVE_RESPONSE).thenApply(RawResponse::data); } @@ -200,7 +199,7 @@ public CompletableFuture retrieveAsync(String settingsUuid, RequestOpt * @param settingsUuid the {@code settings_uuid} * @return the result */ - public JsonNode retrieve(String settingsUuid) { + public WorkflowDetail retrieve(String settingsUuid) { return client.execute(retrieveRequest(settingsUuid), RequestOptions.none(), RETRIEVE_RESPONSE).data(); } @@ -210,7 +209,7 @@ public JsonNode retrieve(String settingsUuid) { * @param settingsUuid the {@code settings_uuid} * @return a future completed with the result */ - public CompletableFuture retrieveAsync(String settingsUuid) { + public CompletableFuture retrieveAsync(String settingsUuid) { return client.executeAsync(retrieveRequest(settingsUuid), RequestOptions.none(), RETRIEVE_RESPONSE).thenApply(RawResponse::data); } @@ -226,7 +225,7 @@ public CompletableFuture retrieveAsync(String settingsUuid) { * @param options per-request options that override the client options * @return the response body */ - public Map update(String settingsUuid, SimpleWorkflowUpdateRequest params, RequestOptions options) { + public WorkflowDetail update(String settingsUuid, SimpleWorkflowUpdateRequest params, RequestOptions options) { return client.execute(updateRequest(settingsUuid, params), options, UPDATE_RESPONSE).data(); } @@ -238,7 +237,7 @@ public Map update(String settingsUuid, SimpleWorkflowUpdateReq * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> updateAsync(String settingsUuid, SimpleWorkflowUpdateRequest params, RequestOptions options) { + public CompletableFuture updateAsync(String settingsUuid, SimpleWorkflowUpdateRequest params, RequestOptions options) { return client.executeAsync(updateRequest(settingsUuid, params), options, UPDATE_RESPONSE).thenApply(RawResponse::data); } @@ -249,7 +248,7 @@ public CompletableFuture> updateAsync(String settingsUuid, * @param params the request parameters * @return the result */ - public Map update(String settingsUuid, SimpleWorkflowUpdateRequest params) { + public WorkflowDetail update(String settingsUuid, SimpleWorkflowUpdateRequest params) { return client.execute(updateRequest(settingsUuid, params), RequestOptions.none(), UPDATE_RESPONSE).data(); } @@ -260,7 +259,7 @@ public Map update(String settingsUuid, SimpleWorkflowUpdateReq * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture> updateAsync(String settingsUuid, SimpleWorkflowUpdateRequest params) { + public CompletableFuture updateAsync(String settingsUuid, SimpleWorkflowUpdateRequest params) { return client.executeAsync(updateRequest(settingsUuid, params), RequestOptions.none(), UPDATE_RESPONSE).thenApply(RawResponse::data); } @@ -317,7 +316,7 @@ private static ApiRequest listRequest(WorkflowsListParams params) { .build(); } - private static final ResponseHandler> CREATE_RESPONSE = Responses.json(Conv.OBJECT); + private static final ResponseHandler CREATE_RESPONSE = Responses.json(Conv.model(WorkflowDetail::fromJson)); private static ApiRequest createRequest(SimpleWorkflowRequest params) { return ApiRequest.builder("POST", "/v3/workflows/") @@ -326,7 +325,7 @@ private static ApiRequest createRequest(SimpleWorkflowRequest params) { .build(); } - private static final ResponseHandler RETRIEVE_RESPONSE = Responses.jsonNode(); + private static final ResponseHandler RETRIEVE_RESPONSE = Responses.json(Conv.model(WorkflowDetail::fromJson)); private static ApiRequest retrieveRequest(String settingsUuid) { return ApiRequest.builder("GET", "/v3/workflows/{settings_uuid}/") @@ -334,7 +333,7 @@ private static ApiRequest retrieveRequest(String settingsUuid) { .build(); } - private static final ResponseHandler> UPDATE_RESPONSE = Responses.json(Conv.OBJECT); + private static final ResponseHandler UPDATE_RESPONSE = Responses.json(Conv.model(WorkflowDetail::fromJson)); private static ApiRequest updateRequest(String settingsUuid, SimpleWorkflowUpdateRequest params) { return ApiRequest.builder("PATCH", "/v3/workflows/{settings_uuid}/") @@ -447,7 +446,7 @@ public CompletableFuture>> listAsync() { * @param options per-request options * @return the result */ - public RawResponse> create(SimpleWorkflowRequest params, RequestOptions options) { + public RawResponse create(SimpleWorkflowRequest params, RequestOptions options) { return client.execute(createRequest(params), options, CREATE_RESPONSE); } @@ -458,7 +457,7 @@ public RawResponse> create(SimpleWorkflowRequest params, R * @param options per-request options * @return a future completed with the result */ - public CompletableFuture>> createAsync(SimpleWorkflowRequest params, RequestOptions options) { + public CompletableFuture> createAsync(SimpleWorkflowRequest params, RequestOptions options) { return client.executeAsync(createRequest(params), options, CREATE_RESPONSE); } @@ -468,7 +467,7 @@ public CompletableFuture>> createAsync(SimpleW * @param params the request parameters * @return the result */ - public RawResponse> create(SimpleWorkflowRequest params) { + public RawResponse create(SimpleWorkflowRequest params) { return client.execute(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); } @@ -478,7 +477,7 @@ public RawResponse> create(SimpleWorkflowRequest params) { * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture>> createAsync(SimpleWorkflowRequest params) { + public CompletableFuture> createAsync(SimpleWorkflowRequest params) { return client.executeAsync(createRequest(params), RequestOptions.none(), CREATE_RESPONSE); } @@ -489,7 +488,7 @@ public CompletableFuture>> createAsync(SimpleW * @param options per-request options * @return the result */ - public RawResponse retrieve(String settingsUuid, RequestOptions options) { + public RawResponse retrieve(String settingsUuid, RequestOptions options) { return client.execute(retrieveRequest(settingsUuid), options, RETRIEVE_RESPONSE); } @@ -500,7 +499,7 @@ public RawResponse retrieve(String settingsUuid, RequestOptions option * @param options per-request options * @return a future completed with the result */ - public CompletableFuture> retrieveAsync(String settingsUuid, RequestOptions options) { + public CompletableFuture> retrieveAsync(String settingsUuid, RequestOptions options) { return client.executeAsync(retrieveRequest(settingsUuid), options, RETRIEVE_RESPONSE); } @@ -510,7 +509,7 @@ public CompletableFuture> retrieveAsync(String settingsUui * @param settingsUuid the {@code settings_uuid} * @return the result */ - public RawResponse retrieve(String settingsUuid) { + public RawResponse retrieve(String settingsUuid) { return client.execute(retrieveRequest(settingsUuid), RequestOptions.none(), RETRIEVE_RESPONSE); } @@ -520,7 +519,7 @@ public RawResponse retrieve(String settingsUuid) { * @param settingsUuid the {@code settings_uuid} * @return a future completed with the result */ - public CompletableFuture> retrieveAsync(String settingsUuid) { + public CompletableFuture> retrieveAsync(String settingsUuid) { return client.executeAsync(retrieveRequest(settingsUuid), RequestOptions.none(), RETRIEVE_RESPONSE); } @@ -532,7 +531,7 @@ public CompletableFuture> retrieveAsync(String settingsUui * @param options per-request options * @return the result */ - public RawResponse> update(String settingsUuid, SimpleWorkflowUpdateRequest params, RequestOptions options) { + public RawResponse update(String settingsUuid, SimpleWorkflowUpdateRequest params, RequestOptions options) { return client.execute(updateRequest(settingsUuid, params), options, UPDATE_RESPONSE); } @@ -544,7 +543,7 @@ public RawResponse> update(String settingsUuid, SimpleWork * @param options per-request options * @return a future completed with the result */ - public CompletableFuture>> updateAsync(String settingsUuid, SimpleWorkflowUpdateRequest params, RequestOptions options) { + public CompletableFuture> updateAsync(String settingsUuid, SimpleWorkflowUpdateRequest params, RequestOptions options) { return client.executeAsync(updateRequest(settingsUuid, params), options, UPDATE_RESPONSE); } @@ -555,7 +554,7 @@ public CompletableFuture>> updateAsync(String * @param params the request parameters * @return the result */ - public RawResponse> update(String settingsUuid, SimpleWorkflowUpdateRequest params) { + public RawResponse update(String settingsUuid, SimpleWorkflowUpdateRequest params) { return client.execute(updateRequest(settingsUuid, params), RequestOptions.none(), UPDATE_RESPONSE); } @@ -566,7 +565,7 @@ public RawResponse> update(String settingsUuid, SimpleWork * @param params the request parameters * @return a future completed with the result */ - public CompletableFuture>> updateAsync(String settingsUuid, SimpleWorkflowUpdateRequest params) { + public CompletableFuture> updateAsync(String settingsUuid, SimpleWorkflowUpdateRequest params) { return client.executeAsync(updateRequest(settingsUuid, params), RequestOptions.none(), UPDATE_RESPONSE); } diff --git a/src/main/java/me/didit/types/AMLDatasetFreshness.java b/src/main/java/me/didit/types/AMLDatasetFreshness.java new file mode 100644 index 0000000..4ca7c43 --- /dev/null +++ b/src/main/java/me/didit/types/AMLDatasetFreshness.java @@ -0,0 +1,314 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** How current the AML datasets were when a screening ran. Dates are never estimated. */ +public final class AMLDatasetFreshness extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("captured_at", "provider", "catalog_version", "sources"); + + private AMLDatasetFreshness(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AMLDatasetFreshness} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AMLDatasetFreshness fromJson(JsonNode json) { + return new AMLDatasetFreshness(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * When Didit read the dataset catalog this freshness comes from. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime capturedAt() { + return Json.required(json, "captured_at", Conv.DATE_TIME); + } + + /** + * Opaque identifier of the AML data source. Treat it as an opaque string; do not branch on it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String provider() { + return Json.required(json, "provider", Conv.STRING); + } + + /** + * Version identifier of the dataset catalog, when the source reports one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional catalogVersion() { + return Json.optional(json, "catalog_version", Conv.STRING); + } + + /** + * Freshness per dataset family. A family whose datasets reported no update date is absent rather than filled with null. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public AMLDatasetFreshness.Sources sources() { + return Json.required(json, "sources", Conv.model(AMLDatasetFreshness.Sources::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AMLDatasetFreshness}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AMLDatasetFreshness from) { + super(from); + } + + /** + * When Didit read the dataset catalog this freshness comes from. + * + * @param capturedAt the value + * @return this builder + */ + public Builder capturedAt(OffsetDateTime capturedAt) { + return set("captured_at", Conv.DATE_TIME, capturedAt); + } + + /** + * Opaque identifier of the AML data source. Treat it as an opaque string; do not branch on it. + * + * @param provider the value + * @return this builder + */ + public Builder provider(String provider) { + return set("provider", Conv.STRING, provider); + } + + /** + * Version identifier of the dataset catalog, when the source reports one. + * + * @param catalogVersion the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder catalogVersion(@Nullable String catalogVersion) { + return setNullable("catalog_version", Conv.STRING, catalogVersion); + } + + /** + * Freshness per dataset family. A family whose datasets reported no update date is absent rather than filled with null. + * + * @param sources the value + * @return this builder + */ + public Builder sources(AMLDatasetFreshness.Sources sources) { + return set("sources", Conv.model(AMLDatasetFreshness.Sources::fromJson), sources); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AMLDatasetFreshness} + * @throws IllegalStateException when a required field is missing + */ + public AMLDatasetFreshness build() { + require("captured_at", false); + require("provider", false); + require("catalog_version", true); + require("sources", false); + return new AMLDatasetFreshness(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Freshness per dataset family. A family whose datasets reported no update date is absent rather than filled with null. + */ + public static final class Sources extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("sanctions", "pep", "warnings", "adverse_media"); + + private Sources(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Sources} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Sources fromJson(JsonNode json) { + return new Sources(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Freshness of one family of AML datasets. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sanctions() { + return Json.optional(json, "sanctions", Conv.model(AMLDatasetSourceFreshness::fromJson)); + } + + /** + * Freshness of one family of AML datasets. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional pep() { + return Json.optional(json, "pep", Conv.model(AMLDatasetSourceFreshness::fromJson)); + } + + /** + * Freshness of one family of AML datasets. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional warnings() { + return Json.optional(json, "warnings", Conv.model(AMLDatasetSourceFreshness::fromJson)); + } + + /** + * Freshness of one family of AML datasets. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional adverseMedia() { + return Json.optional(json, "adverse_media", Conv.model(AMLDatasetSourceFreshness::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Sources}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Sources from) { + super(from); + } + + /** + * Freshness of one family of AML datasets. + * + * @param sanctions the value + * @return this builder + */ + public Builder sanctions(AMLDatasetSourceFreshness sanctions) { + return set("sanctions", Conv.model(AMLDatasetSourceFreshness::fromJson), sanctions); + } + + /** + * Freshness of one family of AML datasets. + * + * @param pep the value + * @return this builder + */ + public Builder pep(AMLDatasetSourceFreshness pep) { + return set("pep", Conv.model(AMLDatasetSourceFreshness::fromJson), pep); + } + + /** + * Freshness of one family of AML datasets. + * + * @param warnings the value + * @return this builder + */ + public Builder warnings(AMLDatasetSourceFreshness warnings) { + return set("warnings", Conv.model(AMLDatasetSourceFreshness::fromJson), warnings); + } + + /** + * Freshness of one family of AML datasets. + * + * @param adverseMedia the value + * @return this builder + */ + public Builder adverseMedia(AMLDatasetSourceFreshness adverseMedia) { + return set("adverse_media", Conv.model(AMLDatasetSourceFreshness::fromJson), adverseMedia); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Sources} + */ + public Sources build() { + return new Sources(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/AMLDatasetSourceFreshness.java b/src/main/java/me/didit/types/AMLDatasetSourceFreshness.java new file mode 100644 index 0000000..fb1a145 --- /dev/null +++ b/src/main/java/me/didit/types/AMLDatasetSourceFreshness.java @@ -0,0 +1,166 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** Freshness of one family of AML datasets. */ +public final class AMLDatasetSourceFreshness extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("last_updated_at", "oldest_updated_at", "dataset_count", "dated_dataset_count"); + + private AMLDatasetSourceFreshness(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AMLDatasetSourceFreshness} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AMLDatasetSourceFreshness fromJson(JsonNode json) { + return new AMLDatasetSourceFreshness(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Most recent update of any dataset in the family. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime lastUpdatedAt() { + return Json.required(json, "last_updated_at", Conv.DATE_TIME); + } + + /** + * Staleness bound: every dataset in the family is at least this current. null when at least one dataset in the family reported no update date, because the bound is then unknown. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional oldestUpdatedAt() { + return Json.optional(json, "oldest_updated_at", Conv.DATE_TIME); + } + + /** + * Number of datasets in the family. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long datasetCount() { + return Json.required(json, "dataset_count", Conv.LONG); + } + + /** + * Number of those datasets that reported an update date. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long datedDatasetCount() { + return Json.required(json, "dated_dataset_count", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AMLDatasetSourceFreshness}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AMLDatasetSourceFreshness from) { + super(from); + } + + /** + * Most recent update of any dataset in the family. + * + * @param lastUpdatedAt the value + * @return this builder + */ + public Builder lastUpdatedAt(OffsetDateTime lastUpdatedAt) { + return set("last_updated_at", Conv.DATE_TIME, lastUpdatedAt); + } + + /** + * Staleness bound: every dataset in the family is at least this current. null when at least one dataset in the family reported no update date, because the bound is then unknown. + * + * @param oldestUpdatedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder oldestUpdatedAt(@Nullable OffsetDateTime oldestUpdatedAt) { + return setNullable("oldest_updated_at", Conv.DATE_TIME, oldestUpdatedAt); + } + + /** + * Number of datasets in the family. + * + * @param datasetCount the value + * @return this builder + */ + public Builder datasetCount(long datasetCount) { + return set("dataset_count", Conv.LONG, datasetCount); + } + + /** + * Number of those datasets that reported an update date. + * + * @param datedDatasetCount the value + * @return this builder + */ + public Builder datedDatasetCount(long datedDatasetCount) { + return set("dated_dataset_count", Conv.LONG, datedDatasetCount); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AMLDatasetSourceFreshness} + * @throws IllegalStateException when a required field is missing + */ + public AMLDatasetSourceFreshness build() { + require("last_updated_at", false); + require("oldest_updated_at", true); + require("dataset_count", false); + require("dated_dataset_count", false); + return new AMLDatasetSourceFreshness(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/BusinessesRetrieveSessionOngoingMonitoringJobResponse.java b/src/main/java/me/didit/types/AMLMonitoringJob.java similarity index 56% rename from src/main/java/me/didit/types/BusinessesRetrieveSessionOngoingMonitoringJobResponse.java rename to src/main/java/me/didit/types/AMLMonitoringJob.java index ee2218b..2978627 100644 --- a/src/main/java/me/didit/types/BusinessesRetrieveSessionOngoingMonitoringJobResponse.java +++ b/src/main/java/me/didit/types/AMLMonitoringJob.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; import java.util.List; import java.util.Map; import java.util.Optional; @@ -14,22 +15,22 @@ import me.didit.core.StringEnum; import org.jspecify.annotations.Nullable; -public final class BusinessesRetrieveSessionOngoingMonitoringJobResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); +public final class AMLMonitoringJob extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "target_type", "total", "processed", "outcomes", "results", "error", "created_at", "completed_at", "estimate", "unmatched"); - private BusinessesRetrieveSessionOngoingMonitoringJobResponse(ObjectNode json, Map> files) { + private AMLMonitoringJob(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code BusinessesRetrieveSessionOngoingMonitoringJobResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code AMLMonitoringJob} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static BusinessesRetrieveSessionOngoingMonitoringJobResponse fromJson(JsonNode json) { - return new BusinessesRetrieveSessionOngoingMonitoringJobResponse(Json.copyObject(json), Map.of()); + public static AMLMonitoringJob fromJson(JsonNode json) { + return new AMLMonitoringJob(Json.copyObject(json), Map.of()); } /** @@ -66,12 +67,12 @@ public Optional jobId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(BusinessesRetrieveSessionOngoingMonitoringJobResponse.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(AMLMonitoringJob.Status::of)); } /** - * The is_enabled field. + * true when the job enables monitoring, false when it disables it. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -81,7 +82,17 @@ public Optional isEnabled() { } /** - * The total field. + * Which profile family the job toggles. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional targetType() { + return Json.optional(json, "target_type", Conv.enumeration(AMLMonitoringJob.TargetType::of)); + } + + /** + * Profiles the job processes. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -91,7 +102,7 @@ public Optional total() { } /** - * The processed field. + * Profiles processed so far. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -101,17 +112,27 @@ public Optional processed() { } /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. + * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional outcomes() { + return Json.optional(json, "outcomes", Conv.model(AMLMonitoringJob.Outcomes::fromJson)); + } + + /** + * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(BusinessesRetrieveSessionOngoingMonitoringJobResponse.Outcomes::fromJson)); + public Optional> results() { + return Json.optional(json, "results", Conv.list(Conv.model(AMLMonitoringJob.Results::fromJson))); } /** - * The error field. + * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -121,13 +142,43 @@ public Optional error() { } /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). + * The created_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.DATE_TIME); + } + + /** + * The completed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional completedAt() { + return Json.optional(json, "completed_at", Conv.DATE_TIME); + } + + /** + * Cost preview. Returned directly when estimate_only is true (nothing is written). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(BusinessesRetrieveSessionOngoingMonitoringJobResponse.Estimate::fromJson)); + public Optional estimate() { + return Json.optional(json, "estimate", Conv.model(AMLMonitoringJob.Estimate::fromJson)); + } + + /** + * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> unmatched() { + return Json.optional(json, "unmatched", Conv.list(Conv.STRING)); } @Override @@ -135,11 +186,11 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link BusinessesRetrieveSessionOngoingMonitoringJobResponse}. */ + /** Builds a {@link AMLMonitoringJob}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(BusinessesRetrieveSessionOngoingMonitoringJobResponse from) { + private Builder(AMLMonitoringJob from) { super(from); } @@ -159,12 +210,12 @@ public Builder jobId(String jobId) { * @param status the value * @return this builder */ - public Builder status(BusinessesRetrieveSessionOngoingMonitoringJobResponse.Status status) { - return set("status", Conv.enumeration(BusinessesRetrieveSessionOngoingMonitoringJobResponse.Status::of), status); + public Builder status(AMLMonitoringJob.Status status) { + return set("status", Conv.enumeration(AMLMonitoringJob.Status::of), status); } /** - * The is_enabled field. + * true when the job enables monitoring, false when it disables it. * * @param isEnabled the value * @return this builder @@ -174,7 +225,17 @@ public Builder isEnabled(boolean isEnabled) { } /** - * The total field. + * Which profile family the job toggles. + * + * @param targetType the value + * @return this builder + */ + public Builder targetType(AMLMonitoringJob.TargetType targetType) { + return set("target_type", Conv.enumeration(AMLMonitoringJob.TargetType::of), targetType); + } + + /** + * Profiles the job processes. * * @param total the value * @return this builder @@ -184,7 +245,7 @@ public Builder total(long total) { } /** - * The processed field. + * Profiles processed so far. * * @param processed the value * @return this builder @@ -194,17 +255,27 @@ public Builder processed(long processed) { } /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. + * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. * * @param outcomes the value * @return this builder */ - public Builder outcomes(BusinessesRetrieveSessionOngoingMonitoringJobResponse.Outcomes outcomes) { - return set("outcomes", Conv.model(BusinessesRetrieveSessionOngoingMonitoringJobResponse.Outcomes::fromJson), outcomes); + public Builder outcomes(AMLMonitoringJob.Outcomes outcomes) { + return set("outcomes", Conv.model(AMLMonitoringJob.Outcomes::fromJson), outcomes); } /** - * The error field. + * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(AMLMonitoringJob.Results::fromJson)), results); + } + + /** + * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. * * @param error the value, or {@code null} to send JSON null * @return this builder @@ -214,22 +285,52 @@ public Builder error(@Nullable String error) { } /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). + * The created_at field. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * The completed_at field. + * + * @param completedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder completedAt(@Nullable OffsetDateTime completedAt) { + return setNullable("completed_at", Conv.DATE_TIME, completedAt); + } + + /** + * Cost preview. Returned directly when estimate_only is true (nothing is written). * * @param estimate the value * @return this builder */ - public Builder estimate(BusinessesRetrieveSessionOngoingMonitoringJobResponse.Estimate estimate) { - return set("estimate", Conv.model(BusinessesRetrieveSessionOngoingMonitoringJobResponse.Estimate::fromJson), estimate); + public Builder estimate(AMLMonitoringJob.Estimate estimate) { + return set("estimate", Conv.model(AMLMonitoringJob.Estimate::fromJson), estimate); + } + + /** + * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. + * + * @param unmatched the value + * @return this builder + */ + public Builder unmatched(List unmatched) { + return set("unmatched", Conv.list(Conv.STRING), unmatched); } /** * Builds the value. * - * @return a new immutable {@code BusinessesRetrieveSessionOngoingMonitoringJobResponse} + * @return a new immutable {@code AMLMonitoringJob} */ - public BusinessesRetrieveSessionOngoingMonitoringJobResponse build() { - return new BusinessesRetrieveSessionOngoingMonitoringJobResponse(buildJson(), buildFiles()); + public AMLMonitoringJob build() { + return new AMLMonitoringJob(buildJson(), buildFiles()); } @Override @@ -287,7 +388,50 @@ public boolean isKnown() { } /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. + * Which profile family the job toggles. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class TargetType extends StringEnum { + /** The value {@code "user"}. */ + public static final TargetType USER = new TargetType("user"); + + /** The value {@code "business"}. */ + public static final TargetType BUSINESS = new TargetType("business"); + + private static final List KNOWN_VALUES = List.of(USER, BUSINESS); + + private TargetType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static TargetType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, TargetType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. */ public static final class Outcomes extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); @@ -336,7 +480,7 @@ public Optional enabled() { } /** - * A new AML screening was created (billed) and monitoring enabled. + * A new AML screening was created (billed) and monitoring enabled on it. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -346,7 +490,7 @@ public Optional screenedAndEnabled() { } /** - * Monitoring was already active (legacy duplicate rows are consolidated). + * Monitoring was already active; nothing changed and nothing was billed. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -356,7 +500,7 @@ public Optional alreadyEnabled() { } /** - * Monitoring disabled on every AML row of the entity. + * Monitoring turned off across every AML screening of the profile. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -366,7 +510,7 @@ public Optional disabled() { } /** - * Skipped: no full name + date of birth + country available to screen. + * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -386,7 +530,7 @@ public Optional skippedBlocked() { } /** - * Skipped: sandbox application. + * Skipped: sandbox application, no screening is created. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -396,7 +540,7 @@ public Optional skippedSandbox() { } /** - * The screening provider call failed. + * The screening could not be completed; retry later. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -406,7 +550,7 @@ public Optional failedProvider() { } /** - * Balance ran out mid-job. + * Balance ran out while the job was running. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -439,7 +583,7 @@ public Builder enabled(long enabled) { } /** - * A new AML screening was created (billed) and monitoring enabled. + * A new AML screening was created (billed) and monitoring enabled on it. * * @param screenedAndEnabled the value * @return this builder @@ -449,7 +593,7 @@ public Builder screenedAndEnabled(long screenedAndEnabled) { } /** - * Monitoring was already active (legacy duplicate rows are consolidated). + * Monitoring was already active; nothing changed and nothing was billed. * * @param alreadyEnabled the value * @return this builder @@ -459,7 +603,7 @@ public Builder alreadyEnabled(long alreadyEnabled) { } /** - * Monitoring disabled on every AML row of the entity. + * Monitoring turned off across every AML screening of the profile. * * @param disabled the value * @return this builder @@ -469,7 +613,7 @@ public Builder disabled(long disabled) { } /** - * Skipped: no full name + date of birth + country available to screen. + * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. * * @param skippedNoIdentityData the value * @return this builder @@ -489,7 +633,7 @@ public Builder skippedBlocked(long skippedBlocked) { } /** - * Skipped: sandbox application. + * Skipped: sandbox application, no screening is created. * * @param skippedSandbox the value * @return this builder @@ -499,7 +643,7 @@ public Builder skippedSandbox(long skippedSandbox) { } /** - * The screening provider call failed. + * The screening could not be completed; retry later. * * @param failedProvider the value * @return this builder @@ -509,7 +653,7 @@ public Builder failedProvider(long failedProvider) { } /** - * Balance ran out mid-job. + * Balance ran out while the job was running. * * @param failedInsufficientBalance the value * @return this builder @@ -534,7 +678,195 @@ protected Builder self() { } } - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ + public static final class Results extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("didit_internal_id", "vendor_data", "outcome"); + + private Results(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Results} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Results fromJson(JsonNode json) { + return new Results(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Didit internal id of the profile. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional diditInternalId() { + return Json.optional(json, "didit_internal_id", Conv.STRING); + } + + /** + * Your own identifier for the profile. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * The outcome field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional outcome() { + return Json.optional(json, "outcome", Conv.enumeration(AMLMonitoringJob.Results.Outcome::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Results}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Results from) { + super(from); + } + + /** + * Didit internal id of the profile. + * + * @param diditInternalId the value + * @return this builder + */ + public Builder diditInternalId(String diditInternalId) { + return set("didit_internal_id", Conv.STRING, diditInternalId); + } + + /** + * Your own identifier for the profile. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * The outcome field. + * + * @param outcome the value + * @return this builder + */ + public Builder outcome(AMLMonitoringJob.Results.Outcome outcome) { + return set("outcome", Conv.enumeration(AMLMonitoringJob.Results.Outcome::of), outcome); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Results} + */ + public Results build() { + return new Results(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Outcome extends StringEnum { + /** The value {@code "enabled"}. */ + public static final Outcome ENABLED = new Outcome("enabled"); + + /** The value {@code "screened_and_enabled"}. */ + public static final Outcome SCREENED_AND_ENABLED = new Outcome("screened_and_enabled"); + + /** The value {@code "already_enabled"}. */ + public static final Outcome ALREADY_ENABLED = new Outcome("already_enabled"); + + /** The value {@code "disabled"}. */ + public static final Outcome DISABLED = new Outcome("disabled"); + + /** The value {@code "skipped_no_identity_data"}. */ + public static final Outcome SKIPPED_NO_IDENTITY_DATA = new Outcome("skipped_no_identity_data"); + + /** The value {@code "skipped_blocked"}. */ + public static final Outcome SKIPPED_BLOCKED = new Outcome("skipped_blocked"); + + /** The value {@code "skipped_sandbox"}. */ + public static final Outcome SKIPPED_SANDBOX = new Outcome("skipped_sandbox"); + + /** The value {@code "failed_provider"}. */ + public static final Outcome FAILED_PROVIDER = new Outcome("failed_provider"); + + /** The value {@code "failed_insufficient_balance"}. */ + public static final Outcome FAILED_INSUFFICIENT_BALANCE = new Outcome("failed_insufficient_balance"); + + private static final List KNOWN_VALUES = List.of(ENABLED, SCREENED_AND_ENABLED, ALREADY_ENABLED, DISABLED, SKIPPED_NO_IDENTITY_DATA, SKIPPED_BLOCKED, SKIPPED_SANDBOX, FAILED_PROVIDER, FAILED_INSUFFICIENT_BALANCE); + + private Outcome(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Outcome of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** Cost preview. Returned directly when estimate_only is true (nothing is written). */ public static final class Estimate extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); @@ -572,7 +904,7 @@ public Builder toBuilder() { } /** - * Entities in the selection. + * Profiles matched by the selection. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -582,7 +914,7 @@ public Optional usersTotal() { } /** - * The already_enabled field. + * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -592,7 +924,7 @@ public Optional alreadyEnabled() { } /** - * Entities whose existing AML screening will be switched to monitored. + * Profiles whose existing AML screening will be switched to monitored (no new screening). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -602,7 +934,7 @@ public Optional withExistingAml() { } /** - * Entities without any AML screening that will be screened first (billed). + * Profiles without any AML screening that will be screened first (billed as a regular AML screening). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -612,7 +944,7 @@ public Optional needingScreening() { } /** - * The skipped_no_identity_data field. + * Profiles that cannot be screened because identity data is missing. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -622,7 +954,7 @@ public Optional skippedNoIdentityData() { } /** - * The skipped_blocked field. + * Blocked users that will be skipped (always 0 for businesses). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -632,7 +964,7 @@ public Optional skippedBlocked() { } /** - * The estimated_screening_cost_usd field. + * One-time cost of the new screenings. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -642,7 +974,7 @@ public Optional estimatedScreeningCostUsd() { } /** - * The estimated_annual_monitoring_cost_usd field. + * Yearly monitoring cost of the profiles that will become monitored. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -652,7 +984,7 @@ public Optional estimatedAnnualMonitoringCostUsd() { } /** - * The balance_sufficient field. + * Whether the organization balance covers the screening plus the first year of monitoring. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -675,7 +1007,7 @@ private Builder(Estimate from) { } /** - * Entities in the selection. + * Profiles matched by the selection. * * @param usersTotal the value * @return this builder @@ -685,7 +1017,7 @@ public Builder usersTotal(long usersTotal) { } /** - * The already_enabled field. + * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). * * @param alreadyEnabled the value * @return this builder @@ -695,7 +1027,7 @@ public Builder alreadyEnabled(long alreadyEnabled) { } /** - * Entities whose existing AML screening will be switched to monitored. + * Profiles whose existing AML screening will be switched to monitored (no new screening). * * @param withExistingAml the value * @return this builder @@ -705,7 +1037,7 @@ public Builder withExistingAml(long withExistingAml) { } /** - * Entities without any AML screening that will be screened first (billed). + * Profiles without any AML screening that will be screened first (billed as a regular AML screening). * * @param needingScreening the value * @return this builder @@ -715,7 +1047,7 @@ public Builder needingScreening(long needingScreening) { } /** - * The skipped_no_identity_data field. + * Profiles that cannot be screened because identity data is missing. * * @param skippedNoIdentityData the value * @return this builder @@ -725,7 +1057,7 @@ public Builder skippedNoIdentityData(long skippedNoIdentityData) { } /** - * The skipped_blocked field. + * Blocked users that will be skipped (always 0 for businesses). * * @param skippedBlocked the value * @return this builder @@ -735,7 +1067,7 @@ public Builder skippedBlocked(long skippedBlocked) { } /** - * The estimated_screening_cost_usd field. + * One-time cost of the new screenings. * * @param estimatedScreeningCostUsd the value * @return this builder @@ -745,7 +1077,7 @@ public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { } /** - * The estimated_annual_monitoring_cost_usd field. + * Yearly monitoring cost of the profiles that will become monitored. * * @param estimatedAnnualMonitoringCostUsd the value * @return this builder @@ -755,7 +1087,7 @@ public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoring } /** - * The balance_sufficient field. + * Whether the organization balance covers the screening plus the first year of monitoring. * * @param balanceSufficient the value * @return this builder diff --git a/src/main/java/me/didit/types/AMLMonitoringToggleResult.java b/src/main/java/me/didit/types/AMLMonitoringToggleResult.java new file mode 100644 index 0000000..93d0ed2 --- /dev/null +++ b/src/main/java/me/didit/types/AMLMonitoringToggleResult.java @@ -0,0 +1,1495 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.JsonUnion; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * + *

One of: {@code CostPreview}, {@code Variant2}. Check with the {@code is...} + * methods and read with the {@code as...} methods; the fields every variant has are readable directly. + */ +public final class AMLMonitoringToggleResult extends JsonUnion { + private AMLMonitoringToggleResult(JsonNode json) { + super(json); + } + + /** + * Reads a {@code AMLMonitoringToggleResult} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static AMLMonitoringToggleResult fromJson(JsonNode json) { + return new AMLMonitoringToggleResult(json.deepCopy()); + } + + /** + * Identifiers of the request that matched no profile in this application, exactly as sent. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List unmatched() { + return Json.required(json, "unmatched", Conv.list(Conv.STRING)); + } + + /** + * Wraps a {@code CostPreview} value. + * + * @param value the value + * @return the union + */ + public static AMLMonitoringToggleResult ofCostPreview(AMLMonitoringToggleResult.CostPreview value) { + return new AMLMonitoringToggleResult(Conv.model(AMLMonitoringToggleResult.CostPreview::fromJson).write(value)); + } + + /** + * Whether this value is a {@code CostPreview}. + * + * Cost preview. Returned directly when estimate_only is true (nothing is written). + * + * @return true when it matches + */ + public boolean isCostPreview() { + return Json.hasFields(json, "users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient", "unmatched"); + } + + /** + * Reads this value as a {@code CostPreview}. + * + * @return the value, or empty when {@link #isCostPreview()} is false + */ + public Optional asCostPreview() { + return isCostPreview() ? Optional.of(Conv.model(AMLMonitoringToggleResult.CostPreview::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code Variant2} value. + * + * @param value the value + * @return the union + */ + public static AMLMonitoringToggleResult ofVariant2(AMLMonitoringToggleResult.Variant2 value) { + return new AMLMonitoringToggleResult(Conv.model(AMLMonitoringToggleResult.Variant2::fromJson).write(value)); + } + + /** + * Whether this value is a {@code Variant2}. + * + * @return true when it matches + */ + public boolean isVariant2() { + return Json.hasFields(json, "job_id", "status", "is_enabled", "target_type", "total", "processed", "outcomes", "results", "error", "created_at", "completed_at", "estimate", "unmatched"); + } + + /** + * Reads this value as a {@code Variant2}. + * + * @return the value, or empty when {@link #isVariant2()} is false + */ + public Optional asVariant2() { + return isVariant2() ? Optional.of(Conv.model(AMLMonitoringToggleResult.Variant2::fromJson).read(json)) : Optional.empty(); + } + + /** Cost preview. Returned directly when estimate_only is true (nothing is written). */ + public static final class CostPreview extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient", "unmatched"); + + private CostPreview(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code CostPreview} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static CostPreview fromJson(JsonNode json) { + return new CostPreview(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Profiles matched by the selection. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long usersTotal() { + return Json.required(json, "users_total", Conv.LONG); + } + + /** + * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long alreadyEnabled() { + return Json.required(json, "already_enabled", Conv.LONG); + } + + /** + * Profiles whose existing AML screening will be switched to monitored (no new screening). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long withExistingAml() { + return Json.required(json, "with_existing_aml", Conv.LONG); + } + + /** + * Profiles without any AML screening that will be screened first (billed as a regular AML screening). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long needingScreening() { + return Json.required(json, "needing_screening", Conv.LONG); + } + + /** + * Profiles that cannot be screened because identity data is missing. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long skippedNoIdentityData() { + return Json.required(json, "skipped_no_identity_data", Conv.LONG); + } + + /** + * Blocked users that will be skipped (always 0 for businesses). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long skippedBlocked() { + return Json.required(json, "skipped_blocked", Conv.LONG); + } + + /** + * One-time cost of the new screenings. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public double estimatedScreeningCostUsd() { + return Json.required(json, "estimated_screening_cost_usd", Conv.DOUBLE); + } + + /** + * Yearly monitoring cost of the profiles that will become monitored. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public double estimatedAnnualMonitoringCostUsd() { + return Json.required(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); + } + + /** + * Whether the organization balance covers the screening plus the first year of monitoring. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean balanceSufficient() { + return Json.required(json, "balance_sufficient", Conv.BOOLEAN); + } + + /** + * Identifiers of the request that matched no profile in this application, exactly as sent. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List unmatched() { + return Json.required(json, "unmatched", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link CostPreview}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(CostPreview from) { + super(from); + } + + /** + * Profiles matched by the selection. + * + * @param usersTotal the value + * @return this builder + */ + public Builder usersTotal(long usersTotal) { + return set("users_total", Conv.LONG, usersTotal); + } + + /** + * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). + * + * @param alreadyEnabled the value + * @return this builder + */ + public Builder alreadyEnabled(long alreadyEnabled) { + return set("already_enabled", Conv.LONG, alreadyEnabled); + } + + /** + * Profiles whose existing AML screening will be switched to monitored (no new screening). + * + * @param withExistingAml the value + * @return this builder + */ + public Builder withExistingAml(long withExistingAml) { + return set("with_existing_aml", Conv.LONG, withExistingAml); + } + + /** + * Profiles without any AML screening that will be screened first (billed as a regular AML screening). + * + * @param needingScreening the value + * @return this builder + */ + public Builder needingScreening(long needingScreening) { + return set("needing_screening", Conv.LONG, needingScreening); + } + + /** + * Profiles that cannot be screened because identity data is missing. + * + * @param skippedNoIdentityData the value + * @return this builder + */ + public Builder skippedNoIdentityData(long skippedNoIdentityData) { + return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); + } + + /** + * Blocked users that will be skipped (always 0 for businesses). + * + * @param skippedBlocked the value + * @return this builder + */ + public Builder skippedBlocked(long skippedBlocked) { + return set("skipped_blocked", Conv.LONG, skippedBlocked); + } + + /** + * One-time cost of the new screenings. + * + * @param estimatedScreeningCostUsd the value + * @return this builder + */ + public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { + return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); + } + + /** + * Yearly monitoring cost of the profiles that will become monitored. + * + * @param estimatedAnnualMonitoringCostUsd the value + * @return this builder + */ + public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { + return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); + } + + /** + * Whether the organization balance covers the screening plus the first year of monitoring. + * + * @param balanceSufficient the value + * @return this builder + */ + public Builder balanceSufficient(boolean balanceSufficient) { + return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); + } + + /** + * Identifiers of the request that matched no profile in this application, exactly as sent. + * + * @param unmatched the value + * @return this builder + */ + public Builder unmatched(List unmatched) { + return set("unmatched", Conv.list(Conv.STRING), unmatched); + } + + /** + * Builds the value. + * + * @return a new immutable {@code CostPreview} + * @throws IllegalStateException when a required field is missing + */ + public CostPreview build() { + require("users_total", false); + require("already_enabled", false); + require("with_existing_aml", false); + require("needing_screening", false); + require("skipped_no_identity_data", false); + require("skipped_blocked", false); + require("estimated_screening_cost_usd", false); + require("estimated_annual_monitoring_cost_usd", false); + require("balance_sufficient", false); + require("unmatched", false); + return new CostPreview(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Variant2 extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "target_type", "total", "processed", "outcomes", "results", "error", "created_at", "completed_at", "estimate", "unmatched"); + + private Variant2(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Variant2} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Variant2 fromJson(JsonNode json) { + return new Variant2(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The job_id field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String jobId() { + return Json.required(json, "job_id", Conv.STRING); + } + + /** + * The status field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public AMLMonitoringToggleResult.Variant2.Status status() { + return Json.required(json, "status", Conv.enumeration(AMLMonitoringToggleResult.Variant2.Status::of)); + } + + /** + * true when the job enables monitoring, false when it disables it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isEnabled() { + return Json.required(json, "is_enabled", Conv.BOOLEAN); + } + + /** + * Which profile family the job toggles. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public AMLMonitoringToggleResult.Variant2.TargetType targetType() { + return Json.required(json, "target_type", Conv.enumeration(AMLMonitoringToggleResult.Variant2.TargetType::of)); + } + + /** + * Profiles the job processes. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long total() { + return Json.required(json, "total", Conv.LONG); + } + + /** + * Profiles processed so far. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long processed() { + return Json.required(json, "processed", Conv.LONG); + } + + /** + * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public AMLMonitoringToggleResult.Variant2.Outcomes outcomes() { + return Json.required(json, "outcomes", Conv.model(AMLMonitoringToggleResult.Variant2.Outcomes::fromJson)); + } + + /** + * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(AMLMonitoringToggleResult.Variant2.Results::fromJson))); + } + + /** + * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional error() { + return Json.optional(json, "error", Conv.STRING); + } + + /** + * The created_at field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + /** + * The completed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional completedAt() { + return Json.optional(json, "completed_at", Conv.DATE_TIME); + } + + /** + * Cost preview. Returned directly when estimate_only is true (nothing is written). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public AMLMonitoringToggleResult.Variant2.Estimate estimate() { + return Json.required(json, "estimate", Conv.model(AMLMonitoringToggleResult.Variant2.Estimate::fromJson)); + } + + /** + * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List unmatched() { + return Json.required(json, "unmatched", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Variant2}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Variant2 from) { + super(from); + } + + /** + * The job_id field. + * + * @param jobId the value + * @return this builder + */ + public Builder jobId(String jobId) { + return set("job_id", Conv.STRING, jobId); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(AMLMonitoringToggleResult.Variant2.Status status) { + return set("status", Conv.enumeration(AMLMonitoringToggleResult.Variant2.Status::of), status); + } + + /** + * true when the job enables monitoring, false when it disables it. + * + * @param isEnabled the value + * @return this builder + */ + public Builder isEnabled(boolean isEnabled) { + return set("is_enabled", Conv.BOOLEAN, isEnabled); + } + + /** + * Which profile family the job toggles. + * + * @param targetType the value + * @return this builder + */ + public Builder targetType(AMLMonitoringToggleResult.Variant2.TargetType targetType) { + return set("target_type", Conv.enumeration(AMLMonitoringToggleResult.Variant2.TargetType::of), targetType); + } + + /** + * Profiles the job processes. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * Profiles processed so far. + * + * @param processed the value + * @return this builder + */ + public Builder processed(long processed) { + return set("processed", Conv.LONG, processed); + } + + /** + * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. + * + * @param outcomes the value + * @return this builder + */ + public Builder outcomes(AMLMonitoringToggleResult.Variant2.Outcomes outcomes) { + return set("outcomes", Conv.model(AMLMonitoringToggleResult.Variant2.Outcomes::fromJson), outcomes); + } + + /** + * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(AMLMonitoringToggleResult.Variant2.Results::fromJson)), results); + } + + /** + * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. + * + * @param error the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder error(@Nullable String error) { + return setNullable("error", Conv.STRING, error); + } + + /** + * The created_at field. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * The completed_at field. + * + * @param completedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder completedAt(@Nullable OffsetDateTime completedAt) { + return setNullable("completed_at", Conv.DATE_TIME, completedAt); + } + + /** + * Cost preview. Returned directly when estimate_only is true (nothing is written). + * + * @param estimate the value + * @return this builder + */ + public Builder estimate(AMLMonitoringToggleResult.Variant2.Estimate estimate) { + return set("estimate", Conv.model(AMLMonitoringToggleResult.Variant2.Estimate::fromJson), estimate); + } + + /** + * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. + * + * @param unmatched the value + * @return this builder + */ + public Builder unmatched(List unmatched) { + return set("unmatched", Conv.list(Conv.STRING), unmatched); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Variant2} + * @throws IllegalStateException when a required field is missing + */ + public Variant2 build() { + require("job_id", false); + require("status", false); + require("is_enabled", false); + require("target_type", false); + require("total", false); + require("processed", false); + require("outcomes", false); + require("results", false); + require("error", true); + require("created_at", false); + require("completed_at", true); + require("estimate", false); + require("unmatched", false); + return new Variant2(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "PENDING"}. */ + public static final Status PENDING = new Status("PENDING"); + + /** The value {@code "PROCESSING"}. */ + public static final Status PROCESSING = new Status("PROCESSING"); + + /** The value {@code "COMPLETED"}. */ + public static final Status COMPLETED = new Status("COMPLETED"); + + /** The value {@code "FAILED"}. */ + public static final Status FAILED = new Status("FAILED"); + + private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Which profile family the job toggles. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class TargetType extends StringEnum { + /** The value {@code "user"}. */ + public static final TargetType USER = new TargetType("user"); + + /** The value {@code "business"}. */ + public static final TargetType BUSINESS = new TargetType("business"); + + private static final List KNOWN_VALUES = List.of(USER, BUSINESS); + + private TargetType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static TargetType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, TargetType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. + */ + public static final class Outcomes extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); + + private Outcomes(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Outcomes fromJson(JsonNode json) { + return new Outcomes(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Monitoring enabled on an existing AML screening. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional enabled() { + return Json.optional(json, "enabled", Conv.LONG); + } + + /** + * A new AML screening was created (billed) and monitoring enabled on it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional screenedAndEnabled() { + return Json.optional(json, "screened_and_enabled", Conv.LONG); + } + + /** + * Monitoring was already active; nothing changed and nothing was billed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional alreadyEnabled() { + return Json.optional(json, "already_enabled", Conv.LONG); + } + + /** + * Monitoring turned off across every AML screening of the profile. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional disabled() { + return Json.optional(json, "disabled", Conv.LONG); + } + + /** + * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional skippedNoIdentityData() { + return Json.optional(json, "skipped_no_identity_data", Conv.LONG); + } + + /** + * Skipped: the user is blocked. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional skippedBlocked() { + return Json.optional(json, "skipped_blocked", Conv.LONG); + } + + /** + * Skipped: sandbox application, no screening is created. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional skippedSandbox() { + return Json.optional(json, "skipped_sandbox", Conv.LONG); + } + + /** + * The screening could not be completed; retry later. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failedProvider() { + return Json.optional(json, "failed_provider", Conv.LONG); + } + + /** + * Balance ran out while the job was running. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failedInsufficientBalance() { + return Json.optional(json, "failed_insufficient_balance", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Outcomes}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Outcomes from) { + super(from); + } + + /** + * Monitoring enabled on an existing AML screening. + * + * @param enabled the value + * @return this builder + */ + public Builder enabled(long enabled) { + return set("enabled", Conv.LONG, enabled); + } + + /** + * A new AML screening was created (billed) and monitoring enabled on it. + * + * @param screenedAndEnabled the value + * @return this builder + */ + public Builder screenedAndEnabled(long screenedAndEnabled) { + return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); + } + + /** + * Monitoring was already active; nothing changed and nothing was billed. + * + * @param alreadyEnabled the value + * @return this builder + */ + public Builder alreadyEnabled(long alreadyEnabled) { + return set("already_enabled", Conv.LONG, alreadyEnabled); + } + + /** + * Monitoring turned off across every AML screening of the profile. + * + * @param disabled the value + * @return this builder + */ + public Builder disabled(long disabled) { + return set("disabled", Conv.LONG, disabled); + } + + /** + * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. + * + * @param skippedNoIdentityData the value + * @return this builder + */ + public Builder skippedNoIdentityData(long skippedNoIdentityData) { + return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); + } + + /** + * Skipped: the user is blocked. + * + * @param skippedBlocked the value + * @return this builder + */ + public Builder skippedBlocked(long skippedBlocked) { + return set("skipped_blocked", Conv.LONG, skippedBlocked); + } + + /** + * Skipped: sandbox application, no screening is created. + * + * @param skippedSandbox the value + * @return this builder + */ + public Builder skippedSandbox(long skippedSandbox) { + return set("skipped_sandbox", Conv.LONG, skippedSandbox); + } + + /** + * The screening could not be completed; retry later. + * + * @param failedProvider the value + * @return this builder + */ + public Builder failedProvider(long failedProvider) { + return set("failed_provider", Conv.LONG, failedProvider); + } + + /** + * Balance ran out while the job was running. + * + * @param failedInsufficientBalance the value + * @return this builder + */ + public Builder failedInsufficientBalance(long failedInsufficientBalance) { + return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Outcomes} + */ + public Outcomes build() { + return new Outcomes(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Results extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("didit_internal_id", "vendor_data", "outcome"); + + private Results(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Results} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Results fromJson(JsonNode json) { + return new Results(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Didit internal id of the profile. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional diditInternalId() { + return Json.optional(json, "didit_internal_id", Conv.STRING); + } + + /** + * Your own identifier for the profile. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * The outcome field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional outcome() { + return Json.optional(json, "outcome", Conv.enumeration(AMLMonitoringToggleResult.Variant2.Results.Outcome::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Results}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Results from) { + super(from); + } + + /** + * Didit internal id of the profile. + * + * @param diditInternalId the value + * @return this builder + */ + public Builder diditInternalId(String diditInternalId) { + return set("didit_internal_id", Conv.STRING, diditInternalId); + } + + /** + * Your own identifier for the profile. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * The outcome field. + * + * @param outcome the value + * @return this builder + */ + public Builder outcome(AMLMonitoringToggleResult.Variant2.Results.Outcome outcome) { + return set("outcome", Conv.enumeration(AMLMonitoringToggleResult.Variant2.Results.Outcome::of), outcome); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Results} + */ + public Results build() { + return new Results(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Outcome extends StringEnum { + /** The value {@code "enabled"}. */ + public static final Outcome ENABLED = new Outcome("enabled"); + + /** The value {@code "screened_and_enabled"}. */ + public static final Outcome SCREENED_AND_ENABLED = new Outcome("screened_and_enabled"); + + /** The value {@code "already_enabled"}. */ + public static final Outcome ALREADY_ENABLED = new Outcome("already_enabled"); + + /** The value {@code "disabled"}. */ + public static final Outcome DISABLED = new Outcome("disabled"); + + /** The value {@code "skipped_no_identity_data"}. */ + public static final Outcome SKIPPED_NO_IDENTITY_DATA = new Outcome("skipped_no_identity_data"); + + /** The value {@code "skipped_blocked"}. */ + public static final Outcome SKIPPED_BLOCKED = new Outcome("skipped_blocked"); + + /** The value {@code "skipped_sandbox"}. */ + public static final Outcome SKIPPED_SANDBOX = new Outcome("skipped_sandbox"); + + /** The value {@code "failed_provider"}. */ + public static final Outcome FAILED_PROVIDER = new Outcome("failed_provider"); + + /** The value {@code "failed_insufficient_balance"}. */ + public static final Outcome FAILED_INSUFFICIENT_BALANCE = new Outcome("failed_insufficient_balance"); + + private static final List KNOWN_VALUES = List.of(ENABLED, SCREENED_AND_ENABLED, ALREADY_ENABLED, DISABLED, SKIPPED_NO_IDENTITY_DATA, SKIPPED_BLOCKED, SKIPPED_SANDBOX, FAILED_PROVIDER, FAILED_INSUFFICIENT_BALANCE); + + private Outcome(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Outcome of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** Cost preview. Returned directly when estimate_only is true (nothing is written). */ + public static final class Estimate extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); + + private Estimate(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Estimate fromJson(JsonNode json) { + return new Estimate(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Profiles matched by the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional usersTotal() { + return Json.optional(json, "users_total", Conv.LONG); + } + + /** + * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional alreadyEnabled() { + return Json.optional(json, "already_enabled", Conv.LONG); + } + + /** + * Profiles whose existing AML screening will be switched to monitored (no new screening). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional withExistingAml() { + return Json.optional(json, "with_existing_aml", Conv.LONG); + } + + /** + * Profiles without any AML screening that will be screened first (billed as a regular AML screening). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional needingScreening() { + return Json.optional(json, "needing_screening", Conv.LONG); + } + + /** + * Profiles that cannot be screened because identity data is missing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional skippedNoIdentityData() { + return Json.optional(json, "skipped_no_identity_data", Conv.LONG); + } + + /** + * Blocked users that will be skipped (always 0 for businesses). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional skippedBlocked() { + return Json.optional(json, "skipped_blocked", Conv.LONG); + } + + /** + * One-time cost of the new screenings. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional estimatedScreeningCostUsd() { + return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); + } + + /** + * Yearly monitoring cost of the profiles that will become monitored. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional estimatedAnnualMonitoringCostUsd() { + return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); + } + + /** + * Whether the organization balance covers the screening plus the first year of monitoring. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional balanceSufficient() { + return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Estimate}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Estimate from) { + super(from); + } + + /** + * Profiles matched by the selection. + * + * @param usersTotal the value + * @return this builder + */ + public Builder usersTotal(long usersTotal) { + return set("users_total", Conv.LONG, usersTotal); + } + + /** + * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). + * + * @param alreadyEnabled the value + * @return this builder + */ + public Builder alreadyEnabled(long alreadyEnabled) { + return set("already_enabled", Conv.LONG, alreadyEnabled); + } + + /** + * Profiles whose existing AML screening will be switched to monitored (no new screening). + * + * @param withExistingAml the value + * @return this builder + */ + public Builder withExistingAml(long withExistingAml) { + return set("with_existing_aml", Conv.LONG, withExistingAml); + } + + /** + * Profiles without any AML screening that will be screened first (billed as a regular AML screening). + * + * @param needingScreening the value + * @return this builder + */ + public Builder needingScreening(long needingScreening) { + return set("needing_screening", Conv.LONG, needingScreening); + } + + /** + * Profiles that cannot be screened because identity data is missing. + * + * @param skippedNoIdentityData the value + * @return this builder + */ + public Builder skippedNoIdentityData(long skippedNoIdentityData) { + return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); + } + + /** + * Blocked users that will be skipped (always 0 for businesses). + * + * @param skippedBlocked the value + * @return this builder + */ + public Builder skippedBlocked(long skippedBlocked) { + return set("skipped_blocked", Conv.LONG, skippedBlocked); + } + + /** + * One-time cost of the new screenings. + * + * @param estimatedScreeningCostUsd the value + * @return this builder + */ + public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { + return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); + } + + /** + * Yearly monitoring cost of the profiles that will become monitored. + * + * @param estimatedAnnualMonitoringCostUsd the value + * @return this builder + */ + public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { + return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); + } + + /** + * Whether the organization balance covers the screening plus the first year of monitoring. + * + * @param balanceSufficient the value + * @return this builder + */ + public Builder balanceSufficient(boolean balanceSufficient) { + return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Estimate} + */ + public Estimate build() { + return new Estimate(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } +} diff --git a/src/main/java/me/didit/types/AMLScreening.java b/src/main/java/me/didit/types/AMLScreening.java new file mode 100644 index 0000000..259bcef --- /dev/null +++ b/src/main/java/me/didit/types/AMLScreening.java @@ -0,0 +1,4896 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class AMLScreening extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "total_hits", "entity_type", "hits", "score", "screened_data", "is_ongoing_monitoring_enabled", "next_ongoing_monitoring_bill_date", "last_ongoing_screening_at", "last_ongoing_screening_datasets_updated_at", "warnings"); + + private AMLScreening(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AMLScreening} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AMLScreening fromJson(JsonNode json) { + return new AMLScreening(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Unique identifier for this AML step in the workflow graph. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + /** + * Feature-level status of this AML screening. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(AMLScreening.Status::of)); + } + + /** + * Total number of matches found + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalHits() { + return Json.optional(json, "total_hits", Conv.LONG); + } + + /** + * Type of entity screened: person or company + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.STRING); + } + + /** + * The hits field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> hits() { + return Json.optional(json, "hits", Conv.list(Conv.model(AMLScreening.Hits::fromJson))); + } + + /** + * Overall AML screening score — the highest risk score across all hits (float, 0-100). null until computed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.DOUBLE); + } + + /** + * The screened_data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional screenedData() { + return Json.optional(json, "screened_data", Conv.model(AMLScreening.ScreenedData::fromJson)); + } + + /** + * Whether AML ongoing monitoring is enabled for this screening. When true, the entity is re-screened periodically and any new hits update the report. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isOngoingMonitoringEnabled() { + return Json.optional(json, "is_ongoing_monitoring_enabled", Conv.BOOLEAN); + } + + /** + * Date (YYYY-MM-DD) when this ongoing-monitoring subscription will next be billed. Computed as the date of the last ongoing-monitoring billing plus 365 days when is_ongoing_monitoring_enabled = true; otherwise null. This is a billing date, not a screening date - use last_ongoing_screening_at for that. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nextOngoingMonitoringBillDate() { + return Json.optional(json, "next_ongoing_monitoring_bill_date", Conv.DATE); + } + + /** + * When ongoing monitoring last completed a re-screening of this record, whether or not the result changed. null when the record has never been re-screened; it is never back-filled from the enablement or billing date. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastOngoingScreeningAt() { + return Json.optional(json, "last_ongoing_screening_at", Conv.DATE_TIME); + } + + /** + * The most recent AML dataset update date the provider reported at the time of that screening. null when the provider reported no dataset dates; never estimated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastOngoingScreeningDatasetsUpdatedAt() { + return Json.optional(json, "last_ongoing_screening_datasets_updated_at", Conv.DATE_TIME); + } + + /** + * The warnings field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(AMLScreening.Warnings::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AMLScreening}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AMLScreening from) { + super(from); + } + + /** + * Unique identifier for this AML step in the workflow graph. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * Feature-level status of this AML screening. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + * @param status the value + * @return this builder + */ + public Builder status(AMLScreening.Status status) { + return set("status", Conv.enumeration(AMLScreening.Status::of), status); + } + + /** + * Total number of matches found + * + * @param totalHits the value + * @return this builder + */ + public Builder totalHits(long totalHits) { + return set("total_hits", Conv.LONG, totalHits); + } + + /** + * Type of entity screened: person or company + * + * @param entityType the value + * @return this builder + */ + public Builder entityType(String entityType) { + return set("entity_type", Conv.STRING, entityType); + } + + /** + * The hits field. + * + * @param hits the value + * @return this builder + */ + public Builder hits(List hits) { + return set("hits", Conv.list(Conv.model(AMLScreening.Hits::fromJson)), hits); + } + + /** + * Overall AML screening score — the highest risk score across all hits (float, 0-100). null until computed. + * + * @param score the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder score(@Nullable Double score) { + return setNullable("score", Conv.DOUBLE, score); + } + + /** + * The screened_data field. + * + * @param screenedData the value + * @return this builder + */ + public Builder screenedData(AMLScreening.ScreenedData screenedData) { + return set("screened_data", Conv.model(AMLScreening.ScreenedData::fromJson), screenedData); + } + + /** + * Whether AML ongoing monitoring is enabled for this screening. When true, the entity is re-screened periodically and any new hits update the report. + * + * @param isOngoingMonitoringEnabled the value + * @return this builder + */ + public Builder isOngoingMonitoringEnabled(boolean isOngoingMonitoringEnabled) { + return set("is_ongoing_monitoring_enabled", Conv.BOOLEAN, isOngoingMonitoringEnabled); + } + + /** + * Date (YYYY-MM-DD) when this ongoing-monitoring subscription will next be billed. Computed as the date of the last ongoing-monitoring billing plus 365 days when is_ongoing_monitoring_enabled = true; otherwise null. This is a billing date, not a screening date - use last_ongoing_screening_at for that. + * + * @param nextOngoingMonitoringBillDate the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nextOngoingMonitoringBillDate(@Nullable LocalDate nextOngoingMonitoringBillDate) { + return setNullable("next_ongoing_monitoring_bill_date", Conv.DATE, nextOngoingMonitoringBillDate); + } + + /** + * When ongoing monitoring last completed a re-screening of this record, whether or not the result changed. null when the record has never been re-screened; it is never back-filled from the enablement or billing date. + * + * @param lastOngoingScreeningAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastOngoingScreeningAt(@Nullable OffsetDateTime lastOngoingScreeningAt) { + return setNullable("last_ongoing_screening_at", Conv.DATE_TIME, lastOngoingScreeningAt); + } + + /** + * The most recent AML dataset update date the provider reported at the time of that screening. null when the provider reported no dataset dates; never estimated. + * + * @param lastOngoingScreeningDatasetsUpdatedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastOngoingScreeningDatasetsUpdatedAt(@Nullable OffsetDateTime lastOngoingScreeningDatasetsUpdatedAt) { + return setNullable("last_ongoing_screening_datasets_updated_at", Conv.DATE_TIME, lastOngoingScreeningDatasetsUpdatedAt); + } + + /** + * The warnings field. + * + * @param warnings the value + * @return this builder + */ + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(AMLScreening.Warnings::fromJson)), warnings); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AMLScreening} + */ + public AMLScreening build() { + return new AMLScreening(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Feature-level status of this AML screening. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Resub Requested"}. */ + public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class Hits extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "url", "match", "score", "target", "caption", "datasets", "features", "rca_name", "last_seen", "risk_view", "first_seen", "properties", "match_score", "risk_score", "review_status", "score_breakdown", "pep_matches", "linked_entities", "warning_matches", "sanction_matches", "adverse_media_details", "adverse_media_matches", "additional_information"); + + private Hits(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Hits} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Hits fromJson(JsonNode json) { + return new Hits(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Unique identifier of the match + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * Source URL for the match + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional url() { + return Json.optional(json, "url", Conv.STRING); + } + + /** + * Whether this is a confirmed match + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional match() { + return Json.optional(json, "match", Conv.BOOLEAN); + } + + /** + * Deprecated weighted match score in the 0-1 range (e.g. 0.73). Use match_score (0-100) instead. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * + * @deprecated Deprecated by the API. + */ + @Deprecated + public Optional score() { + return Json.optional(json, "score", Conv.DOUBLE); + } + + /** + * Whether the matched entity is the direct target of the listing (as opposed to a related party). null when the source does not specify it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional target() { + return Json.optional(json, "target", Conv.BOOLEAN); + } + + /** + * Caption or title of the match + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional caption() { + return Json.optional(json, "caption", Conv.STRING); + } + + /** + * List of dataset categories where the match was found. Indicates whether the entity appeared in PEP lists (with level), sanctions databases, adverse media, warnings/regulatory enforcement, or other compliance datasets. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> datasets() { + return Json.optional(json, "datasets", Conv.list(Conv.enumeration(AMLScreening.Hits.Datasets::of))); + } + + /** + * Additional features of the match + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> features() { + return Json.optional(json, "features", Conv.OBJECT); + } + + /** + * RCA name if applicable + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rcaName() { + return Json.optional(json, "rca_name", Conv.STRING); + } + + /** + * Date when match was last seen + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastSeen() { + return Json.optional(json, "last_seen", Conv.DATE_TIME); + } + + /** + * The risk_view field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional riskView() { + return Json.optional(json, "risk_view", Conv.model(AMLScreening.Hits.RiskView::fromJson)); + } + + /** + * Date when match was first seen + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional firstSeen() { + return Json.optional(json, "first_seen", Conv.DATE_TIME); + } + + /** + * The properties field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional properties() { + return Json.optional(json, "properties", Conv.model(AMLScreening.Hits.Properties::fromJson)); + } + + /** + * Weighted identity confidence score (0-100) calculated from name similarity, date of birth, and country matching. Used to determine if hit is a False Positive or Possible Match. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchScore() { + return Json.optional(json, "match_score", Conv.DOUBLE); + } + + /** + * Entity risk score (0-100). Used to determine final AML status (Approved/In Review/Declined) based on the highest risk_score among non-False-Positive hits. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional riskScore() { + return Json.optional(json, "risk_score", Conv.DOUBLE); + } + + /** + * Review status for this hit. Can be 'Unreviewed', 'Confirmed Match', 'False Positive', or 'Inconclusive'. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reviewStatus() { + return Json.optional(json, "review_status", Conv.enumeration(AMLScreening.Hits.ReviewStatus::of)); + } + + /** + * Detailed breakdown of how the match_score was calculated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scoreBreakdown() { + return Json.optional(json, "score_breakdown", Conv.model(AMLScreening.Hits.ScoreBreakdown::fromJson)); + } + + /** + * The pep_matches field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> pepMatches() { + return Json.optional(json, "pep_matches", Conv.list(Conv.model(AMLScreening.Hits.PepMatches::fromJson))); + } + + /** + * The linked_entities field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> linkedEntities() { + return Json.optional(json, "linked_entities", Conv.list(Conv.model(AMLScreening.Hits.LinkedEntities::fromJson))); + } + + /** + * List of warning and regulatory enforcement matches. Includes entries from categories: Warning, SIP, Warnings and Regulatory Enforcement, Fitness and Probity, SIE, and Insolvency. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> warningMatches() { + return Json.optional(json, "warning_matches", Conv.list(Conv.model(AMLScreening.Hits.WarningMatches::fromJson))); + } + + /** + * List of sanction list matches found for this entity. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> sanctionMatches() { + return Json.optional(json, "sanction_matches", Conv.list(Conv.model(AMLScreening.Hits.SanctionMatches::fromJson))); + } + + /** + * The adverse_media_details field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional adverseMediaDetails() { + return Json.optional(json, "adverse_media_details", Conv.model(AMLScreening.Hits.AdverseMediaDetails::fromJson)); + } + + /** + * The adverse_media_matches field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> adverseMediaMatches() { + return Json.optional(json, "adverse_media_matches", Conv.list(Conv.model(AMLScreening.Hits.AdverseMediaMatches::fromJson))); + } + + /** + * The additional_information field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> additionalInformation() { + return Json.optional(json, "additional_information", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Hits}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Hits from) { + super(from); + } + + /** + * Unique identifier of the match + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * Source URL for the match + * + * @param url the value + * @return this builder + */ + public Builder url(String url) { + return set("url", Conv.STRING, url); + } + + /** + * Whether this is a confirmed match + * + * @param match the value + * @return this builder + */ + public Builder match(boolean match) { + return set("match", Conv.BOOLEAN, match); + } + + /** + * Deprecated weighted match score in the 0-1 range (e.g. 0.73). Use match_score (0-100) instead. + * + * @param score the value, or {@code null} to send JSON null + * @return this builder + * + * @deprecated Deprecated by the API. + */ + @Deprecated + public Builder score(@Nullable Double score) { + return setNullable("score", Conv.DOUBLE, score); + } + + /** + * Whether the matched entity is the direct target of the listing (as opposed to a related party). null when the source does not specify it. + * + * @param target the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder target(@Nullable Boolean target) { + return setNullable("target", Conv.BOOLEAN, target); + } + + /** + * Caption or title of the match + * + * @param caption the value + * @return this builder + */ + public Builder caption(String caption) { + return set("caption", Conv.STRING, caption); + } + + /** + * List of dataset categories where the match was found. Indicates whether the entity appeared in PEP lists (with level), sanctions databases, adverse media, warnings/regulatory enforcement, or other compliance datasets. + * + * @param datasets the value + * @return this builder + */ + public Builder datasets(List datasets) { + return set("datasets", Conv.list(Conv.enumeration(AMLScreening.Hits.Datasets::of)), datasets); + } + + /** + * Additional features of the match + * + * @param features the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder features(@Nullable Map features) { + return setAny("features", features); + } + + /** + * RCA name if applicable + * + * @param rcaName the value + * @return this builder + */ + public Builder rcaName(String rcaName) { + return set("rca_name", Conv.STRING, rcaName); + } + + /** + * Date when match was last seen + * + * @param lastSeen the value + * @return this builder + */ + public Builder lastSeen(OffsetDateTime lastSeen) { + return set("last_seen", Conv.DATE_TIME, lastSeen); + } + + /** + * The risk_view field. + * + * @param riskView the value + * @return this builder + */ + public Builder riskView(AMLScreening.Hits.RiskView riskView) { + return set("risk_view", Conv.model(AMLScreening.Hits.RiskView::fromJson), riskView); + } + + /** + * Date when match was first seen + * + * @param firstSeen the value + * @return this builder + */ + public Builder firstSeen(OffsetDateTime firstSeen) { + return set("first_seen", Conv.DATE_TIME, firstSeen); + } + + /** + * The properties field. + * + * @param properties the value + * @return this builder + */ + public Builder properties(AMLScreening.Hits.Properties properties) { + return set("properties", Conv.model(AMLScreening.Hits.Properties::fromJson), properties); + } + + /** + * Weighted identity confidence score (0-100) calculated from name similarity, date of birth, and country matching. Used to determine if hit is a False Positive or Possible Match. + * + * @param matchScore the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder matchScore(@Nullable Double matchScore) { + return setNullable("match_score", Conv.DOUBLE, matchScore); + } + + /** + * Entity risk score (0-100). Used to determine final AML status (Approved/In Review/Declined) based on the highest risk_score among non-False-Positive hits. + * + * @param riskScore the value + * @return this builder + */ + public Builder riskScore(double riskScore) { + return set("risk_score", Conv.DOUBLE, riskScore); + } + + /** + * Review status for this hit. Can be 'Unreviewed', 'Confirmed Match', 'False Positive', or 'Inconclusive'. + * + * @param reviewStatus the value + * @return this builder + */ + public Builder reviewStatus(AMLScreening.Hits.ReviewStatus reviewStatus) { + return set("review_status", Conv.enumeration(AMLScreening.Hits.ReviewStatus::of), reviewStatus); + } + + /** + * Detailed breakdown of how the match_score was calculated. + * + * @param scoreBreakdown the value + * @return this builder + */ + public Builder scoreBreakdown(AMLScreening.Hits.ScoreBreakdown scoreBreakdown) { + return set("score_breakdown", Conv.model(AMLScreening.Hits.ScoreBreakdown::fromJson), scoreBreakdown); + } + + /** + * The pep_matches field. + * + * @param pepMatches the value + * @return this builder + */ + public Builder pepMatches(List pepMatches) { + return set("pep_matches", Conv.list(Conv.model(AMLScreening.Hits.PepMatches::fromJson)), pepMatches); + } + + /** + * The linked_entities field. + * + * @param linkedEntities the value + * @return this builder + */ + public Builder linkedEntities(List linkedEntities) { + return set("linked_entities", Conv.list(Conv.model(AMLScreening.Hits.LinkedEntities::fromJson)), linkedEntities); + } + + /** + * List of warning and regulatory enforcement matches. Includes entries from categories: Warning, SIP, Warnings and Regulatory Enforcement, Fitness and Probity, SIE, and Insolvency. + * + * @param warningMatches the value + * @return this builder + */ + public Builder warningMatches(List warningMatches) { + return set("warning_matches", Conv.list(Conv.model(AMLScreening.Hits.WarningMatches::fromJson)), warningMatches); + } + + /** + * List of sanction list matches found for this entity. + * + * @param sanctionMatches the value + * @return this builder + */ + public Builder sanctionMatches(List sanctionMatches) { + return set("sanction_matches", Conv.list(Conv.model(AMLScreening.Hits.SanctionMatches::fromJson)), sanctionMatches); + } + + /** + * The adverse_media_details field. + * + * @param adverseMediaDetails the value + * @return this builder + */ + public Builder adverseMediaDetails(AMLScreening.Hits.AdverseMediaDetails adverseMediaDetails) { + return set("adverse_media_details", Conv.model(AMLScreening.Hits.AdverseMediaDetails::fromJson), adverseMediaDetails); + } + + /** + * The adverse_media_matches field. + * + * @param adverseMediaMatches the value + * @return this builder + */ + public Builder adverseMediaMatches(List adverseMediaMatches) { + return set("adverse_media_matches", Conv.list(Conv.model(AMLScreening.Hits.AdverseMediaMatches::fromJson)), adverseMediaMatches); + } + + /** + * The additional_information field. + * + * @param additionalInformation the value + * @return this builder + */ + public Builder additionalInformation(Map additionalInformation) { + return setAny("additional_information", additionalInformation); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Hits} + */ + public Hits build() { + return new Hits(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Datasets extends StringEnum { + /** The value {@code "PEP"}. */ + public static final Datasets PEP = new Datasets("PEP"); + + /** The value {@code "PEP Level 1"}. */ + public static final Datasets PEP_LEVEL_1 = new Datasets("PEP Level 1"); + + /** The value {@code "PEP Level 2"}. */ + public static final Datasets PEP_LEVEL_2 = new Datasets("PEP Level 2"); + + /** The value {@code "PEP Level 3"}. */ + public static final Datasets PEP_LEVEL_3 = new Datasets("PEP Level 3"); + + /** The value {@code "PEP Level 4"}. */ + public static final Datasets PEP_LEVEL_4 = new Datasets("PEP Level 4"); + + /** The value {@code "Sanctions"}. */ + public static final Datasets SANCTIONS = new Datasets("Sanctions"); + + /** The value {@code "Adverse Media"}. */ + public static final Datasets ADVERSE_MEDIA = new Datasets("Adverse Media"); + + /** The value {@code "Warnings and Regulatory Enforcement"}. */ + public static final Datasets WARNINGS_AND_REGULATORY_ENFORCEMENT = new Datasets("Warnings and Regulatory Enforcement"); + + /** The value {@code "Fitness and Probity"}. */ + public static final Datasets FITNESS_AND_PROBITY = new Datasets("Fitness and Probity"); + + /** The value {@code "SIP"}. */ + public static final Datasets SIP = new Datasets("SIP"); + + /** The value {@code "SIE"}. */ + public static final Datasets SIE = new Datasets("SIE"); + + /** The value {@code "Insolvency"}. */ + public static final Datasets INSOLVENCY = new Datasets("Insolvency"); + + private static final List KNOWN_VALUES = List.of(PEP, PEP_LEVEL_1, PEP_LEVEL_2, PEP_LEVEL_3, PEP_LEVEL_4, SANCTIONS, ADVERSE_MEDIA, WARNINGS_AND_REGULATORY_ENFORCEMENT, FITNESS_AND_PROBITY, SIP, SIE, INSOLVENCY); + + private Datasets(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Datasets of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Datasets::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class RiskView extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("crimes", "countries", "categories", "custom_list"); + + private RiskView(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code RiskView} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static RiskView fromJson(JsonNode json) { + return new RiskView(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The crimes field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional crimes() { + return Json.optional(json, "crimes", Conv.model(AMLScreening.Hits.RiskView.Crimes::fromJson)); + } + + /** + * The countries field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countries() { + return Json.optional(json, "countries", Conv.model(AMLScreening.Hits.RiskView.Countries::fromJson)); + } + + /** + * The categories field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional categories() { + return Json.optional(json, "categories", Conv.model(AMLScreening.Hits.RiskView.Categories::fromJson)); + } + + /** + * The custom_list field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> customList() { + return Json.optional(json, "custom_list", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link RiskView}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(RiskView from) { + super(from); + } + + /** + * The crimes field. + * + * @param crimes the value + * @return this builder + */ + public Builder crimes(AMLScreening.Hits.RiskView.Crimes crimes) { + return set("crimes", Conv.model(AMLScreening.Hits.RiskView.Crimes::fromJson), crimes); + } + + /** + * The countries field. + * + * @param countries the value + * @return this builder + */ + public Builder countries(AMLScreening.Hits.RiskView.Countries countries) { + return set("countries", Conv.model(AMLScreening.Hits.RiskView.Countries::fromJson), countries); + } + + /** + * The categories field. + * + * @param categories the value + * @return this builder + */ + public Builder categories(AMLScreening.Hits.RiskView.Categories categories) { + return set("categories", Conv.model(AMLScreening.Hits.RiskView.Categories::fromJson), categories); + } + + /** + * The custom_list field. + * + * @param customList the value + * @return this builder + */ + public Builder customList(Map customList) { + return setAny("custom_list", customList); + } + + /** + * Builds the value. + * + * @return a new immutable {@code RiskView} + */ + public RiskView build() { + return new RiskView(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Crimes extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("score", "weightage", "risk_level", "risk_scores"); + + private Crimes(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Crimes} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Crimes fromJson(JsonNode json) { + return new Crimes(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The score field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.LONG); + } + + /** + * The weightage field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional weightage() { + return Json.optional(json, "weightage", Conv.LONG); + } + + /** + * The risk_level field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional riskLevel() { + return Json.optional(json, "risk_level", Conv.STRING); + } + + /** + * The risk_scores field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> riskScores() { + return Json.optional(json, "risk_scores", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Crimes}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Crimes from) { + super(from); + } + + /** + * The score field. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * The weightage field. + * + * @param weightage the value + * @return this builder + */ + public Builder weightage(long weightage) { + return set("weightage", Conv.LONG, weightage); + } + + /** + * The risk_level field. + * + * @param riskLevel the value + * @return this builder + */ + public Builder riskLevel(String riskLevel) { + return set("risk_level", Conv.STRING, riskLevel); + } + + /** + * The risk_scores field. + * + * @param riskScores the value + * @return this builder + */ + public Builder riskScores(Map riskScores) { + return setAny("risk_scores", riskScores); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Crimes} + */ + public Crimes build() { + return new Crimes(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Countries extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("score", "weightage", "risk_level", "risk_scores"); + + private Countries(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Countries} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Countries fromJson(JsonNode json) { + return new Countries(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The score field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.LONG); + } + + /** + * The weightage field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional weightage() { + return Json.optional(json, "weightage", Conv.LONG); + } + + /** + * The risk_level field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional riskLevel() { + return Json.optional(json, "risk_level", Conv.STRING); + } + + /** + * The risk_scores field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> riskScores() { + return Json.optional(json, "risk_scores", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Countries}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Countries from) { + super(from); + } + + /** + * The score field. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * The weightage field. + * + * @param weightage the value + * @return this builder + */ + public Builder weightage(long weightage) { + return set("weightage", Conv.LONG, weightage); + } + + /** + * The risk_level field. + * + * @param riskLevel the value + * @return this builder + */ + public Builder riskLevel(String riskLevel) { + return set("risk_level", Conv.STRING, riskLevel); + } + + /** + * The risk_scores field. + * + * @param riskScores the value + * @return this builder + */ + public Builder riskScores(Map riskScores) { + return setAny("risk_scores", riskScores); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Countries} + */ + public Countries build() { + return new Countries(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Categories extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("score", "weightage", "risk_level", "risk_scores"); + + private Categories(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Categories} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Categories fromJson(JsonNode json) { + return new Categories(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The score field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.LONG); + } + + /** + * The weightage field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional weightage() { + return Json.optional(json, "weightage", Conv.LONG); + } + + /** + * The risk_level field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional riskLevel() { + return Json.optional(json, "risk_level", Conv.STRING); + } + + /** + * The risk_scores field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> riskScores() { + return Json.optional(json, "risk_scores", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Categories}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Categories from) { + super(from); + } + + /** + * The score field. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * The weightage field. + * + * @param weightage the value + * @return this builder + */ + public Builder weightage(long weightage) { + return set("weightage", Conv.LONG, weightage); + } + + /** + * The risk_level field. + * + * @param riskLevel the value + * @return this builder + */ + public Builder riskLevel(String riskLevel) { + return set("risk_level", Conv.STRING, riskLevel); + } + + /** + * The risk_scores field. + * + * @param riskScores the value + * @return this builder + */ + public Builder riskScores(Map riskScores) { + return setAny("risk_scores", riskScores); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Categories} + */ + public Categories build() { + return new Categories(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } + + public static final class Properties extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "alias", "notes", "title", "gender", "height", "topics", "weight", "address", "country", "website", "eyeColor", "keywords", "lastName", "position", "religion", "birthDate", "education", "ethnicity", "firstName", "hairColor", "weakAlias", "birthPlace", "modifiedAt", "wikidataId", "citizenship", "nationality"); + + private Properties(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Properties} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Properties fromJson(JsonNode json) { + return new Properties(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> name() { + return Json.optional(json, "name", Conv.list(Conv.STRING)); + } + + /** + * The alias field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> alias() { + return Json.optional(json, "alias", Conv.list(Conv.STRING)); + } + + /** + * The notes field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> notes() { + return Json.optional(json, "notes", Conv.list(Conv.STRING)); + } + + /** + * The title field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional title() { + return Json.optional(json, "title", Conv.STRING); + } + + /** + * The gender field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> gender() { + return Json.optional(json, "gender", Conv.list(Conv.STRING)); + } + + /** + * The height field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional height() { + return Json.optional(json, "height", Conv.STRING); + } + + /** + * The topics field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional topics() { + return Json.optional(json, "topics", Conv.STRING); + } + + /** + * The weight field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional weight() { + return Json.optional(json, "weight", Conv.STRING); + } + + /** + * The address field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional address() { + return Json.optional(json, "address", Conv.STRING); + } + + /** + * The country field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + /** + * The website field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional website() { + return Json.optional(json, "website", Conv.STRING); + } + + /** + * The eyeColor field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional eyeColor() { + return Json.optional(json, "eyeColor", Conv.STRING); + } + + /** + * The keywords field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional keywords() { + return Json.optional(json, "keywords", Conv.STRING); + } + + /** + * The lastName field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> lastName() { + return Json.optional(json, "lastName", Conv.list(Conv.STRING)); + } + + /** + * The position field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional position() { + return Json.optional(json, "position", Conv.STRING); + } + + /** + * The religion field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional religion() { + return Json.optional(json, "religion", Conv.STRING); + } + + /** + * The birthDate field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> birthDate() { + return Json.optional(json, "birthDate", Conv.list(Conv.STRING)); + } + + /** + * The education field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> education() { + return Json.optional(json, "education", Conv.list(Conv.STRING)); + } + + /** + * The ethnicity field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ethnicity() { + return Json.optional(json, "ethnicity", Conv.STRING); + } + + /** + * The firstName field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> firstName() { + return Json.optional(json, "firstName", Conv.list(Conv.STRING)); + } + + /** + * The hairColor field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional hairColor() { + return Json.optional(json, "hairColor", Conv.STRING); + } + + /** + * The weakAlias field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional weakAlias() { + return Json.optional(json, "weakAlias", Conv.STRING); + } + + /** + * The birthPlace field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> birthPlace() { + return Json.optional(json, "birthPlace", Conv.list(Conv.STRING)); + } + + /** + * The modifiedAt field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional modifiedAt() { + return Json.optional(json, "modifiedAt", Conv.STRING); + } + + /** + * The wikidataId field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional wikidataId() { + return Json.optional(json, "wikidataId", Conv.STRING); + } + + /** + * The citizenship field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional citizenship() { + return Json.optional(json, "citizenship", Conv.STRING); + } + + /** + * The nationality field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nationality() { + return Json.optional(json, "nationality", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Properties}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Properties from) { + super(from); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(List name) { + return set("name", Conv.list(Conv.STRING), name); + } + + /** + * The alias field. + * + * @param alias the value + * @return this builder + */ + public Builder alias(List alias) { + return set("alias", Conv.list(Conv.STRING), alias); + } + + /** + * The notes field. + * + * @param notes the value + * @return this builder + */ + public Builder notes(List notes) { + return set("notes", Conv.list(Conv.STRING), notes); + } + + /** + * The title field. + * + * @param title the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder title(@Nullable String title) { + return setNullable("title", Conv.STRING, title); + } + + /** + * The gender field. + * + * @param gender the value + * @return this builder + */ + public Builder gender(List gender) { + return set("gender", Conv.list(Conv.STRING), gender); + } + + /** + * The height field. + * + * @param height the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder height(@Nullable String height) { + return setNullable("height", Conv.STRING, height); + } + + /** + * The topics field. + * + * @param topics the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder topics(@Nullable String topics) { + return setNullable("topics", Conv.STRING, topics); + } + + /** + * The weight field. + * + * @param weight the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder weight(@Nullable String weight) { + return setNullable("weight", Conv.STRING, weight); + } + + /** + * The address field. + * + * @param address the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder address(@Nullable String address) { + return setNullable("address", Conv.STRING, address); + } + + /** + * The country field. + * + * @param country the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder country(@Nullable String country) { + return setNullable("country", Conv.STRING, country); + } + + /** + * The website field. + * + * @param website the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder website(@Nullable String website) { + return setNullable("website", Conv.STRING, website); + } + + /** + * The eyeColor field. + * + * @param eyeColor the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder eyeColor(@Nullable String eyeColor) { + return setNullable("eyeColor", Conv.STRING, eyeColor); + } + + /** + * The keywords field. + * + * @param keywords the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder keywords(@Nullable String keywords) { + return setNullable("keywords", Conv.STRING, keywords); + } + + /** + * The lastName field. + * + * @param lastName the value + * @return this builder + */ + public Builder lastName(List lastName) { + return set("lastName", Conv.list(Conv.STRING), lastName); + } + + /** + * The position field. + * + * @param position the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder position(@Nullable String position) { + return setNullable("position", Conv.STRING, position); + } + + /** + * The religion field. + * + * @param religion the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder religion(@Nullable String religion) { + return setNullable("religion", Conv.STRING, religion); + } + + /** + * The birthDate field. + * + * @param birthDate the value + * @return this builder + */ + public Builder birthDate(List birthDate) { + return set("birthDate", Conv.list(Conv.STRING), birthDate); + } + + /** + * The education field. + * + * @param education the value + * @return this builder + */ + public Builder education(List education) { + return set("education", Conv.list(Conv.STRING), education); + } + + /** + * The ethnicity field. + * + * @param ethnicity the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ethnicity(@Nullable String ethnicity) { + return setNullable("ethnicity", Conv.STRING, ethnicity); + } + + /** + * The firstName field. + * + * @param firstName the value + * @return this builder + */ + public Builder firstName(List firstName) { + return set("firstName", Conv.list(Conv.STRING), firstName); + } + + /** + * The hairColor field. + * + * @param hairColor the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder hairColor(@Nullable String hairColor) { + return setNullable("hairColor", Conv.STRING, hairColor); + } + + /** + * The weakAlias field. + * + * @param weakAlias the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder weakAlias(@Nullable String weakAlias) { + return setNullable("weakAlias", Conv.STRING, weakAlias); + } + + /** + * The birthPlace field. + * + * @param birthPlace the value + * @return this builder + */ + public Builder birthPlace(List birthPlace) { + return set("birthPlace", Conv.list(Conv.STRING), birthPlace); + } + + /** + * The modifiedAt field. + * + * @param modifiedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder modifiedAt(@Nullable String modifiedAt) { + return setNullable("modifiedAt", Conv.STRING, modifiedAt); + } + + /** + * The wikidataId field. + * + * @param wikidataId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder wikidataId(@Nullable String wikidataId) { + return setNullable("wikidataId", Conv.STRING, wikidataId); + } + + /** + * The citizenship field. + * + * @param citizenship the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder citizenship(@Nullable String citizenship) { + return setNullable("citizenship", Conv.STRING, citizenship); + } + + /** + * The nationality field. + * + * @param nationality the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nationality(@Nullable String nationality) { + return setNullable("nationality", Conv.STRING, nationality); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Properties} + */ + public Properties build() { + return new Properties(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * Review status for this hit. Can be 'Unreviewed', 'Confirmed Match', 'False Positive', or 'Inconclusive'. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ReviewStatus extends StringEnum { + /** The value {@code "Unreviewed"}. */ + public static final ReviewStatus UNREVIEWED = new ReviewStatus("Unreviewed"); + + /** The value {@code "Confirmed Match"}. */ + public static final ReviewStatus CONFIRMED_MATCH = new ReviewStatus("Confirmed Match"); + + /** The value {@code "False Positive"}. */ + public static final ReviewStatus FALSE_POSITIVE = new ReviewStatus("False Positive"); + + /** The value {@code "Inconclusive"}. */ + public static final ReviewStatus INCONCLUSIVE = new ReviewStatus("Inconclusive"); + + private static final List KNOWN_VALUES = List.of(UNREVIEWED, CONFIRMED_MATCH, FALSE_POSITIVE, INCONCLUSIVE); + + private ReviewStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ReviewStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ReviewStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** Detailed breakdown of how the match_score was calculated. */ + public static final class ScoreBreakdown extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name_score", "name_weight", "name_weight_normalized", "name_contribution", "dob_score", "dob_weight", "dob_weight_normalized", "dob_contribution", "country_score", "country_weight", "country_weight_normalized", "country_contribution", "document_number_match_type", "document_number_effect", "total_score"); + + private ScoreBreakdown(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ScoreBreakdown} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ScoreBreakdown fromJson(JsonNode json) { + return new ScoreBreakdown(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Raw name similarity percentage (0-100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameScore() { + return Json.optional(json, "name_score", Conv.LONG); + } + + /** + * Original weight for name from config (0-100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameWeight() { + return Json.optional(json, "name_weight", Conv.LONG); + } + + /** + * Normalized weight for name after re-weighting for missing data (0-100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameWeightNormalized() { + return Json.optional(json, "name_weight_normalized", Conv.DOUBLE); + } + + /** + * Points contributed by name to the total score. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameContribution() { + return Json.optional(json, "name_contribution", Conv.DOUBLE); + } + + /** + * DOB match score (-100 to 100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dobScore() { + return Json.optional(json, "dob_score", Conv.LONG); + } + + /** + * Original weight for DOB from config (0-100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dobWeight() { + return Json.optional(json, "dob_weight", Conv.LONG); + } + + /** + * Normalized weight for DOB after re-weighting for missing data (0-100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dobWeightNormalized() { + return Json.optional(json, "dob_weight_normalized", Conv.DOUBLE); + } + + /** + * Points contributed by DOB to the total score. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dobContribution() { + return Json.optional(json, "dob_contribution", Conv.DOUBLE); + } + + /** + * Country match score (-100 to 100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countryScore() { + return Json.optional(json, "country_score", Conv.LONG); + } + + /** + * Original weight for country from config (0-100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countryWeight() { + return Json.optional(json, "country_weight", Conv.LONG); + } + + /** + * Normalized weight for country after re-weighting for missing data (0-100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countryWeightNormalized() { + return Json.optional(json, "country_weight_normalized", Conv.DOUBLE); + } + + /** + * Points contributed by country to the total score. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countryContribution() { + return Json.optional(json, "country_contribution", Conv.DOUBLE); + } + + /** + * Result of document number 'Golden Key' evaluation. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentNumberMatchType() { + return Json.optional(json, "document_number_match_type", Conv.enumeration(AMLScreening.Hits.ScoreBreakdown.DocumentNumberMatchType::of)); + } + + /** + * Human-readable description of the document number effect on the score. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentNumberEffect() { + return Json.optional(json, "document_number_effect", Conv.STRING); + } + + /** + * Final weighted match score (0-100). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalScore() { + return Json.optional(json, "total_score", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ScoreBreakdown}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ScoreBreakdown from) { + super(from); + } + + /** + * Raw name similarity percentage (0-100). + * + * @param nameScore the value + * @return this builder + */ + public Builder nameScore(long nameScore) { + return set("name_score", Conv.LONG, nameScore); + } + + /** + * Original weight for name from config (0-100). + * + * @param nameWeight the value + * @return this builder + */ + public Builder nameWeight(long nameWeight) { + return set("name_weight", Conv.LONG, nameWeight); + } + + /** + * Normalized weight for name after re-weighting for missing data (0-100). + * + * @param nameWeightNormalized the value + * @return this builder + */ + public Builder nameWeightNormalized(double nameWeightNormalized) { + return set("name_weight_normalized", Conv.DOUBLE, nameWeightNormalized); + } + + /** + * Points contributed by name to the total score. + * + * @param nameContribution the value + * @return this builder + */ + public Builder nameContribution(double nameContribution) { + return set("name_contribution", Conv.DOUBLE, nameContribution); + } + + /** + * DOB match score (-100 to 100). + * + * @param dobScore the value + * @return this builder + */ + public Builder dobScore(long dobScore) { + return set("dob_score", Conv.LONG, dobScore); + } + + /** + * Original weight for DOB from config (0-100). + * + * @param dobWeight the value + * @return this builder + */ + public Builder dobWeight(long dobWeight) { + return set("dob_weight", Conv.LONG, dobWeight); + } + + /** + * Normalized weight for DOB after re-weighting for missing data (0-100). + * + * @param dobWeightNormalized the value + * @return this builder + */ + public Builder dobWeightNormalized(double dobWeightNormalized) { + return set("dob_weight_normalized", Conv.DOUBLE, dobWeightNormalized); + } + + /** + * Points contributed by DOB to the total score. + * + * @param dobContribution the value + * @return this builder + */ + public Builder dobContribution(double dobContribution) { + return set("dob_contribution", Conv.DOUBLE, dobContribution); + } + + /** + * Country match score (-100 to 100). + * + * @param countryScore the value + * @return this builder + */ + public Builder countryScore(long countryScore) { + return set("country_score", Conv.LONG, countryScore); + } + + /** + * Original weight for country from config (0-100). + * + * @param countryWeight the value + * @return this builder + */ + public Builder countryWeight(long countryWeight) { + return set("country_weight", Conv.LONG, countryWeight); + } + + /** + * Normalized weight for country after re-weighting for missing data (0-100). + * + * @param countryWeightNormalized the value + * @return this builder + */ + public Builder countryWeightNormalized(double countryWeightNormalized) { + return set("country_weight_normalized", Conv.DOUBLE, countryWeightNormalized); + } + + /** + * Points contributed by country to the total score. + * + * @param countryContribution the value + * @return this builder + */ + public Builder countryContribution(double countryContribution) { + return set("country_contribution", Conv.DOUBLE, countryContribution); + } + + /** + * Result of document number 'Golden Key' evaluation. + * + * @param documentNumberMatchType the value + * @return this builder + */ + public Builder documentNumberMatchType(AMLScreening.Hits.ScoreBreakdown.DocumentNumberMatchType documentNumberMatchType) { + return set("document_number_match_type", Conv.enumeration(AMLScreening.Hits.ScoreBreakdown.DocumentNumberMatchType::of), documentNumberMatchType); + } + + /** + * Human-readable description of the document number effect on the score. + * + * @param documentNumberEffect the value + * @return this builder + */ + public Builder documentNumberEffect(String documentNumberEffect) { + return set("document_number_effect", Conv.STRING, documentNumberEffect); + } + + /** + * Final weighted match score (0-100). + * + * @param totalScore the value + * @return this builder + */ + public Builder totalScore(long totalScore) { + return set("total_score", Conv.LONG, totalScore); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ScoreBreakdown} + */ + public ScoreBreakdown build() { + return new ScoreBreakdown(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Result of document number 'Golden Key' evaluation. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DocumentNumberMatchType extends StringEnum { + /** The value {@code "MATCH"}. */ + public static final DocumentNumberMatchType MATCH = new DocumentNumberMatchType("MATCH"); + + /** The value {@code "NEUTRAL"}. */ + public static final DocumentNumberMatchType NEUTRAL = new DocumentNumberMatchType("NEUTRAL"); + + /** The value {@code "HARD_MISMATCH"}. */ + public static final DocumentNumberMatchType HARD_MISMATCH = new DocumentNumberMatchType("HARD_MISMATCH"); + + private static final List KNOWN_VALUES = List.of(MATCH, NEUTRAL, HARD_MISMATCH); + + private DocumentNumberMatchType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DocumentNumberMatchType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DocumentNumberMatchType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + public static final class PepMatches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("aliases", "education", "list_name", "publisher", "source_url", "description", "matched_name", "pep_position", "date_of_birth", "other_sources", "place_of_birth"); + + private PepMatches(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code PepMatches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PepMatches fromJson(JsonNode json) { + return new PepMatches(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The aliases field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> aliases() { + return Json.optional(json, "aliases", Conv.list(Conv.STRING)); + } + + /** + * The education field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> education() { + return Json.optional(json, "education", Conv.list(Conv.STRING)); + } + + /** + * The list_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional listName() { + return Json.optional(json, "list_name", Conv.STRING); + } + + /** + * The publisher field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional publisher() { + return Json.optional(json, "publisher", Conv.STRING); + } + + /** + * The source_url field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceUrl() { + return Json.optional(json, "source_url", Conv.STRING); + } + + /** + * The description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } + + /** + * The matched_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchedName() { + return Json.optional(json, "matched_name", Conv.STRING); + } + + /** + * The pep_position field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional pepPosition() { + return Json.optional(json, "pep_position", Conv.STRING); + } + + /** + * The date_of_birth field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateOfBirth() { + return Json.optional(json, "date_of_birth", Conv.STRING); + } + + /** + * The other_sources field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> otherSources() { + return Json.optional(json, "other_sources", Conv.list(Conv.STRING)); + } + + /** + * The place_of_birth field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional placeOfBirth() { + return Json.optional(json, "place_of_birth", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link PepMatches}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(PepMatches from) { + super(from); + } + + /** + * The aliases field. + * + * @param aliases the value + * @return this builder + */ + public Builder aliases(List aliases) { + return set("aliases", Conv.list(Conv.STRING), aliases); + } + + /** + * The education field. + * + * @param education the value + * @return this builder + */ + public Builder education(List education) { + return set("education", Conv.list(Conv.STRING), education); + } + + /** + * The list_name field. + * + * @param listName the value + * @return this builder + */ + public Builder listName(String listName) { + return set("list_name", Conv.STRING, listName); + } + + /** + * The publisher field. + * + * @param publisher the value + * @return this builder + */ + public Builder publisher(String publisher) { + return set("publisher", Conv.STRING, publisher); + } + + /** + * The source_url field. + * + * @param sourceUrl the value + * @return this builder + */ + public Builder sourceUrl(String sourceUrl) { + return set("source_url", Conv.STRING, sourceUrl); + } + + /** + * The description field. + * + * @param description the value + * @return this builder + */ + public Builder description(String description) { + return set("description", Conv.STRING, description); + } + + /** + * The matched_name field. + * + * @param matchedName the value + * @return this builder + */ + public Builder matchedName(String matchedName) { + return set("matched_name", Conv.STRING, matchedName); + } + + /** + * The pep_position field. + * + * @param pepPosition the value + * @return this builder + */ + public Builder pepPosition(String pepPosition) { + return set("pep_position", Conv.STRING, pepPosition); + } + + /** + * The date_of_birth field. + * + * @param dateOfBirth the value + * @return this builder + */ + public Builder dateOfBirth(String dateOfBirth) { + return set("date_of_birth", Conv.STRING, dateOfBirth); + } + + /** + * The other_sources field. + * + * @param otherSources the value + * @return this builder + */ + public Builder otherSources(List otherSources) { + return set("other_sources", Conv.list(Conv.STRING), otherSources); + } + + /** + * The place_of_birth field. + * + * @param placeOfBirth the value + * @return this builder + */ + public Builder placeOfBirth(String placeOfBirth) { + return set("place_of_birth", Conv.STRING, placeOfBirth); + } + + /** + * Builds the value. + * + * @return a new immutable {@code PepMatches} + */ + public PepMatches build() { + return new PepMatches(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class LinkedEntities extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "active", "status", "details", "relation"); + + private LinkedEntities(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code LinkedEntities} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static LinkedEntities fromJson(JsonNode json) { + return new LinkedEntities(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> name() { + return Json.optional(json, "name", Conv.list(Conv.STRING)); + } + + /** + * The active field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> active() { + return Json.optional(json, "active", Conv.list(Conv.STRING)); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> status() { + return Json.optional(json, "status", Conv.list(Conv.STRING)); + } + + /** + * The details field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> details() { + return Json.optional(json, "details", Conv.list(Conv.STRING)); + } + + /** + * The relation field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> relation() { + return Json.optional(json, "relation", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link LinkedEntities}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(LinkedEntities from) { + super(from); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(List name) { + return set("name", Conv.list(Conv.STRING), name); + } + + /** + * The active field. + * + * @param active the value + * @return this builder + */ + public Builder active(List active) { + return set("active", Conv.list(Conv.STRING), active); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(List status) { + return set("status", Conv.list(Conv.STRING), status); + } + + /** + * The details field. + * + * @param details the value + * @return this builder + */ + public Builder details(List details) { + return set("details", Conv.list(Conv.STRING), details); + } + + /** + * The relation field. + * + * @param relation the value + * @return this builder + */ + public Builder relation(List relation) { + return set("relation", Conv.list(Conv.STRING), relation); + } + + /** + * Builds the value. + * + * @return a new immutable {@code LinkedEntities} + */ + public LinkedEntities build() { + return new LinkedEntities(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class WarningMatches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("list_name", "matched_name", "description", "publisher", "source_url", "other_sources", "countries", "created_at", "updated_at", "additional_data"); + + private WarningMatches(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WarningMatches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WarningMatches fromJson(JsonNode json) { + return new WarningMatches(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Name of the warning list publisher + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional listName() { + return Json.optional(json, "list_name", Conv.STRING); + } + + /** + * Name matched in the warning list + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchedName() { + return Json.optional(json, "matched_name", Conv.STRING); + } + + /** + * Description of the warning entry + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } + + /** + * Publisher or source of the warning + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional publisher() { + return Json.optional(json, "publisher", Conv.STRING); + } + + /** + * URL to the source of the warning + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceUrl() { + return Json.optional(json, "source_url", Conv.STRING); + } + + /** + * Additional source URLs + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> otherSources() { + return Json.optional(json, "other_sources", Conv.list(Conv.STRING)); + } + + /** + * Countries associated with the warning + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> countries() { + return Json.optional(json, "countries", Conv.list(Conv.STRING)); + } + + /** + * Date the warning entry was created + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.STRING); + } + + /** + * Date the warning entry was last updated + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional updatedAt() { + return Json.optional(json, "updated_at", Conv.STRING); + } + + /** + * Additional data from the warning source. May contain a data object with case_details array. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional additionalData() { + return Json.optional(json, "additional_data", Conv.model(AMLScreening.Hits.WarningMatches.AdditionalData::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WarningMatches}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WarningMatches from) { + super(from); + } + + /** + * Name of the warning list publisher + * + * @param listName the value + * @return this builder + */ + public Builder listName(String listName) { + return set("list_name", Conv.STRING, listName); + } + + /** + * Name matched in the warning list + * + * @param matchedName the value + * @return this builder + */ + public Builder matchedName(String matchedName) { + return set("matched_name", Conv.STRING, matchedName); + } + + /** + * Description of the warning entry + * + * @param description the value + * @return this builder + */ + public Builder description(String description) { + return set("description", Conv.STRING, description); + } + + /** + * Publisher or source of the warning + * + * @param publisher the value + * @return this builder + */ + public Builder publisher(String publisher) { + return set("publisher", Conv.STRING, publisher); + } + + /** + * URL to the source of the warning + * + * @param sourceUrl the value + * @return this builder + */ + public Builder sourceUrl(String sourceUrl) { + return set("source_url", Conv.STRING, sourceUrl); + } + + /** + * Additional source URLs + * + * @param otherSources the value + * @return this builder + */ + public Builder otherSources(List otherSources) { + return set("other_sources", Conv.list(Conv.STRING), otherSources); + } + + /** + * Countries associated with the warning + * + * @param countries the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder countries(@Nullable List countries) { + return setNullable("countries", Conv.list(Conv.STRING), countries); + } + + /** + * Date the warning entry was created + * + * @param createdAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder createdAt(@Nullable String createdAt) { + return setNullable("created_at", Conv.STRING, createdAt); + } + + /** + * Date the warning entry was last updated + * + * @param updatedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder updatedAt(@Nullable String updatedAt) { + return setNullable("updated_at", Conv.STRING, updatedAt); + } + + /** + * Additional data from the warning source. May contain a data object with case_details array. + * + * @param additionalData the value + * @return this builder + */ + public Builder additionalData(AMLScreening.Hits.WarningMatches.AdditionalData additionalData) { + return set("additional_data", Conv.model(AMLScreening.Hits.WarningMatches.AdditionalData::fromJson), additionalData); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WarningMatches} + */ + public WarningMatches build() { + return new WarningMatches(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Additional data from the warning source. May contain a data object with case_details array. + */ + public static final class AdditionalData extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("data"); + + private AdditionalData(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AdditionalData} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AdditionalData fromJson(JsonNode json) { + return new AdditionalData(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional data() { + return Json.optional(json, "data", Conv.model(AMLScreening.Hits.WarningMatches.AdditionalData.Data::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AdditionalData}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AdditionalData from) { + super(from); + } + + /** + * The data field. + * + * @param data the value + * @return this builder + */ + public Builder data(AMLScreening.Hits.WarningMatches.AdditionalData.Data data) { + return set("data", Conv.model(AMLScreening.Hits.WarningMatches.AdditionalData.Data::fromJson), data); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AdditionalData} + */ + public AdditionalData build() { + return new AdditionalData(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Data extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("case_details"); + + private Data(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Data} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Data fromJson(JsonNode json) { + return new Data(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Details of warning cases associated with this match + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> caseDetails() { + return Json.optional(json, "case_details", Conv.list(Conv.model(AMLScreening.Hits.WarningMatches.AdditionalData.Data.CaseDetails::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Data}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Data from) { + super(from); + } + + /** + * Details of warning cases associated with this match + * + * @param caseDetails the value + * @return this builder + */ + public Builder caseDetails(List caseDetails) { + return set("case_details", Conv.list(Conv.model(AMLScreening.Hits.WarningMatches.AdditionalData.Data.CaseDetails::fromJson)), caseDetails); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Data} + */ + public Data build() { + return new Data(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class CaseDetails extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("case_type"); + + private CaseDetails(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code CaseDetails} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static CaseDetails fromJson(JsonNode json) { + return new CaseDetails(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Type of the warning case + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional caseType() { + return Json.optional(json, "case_type", Conv.enumeration(AMLScreening.Hits.WarningMatches.AdditionalData.Data.CaseDetails.CaseType::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link CaseDetails}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(CaseDetails from) { + super(from); + } + + /** + * Type of the warning case + * + * @param caseType the value + * @return this builder + */ + public Builder caseType(AMLScreening.Hits.WarningMatches.AdditionalData.Data.CaseDetails.CaseType caseType) { + return set("case_type", Conv.enumeration(AMLScreening.Hits.WarningMatches.AdditionalData.Data.CaseDetails.CaseType::of), caseType); + } + + /** + * Builds the value. + * + * @return a new immutable {@code CaseDetails} + */ + public CaseDetails build() { + return new CaseDetails(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Type of the warning case + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class CaseType extends StringEnum { + /** The value {@code "Accusation"}. */ + public static final CaseType ACCUSATION = new CaseType("Accusation"); + + /** The value {@code "Arrest"}. */ + public static final CaseType ARREST = new CaseType("Arrest"); + + /** The value {@code "Bribery"}. */ + public static final CaseType BRIBERY = new CaseType("Bribery"); + + /** The value {@code "Criminal"}. */ + public static final CaseType CRIMINAL = new CaseType("Criminal"); + + /** The value {@code "Fraud"}. */ + public static final CaseType FRAUD = new CaseType("Fraud"); + + /** The value {@code "Illegal Fundraising"}. */ + public static final CaseType ILLEGAL_FUNDRAISING = new CaseType("Illegal Fundraising"); + + /** The value {@code "Non-conviction Terror"}. */ + public static final CaseType NON_CONVICTION_TERROR = new CaseType("Non-conviction Terror"); + + /** The value {@code "Proclaimed Offender"}. */ + public static final CaseType PROCLAIMED_OFFENDER = new CaseType("Proclaimed Offender"); + + /** The value {@code "Regulatory Enforcement"}. */ + public static final CaseType REGULATORY_ENFORCEMENT = new CaseType("Regulatory Enforcement"); + + /** The value {@code "Terrorism"}. */ + public static final CaseType TERRORISM = new CaseType("Terrorism"); + + /** The value {@code "Terrorism-related"}. */ + public static final CaseType TERRORISM_RELATED = new CaseType("Terrorism-related"); + + /** The value {@code "Terrorism-related listing"}. */ + public static final CaseType TERRORISM_RELATED_LISTING = new CaseType("Terrorism-related listing"); + + private static final List KNOWN_VALUES = List.of(ACCUSATION, ARREST, BRIBERY, CRIMINAL, FRAUD, ILLEGAL_FUNDRAISING, NON_CONVICTION_TERROR, PROCLAIMED_OFFENDER, REGULATORY_ENFORCEMENT, TERRORISM, TERRORISM_RELATED, TERRORISM_RELATED_LISTING); + + private CaseType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static CaseType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, CaseType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + } + } + } + + public static final class SanctionMatches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("list_name", "matched_name", "description", "reason", "source_url", "other_sources", "details", "legal_basis", "listed_on", "sanction_list", "sanction_program", "sanctioning_authority", "updated_on", "additional_data"); + + private SanctionMatches(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SanctionMatches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SanctionMatches fromJson(JsonNode json) { + return new SanctionMatches(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Name(s) of the sanction list(s) + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> listName() { + return Json.optional(json, "list_name", Conv.list(Conv.STRING)); + } + + /** + * Name matched in the sanction list + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchedName() { + return Json.optional(json, "matched_name", Conv.STRING); + } + + /** + * Description of the sanction entry + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } + + /** + * Free-text reason why the entity was sanctioned. Joined from the sanction list's reason entries. Examples: 'Executive Order 13224 (Terrorism)', 'Support for actions undermining Ukraine's sovereignty', 'Listed due to national security concerns.' + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * URL to the source of the sanction + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceUrl() { + return Json.optional(json, "source_url", Conv.STRING); + } + + /** + * Additional source URLs + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> otherSources() { + return Json.optional(json, "other_sources", Conv.list(Conv.STRING)); + } + + /** + * Additional details about the sanction + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> details() { + return Json.optional(json, "details", Conv.list(Conv.STRING)); + } + + /** + * Legal basis for the sanction + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> legalBasis() { + return Json.optional(json, "legal_basis", Conv.list(Conv.STRING)); + } + + /** + * Date(s) when the entity was listed + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> listedOn() { + return Json.optional(json, "listed_on", Conv.list(Conv.STRING)); + } + + /** + * Sanction list identifier(s) + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> sanctionList() { + return Json.optional(json, "sanction_list", Conv.list(Conv.STRING)); + } + + /** + * Sanction program(s) the entity is listed under + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> sanctionProgram() { + return Json.optional(json, "sanction_program", Conv.list(Conv.STRING)); + } + + /** + * Authority/authorities that imposed the sanction + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> sanctioningAuthority() { + return Json.optional(json, "sanctioning_authority", Conv.list(Conv.STRING)); + } + + /** + * Date(s) when the sanction entry was last updated + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> updatedOn() { + return Json.optional(json, "updated_on", Conv.list(Conv.STRING)); + } + + /** + * Additional data from the sanction source not captured in the main fields + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> additionalData() { + return Json.optional(json, "additional_data", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SanctionMatches}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SanctionMatches from) { + super(from); + } + + /** + * Name(s) of the sanction list(s) + * + * @param listName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder listName(@Nullable List listName) { + return setNullable("list_name", Conv.list(Conv.STRING), listName); + } + + /** + * Name matched in the sanction list + * + * @param matchedName the value + * @return this builder + */ + public Builder matchedName(String matchedName) { + return set("matched_name", Conv.STRING, matchedName); + } + + /** + * Description of the sanction entry + * + * @param description the value + * @return this builder + */ + public Builder description(String description) { + return set("description", Conv.STRING, description); + } + + /** + * Free-text reason why the entity was sanctioned. Joined from the sanction list's reason entries. Examples: 'Executive Order 13224 (Terrorism)', 'Support for actions undermining Ukraine's sovereignty', 'Listed due to national security concerns.' + * + * @param reason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reason(@Nullable String reason) { + return setNullable("reason", Conv.STRING, reason); + } + + /** + * URL to the source of the sanction + * + * @param sourceUrl the value + * @return this builder + */ + public Builder sourceUrl(String sourceUrl) { + return set("source_url", Conv.STRING, sourceUrl); + } + + /** + * Additional source URLs + * + * @param otherSources the value + * @return this builder + */ + public Builder otherSources(List otherSources) { + return set("other_sources", Conv.list(Conv.STRING), otherSources); + } + + /** + * Additional details about the sanction + * + * @param details the value + * @return this builder + */ + public Builder details(List details) { + return set("details", Conv.list(Conv.STRING), details); + } + + /** + * Legal basis for the sanction + * + * @param legalBasis the value + * @return this builder + */ + public Builder legalBasis(List legalBasis) { + return set("legal_basis", Conv.list(Conv.STRING), legalBasis); + } + + /** + * Date(s) when the entity was listed + * + * @param listedOn the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder listedOn(@Nullable List listedOn) { + return setNullable("listed_on", Conv.list(Conv.STRING), listedOn); + } + + /** + * Sanction list identifier(s) + * + * @param sanctionList the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sanctionList(@Nullable List sanctionList) { + return setNullable("sanction_list", Conv.list(Conv.STRING), sanctionList); + } + + /** + * Sanction program(s) the entity is listed under + * + * @param sanctionProgram the value + * @return this builder + */ + public Builder sanctionProgram(List sanctionProgram) { + return set("sanction_program", Conv.list(Conv.STRING), sanctionProgram); + } + + /** + * Authority/authorities that imposed the sanction + * + * @param sanctioningAuthority the value + * @return this builder + */ + public Builder sanctioningAuthority(List sanctioningAuthority) { + return set("sanctioning_authority", Conv.list(Conv.STRING), sanctioningAuthority); + } + + /** + * Date(s) when the sanction entry was last updated + * + * @param updatedOn the value + * @return this builder + */ + public Builder updatedOn(List updatedOn) { + return set("updated_on", Conv.list(Conv.STRING), updatedOn); + } + + /** + * Additional data from the sanction source not captured in the main fields + * + * @param additionalData the value + * @return this builder + */ + public Builder additionalData(Map additionalData) { + return setAny("additional_data", additionalData); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SanctionMatches} + */ + public SanctionMatches build() { + return new SanctionMatches(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class AdverseMediaDetails extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("sentiment", "entity_type", "sentiment_score", "adverse_keywords"); + + private AdverseMediaDetails(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AdverseMediaDetails} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AdverseMediaDetails fromJson(JsonNode json) { + return new AdverseMediaDetails(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The sentiment field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sentiment() { + return Json.optional(json, "sentiment", Conv.STRING); + } + + /** + * The entity_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.STRING); + } + + /** + * The sentiment_score field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sentimentScore() { + return Json.optional(json, "sentiment_score", Conv.LONG); + } + + /** + * The adverse_keywords field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> adverseKeywords() { + return Json.optional(json, "adverse_keywords", Conv.map(Conv.LONG)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AdverseMediaDetails}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AdverseMediaDetails from) { + super(from); + } + + /** + * The sentiment field. + * + * @param sentiment the value + * @return this builder + */ + public Builder sentiment(String sentiment) { + return set("sentiment", Conv.STRING, sentiment); + } + + /** + * The entity_type field. + * + * @param entityType the value + * @return this builder + */ + public Builder entityType(String entityType) { + return set("entity_type", Conv.STRING, entityType); + } + + /** + * The sentiment_score field. + * + * @param sentimentScore the value + * @return this builder + */ + public Builder sentimentScore(long sentimentScore) { + return set("sentiment_score", Conv.LONG, sentimentScore); + } + + /** + * The adverse_keywords field. + * + * @param adverseKeywords the value + * @return this builder + */ + public Builder adverseKeywords(Map adverseKeywords) { + return set("adverse_keywords", Conv.map(Conv.LONG), adverseKeywords); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AdverseMediaDetails} + */ + public AdverseMediaDetails build() { + return new AdverseMediaDetails(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class AdverseMediaMatches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("country", "summary", "headline", "sentiment", "thumbnail", "source_url", "author_name", "other_sources", "adverse_keywords", "sentiment_score", "publication_date"); + + private AdverseMediaMatches(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AdverseMediaMatches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AdverseMediaMatches fromJson(JsonNode json) { + return new AdverseMediaMatches(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The country field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + /** + * The summary field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional summary() { + return Json.optional(json, "summary", Conv.STRING); + } + + /** + * The headline field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional headline() { + return Json.optional(json, "headline", Conv.STRING); + } + + /** + * The sentiment field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sentiment() { + return Json.optional(json, "sentiment", Conv.STRING); + } + + /** + * The thumbnail field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional thumbnail() { + return Json.optional(json, "thumbnail", Conv.STRING); + } + + /** + * The source_url field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceUrl() { + return Json.optional(json, "source_url", Conv.STRING); + } + + /** + * The author_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional authorName() { + return Json.optional(json, "author_name", Conv.STRING); + } + + /** + * The other_sources field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> otherSources() { + return Json.optional(json, "other_sources", Conv.list(Conv.STRING)); + } + + /** + * The adverse_keywords field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> adverseKeywords() { + return Json.optional(json, "adverse_keywords", Conv.list(Conv.STRING)); + } + + /** + * The sentiment_score field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sentimentScore() { + return Json.optional(json, "sentiment_score", Conv.LONG); + } + + /** + * The publication_date field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional publicationDate() { + return Json.optional(json, "publication_date", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AdverseMediaMatches}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AdverseMediaMatches from) { + super(from); + } + + /** + * The country field. + * + * @param country the value + * @return this builder + */ + public Builder country(String country) { + return set("country", Conv.STRING, country); + } + + /** + * The summary field. + * + * @param summary the value + * @return this builder + */ + public Builder summary(String summary) { + return set("summary", Conv.STRING, summary); + } + + /** + * The headline field. + * + * @param headline the value + * @return this builder + */ + public Builder headline(String headline) { + return set("headline", Conv.STRING, headline); + } + + /** + * The sentiment field. + * + * @param sentiment the value + * @return this builder + */ + public Builder sentiment(String sentiment) { + return set("sentiment", Conv.STRING, sentiment); + } + + /** + * The thumbnail field. + * + * @param thumbnail the value + * @return this builder + */ + public Builder thumbnail(String thumbnail) { + return set("thumbnail", Conv.STRING, thumbnail); + } + + /** + * The source_url field. + * + * @param sourceUrl the value + * @return this builder + */ + public Builder sourceUrl(String sourceUrl) { + return set("source_url", Conv.STRING, sourceUrl); + } + + /** + * The author_name field. + * + * @param authorName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder authorName(@Nullable String authorName) { + return setNullable("author_name", Conv.STRING, authorName); + } + + /** + * The other_sources field. + * + * @param otherSources the value + * @return this builder + */ + public Builder otherSources(List otherSources) { + return set("other_sources", Conv.list(Conv.STRING), otherSources); + } + + /** + * The adverse_keywords field. + * + * @param adverseKeywords the value + * @return this builder + */ + public Builder adverseKeywords(List adverseKeywords) { + return set("adverse_keywords", Conv.list(Conv.STRING), adverseKeywords); + } + + /** + * The sentiment_score field. + * + * @param sentimentScore the value + * @return this builder + */ + public Builder sentimentScore(long sentimentScore) { + return set("sentiment_score", Conv.LONG, sentimentScore); + } + + /** + * The publication_date field. + * + * @param publicationDate the value + * @return this builder + */ + public Builder publicationDate(OffsetDateTime publicationDate) { + return set("publication_date", Conv.DATE_TIME, publicationDate); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AdverseMediaMatches} + */ + public AdverseMediaMatches build() { + return new AdverseMediaMatches(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } + + public static final class ScreenedData extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("full_name", "nationality", "date_of_birth", "document_number"); + + private ScreenedData(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ScreenedData} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ScreenedData fromJson(JsonNode json) { + return new ScreenedData(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The full_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fullName() { + return Json.optional(json, "full_name", Conv.STRING); + } + + /** + * The nationality field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nationality() { + return Json.optional(json, "nationality", Conv.STRING); + } + + /** + * The date_of_birth field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateOfBirth() { + return Json.optional(json, "date_of_birth", Conv.DATE); + } + + /** + * The document_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentNumber() { + return Json.optional(json, "document_number", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ScreenedData}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ScreenedData from) { + super(from); + } + + /** + * The full_name field. + * + * @param fullName the value + * @return this builder + */ + public Builder fullName(String fullName) { + return set("full_name", Conv.STRING, fullName); + } + + /** + * The nationality field. + * + * @param nationality the value + * @return this builder + */ + public Builder nationality(String nationality) { + return set("nationality", Conv.STRING, nationality); + } + + /** + * The date_of_birth field. + * + * @param dateOfBirth the value + * @return this builder + */ + public Builder dateOfBirth(LocalDate dateOfBirth) { + return set("date_of_birth", Conv.DATE, dateOfBirth); + } + + /** + * The document_number field. + * + * @param documentNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder documentNumber(@Nullable String documentNumber) { + return setNullable("document_number", Conv.STRING, documentNumber); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ScreenedData} + */ + public ScreenedData build() { + return new ScreenedData(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Warnings extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); + + private Warnings(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Warnings fromJson(JsonNode json) { + return new Warnings(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Within an AML screening this is typically AML, but the enum above is authoritative across all feature warnings on the decision payload. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(AMLScreening.Warnings.Feature::of)); + } + + /** + * The risk field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.STRING); + } + + /** + * The additional_data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> additionalData() { + return Json.optional(json, "additional_data", Conv.OBJECT); + } + + /** + * The log_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional logType() { + return Json.optional(json, "log_type", Conv.STRING); + } + + /** + * The short_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional shortDescription() { + return Json.optional(json, "short_description", Conv.STRING); + } + + /** + * The long_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional longDescription() { + return Json.optional(json, "long_description", Conv.STRING); + } + + /** + * The workflow graph node ID that generated this warning + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Warnings}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Warnings from) { + super(from); + } + + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Within an AML screening this is typically AML, but the enum above is authoritative across all feature warnings on the decision payload. + * + * @param feature the value + * @return this builder + */ + public Builder feature(AMLScreening.Warnings.Feature feature) { + return set("feature", Conv.enumeration(AMLScreening.Warnings.Feature::of), feature); + } + + /** + * The risk field. + * + * @param risk the value + * @return this builder + */ + public Builder risk(String risk) { + return set("risk", Conv.STRING, risk); + } + + /** + * The additional_data field. + * + * @param additionalData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder additionalData(@Nullable Map additionalData) { + return setAny("additional_data", additionalData); + } + + /** + * The log_type field. + * + * @param logType the value + * @return this builder + */ + public Builder logType(String logType) { + return set("log_type", Conv.STRING, logType); + } + + /** + * The short_description field. + * + * @param shortDescription the value + * @return this builder + */ + public Builder shortDescription(String shortDescription) { + return set("short_description", Conv.STRING, shortDescription); + } + + /** + * The long_description field. + * + * @param longDescription the value + * @return this builder + */ + public Builder longDescription(String longDescription) { + return set("long_description", Conv.STRING, longDescription); + } + + /** + * The workflow graph node ID that generated this warning + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Warnings} + */ + public Warnings build() { + return new Warnings(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Within an AML screening this is typically AML, but the enum above is authoritative across all feature warnings on the decision payload. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Feature extends StringEnum { + /** The value {@code "ID_VERIFICATION"}. */ + public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); + + /** The value {@code "NFC"}. */ + public static final Feature NFC = new Feature("NFC"); + + /** The value {@code "LIVENESS"}. */ + public static final Feature LIVENESS = new Feature("LIVENESS"); + + /** The value {@code "FACEMATCH"}. */ + public static final Feature FACEMATCH = new Feature("FACEMATCH"); + + /** The value {@code "PROOF_OF_ADDRESS"}. */ + public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); + + /** The value {@code "PHONE"}. */ + public static final Feature PHONE = new Feature("PHONE"); + + /** The value {@code "EMAIL"}. */ + public static final Feature EMAIL = new Feature("EMAIL"); + + /** The value {@code "AML"}. */ + public static final Feature AML = new Feature("AML"); + + /** The value {@code "LOCATION"}. */ + public static final Feature LOCATION = new Feature("LOCATION"); + + /** The value {@code "DATABASE_VALIDATION"}. */ + public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); + + /** The value {@code "QUESTIONNAIRE"}. */ + public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); + + /** The value {@code "DOCUMENT_AI"}. */ + public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); + + /** The value {@code "KYB_REGISTRY"}. */ + public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); + + /** The value {@code "KYB_DOCUMENTS"}. */ + public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); + + /** The value {@code "KYB_KEY_PEOPLE"}. */ + public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); + + private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); + + private Feature(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Feature of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/AMLScreeningRun.java b/src/main/java/me/didit/types/AMLScreeningRun.java new file mode 100644 index 0000000..ad5b06a --- /dev/null +++ b/src/main/java/me/didit/types/AMLScreeningRun.java @@ -0,0 +1,431 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** One completed ongoing-monitoring re-screen of an AML check. */ +public final class AMLScreeningRun extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "completed_at", "started_at", "outcome", "trigger", "provider", "status", "score", "total_hits", "dataset_freshness"); + + private AMLScreeningRun(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AMLScreeningRun} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AMLScreeningRun fromJson(JsonNode json) { + return new AMLScreeningRun(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of this screening run. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * When this subject's re-screen finished. Results are ordered by it, newest first. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime completedAt() { + return Json.required(json, "completed_at", Conv.DATE_TIME); + } + + /** + * When the monitoring batch this re-screen belongs to started. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime startedAt() { + return Json.required(json, "started_at", Conv.DATE_TIME); + } + + /** + * NO_CHANGE: completed and the result did not move. CHANGED: completed and the score, status, hit count or hits changed. FAILED: no usable answer for this subject, so the check was left as it was. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public AMLScreeningRun.Outcome outcome() { + return Json.required(json, "outcome", Conv.enumeration(AMLScreeningRun.Outcome::of)); + } + + /** + * What started the screening. Always ONGOING_MONITORING today. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public AMLScreeningRun.Trigger trigger() { + return Json.required(json, "trigger", Conv.enumeration(AMLScreeningRun.Trigger::of)); + } + + /** + * Opaque identifier of the AML data source that answered. Treat it as an opaque string; do not branch on it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String provider() { + return Json.required(json, "provider", Conv.STRING); + } + + /** + * AML check status this run left the check in. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(AMLScreeningRun.Status::of)); + } + + /** + * AML score this run left the check in. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.DOUBLE); + } + + /** + * Number of hits this run left the check in. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalHits() { + return Json.optional(json, "total_hits", Conv.LONG); + } + + /** + * Dataset freshness behind this run, or null when no dates were available. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional datasetFreshness() { + return Json.optional(json, "dataset_freshness", Conv.model(AMLDatasetFreshness::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AMLScreeningRun}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AMLScreeningRun from) { + super(from); + } + + /** + * Identifier of this screening run. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * When this subject's re-screen finished. Results are ordered by it, newest first. + * + * @param completedAt the value + * @return this builder + */ + public Builder completedAt(OffsetDateTime completedAt) { + return set("completed_at", Conv.DATE_TIME, completedAt); + } + + /** + * When the monitoring batch this re-screen belongs to started. + * + * @param startedAt the value + * @return this builder + */ + public Builder startedAt(OffsetDateTime startedAt) { + return set("started_at", Conv.DATE_TIME, startedAt); + } + + /** + * NO_CHANGE: completed and the result did not move. CHANGED: completed and the score, status, hit count or hits changed. FAILED: no usable answer for this subject, so the check was left as it was. + * + * @param outcome the value + * @return this builder + */ + public Builder outcome(AMLScreeningRun.Outcome outcome) { + return set("outcome", Conv.enumeration(AMLScreeningRun.Outcome::of), outcome); + } + + /** + * What started the screening. Always ONGOING_MONITORING today. + * + * @param trigger the value + * @return this builder + */ + public Builder trigger(AMLScreeningRun.Trigger trigger) { + return set("trigger", Conv.enumeration(AMLScreeningRun.Trigger::of), trigger); + } + + /** + * Opaque identifier of the AML data source that answered. Treat it as an opaque string; do not branch on it. + * + * @param provider the value + * @return this builder + */ + public Builder provider(String provider) { + return set("provider", Conv.STRING, provider); + } + + /** + * AML check status this run left the check in. + * + * @param status the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder status(AMLScreeningRun.@Nullable Status status) { + return setNullable("status", Conv.enumeration(AMLScreeningRun.Status::of), status); + } + + /** + * AML score this run left the check in. + * + * @param score the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder score(@Nullable Double score) { + return setNullable("score", Conv.DOUBLE, score); + } + + /** + * Number of hits this run left the check in. + * + * @param totalHits the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder totalHits(@Nullable Long totalHits) { + return setNullable("total_hits", Conv.LONG, totalHits); + } + + /** + * Dataset freshness behind this run, or null when no dates were available. + * + * @param datasetFreshness the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder datasetFreshness(@Nullable AMLDatasetFreshness datasetFreshness) { + return setNullable("dataset_freshness", Conv.model(AMLDatasetFreshness::fromJson), datasetFreshness); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AMLScreeningRun} + * @throws IllegalStateException when a required field is missing + */ + public AMLScreeningRun build() { + require("uuid", false); + require("completed_at", false); + require("started_at", false); + require("outcome", false); + require("trigger", false); + require("provider", false); + require("status", true); + require("score", true); + require("total_hits", true); + require("dataset_freshness", true); + return new AMLScreeningRun(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * NO_CHANGE: completed and the result did not move. CHANGED: completed and the score, status, hit count or hits changed. FAILED: no usable answer for this subject, so the check was left as it was. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Outcome extends StringEnum { + /** The value {@code "NO_CHANGE"}. */ + public static final Outcome NO_CHANGE = new Outcome("NO_CHANGE"); + + /** The value {@code "CHANGED"}. */ + public static final Outcome CHANGED = new Outcome("CHANGED"); + + /** The value {@code "FAILED"}. */ + public static final Outcome FAILED = new Outcome("FAILED"); + + private static final List KNOWN_VALUES = List.of(NO_CHANGE, CHANGED, FAILED); + + private Outcome(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Outcome of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * What started the screening. Always ONGOING_MONITORING today. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Trigger extends StringEnum { + /** The value {@code "ONGOING_MONITORING"}. */ + public static final Trigger ONGOING_MONITORING = new Trigger("ONGOING_MONITORING"); + + private static final List KNOWN_VALUES = List.of(ONGOING_MONITORING); + + private Trigger(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Trigger of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Trigger::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * AML check status this run left the check in. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Resub Requested"}. */ + public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/AccountApplicationsListParams.java b/src/main/java/me/didit/types/AccountApplicationsListParams.java new file mode 100644 index 0000000..f529fea --- /dev/null +++ b/src/main/java/me/didit/types/AccountApplicationsListParams.java @@ -0,0 +1,119 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code account.applications.list}. */ +public final class AccountApplicationsListParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("limit", "offset"); + + private AccountApplicationsListParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AccountApplicationsListParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AccountApplicationsListParams fromJson(JsonNode json) { + return new AccountApplicationsListParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Number of applications per page. Defaults to 50. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Number of applications to skip before the page starts. Defaults to 0. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AccountApplicationsListParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AccountApplicationsListParams from) { + super(from); + } + + /** + * Number of applications per page. Defaults to 50. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Number of applications to skip before the page starts. Defaults to 0. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AccountApplicationsListParams} + */ + public AccountApplicationsListParams build() { + return new AccountApplicationsListParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/AccountOrganizationsListParams.java b/src/main/java/me/didit/types/AccountOrganizationsListParams.java new file mode 100644 index 0000000..a3f030f --- /dev/null +++ b/src/main/java/me/didit/types/AccountOrganizationsListParams.java @@ -0,0 +1,119 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code account.organizations.list}. */ +public final class AccountOrganizationsListParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("limit", "offset"); + + private AccountOrganizationsListParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AccountOrganizationsListParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AccountOrganizationsListParams fromJson(JsonNode json) { + return new AccountOrganizationsListParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Page size. Defaults to 50. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Zero-based offset of the first result. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AccountOrganizationsListParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AccountOrganizationsListParams from) { + super(from); + } + + /** + * Page size. Defaults to 50. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Zero-based offset of the first result. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AccountOrganizationsListParams} + */ + public AccountOrganizationsListParams build() { + return new AccountOrganizationsListParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/AccountResendOtpResponse.java b/src/main/java/me/didit/types/AccountResendOtpResponse.java new file mode 100644 index 0000000..ef8a7dc --- /dev/null +++ b/src/main/java/me/didit/types/AccountResendOtpResponse.java @@ -0,0 +1,120 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class AccountResendOtpResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("message", "email"); + + private AccountResendOtpResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AccountResendOtpResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AccountResendOtpResponse fromJson(JsonNode json) { + return new AccountResendOtpResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Always A new verification code has been sent to your email. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String message() { + return Json.required(json, "message", Conv.STRING); + } + + /** + * The address the code was sent to, normalized: lowercased, with any +tag removed from the local part (Dev+test@Example.com becomes dev@example.com). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String email() { + return Json.required(json, "email", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AccountResendOtpResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AccountResendOtpResponse from) { + super(from); + } + + /** + * Always A new verification code has been sent to your email. + * + * @param message the value + * @return this builder + */ + public Builder message(String message) { + return set("message", Conv.STRING, message); + } + + /** + * The address the code was sent to, normalized: lowercased, with any +tag removed from the local part (Dev+test@Example.com becomes dev@example.com). + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AccountResendOtpResponse} + * @throws IllegalStateException when a required field is missing + */ + public AccountResendOtpResponse build() { + require("message", false); + require("email", false); + return new AccountResendOtpResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/AmlRetrieveDatasetFreshnessResponse.java b/src/main/java/me/didit/types/AmlRetrieveDatasetFreshnessResponse.java new file mode 100644 index 0000000..a3b3f0c --- /dev/null +++ b/src/main/java/me/didit/types/AmlRetrieveDatasetFreshnessResponse.java @@ -0,0 +1,169 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class AmlRetrieveDatasetFreshnessResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("dataset_freshness", "availability"); + + private AmlRetrieveDatasetFreshnessResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AmlRetrieveDatasetFreshnessResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AmlRetrieveDatasetFreshnessResponse fromJson(JsonNode json) { + return new AmlRetrieveDatasetFreshnessResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The latest reading, or null when availability is not available. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional datasetFreshness() { + return Json.optional(json, "dataset_freshness", Conv.model(AMLDatasetFreshness::fromJson)); + } + + /** + * available when a reading exists; not_configured when dataset dates are not collected in this environment; not_yet_collected when collection is set up but no reading exists yet. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public AmlRetrieveDatasetFreshnessResponse.Availability availability() { + return Json.required(json, "availability", Conv.enumeration(AmlRetrieveDatasetFreshnessResponse.Availability::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AmlRetrieveDatasetFreshnessResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AmlRetrieveDatasetFreshnessResponse from) { + super(from); + } + + /** + * The latest reading, or null when availability is not available. + * + * @param datasetFreshness the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder datasetFreshness(@Nullable AMLDatasetFreshness datasetFreshness) { + return setNullable("dataset_freshness", Conv.model(AMLDatasetFreshness::fromJson), datasetFreshness); + } + + /** + * available when a reading exists; not_configured when dataset dates are not collected in this environment; not_yet_collected when collection is set up but no reading exists yet. + * + * @param availability the value + * @return this builder + */ + public Builder availability(AmlRetrieveDatasetFreshnessResponse.Availability availability) { + return set("availability", Conv.enumeration(AmlRetrieveDatasetFreshnessResponse.Availability::of), availability); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AmlRetrieveDatasetFreshnessResponse} + * @throws IllegalStateException when a required field is missing + */ + public AmlRetrieveDatasetFreshnessResponse build() { + require("dataset_freshness", true); + require("availability", false); + return new AmlRetrieveDatasetFreshnessResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * available when a reading exists; not_configured when dataset dates are not collected in this environment; not_yet_collected when collection is set up but no reading exists yet. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Availability extends StringEnum { + /** The value {@code "available"}. */ + public static final Availability AVAILABLE = new Availability("available"); + + /** The value {@code "not_configured"}. */ + public static final Availability NOT_CONFIGURED = new Availability("not_configured"); + + /** The value {@code "not_yet_collected"}. */ + public static final Availability NOT_YET_COLLECTED = new Availability("not_yet_collected"); + + private static final List KNOWN_VALUES = List.of(AVAILABLE, NOT_CONFIGURED, NOT_YET_COLLECTED); + + private Availability(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Availability of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Availability::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/AnalyticsRetrieveParams.java b/src/main/java/me/didit/types/AnalyticsRetrieveParams.java new file mode 100644 index 0000000..83ff22c --- /dev/null +++ b/src/main/java/me/didit/types/AnalyticsRetrieveParams.java @@ -0,0 +1,329 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code analytics.retrieve}. */ +public final class AnalyticsRetrieveParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("start_date", "end_date", "scope", "widget_keys", "workflow_ids", "versions", "branch_path_ids", "tag", "transaction_type", "action_type"); + + private AnalyticsRetrieveParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AnalyticsRetrieveParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AnalyticsRetrieveParams fromJson(JsonNode json) { + return new AnalyticsRetrieveParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * First day of the range, YYYY-MM-DD, inclusive (UTC). Send it together with end_date: when either is missing, both are ignored and the range runs from 30 days before today through today. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional startDate() { + return Json.optional(json, "start_date", Conv.DATE); + } + + /** + * Last day of the range, YYYY-MM-DD, inclusive (UTC). A future date counts as today. The range may not exceed 730 days. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional endDate() { + return Json.optional(json, "end_date", Conv.DATE); + } + + /** + * Dashboard tab to return widgets for (case-insensitive). Omit it, together with widget_keys, for the KYC summary. Sending only widget_keys implies kyc. home accepts any KYC, KYB or transaction widget and defaults to the application's saved home layout. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(AnalyticsRetrieveParams.Scope::of)); + } + + /** + * Comma-separated widget keys, answered in this order. Keys the scope does not accept are dropped; when none is left you get the scope's defaults. KYC: kyc_sessions_volume, kyc_warning_insights, kyc_id_locations, kyc_ip_locations, kyc_workflow_funnel, kyc_average_verification_time, kyc_conversion_rate, kyc_conversion_by_country, kyc_resubmission_insights, kyc_original_status_distribution, kyc_database_validation_coverage, kyc_database_validation_latency, kyc_demographics, kyc_device_details. KYB: kyb_sessions_volume, kyb_workflow_funnel, kyb_average_verification_time, kyb_ip_locations, kyb_business_country_distribution, kyb_warning_insights, kyb_conversion_rate, kyb_conversion_by_country, kyb_industry_breakdown, kyb_resubmission_insights, kyb_original_status_distribution, kyb_registry_coverage, kyb_registry_data_completeness, kyb_device_details. Transactions: tx_count_total, tx_count_daily, tx_volume_total_preferred_currency, tx_volume_daily_preferred_currency, tx_average_amount, tx_average_amount_daily, tx_transaction_category_breakdown, tx_action_type_breakdown, tx_status_volume_trend, tx_user_ip_or_country_distribution, tx_tag_breakdown, tx_top_rules_triggered, tx_team_member_performance, tx_score_histogram. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional widgetKeys() { + return Json.optional(json, "widget_keys", Conv.STRING); + } + + /** + * Comma-separated workflow IDs. Limits the KYC summary and the KYC and KYB widgets to those workflows. Values that are not UUIDs are ignored; when none is valid the filter is dropped. The summary also accepts workflow_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowIds() { + return Json.optional(json, "workflow_ids", Conv.STRING); + } + + /** + * Comma-separated workflow version numbers. Applies only together with workflow_ids; values that are not whole numbers are ignored. version is accepted as an alias. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional versions() { + return Json.optional(json, "versions", Conv.STRING); + } + + /** + * Comma-separated branch path IDs. Applies only together with workflow_ids, and only to KYC figures. Figures are currently recorded only for default (all paths combined), so any other value returns empty figures. branch_path_id is accepted as an alias. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional branchPathIds() { + return Json.optional(json, "branch_path_ids", Conv.STRING); + } + + /** + * Transaction widgets only: comma-separated tag UUIDs or tag names. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional tag() { + return Json.optional(json, "tag", Conv.STRING); + } + + /** + * Transaction widgets only: comma-separated transaction categories. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionType() { + return Json.optional(json, "transaction_type", Conv.STRING); + } + + /** + * Transaction widgets only: comma-separated action types (case-insensitive). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actionType() { + return Json.optional(json, "action_type", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AnalyticsRetrieveParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AnalyticsRetrieveParams from) { + super(from); + } + + /** + * First day of the range, YYYY-MM-DD, inclusive (UTC). Send it together with end_date: when either is missing, both are ignored and the range runs from 30 days before today through today. + * + * @param startDate the value + * @return this builder + */ + public Builder startDate(LocalDate startDate) { + return set("start_date", Conv.DATE, startDate); + } + + /** + * Last day of the range, YYYY-MM-DD, inclusive (UTC). A future date counts as today. The range may not exceed 730 days. + * + * @param endDate the value + * @return this builder + */ + public Builder endDate(LocalDate endDate) { + return set("end_date", Conv.DATE, endDate); + } + + /** + * Dashboard tab to return widgets for (case-insensitive). Omit it, together with widget_keys, for the KYC summary. Sending only widget_keys implies kyc. home accepts any KYC, KYB or transaction widget and defaults to the application's saved home layout. + * + * @param scope the value + * @return this builder + */ + public Builder scope(AnalyticsRetrieveParams.Scope scope) { + return set("scope", Conv.enumeration(AnalyticsRetrieveParams.Scope::of), scope); + } + + /** + * Comma-separated widget keys, answered in this order. Keys the scope does not accept are dropped; when none is left you get the scope's defaults. KYC: kyc_sessions_volume, kyc_warning_insights, kyc_id_locations, kyc_ip_locations, kyc_workflow_funnel, kyc_average_verification_time, kyc_conversion_rate, kyc_conversion_by_country, kyc_resubmission_insights, kyc_original_status_distribution, kyc_database_validation_coverage, kyc_database_validation_latency, kyc_demographics, kyc_device_details. KYB: kyb_sessions_volume, kyb_workflow_funnel, kyb_average_verification_time, kyb_ip_locations, kyb_business_country_distribution, kyb_warning_insights, kyb_conversion_rate, kyb_conversion_by_country, kyb_industry_breakdown, kyb_resubmission_insights, kyb_original_status_distribution, kyb_registry_coverage, kyb_registry_data_completeness, kyb_device_details. Transactions: tx_count_total, tx_count_daily, tx_volume_total_preferred_currency, tx_volume_daily_preferred_currency, tx_average_amount, tx_average_amount_daily, tx_transaction_category_breakdown, tx_action_type_breakdown, tx_status_volume_trend, tx_user_ip_or_country_distribution, tx_tag_breakdown, tx_top_rules_triggered, tx_team_member_performance, tx_score_histogram. + * + * @param widgetKeys the value + * @return this builder + */ + public Builder widgetKeys(String widgetKeys) { + return set("widget_keys", Conv.STRING, widgetKeys); + } + + /** + * Comma-separated workflow IDs. Limits the KYC summary and the KYC and KYB widgets to those workflows. Values that are not UUIDs are ignored; when none is valid the filter is dropped. The summary also accepts workflow_id. + * + * @param workflowIds the value + * @return this builder + */ + public Builder workflowIds(String workflowIds) { + return set("workflow_ids", Conv.STRING, workflowIds); + } + + /** + * Comma-separated workflow version numbers. Applies only together with workflow_ids; values that are not whole numbers are ignored. version is accepted as an alias. + * + * @param versions the value + * @return this builder + */ + public Builder versions(String versions) { + return set("versions", Conv.STRING, versions); + } + + /** + * Comma-separated branch path IDs. Applies only together with workflow_ids, and only to KYC figures. Figures are currently recorded only for default (all paths combined), so any other value returns empty figures. branch_path_id is accepted as an alias. + * + * @param branchPathIds the value + * @return this builder + */ + public Builder branchPathIds(String branchPathIds) { + return set("branch_path_ids", Conv.STRING, branchPathIds); + } + + /** + * Transaction widgets only: comma-separated tag UUIDs or tag names. + * + * @param tag the value + * @return this builder + */ + public Builder tag(String tag) { + return set("tag", Conv.STRING, tag); + } + + /** + * Transaction widgets only: comma-separated transaction categories. + * + * @param transactionType the value + * @return this builder + */ + public Builder transactionType(String transactionType) { + return set("transaction_type", Conv.STRING, transactionType); + } + + /** + * Transaction widgets only: comma-separated action types (case-insensitive). + * + * @param actionType the value + * @return this builder + */ + public Builder actionType(String actionType) { + return set("action_type", Conv.STRING, actionType); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AnalyticsRetrieveParams} + */ + public AnalyticsRetrieveParams build() { + return new AnalyticsRetrieveParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "home"}. */ + public static final Scope HOME = new Scope("home"); + + /** The value {@code "kyc"}. */ + public static final Scope KYC = new Scope("kyc"); + + /** The value {@code "kyb"}. */ + public static final Scope KYB = new Scope("kyb"); + + /** The value {@code "transactions"}. */ + public static final Scope TRANSACTIONS = new Scope("transactions"); + + private static final List KNOWN_VALUES = List.of(HOME, KYC, KYB, TRANSACTIONS); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/AnalyticsRetrieveResponse.java b/src/main/java/me/didit/types/AnalyticsRetrieveResponse.java new file mode 100644 index 0000000..2e98455 --- /dev/null +++ b/src/main/java/me/didit/types/AnalyticsRetrieveResponse.java @@ -0,0 +1,89 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.Optional; +import me.didit.core.Conv; +import me.didit.core.Json; +import me.didit.core.JsonUnion; + +/** + * + *

One of: {@code ApplicationAnalyticsSummary}, {@code ApplicationAnalyticsWidgetResponse}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ +public final class AnalyticsRetrieveResponse extends JsonUnion { + private AnalyticsRetrieveResponse(JsonNode json) { + super(json); + } + + /** + * Reads a {@code AnalyticsRetrieveResponse} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static AnalyticsRetrieveResponse fromJson(JsonNode json) { + return new AnalyticsRetrieveResponse(json.deepCopy()); + } + + /** + * Wraps a {@code ApplicationAnalyticsSummary} value. + * + * @param value the value + * @return the union + */ + public static AnalyticsRetrieveResponse ofApplicationAnalyticsSummary(ApplicationAnalyticsSummary value) { + return new AnalyticsRetrieveResponse(Conv.model(ApplicationAnalyticsSummary::fromJson).write(value)); + } + + /** + * Whether this value is a {@code ApplicationAnalyticsSummary}. + * + * User verification (KYC) summary, returned when neither scope nor widget_keys is sent. + * + * @return true when it matches + */ + public boolean isApplicationAnalyticsSummary() { + return Json.hasFields(json, "data_points", "request_breakdown", "previous_data_points", "previous_request_breakdown", "conversion_rate", "location_stats", "demographic_stats", "device_details", "workflow_funnel_stats", "verification_time_stats", "resubmission_stats", "warning_stats", "original_status_stats", "grouping_mode", "demographic_time_series", "device_time_series", "verification_time_time_series", "resubmission_time_series", "original_status_time_series"); + } + + /** + * Reads this value as a {@code ApplicationAnalyticsSummary}. + * + * @return the value, or empty when {@link #isApplicationAnalyticsSummary()} is false + */ + public Optional asApplicationAnalyticsSummary() { + return isApplicationAnalyticsSummary() ? Optional.of(Conv.model(ApplicationAnalyticsSummary::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code ApplicationAnalyticsWidgetResponse} value. + * + * @param value the value + * @return the union + */ + public static AnalyticsRetrieveResponse ofApplicationAnalyticsWidgetResponse(ApplicationAnalyticsWidgetResponse value) { + return new AnalyticsRetrieveResponse(Conv.model(ApplicationAnalyticsWidgetResponse::fromJson).write(value)); + } + + /** + * Whether this value is a {@code ApplicationAnalyticsWidgetResponse}. + * + * Dashboard widgets, returned when scope or widget_keys is sent. + * + * @return true when it matches + */ + public boolean isApplicationAnalyticsWidgetResponse() { + return Json.hasFields(json, "scope", "available_widget_keys", "requested_widget_keys", "widgets"); + } + + /** + * Reads this value as a {@code ApplicationAnalyticsWidgetResponse}. + * + * @return the value, or empty when {@link #isApplicationAnalyticsWidgetResponse()} is false + */ + public Optional asApplicationAnalyticsWidgetResponse() { + return isApplicationAnalyticsWidgetResponse() ? Optional.of(Conv.model(ApplicationAnalyticsWidgetResponse::fromJson).read(json)) : Optional.empty(); + } +} diff --git a/src/main/java/me/didit/types/Application.java b/src/main/java/me/didit/types/Application.java index 8b15be6..b882c55 100644 --- a/src/main/java/me/didit/types/Application.java +++ b/src/main/java/me/didit/types/Application.java @@ -18,7 +18,7 @@ * Full application record. uuid, client_id, and api_key never change after creation. */ public final class Application extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "name", "client_id", "api_key", "website_url", "redirect_uris", "terms_url", "privacy_url", "description", "created_at"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "name", "client_id", "api_key", "website_url", "redirect_uris", "terms_url", "privacy_url", "description", "created_at", "products"); private Application(ObjectNode json, Map> files) { super(json, files); @@ -84,7 +84,7 @@ public Optional clientId() { } /** - * Long-lived secret (also called client_secret). Use as the x-api-key header for every call to https://verification.didit.me/v3/... (sessions, workflows, AML, etc.). Treat as a credential; never expose client-side. + * Long-lived secret (also called client_secret). Use as the x-api-key header for every call to https://verification.didit.me/v3/... (sessions, workflows, AML, etc.). Treat as a credential; never expose client-side. New keys start with sk_live_ (live application) or sk_test_ (sandbox application), followed by 43 URL-safe characters (letters, digits, - and _). Keys created before this format have no prefix and keep working unchanged. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -153,6 +153,16 @@ public Optional createdAt() { return Json.optional(json, "created_at", Conv.DATE_TIME); } + /** + * The application's products. Always all three, in this order: identity, transactions, authentication. A new application has identity enabled with every module on, and transactions and authentication disabled. Read-only: a products key in a create or update body is ignored. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> products() { + return Json.optional(json, "products", Conv.list(Conv.model(ApplicationProduct::fromJson))); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -197,7 +207,7 @@ public Builder clientId(String clientId) { } /** - * Long-lived secret (also called client_secret). Use as the x-api-key header for every call to https://verification.didit.me/v3/... (sessions, workflows, AML, etc.). Treat as a credential; never expose client-side. + * Long-lived secret (also called client_secret). Use as the x-api-key header for every call to https://verification.didit.me/v3/... (sessions, workflows, AML, etc.). Treat as a credential; never expose client-side. New keys start with sk_live_ (live application) or sk_test_ (sandbox application), followed by 43 URL-safe characters (letters, digits, - and _). Keys created before this format have no prefix and keep working unchanged. * * @param apiKey the value * @return this builder @@ -266,6 +276,16 @@ public Builder createdAt(OffsetDateTime createdAt) { return set("created_at", Conv.DATE_TIME, createdAt); } + /** + * The application's products. Always all three, in this order: identity, transactions, authentication. A new application has identity enabled with every module on, and transactions and authentication disabled. Read-only: a products key in a create or update body is ignored. + * + * @param products the value + * @return this builder + */ + public Builder products(List products) { + return set("products", Conv.list(Conv.model(ApplicationProduct::fromJson)), products); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsCountryCount.java b/src/main/java/me/didit/types/ApplicationAnalyticsCountryCount.java new file mode 100644 index 0000000..b27d4cf --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsCountryCount.java @@ -0,0 +1,138 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsCountryCount extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("country", "country_code", "session_count"); + + private ApplicationAnalyticsCountryCount(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsCountryCount} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsCountryCount fromJson(JsonNode json) { + return new ApplicationAnalyticsCountryCount(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Country name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + /** + * Two-letter country code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countryCode() { + return Json.optional(json, "country_code", Conv.STRING); + } + + /** + * Sessions from that country. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionCount() { + return Json.optional(json, "session_count", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsCountryCount}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsCountryCount from) { + super(from); + } + + /** + * Country name. + * + * @param country the value + * @return this builder + */ + public Builder country(String country) { + return set("country", Conv.STRING, country); + } + + /** + * Two-letter country code. + * + * @param countryCode the value + * @return this builder + */ + public Builder countryCode(String countryCode) { + return set("country_code", Conv.STRING, countryCode); + } + + /** + * Sessions from that country. + * + * @param sessionCount the value + * @return this builder + */ + public Builder sessionCount(long sessionCount) { + return set("session_count", Conv.LONG, sessionCount); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsCountryCount} + */ + public ApplicationAnalyticsCountryCount build() { + return new ApplicationAnalyticsCountryCount(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsDataPoint.java b/src/main/java/me/didit/types/ApplicationAnalyticsDataPoint.java new file mode 100644 index 0000000..5305fb3 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsDataPoint.java @@ -0,0 +1,349 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** + * Session counts for one day, by creation date (UTC). Each count reflects the session's current status. Points of the previous period carry at least the required fields. + */ +public final class ApplicationAnalyticsDataPoint extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("rounded_date", "total", "approved", "declined", "canceled", "pending", "in_review", "expired", "abandoned", "in_progress", "not_started", "manually_approved", "manually_declined"); + + private ApplicationAnalyticsDataPoint(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsDataPoint} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsDataPoint fromJson(JsonNode json) { + return new ApplicationAnalyticsDataPoint(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The day, YYYY-MM-DD. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public LocalDate roundedDate() { + return Json.required(json, "rounded_date", Conv.DATE); + } + + /** + * Sessions created that day. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long total() { + return Json.required(json, "total", Conv.LONG); + } + + /** + * Of those, sessions Approved or Kyc Expired. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long approved() { + return Json.required(json, "approved", Conv.LONG); + } + + /** + * Sessions Declined. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long declined() { + return Json.required(json, "declined", Conv.LONG); + } + + /** + * Sessions Expired or Abandoned. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long canceled() { + return Json.required(json, "canceled", Conv.LONG); + } + + /** + * Sessions Not Started, In Progress or In Review. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long pending() { + return Json.required(json, "pending", Conv.LONG); + } + + /** + * Sessions In Review. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional inReview() { + return Json.optional(json, "in_review", Conv.LONG); + } + + /** + * Sessions Expired. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expired() { + return Json.optional(json, "expired", Conv.LONG); + } + + /** + * Sessions Abandoned. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional abandoned() { + return Json.optional(json, "abandoned", Conv.LONG); + } + + /** + * Sessions In Progress. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional inProgress() { + return Json.optional(json, "in_progress", Conv.LONG); + } + + /** + * Sessions Not Started. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional notStarted() { + return Json.optional(json, "not_started", Conv.LONG); + } + + /** + * Sessions a reviewer approved after the workflow gave another verdict. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional manuallyApproved() { + return Json.optional(json, "manually_approved", Conv.LONG); + } + + /** + * Sessions a reviewer declined after the workflow gave another verdict. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional manuallyDeclined() { + return Json.optional(json, "manually_declined", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsDataPoint}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsDataPoint from) { + super(from); + } + + /** + * The day, YYYY-MM-DD. + * + * @param roundedDate the value + * @return this builder + */ + public Builder roundedDate(LocalDate roundedDate) { + return set("rounded_date", Conv.DATE, roundedDate); + } + + /** + * Sessions created that day. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * Of those, sessions Approved or Kyc Expired. + * + * @param approved the value + * @return this builder + */ + public Builder approved(long approved) { + return set("approved", Conv.LONG, approved); + } + + /** + * Sessions Declined. + * + * @param declined the value + * @return this builder + */ + public Builder declined(long declined) { + return set("declined", Conv.LONG, declined); + } + + /** + * Sessions Expired or Abandoned. + * + * @param canceled the value + * @return this builder + */ + public Builder canceled(long canceled) { + return set("canceled", Conv.LONG, canceled); + } + + /** + * Sessions Not Started, In Progress or In Review. + * + * @param pending the value + * @return this builder + */ + public Builder pending(long pending) { + return set("pending", Conv.LONG, pending); + } + + /** + * Sessions In Review. + * + * @param inReview the value + * @return this builder + */ + public Builder inReview(long inReview) { + return set("in_review", Conv.LONG, inReview); + } + + /** + * Sessions Expired. + * + * @param expired the value + * @return this builder + */ + public Builder expired(long expired) { + return set("expired", Conv.LONG, expired); + } + + /** + * Sessions Abandoned. + * + * @param abandoned the value + * @return this builder + */ + public Builder abandoned(long abandoned) { + return set("abandoned", Conv.LONG, abandoned); + } + + /** + * Sessions In Progress. + * + * @param inProgress the value + * @return this builder + */ + public Builder inProgress(long inProgress) { + return set("in_progress", Conv.LONG, inProgress); + } + + /** + * Sessions Not Started. + * + * @param notStarted the value + * @return this builder + */ + public Builder notStarted(long notStarted) { + return set("not_started", Conv.LONG, notStarted); + } + + /** + * Sessions a reviewer approved after the workflow gave another verdict. + * + * @param manuallyApproved the value + * @return this builder + */ + public Builder manuallyApproved(long manuallyApproved) { + return set("manually_approved", Conv.LONG, manuallyApproved); + } + + /** + * Sessions a reviewer declined after the workflow gave another verdict. + * + * @param manuallyDeclined the value + * @return this builder + */ + public Builder manuallyDeclined(long manuallyDeclined) { + return set("manually_declined", Conv.LONG, manuallyDeclined); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsDataPoint} + * @throws IllegalStateException when a required field is missing + */ + public ApplicationAnalyticsDataPoint build() { + require("rounded_date", false); + require("total", false); + require("approved", false); + require("declined", false); + require("canceled", false); + require("pending", false); + return new ApplicationAnalyticsDataPoint(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsDemographicPoint.java b/src/main/java/me/didit/types/ApplicationAnalyticsDemographicPoint.java new file mode 100644 index 0000000..c895ab9 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsDemographicPoint.java @@ -0,0 +1,138 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsDemographicPoint extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("date", "age_distribution", "gender_distribution"); + + private ApplicationAnalyticsDemographicPoint(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsDemographicPoint} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsDemographicPoint fromJson(JsonNode json) { + return new ApplicationAnalyticsDemographicPoint(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional date() { + return Json.optional(json, "date", Conv.STRING); + } + + /** + * The age_distribution field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> ageDistribution() { + return Json.optional(json, "age_distribution", Conv.map(Conv.LONG)); + } + + /** + * The gender_distribution field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> genderDistribution() { + return Json.optional(json, "gender_distribution", Conv.map(Conv.LONG)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsDemographicPoint}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsDemographicPoint from) { + super(from); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @param date the value + * @return this builder + */ + public Builder date(String date) { + return set("date", Conv.STRING, date); + } + + /** + * The age_distribution field. + * + * @param ageDistribution the value + * @return this builder + */ + public Builder ageDistribution(Map ageDistribution) { + return set("age_distribution", Conv.map(Conv.LONG), ageDistribution); + } + + /** + * The gender_distribution field. + * + * @param genderDistribution the value + * @return this builder + */ + public Builder genderDistribution(Map genderDistribution) { + return set("gender_distribution", Conv.map(Conv.LONG), genderDistribution); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsDemographicPoint} + */ + public ApplicationAnalyticsDemographicPoint build() { + return new ApplicationAnalyticsDemographicPoint(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsDemographicStats.java b/src/main/java/me/didit/types/ApplicationAnalyticsDemographicStats.java new file mode 100644 index 0000000..9ece21c --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsDemographicStats.java @@ -0,0 +1,304 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsDemographicStats extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("age_distribution", "gender_distribution"); + + private ApplicationAnalyticsDemographicStats(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsDemographicStats} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsDemographicStats fromJson(JsonNode json) { + return new ApplicationAnalyticsDemographicStats(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Sessions per age band from the ID document. Empty when there is no data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ageDistribution() { + return Json.optional(json, "age_distribution", Conv.model(ApplicationAnalyticsDemographicStats.AgeDistribution::fromJson)); + } + + /** + * Sessions per document gender code: M, F or U (unknown). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> genderDistribution() { + return Json.optional(json, "gender_distribution", Conv.map(Conv.LONG)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsDemographicStats}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsDemographicStats from) { + super(from); + } + + /** + * Sessions per age band from the ID document. Empty when there is no data. + * + * @param ageDistribution the value + * @return this builder + */ + public Builder ageDistribution(ApplicationAnalyticsDemographicStats.AgeDistribution ageDistribution) { + return set("age_distribution", Conv.model(ApplicationAnalyticsDemographicStats.AgeDistribution::fromJson), ageDistribution); + } + + /** + * Sessions per document gender code: M, F or U (unknown). + * + * @param genderDistribution the value + * @return this builder + */ + public Builder genderDistribution(Map genderDistribution) { + return set("gender_distribution", Conv.map(Conv.LONG), genderDistribution); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsDemographicStats} + */ + public ApplicationAnalyticsDemographicStats build() { + return new ApplicationAnalyticsDemographicStats(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** Sessions per age band from the ID document. Empty when there is no data. */ + public static final class AgeDistribution extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("under_18", "18_24", "25_34", "35_44", "45_64", "65_plus"); + + private AgeDistribution(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AgeDistribution} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AgeDistribution fromJson(JsonNode json) { + return new AgeDistribution(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The under_18 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional under18() { + return Json.optional(json, "under_18", Conv.LONG); + } + + /** + * The 18_24 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _1824() { + return Json.optional(json, "18_24", Conv.LONG); + } + + /** + * The 25_34 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _2534() { + return Json.optional(json, "25_34", Conv.LONG); + } + + /** + * The 35_44 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _3544() { + return Json.optional(json, "35_44", Conv.LONG); + } + + /** + * The 45_64 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _4564() { + return Json.optional(json, "45_64", Conv.LONG); + } + + /** + * The 65_plus field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _65Plus() { + return Json.optional(json, "65_plus", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AgeDistribution}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AgeDistribution from) { + super(from); + } + + /** + * The under_18 field. + * + * @param under18 the value + * @return this builder + */ + public Builder under18(long under18) { + return set("under_18", Conv.LONG, under18); + } + + /** + * The 18_24 field. + * + * @param _1824 the value + * @return this builder + */ + public Builder _1824(long _1824) { + return set("18_24", Conv.LONG, _1824); + } + + /** + * The 25_34 field. + * + * @param _2534 the value + * @return this builder + */ + public Builder _2534(long _2534) { + return set("25_34", Conv.LONG, _2534); + } + + /** + * The 35_44 field. + * + * @param _3544 the value + * @return this builder + */ + public Builder _3544(long _3544) { + return set("35_44", Conv.LONG, _3544); + } + + /** + * The 45_64 field. + * + * @param _4564 the value + * @return this builder + */ + public Builder _4564(long _4564) { + return set("45_64", Conv.LONG, _4564); + } + + /** + * The 65_plus field. + * + * @param _65Plus the value + * @return this builder + */ + public Builder _65Plus(long _65Plus) { + return set("65_plus", Conv.LONG, _65Plus); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AgeDistribution} + */ + public AgeDistribution build() { + return new AgeDistribution(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsDeviceDetails.java b/src/main/java/me/didit/types/ApplicationAnalyticsDeviceDetails.java new file mode 100644 index 0000000..0e63e5c --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsDeviceDetails.java @@ -0,0 +1,454 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Top five of each, most sessions first. */ +public final class ApplicationAnalyticsDeviceDetails extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("device_models", "browsers", "operating_systems"); + + private ApplicationAnalyticsDeviceDetails(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsDeviceDetails} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsDeviceDetails fromJson(JsonNode json) { + return new ApplicationAnalyticsDeviceDetails(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Device brands: Apple, Android, LG, everything else grouped as Other. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> deviceModels() { + return Json.optional(json, "device_models", Conv.list(Conv.model(ApplicationAnalyticsDeviceDetails.DeviceModels::fromJson))); + } + + /** + * Browsers: Chrome, Safari, Didit App, Firefox, everything else grouped as Other. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> browsers() { + return Json.optional(json, "browsers", Conv.list(Conv.model(ApplicationAnalyticsDeviceDetails.Browsers::fromJson))); + } + + /** + * Operating systems. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> operatingSystems() { + return Json.optional(json, "operating_systems", Conv.list(Conv.model(ApplicationAnalyticsDeviceDetails.OperatingSystems::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsDeviceDetails}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsDeviceDetails from) { + super(from); + } + + /** + * Device brands: Apple, Android, LG, everything else grouped as Other. + * + * @param deviceModels the value + * @return this builder + */ + public Builder deviceModels(List deviceModels) { + return set("device_models", Conv.list(Conv.model(ApplicationAnalyticsDeviceDetails.DeviceModels::fromJson)), deviceModels); + } + + /** + * Browsers: Chrome, Safari, Didit App, Firefox, everything else grouped as Other. + * + * @param browsers the value + * @return this builder + */ + public Builder browsers(List browsers) { + return set("browsers", Conv.list(Conv.model(ApplicationAnalyticsDeviceDetails.Browsers::fromJson)), browsers); + } + + /** + * Operating systems. + * + * @param operatingSystems the value + * @return this builder + */ + public Builder operatingSystems(List operatingSystems) { + return set("operating_systems", Conv.list(Conv.model(ApplicationAnalyticsDeviceDetails.OperatingSystems::fromJson)), operatingSystems); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsDeviceDetails} + */ + public ApplicationAnalyticsDeviceDetails build() { + return new ApplicationAnalyticsDeviceDetails(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class DeviceModels extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("device", "count"); + + private DeviceModels(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code DeviceModels} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DeviceModels fromJson(JsonNode json) { + return new DeviceModels(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The device field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional device() { + return Json.optional(json, "device", Conv.STRING); + } + + /** + * The count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional count() { + return Json.optional(json, "count", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link DeviceModels}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(DeviceModels from) { + super(from); + } + + /** + * The device field. + * + * @param device the value + * @return this builder + */ + public Builder device(String device) { + return set("device", Conv.STRING, device); + } + + /** + * The count field. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Builds the value. + * + * @return a new immutable {@code DeviceModels} + */ + public DeviceModels build() { + return new DeviceModels(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Browsers extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("browser", "count"); + + private Browsers(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Browsers} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Browsers fromJson(JsonNode json) { + return new Browsers(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The browser field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional browser() { + return Json.optional(json, "browser", Conv.STRING); + } + + /** + * The count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional count() { + return Json.optional(json, "count", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Browsers}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Browsers from) { + super(from); + } + + /** + * The browser field. + * + * @param browser the value + * @return this builder + */ + public Builder browser(String browser) { + return set("browser", Conv.STRING, browser); + } + + /** + * The count field. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Browsers} + */ + public Browsers build() { + return new Browsers(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class OperatingSystems extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("os", "count"); + + private OperatingSystems(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code OperatingSystems} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static OperatingSystems fromJson(JsonNode json) { + return new OperatingSystems(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The os field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional os() { + return Json.optional(json, "os", Conv.STRING); + } + + /** + * The count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional count() { + return Json.optional(json, "count", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link OperatingSystems}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(OperatingSystems from) { + super(from); + } + + /** + * The os field. + * + * @param os the value + * @return this builder + */ + public Builder os(String os) { + return set("os", Conv.STRING, os); + } + + /** + * The count field. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Builds the value. + * + * @return a new immutable {@code OperatingSystems} + */ + public OperatingSystems build() { + return new OperatingSystems(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsDevicePoint.java b/src/main/java/me/didit/types/ApplicationAnalyticsDevicePoint.java new file mode 100644 index 0000000..49d8a60 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsDevicePoint.java @@ -0,0 +1,158 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsDevicePoint extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("date", "devices", "browsers", "operating_systems"); + + private ApplicationAnalyticsDevicePoint(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsDevicePoint} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsDevicePoint fromJson(JsonNode json) { + return new ApplicationAnalyticsDevicePoint(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional date() { + return Json.optional(json, "date", Conv.STRING); + } + + /** + * The devices field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> devices() { + return Json.optional(json, "devices", Conv.map(Conv.LONG)); + } + + /** + * The browsers field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> browsers() { + return Json.optional(json, "browsers", Conv.map(Conv.LONG)); + } + + /** + * The operating_systems field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> operatingSystems() { + return Json.optional(json, "operating_systems", Conv.map(Conv.LONG)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsDevicePoint}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsDevicePoint from) { + super(from); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @param date the value + * @return this builder + */ + public Builder date(String date) { + return set("date", Conv.STRING, date); + } + + /** + * The devices field. + * + * @param devices the value + * @return this builder + */ + public Builder devices(Map devices) { + return set("devices", Conv.map(Conv.LONG), devices); + } + + /** + * The browsers field. + * + * @param browsers the value + * @return this builder + */ + public Builder browsers(Map browsers) { + return set("browsers", Conv.map(Conv.LONG), browsers); + } + + /** + * The operating_systems field. + * + * @param operatingSystems the value + * @return this builder + */ + public Builder operatingSystems(Map operatingSystems) { + return set("operating_systems", Conv.map(Conv.LONG), operatingSystems); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsDevicePoint} + */ + public ApplicationAnalyticsDevicePoint build() { + return new ApplicationAnalyticsDevicePoint(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsLocationStats.java b/src/main/java/me/didit/types/ApplicationAnalyticsLocationStats.java new file mode 100644 index 0000000..e439dc7 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsLocationStats.java @@ -0,0 +1,469 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class ApplicationAnalyticsLocationStats extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("ip_countries", "countries", "id_countries", "conversion_by_country", "cities"); + + private ApplicationAnalyticsLocationStats(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsLocationStats} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsLocationStats fromJson(JsonNode json) { + return new ApplicationAnalyticsLocationStats(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Countries of the users' IP addresses, most sessions first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> ipCountries() { + return Json.optional(json, "ip_countries", Conv.list(Conv.model(ApplicationAnalyticsCountryCount::fromJson))); + } + + /** + * Same list as ip_countries. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> countries() { + return Json.optional(json, "countries", Conv.list(Conv.model(ApplicationAnalyticsCountryCount::fromJson))); + } + + /** + * Issuing countries of the verified ID documents, most sessions first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> idCountries() { + return Json.optional(json, "id_countries", Conv.list(Conv.model(ApplicationAnalyticsCountryCount::fromJson))); + } + + /** + * Per document issuing country: finished sessions (approved, declined or in review) and how many were approved. Most finished first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> conversionByCountry() { + return Json.optional(json, "conversion_by_country", Conv.list(Conv.model(ApplicationAnalyticsLocationStats.ConversionByCountry::fromJson))); + } + + /** + * Up to 100 IP cities, most sessions first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> cities() { + return Json.optional(json, "cities", Conv.list(Conv.model(ApplicationAnalyticsLocationStats.Cities::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsLocationStats}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsLocationStats from) { + super(from); + } + + /** + * Countries of the users' IP addresses, most sessions first. + * + * @param ipCountries the value + * @return this builder + */ + public Builder ipCountries(List ipCountries) { + return set("ip_countries", Conv.list(Conv.model(ApplicationAnalyticsCountryCount::fromJson)), ipCountries); + } + + /** + * Same list as ip_countries. + * + * @param countries the value + * @return this builder + */ + public Builder countries(List countries) { + return set("countries", Conv.list(Conv.model(ApplicationAnalyticsCountryCount::fromJson)), countries); + } + + /** + * Issuing countries of the verified ID documents, most sessions first. + * + * @param idCountries the value + * @return this builder + */ + public Builder idCountries(List idCountries) { + return set("id_countries", Conv.list(Conv.model(ApplicationAnalyticsCountryCount::fromJson)), idCountries); + } + + /** + * Per document issuing country: finished sessions (approved, declined or in review) and how many were approved. Most finished first. + * + * @param conversionByCountry the value + * @return this builder + */ + public Builder conversionByCountry(List conversionByCountry) { + return set("conversion_by_country", Conv.list(Conv.model(ApplicationAnalyticsLocationStats.ConversionByCountry::fromJson)), conversionByCountry); + } + + /** + * Up to 100 IP cities, most sessions first. + * + * @param cities the value + * @return this builder + */ + public Builder cities(List cities) { + return set("cities", Conv.list(Conv.model(ApplicationAnalyticsLocationStats.Cities::fromJson)), cities); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsLocationStats} + */ + public ApplicationAnalyticsLocationStats build() { + return new ApplicationAnalyticsLocationStats(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class ConversionByCountry extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("country", "country_code", "finished", "approved"); + + private ConversionByCountry(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ConversionByCountry} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ConversionByCountry fromJson(JsonNode json) { + return new ConversionByCountry(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The country field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + /** + * The country_code field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countryCode() { + return Json.optional(json, "country_code", Conv.STRING); + } + + /** + * The finished field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional finished() { + return Json.optional(json, "finished", Conv.LONG); + } + + /** + * The approved field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional approved() { + return Json.optional(json, "approved", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ConversionByCountry}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ConversionByCountry from) { + super(from); + } + + /** + * The country field. + * + * @param country the value + * @return this builder + */ + public Builder country(String country) { + return set("country", Conv.STRING, country); + } + + /** + * The country_code field. + * + * @param countryCode the value + * @return this builder + */ + public Builder countryCode(String countryCode) { + return set("country_code", Conv.STRING, countryCode); + } + + /** + * The finished field. + * + * @param finished the value + * @return this builder + */ + public Builder finished(long finished) { + return set("finished", Conv.LONG, finished); + } + + /** + * The approved field. + * + * @param approved the value + * @return this builder + */ + public Builder approved(long approved) { + return set("approved", Conv.LONG, approved); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ConversionByCountry} + */ + public ConversionByCountry build() { + return new ConversionByCountry(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Cities extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("location", "session_count", "latitude", "longitude"); + + private Cities(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Cities} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Cities fromJson(JsonNode json) { + return new Cities(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * City, Country. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional location() { + return Json.optional(json, "location", Conv.STRING); + } + + /** + * The session_count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionCount() { + return Json.optional(json, "session_count", Conv.LONG); + } + + /** + * The latitude field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional latitude() { + return Json.optional(json, "latitude", Conv.DOUBLE); + } + + /** + * The longitude field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional longitude() { + return Json.optional(json, "longitude", Conv.DOUBLE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Cities}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Cities from) { + super(from); + } + + /** + * City, Country. + * + * @param location the value + * @return this builder + */ + public Builder location(String location) { + return set("location", Conv.STRING, location); + } + + /** + * The session_count field. + * + * @param sessionCount the value + * @return this builder + */ + public Builder sessionCount(long sessionCount) { + return set("session_count", Conv.LONG, sessionCount); + } + + /** + * The latitude field. + * + * @param latitude the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder latitude(@Nullable Double latitude) { + return setNullable("latitude", Conv.DOUBLE, latitude); + } + + /** + * The longitude field. + * + * @param longitude the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder longitude(@Nullable Double longitude) { + return setNullable("longitude", Conv.DOUBLE, longitude); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Cities} + */ + public Cities build() { + return new Cities(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsOriginalStatusPoint.java b/src/main/java/me/didit/types/ApplicationAnalyticsOriginalStatusPoint.java new file mode 100644 index 0000000..cda092a --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsOriginalStatusPoint.java @@ -0,0 +1,158 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsOriginalStatusPoint extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("date", "approved", "in_review", "declined"); + + private ApplicationAnalyticsOriginalStatusPoint(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsOriginalStatusPoint} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsOriginalStatusPoint fromJson(JsonNode json) { + return new ApplicationAnalyticsOriginalStatusPoint(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional date() { + return Json.optional(json, "date", Conv.STRING); + } + + /** + * The approved field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional approved() { + return Json.optional(json, "approved", Conv.LONG); + } + + /** + * The in_review field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional inReview() { + return Json.optional(json, "in_review", Conv.LONG); + } + + /** + * The declined field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional declined() { + return Json.optional(json, "declined", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsOriginalStatusPoint}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsOriginalStatusPoint from) { + super(from); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @param date the value + * @return this builder + */ + public Builder date(String date) { + return set("date", Conv.STRING, date); + } + + /** + * The approved field. + * + * @param approved the value + * @return this builder + */ + public Builder approved(long approved) { + return set("approved", Conv.LONG, approved); + } + + /** + * The in_review field. + * + * @param inReview the value + * @return this builder + */ + public Builder inReview(long inReview) { + return set("in_review", Conv.LONG, inReview); + } + + /** + * The declined field. + * + * @param declined the value + * @return this builder + */ + public Builder declined(long declined) { + return set("declined", Conv.LONG, declined); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsOriginalStatusPoint} + */ + public ApplicationAnalyticsOriginalStatusPoint build() { + return new ApplicationAnalyticsOriginalStatusPoint(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsOriginalStatusStats.java b/src/main/java/me/didit/types/ApplicationAnalyticsOriginalStatusStats.java new file mode 100644 index 0000000..e9b24c5 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsOriginalStatusStats.java @@ -0,0 +1,139 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** The workflow's automatic verdict, before any reviewer changed it. */ +public final class ApplicationAnalyticsOriginalStatusStats extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("approved", "in_review", "declined"); + + private ApplicationAnalyticsOriginalStatusStats(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsOriginalStatusStats} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsOriginalStatusStats fromJson(JsonNode json) { + return new ApplicationAnalyticsOriginalStatusStats(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The approved field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional approved() { + return Json.optional(json, "approved", Conv.LONG); + } + + /** + * The in_review field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional inReview() { + return Json.optional(json, "in_review", Conv.LONG); + } + + /** + * The declined field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional declined() { + return Json.optional(json, "declined", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsOriginalStatusStats}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsOriginalStatusStats from) { + super(from); + } + + /** + * The approved field. + * + * @param approved the value + * @return this builder + */ + public Builder approved(long approved) { + return set("approved", Conv.LONG, approved); + } + + /** + * The in_review field. + * + * @param inReview the value + * @return this builder + */ + public Builder inReview(long inReview) { + return set("in_review", Conv.LONG, inReview); + } + + /** + * The declined field. + * + * @param declined the value + * @return this builder + */ + public Builder declined(long declined) { + return set("declined", Conv.LONG, declined); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsOriginalStatusStats} + */ + public ApplicationAnalyticsOriginalStatusStats build() { + return new ApplicationAnalyticsOriginalStatusStats(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsRequestBreakdown.java b/src/main/java/me/didit/types/ApplicationAnalyticsRequestBreakdown.java new file mode 100644 index 0000000..6800199 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsRequestBreakdown.java @@ -0,0 +1,307 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** + * Status totals for the range. The previous-period breakdown carries at least the required fields. + */ +public final class ApplicationAnalyticsRequestBreakdown extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("total_approved", "total_in_review", "total_declined", "total_canceled", "total_pending", "total_expired", "total_abandoned", "total_in_progress", "total_not_started", "total_manually_approved", "total_manually_declined"); + + private ApplicationAnalyticsRequestBreakdown(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsRequestBreakdown} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsRequestBreakdown fromJson(JsonNode json) { + return new ApplicationAnalyticsRequestBreakdown(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Sessions Approved or Kyc Expired. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long totalApproved() { + return Json.required(json, "total_approved", Conv.LONG); + } + + /** + * Sessions In Review. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long totalInReview() { + return Json.required(json, "total_in_review", Conv.LONG); + } + + /** + * Sessions Declined. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long totalDeclined() { + return Json.required(json, "total_declined", Conv.LONG); + } + + /** + * Sessions Expired or Abandoned. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long totalCanceled() { + return Json.required(json, "total_canceled", Conv.LONG); + } + + /** + * Sessions Not Started, In Progress or In Review (includes total_in_review). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long totalPending() { + return Json.required(json, "total_pending", Conv.LONG); + } + + /** + * Sessions Expired. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalExpired() { + return Json.optional(json, "total_expired", Conv.LONG); + } + + /** + * Sessions Abandoned. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalAbandoned() { + return Json.optional(json, "total_abandoned", Conv.LONG); + } + + /** + * Sessions In Progress. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalInProgress() { + return Json.optional(json, "total_in_progress", Conv.LONG); + } + + /** + * Sessions Not Started. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalNotStarted() { + return Json.optional(json, "total_not_started", Conv.LONG); + } + + /** + * Approved by a reviewer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalManuallyApproved() { + return Json.optional(json, "total_manually_approved", Conv.LONG); + } + + /** + * Declined by a reviewer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalManuallyDeclined() { + return Json.optional(json, "total_manually_declined", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsRequestBreakdown}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsRequestBreakdown from) { + super(from); + } + + /** + * Sessions Approved or Kyc Expired. + * + * @param totalApproved the value + * @return this builder + */ + public Builder totalApproved(long totalApproved) { + return set("total_approved", Conv.LONG, totalApproved); + } + + /** + * Sessions In Review. + * + * @param totalInReview the value + * @return this builder + */ + public Builder totalInReview(long totalInReview) { + return set("total_in_review", Conv.LONG, totalInReview); + } + + /** + * Sessions Declined. + * + * @param totalDeclined the value + * @return this builder + */ + public Builder totalDeclined(long totalDeclined) { + return set("total_declined", Conv.LONG, totalDeclined); + } + + /** + * Sessions Expired or Abandoned. + * + * @param totalCanceled the value + * @return this builder + */ + public Builder totalCanceled(long totalCanceled) { + return set("total_canceled", Conv.LONG, totalCanceled); + } + + /** + * Sessions Not Started, In Progress or In Review (includes total_in_review). + * + * @param totalPending the value + * @return this builder + */ + public Builder totalPending(long totalPending) { + return set("total_pending", Conv.LONG, totalPending); + } + + /** + * Sessions Expired. + * + * @param totalExpired the value + * @return this builder + */ + public Builder totalExpired(long totalExpired) { + return set("total_expired", Conv.LONG, totalExpired); + } + + /** + * Sessions Abandoned. + * + * @param totalAbandoned the value + * @return this builder + */ + public Builder totalAbandoned(long totalAbandoned) { + return set("total_abandoned", Conv.LONG, totalAbandoned); + } + + /** + * Sessions In Progress. + * + * @param totalInProgress the value + * @return this builder + */ + public Builder totalInProgress(long totalInProgress) { + return set("total_in_progress", Conv.LONG, totalInProgress); + } + + /** + * Sessions Not Started. + * + * @param totalNotStarted the value + * @return this builder + */ + public Builder totalNotStarted(long totalNotStarted) { + return set("total_not_started", Conv.LONG, totalNotStarted); + } + + /** + * Approved by a reviewer. + * + * @param totalManuallyApproved the value + * @return this builder + */ + public Builder totalManuallyApproved(long totalManuallyApproved) { + return set("total_manually_approved", Conv.LONG, totalManuallyApproved); + } + + /** + * Declined by a reviewer. + * + * @param totalManuallyDeclined the value + * @return this builder + */ + public Builder totalManuallyDeclined(long totalManuallyDeclined) { + return set("total_manually_declined", Conv.LONG, totalManuallyDeclined); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsRequestBreakdown} + * @throws IllegalStateException when a required field is missing + */ + public ApplicationAnalyticsRequestBreakdown build() { + require("total_approved", false); + require("total_in_review", false); + require("total_declined", false); + require("total_canceled", false); + require("total_pending", false); + return new ApplicationAnalyticsRequestBreakdown(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsResubmissionPoint.java b/src/main/java/me/didit/types/ApplicationAnalyticsResubmissionPoint.java new file mode 100644 index 0000000..615789f --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsResubmissionPoint.java @@ -0,0 +1,178 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsResubmissionPoint extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("date", "first_pass", "second", "third_plus", "abandoned"); + + private ApplicationAnalyticsResubmissionPoint(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsResubmissionPoint} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsResubmissionPoint fromJson(JsonNode json) { + return new ApplicationAnalyticsResubmissionPoint(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional date() { + return Json.optional(json, "date", Conv.STRING); + } + + /** + * The first_pass field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional firstPass() { + return Json.optional(json, "first_pass", Conv.LONG); + } + + /** + * The second field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional second() { + return Json.optional(json, "second", Conv.LONG); + } + + /** + * The third_plus field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional thirdPlus() { + return Json.optional(json, "third_plus", Conv.LONG); + } + + /** + * The abandoned field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional abandoned() { + return Json.optional(json, "abandoned", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsResubmissionPoint}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsResubmissionPoint from) { + super(from); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @param date the value + * @return this builder + */ + public Builder date(String date) { + return set("date", Conv.STRING, date); + } + + /** + * The first_pass field. + * + * @param firstPass the value + * @return this builder + */ + public Builder firstPass(long firstPass) { + return set("first_pass", Conv.LONG, firstPass); + } + + /** + * The second field. + * + * @param second the value + * @return this builder + */ + public Builder second(long second) { + return set("second", Conv.LONG, second); + } + + /** + * The third_plus field. + * + * @param thirdPlus the value + * @return this builder + */ + public Builder thirdPlus(long thirdPlus) { + return set("third_plus", Conv.LONG, thirdPlus); + } + + /** + * The abandoned field. + * + * @param abandoned the value + * @return this builder + */ + public Builder abandoned(long abandoned) { + return set("abandoned", Conv.LONG, abandoned); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsResubmissionPoint} + */ + public ApplicationAnalyticsResubmissionPoint build() { + return new ApplicationAnalyticsResubmissionPoint(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsResubmissionStats.java b/src/main/java/me/didit/types/ApplicationAnalyticsResubmissionStats.java new file mode 100644 index 0000000..36c25a0 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsResubmissionStats.java @@ -0,0 +1,158 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsResubmissionStats extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("first_pass", "second", "third_plus", "abandoned"); + + private ApplicationAnalyticsResubmissionStats(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsResubmissionStats} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsResubmissionStats fromJson(JsonNode json) { + return new ApplicationAnalyticsResubmissionStats(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Finished sessions with no resubmission. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional firstPass() { + return Json.optional(json, "first_pass", Conv.LONG); + } + + /** + * Finished sessions resubmitted once. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional second() { + return Json.optional(json, "second", Conv.LONG); + } + + /** + * Finished sessions resubmitted two or more times. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional thirdPlus() { + return Json.optional(json, "third_plus", Conv.LONG); + } + + /** + * Abandoned sessions. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional abandoned() { + return Json.optional(json, "abandoned", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsResubmissionStats}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsResubmissionStats from) { + super(from); + } + + /** + * Finished sessions with no resubmission. + * + * @param firstPass the value + * @return this builder + */ + public Builder firstPass(long firstPass) { + return set("first_pass", Conv.LONG, firstPass); + } + + /** + * Finished sessions resubmitted once. + * + * @param second the value + * @return this builder + */ + public Builder second(long second) { + return set("second", Conv.LONG, second); + } + + /** + * Finished sessions resubmitted two or more times. + * + * @param thirdPlus the value + * @return this builder + */ + public Builder thirdPlus(long thirdPlus) { + return set("third_plus", Conv.LONG, thirdPlus); + } + + /** + * Abandoned sessions. + * + * @param abandoned the value + * @return this builder + */ + public Builder abandoned(long abandoned) { + return set("abandoned", Conv.LONG, abandoned); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsResubmissionStats} + */ + public ApplicationAnalyticsResubmissionStats build() { + return new ApplicationAnalyticsResubmissionStats(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsSummary.java b/src/main/java/me/didit/types/ApplicationAnalyticsSummary.java new file mode 100644 index 0000000..a00632c --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsSummary.java @@ -0,0 +1,529 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * User verification (KYC) summary, returned when neither scope nor widget_keys is sent. + */ +public final class ApplicationAnalyticsSummary extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("data_points", "request_breakdown", "previous_data_points", "previous_request_breakdown", "conversion_rate", "location_stats", "demographic_stats", "device_details", "workflow_funnel_stats", "verification_time_stats", "resubmission_stats", "warning_stats", "original_status_stats", "grouping_mode", "demographic_time_series", "device_time_series", "verification_time_time_series", "resubmission_time_series", "original_status_time_series"); + + private ApplicationAnalyticsSummary(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsSummary} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsSummary fromJson(JsonNode json) { + return new ApplicationAnalyticsSummary(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * One point per day, from the first day with sessions in the range to end_date. Always daily, whatever grouping_mode says. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List dataPoints() { + return Json.required(json, "data_points", Conv.list(Conv.model(ApplicationAnalyticsDataPoint::fromJson))); + } + + /** + * Status totals for the range. The previous-period breakdown carries at least the required fields. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsRequestBreakdown requestBreakdown() { + return Json.required(json, "request_breakdown", Conv.model(ApplicationAnalyticsRequestBreakdown::fromJson)); + } + + /** + * Daily points for the period of the same length right before start_date. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List previousDataPoints() { + return Json.required(json, "previous_data_points", Conv.list(Conv.model(ApplicationAnalyticsDataPoint::fromJson))); + } + + /** + * Status totals for the range. The previous-period breakdown carries at least the required fields. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsRequestBreakdown previousRequestBreakdown() { + return Json.required(json, "previous_request_breakdown", Conv.model(ApplicationAnalyticsRequestBreakdown::fromJson)); + } + + /** + * total_approved / (total_approved + total_declined + total_in_review) x 100, one decimal. null when nothing finished. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional conversionRate() { + return Json.optional(json, "conversion_rate", Conv.DOUBLE); + } + + /** + * The location_stats field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsLocationStats locationStats() { + return Json.required(json, "location_stats", Conv.model(ApplicationAnalyticsLocationStats::fromJson)); + } + + /** + * The demographic_stats field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsDemographicStats demographicStats() { + return Json.required(json, "demographic_stats", Conv.model(ApplicationAnalyticsDemographicStats::fromJson)); + } + + /** + * Top five of each, most sessions first. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsDeviceDetails deviceDetails() { + return Json.required(json, "device_details", Conv.model(ApplicationAnalyticsDeviceDetails::fromJson)); + } + + /** + * Keyed by workflow ID. total is the sessions started on that workflow; steps counts the sessions that reached each step (OCR, LIVENESS, FACE_MATCH, AML, POA, PHONE, EMAIL, NFC, DATABASE_VALIDATION, QUESTIONNAIRE, IP_ANALYSIS, AGE_ESTIMATION, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE). Reaching a step is not passing it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map workflowFunnelStats() { + return Json.required(json, "workflow_funnel_stats", Conv.map(Conv.model(ApplicationAnalyticsWorkflowFunnelValue::fromJson))); + } + + /** + * Active time users spent verifying, ignoring idle gaps. An empty object when there is no data. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsVerificationTimeStats verificationTimeStats() { + return Json.required(json, "verification_time_stats", Conv.model(ApplicationAnalyticsVerificationTimeStats::fromJson)); + } + + /** + * The resubmission_stats field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsResubmissionStats resubmissionStats() { + return Json.required(json, "resubmission_stats", Conv.model(ApplicationAnalyticsResubmissionStats::fromJson)); + } + + /** + * Keyed by feature (ID_VERIFICATION, LIVENESS, FACEMATCH, AML, PROOF_OF_ADDRESS, PHONE, EMAIL, NFC, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE and the KYB features). total is the sessions that reached the feature; risks counts sessions per warning code and level. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map warningStats() { + return Json.required(json, "warning_stats", Conv.map(Conv.model(ApplicationAnalyticsWarningStatsValue::fromJson))); + } + + /** + * The workflow's automatic verdict, before any reviewer changed it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsOriginalStatusStats originalStatusStats() { + return Json.required(json, "original_status_stats", Conv.model(ApplicationAnalyticsOriginalStatusStats::fromJson)); + } + + /** + * How the *_time_series lists are grouped: day for up to 14 days, week up to 90, month beyond. Measured from the first day with sessions in the range. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsSummary.GroupingMode groupingMode() { + return Json.required(json, "grouping_mode", Conv.enumeration(ApplicationAnalyticsSummary.GroupingMode::of)); + } + + /** + * The demographic_time_series field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List demographicTimeSeries() { + return Json.required(json, "demographic_time_series", Conv.list(Conv.model(ApplicationAnalyticsDemographicPoint::fromJson))); + } + + /** + * The device_time_series field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List deviceTimeSeries() { + return Json.required(json, "device_time_series", Conv.list(Conv.model(ApplicationAnalyticsDevicePoint::fromJson))); + } + + /** + * The verification_time_time_series field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List verificationTimeTimeSeries() { + return Json.required(json, "verification_time_time_series", Conv.list(Conv.model(ApplicationAnalyticsVerificationTimePoint::fromJson))); + } + + /** + * The resubmission_time_series field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List resubmissionTimeSeries() { + return Json.required(json, "resubmission_time_series", Conv.list(Conv.model(ApplicationAnalyticsResubmissionPoint::fromJson))); + } + + /** + * The original_status_time_series field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List originalStatusTimeSeries() { + return Json.required(json, "original_status_time_series", Conv.list(Conv.model(ApplicationAnalyticsOriginalStatusPoint::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsSummary}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsSummary from) { + super(from); + } + + /** + * One point per day, from the first day with sessions in the range to end_date. Always daily, whatever grouping_mode says. + * + * @param dataPoints the value + * @return this builder + */ + public Builder dataPoints(List dataPoints) { + return set("data_points", Conv.list(Conv.model(ApplicationAnalyticsDataPoint::fromJson)), dataPoints); + } + + /** + * Status totals for the range. The previous-period breakdown carries at least the required fields. + * + * @param requestBreakdown the value + * @return this builder + */ + public Builder requestBreakdown(ApplicationAnalyticsRequestBreakdown requestBreakdown) { + return set("request_breakdown", Conv.model(ApplicationAnalyticsRequestBreakdown::fromJson), requestBreakdown); + } + + /** + * Daily points for the period of the same length right before start_date. + * + * @param previousDataPoints the value + * @return this builder + */ + public Builder previousDataPoints(List previousDataPoints) { + return set("previous_data_points", Conv.list(Conv.model(ApplicationAnalyticsDataPoint::fromJson)), previousDataPoints); + } + + /** + * Status totals for the range. The previous-period breakdown carries at least the required fields. + * + * @param previousRequestBreakdown the value + * @return this builder + */ + public Builder previousRequestBreakdown(ApplicationAnalyticsRequestBreakdown previousRequestBreakdown) { + return set("previous_request_breakdown", Conv.model(ApplicationAnalyticsRequestBreakdown::fromJson), previousRequestBreakdown); + } + + /** + * total_approved / (total_approved + total_declined + total_in_review) x 100, one decimal. null when nothing finished. + * + * @param conversionRate the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder conversionRate(@Nullable Double conversionRate) { + return setNullable("conversion_rate", Conv.DOUBLE, conversionRate); + } + + /** + * The location_stats field. + * + * @param locationStats the value + * @return this builder + */ + public Builder locationStats(ApplicationAnalyticsLocationStats locationStats) { + return set("location_stats", Conv.model(ApplicationAnalyticsLocationStats::fromJson), locationStats); + } + + /** + * The demographic_stats field. + * + * @param demographicStats the value + * @return this builder + */ + public Builder demographicStats(ApplicationAnalyticsDemographicStats demographicStats) { + return set("demographic_stats", Conv.model(ApplicationAnalyticsDemographicStats::fromJson), demographicStats); + } + + /** + * Top five of each, most sessions first. + * + * @param deviceDetails the value + * @return this builder + */ + public Builder deviceDetails(ApplicationAnalyticsDeviceDetails deviceDetails) { + return set("device_details", Conv.model(ApplicationAnalyticsDeviceDetails::fromJson), deviceDetails); + } + + /** + * Keyed by workflow ID. total is the sessions started on that workflow; steps counts the sessions that reached each step (OCR, LIVENESS, FACE_MATCH, AML, POA, PHONE, EMAIL, NFC, DATABASE_VALIDATION, QUESTIONNAIRE, IP_ANALYSIS, AGE_ESTIMATION, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE). Reaching a step is not passing it. + * + * @param workflowFunnelStats the value + * @return this builder + */ + public Builder workflowFunnelStats(Map workflowFunnelStats) { + return set("workflow_funnel_stats", Conv.map(Conv.model(ApplicationAnalyticsWorkflowFunnelValue::fromJson)), workflowFunnelStats); + } + + /** + * Active time users spent verifying, ignoring idle gaps. An empty object when there is no data. + * + * @param verificationTimeStats the value + * @return this builder + */ + public Builder verificationTimeStats(ApplicationAnalyticsVerificationTimeStats verificationTimeStats) { + return set("verification_time_stats", Conv.model(ApplicationAnalyticsVerificationTimeStats::fromJson), verificationTimeStats); + } + + /** + * The resubmission_stats field. + * + * @param resubmissionStats the value + * @return this builder + */ + public Builder resubmissionStats(ApplicationAnalyticsResubmissionStats resubmissionStats) { + return set("resubmission_stats", Conv.model(ApplicationAnalyticsResubmissionStats::fromJson), resubmissionStats); + } + + /** + * Keyed by feature (ID_VERIFICATION, LIVENESS, FACEMATCH, AML, PROOF_OF_ADDRESS, PHONE, EMAIL, NFC, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE and the KYB features). total is the sessions that reached the feature; risks counts sessions per warning code and level. + * + * @param warningStats the value + * @return this builder + */ + public Builder warningStats(Map warningStats) { + return set("warning_stats", Conv.map(Conv.model(ApplicationAnalyticsWarningStatsValue::fromJson)), warningStats); + } + + /** + * The workflow's automatic verdict, before any reviewer changed it. + * + * @param originalStatusStats the value + * @return this builder + */ + public Builder originalStatusStats(ApplicationAnalyticsOriginalStatusStats originalStatusStats) { + return set("original_status_stats", Conv.model(ApplicationAnalyticsOriginalStatusStats::fromJson), originalStatusStats); + } + + /** + * How the *_time_series lists are grouped: day for up to 14 days, week up to 90, month beyond. Measured from the first day with sessions in the range. + * + * @param groupingMode the value + * @return this builder + */ + public Builder groupingMode(ApplicationAnalyticsSummary.GroupingMode groupingMode) { + return set("grouping_mode", Conv.enumeration(ApplicationAnalyticsSummary.GroupingMode::of), groupingMode); + } + + /** + * The demographic_time_series field. + * + * @param demographicTimeSeries the value + * @return this builder + */ + public Builder demographicTimeSeries(List demographicTimeSeries) { + return set("demographic_time_series", Conv.list(Conv.model(ApplicationAnalyticsDemographicPoint::fromJson)), demographicTimeSeries); + } + + /** + * The device_time_series field. + * + * @param deviceTimeSeries the value + * @return this builder + */ + public Builder deviceTimeSeries(List deviceTimeSeries) { + return set("device_time_series", Conv.list(Conv.model(ApplicationAnalyticsDevicePoint::fromJson)), deviceTimeSeries); + } + + /** + * The verification_time_time_series field. + * + * @param verificationTimeTimeSeries the value + * @return this builder + */ + public Builder verificationTimeTimeSeries(List verificationTimeTimeSeries) { + return set("verification_time_time_series", Conv.list(Conv.model(ApplicationAnalyticsVerificationTimePoint::fromJson)), verificationTimeTimeSeries); + } + + /** + * The resubmission_time_series field. + * + * @param resubmissionTimeSeries the value + * @return this builder + */ + public Builder resubmissionTimeSeries(List resubmissionTimeSeries) { + return set("resubmission_time_series", Conv.list(Conv.model(ApplicationAnalyticsResubmissionPoint::fromJson)), resubmissionTimeSeries); + } + + /** + * The original_status_time_series field. + * + * @param originalStatusTimeSeries the value + * @return this builder + */ + public Builder originalStatusTimeSeries(List originalStatusTimeSeries) { + return set("original_status_time_series", Conv.list(Conv.model(ApplicationAnalyticsOriginalStatusPoint::fromJson)), originalStatusTimeSeries); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsSummary} + * @throws IllegalStateException when a required field is missing + */ + public ApplicationAnalyticsSummary build() { + require("data_points", false); + require("request_breakdown", false); + require("previous_data_points", false); + require("previous_request_breakdown", false); + require("conversion_rate", true); + require("location_stats", false); + require("demographic_stats", false); + require("device_details", false); + require("workflow_funnel_stats", false); + require("verification_time_stats", false); + require("resubmission_stats", false); + require("warning_stats", false); + require("original_status_stats", false); + require("grouping_mode", false); + require("demographic_time_series", false); + require("device_time_series", false); + require("verification_time_time_series", false); + require("resubmission_time_series", false); + require("original_status_time_series", false); + return new ApplicationAnalyticsSummary(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * How the *_time_series lists are grouped: day for up to 14 days, week up to 90, month beyond. Measured from the first day with sessions in the range. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class GroupingMode extends StringEnum { + /** The value {@code "day"}. */ + public static final GroupingMode DAY = new GroupingMode("day"); + + /** The value {@code "week"}. */ + public static final GroupingMode WEEK = new GroupingMode("week"); + + /** The value {@code "month"}. */ + public static final GroupingMode MONTH = new GroupingMode("month"); + + private static final List KNOWN_VALUES = List.of(DAY, WEEK, MONTH); + + private GroupingMode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static GroupingMode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, GroupingMode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsVerificationTimePoint.java b/src/main/java/me/didit/types/ApplicationAnalyticsVerificationTimePoint.java new file mode 100644 index 0000000..ee83e3c --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsVerificationTimePoint.java @@ -0,0 +1,138 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsVerificationTimePoint extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("date", "overall_avg", "per_feature"); + + private ApplicationAnalyticsVerificationTimePoint(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsVerificationTimePoint} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsVerificationTimePoint fromJson(JsonNode json) { + return new ApplicationAnalyticsVerificationTimePoint(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional date() { + return Json.optional(json, "date", Conv.STRING); + } + + /** + * Average active seconds per session. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional overallAvg() { + return Json.optional(json, "overall_avg", Conv.DOUBLE); + } + + /** + * Average active seconds per feature. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> perFeature() { + return Json.optional(json, "per_feature", Conv.map(Conv.DOUBLE)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsVerificationTimePoint}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsVerificationTimePoint from) { + super(from); + } + + /** + * Period key: YYYY-MM-DD for a day, the Monday of a week, or YYYY-MM for a month, following grouping_mode. + * + * @param date the value + * @return this builder + */ + public Builder date(String date) { + return set("date", Conv.STRING, date); + } + + /** + * Average active seconds per session. + * + * @param overallAvg the value + * @return this builder + */ + public Builder overallAvg(double overallAvg) { + return set("overall_avg", Conv.DOUBLE, overallAvg); + } + + /** + * Average active seconds per feature. + * + * @param perFeature the value + * @return this builder + */ + public Builder perFeature(Map perFeature) { + return set("per_feature", Conv.map(Conv.DOUBLE), perFeature); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsVerificationTimePoint} + */ + public ApplicationAnalyticsVerificationTimePoint build() { + return new ApplicationAnalyticsVerificationTimePoint(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsVerificationTimeStats.java b/src/main/java/me/didit/types/ApplicationAnalyticsVerificationTimeStats.java new file mode 100644 index 0000000..815dedd --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsVerificationTimeStats.java @@ -0,0 +1,411 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** + * Active time users spent verifying, ignoring idle gaps. An empty object when there is no data. + */ +public final class ApplicationAnalyticsVerificationTimeStats extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("overall_avg_seconds", "overall_count", "per_feature", "per_sub_step"); + + private ApplicationAnalyticsVerificationTimeStats(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsVerificationTimeStats} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsVerificationTimeStats fromJson(JsonNode json) { + return new ApplicationAnalyticsVerificationTimeStats(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Average per session, one decimal. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional overallAvgSeconds() { + return Json.optional(json, "overall_avg_seconds", Conv.DOUBLE); + } + + /** + * Sessions measured. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional overallCount() { + return Json.optional(json, "overall_count", Conv.LONG); + } + + /** + * Keyed by feature, for example OCR or LIVENESS. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> perFeature() { + return Json.optional(json, "per_feature", Conv.map(Conv.model(ApplicationAnalyticsVerificationTimeStats.PerFeatureValue::fromJson))); + } + + /** + * Keyed by feature, then by sub-step, for example front_id_photo. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional>> perSubStep() { + return Json.optional(json, "per_sub_step", Conv.map(Conv.map(Conv.model(ApplicationAnalyticsVerificationTimeStats.PerSubStepValueValue::fromJson)))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsVerificationTimeStats}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsVerificationTimeStats from) { + super(from); + } + + /** + * Average per session, one decimal. + * + * @param overallAvgSeconds the value + * @return this builder + */ + public Builder overallAvgSeconds(double overallAvgSeconds) { + return set("overall_avg_seconds", Conv.DOUBLE, overallAvgSeconds); + } + + /** + * Sessions measured. + * + * @param overallCount the value + * @return this builder + */ + public Builder overallCount(long overallCount) { + return set("overall_count", Conv.LONG, overallCount); + } + + /** + * Keyed by feature, for example OCR or LIVENESS. + * + * @param perFeature the value + * @return this builder + */ + public Builder perFeature(Map perFeature) { + return set("per_feature", Conv.map(Conv.model(ApplicationAnalyticsVerificationTimeStats.PerFeatureValue::fromJson)), perFeature); + } + + /** + * Keyed by feature, then by sub-step, for example front_id_photo. + * + * @param perSubStep the value + * @return this builder + */ + public Builder perSubStep(Map> perSubStep) { + return set("per_sub_step", Conv.map(Conv.map(Conv.model(ApplicationAnalyticsVerificationTimeStats.PerSubStepValueValue::fromJson))), perSubStep); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsVerificationTimeStats} + */ + public ApplicationAnalyticsVerificationTimeStats build() { + return new ApplicationAnalyticsVerificationTimeStats(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class PerFeatureValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("avg_seconds", "total_seconds", "count"); + + private PerFeatureValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code PerFeatureValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PerFeatureValue fromJson(JsonNode json) { + return new PerFeatureValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The avg_seconds field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional avgSeconds() { + return Json.optional(json, "avg_seconds", Conv.DOUBLE); + } + + /** + * The total_seconds field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalSeconds() { + return Json.optional(json, "total_seconds", Conv.DOUBLE); + } + + /** + * The count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional count() { + return Json.optional(json, "count", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link PerFeatureValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(PerFeatureValue from) { + super(from); + } + + /** + * The avg_seconds field. + * + * @param avgSeconds the value + * @return this builder + */ + public Builder avgSeconds(double avgSeconds) { + return set("avg_seconds", Conv.DOUBLE, avgSeconds); + } + + /** + * The total_seconds field. + * + * @param totalSeconds the value + * @return this builder + */ + public Builder totalSeconds(double totalSeconds) { + return set("total_seconds", Conv.DOUBLE, totalSeconds); + } + + /** + * The count field. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Builds the value. + * + * @return a new immutable {@code PerFeatureValue} + */ + public PerFeatureValue build() { + return new PerFeatureValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class PerSubStepValueValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("avg_seconds", "total_seconds", "count"); + + private PerSubStepValueValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code PerSubStepValueValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PerSubStepValueValue fromJson(JsonNode json) { + return new PerSubStepValueValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The avg_seconds field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional avgSeconds() { + return Json.optional(json, "avg_seconds", Conv.DOUBLE); + } + + /** + * The total_seconds field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalSeconds() { + return Json.optional(json, "total_seconds", Conv.DOUBLE); + } + + /** + * The count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional count() { + return Json.optional(json, "count", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link PerSubStepValueValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(PerSubStepValueValue from) { + super(from); + } + + /** + * The avg_seconds field. + * + * @param avgSeconds the value + * @return this builder + */ + public Builder avgSeconds(double avgSeconds) { + return set("avg_seconds", Conv.DOUBLE, avgSeconds); + } + + /** + * The total_seconds field. + * + * @param totalSeconds the value + * @return this builder + */ + public Builder totalSeconds(double totalSeconds) { + return set("total_seconds", Conv.DOUBLE, totalSeconds); + } + + /** + * The count field. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Builds the value. + * + * @return a new immutable {@code PerSubStepValueValue} + */ + public PerSubStepValueValue build() { + return new PerSubStepValueValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsWarningStatsValue.java b/src/main/java/me/didit/types/ApplicationAnalyticsWarningStatsValue.java new file mode 100644 index 0000000..a453236 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsWarningStatsValue.java @@ -0,0 +1,243 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsWarningStatsValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("total", "risks"); + + private ApplicationAnalyticsWarningStatsValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsWarningStatsValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsWarningStatsValue fromJson(JsonNode json) { + return new ApplicationAnalyticsWarningStatsValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The total field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional total() { + return Json.optional(json, "total", Conv.LONG); + } + + /** + * The risks field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> risks() { + return Json.optional(json, "risks", Conv.map(Conv.model(ApplicationAnalyticsWarningStatsValue.RisksValue::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsWarningStatsValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsWarningStatsValue from) { + super(from); + } + + /** + * The total field. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * The risks field. + * + * @param risks the value + * @return this builder + */ + public Builder risks(Map risks) { + return set("risks", Conv.map(Conv.model(ApplicationAnalyticsWarningStatsValue.RisksValue::fromJson)), risks); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsWarningStatsValue} + */ + public ApplicationAnalyticsWarningStatsValue build() { + return new ApplicationAnalyticsWarningStatsValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class RisksValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("error", "warning", "information"); + + private RisksValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code RisksValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static RisksValue fromJson(JsonNode json) { + return new RisksValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The error field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional error() { + return Json.optional(json, "error", Conv.LONG); + } + + /** + * The warning field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional warning() { + return Json.optional(json, "warning", Conv.LONG); + } + + /** + * The information field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional information() { + return Json.optional(json, "information", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link RisksValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(RisksValue from) { + super(from); + } + + /** + * The error field. + * + * @param error the value + * @return this builder + */ + public Builder error(long error) { + return set("error", Conv.LONG, error); + } + + /** + * The warning field. + * + * @param warning the value + * @return this builder + */ + public Builder warning(long warning) { + return set("warning", Conv.LONG, warning); + } + + /** + * The information field. + * + * @param information the value + * @return this builder + */ + public Builder information(long information) { + return set("information", Conv.LONG, information); + } + + /** + * Builds the value. + * + * @return a new immutable {@code RisksValue} + */ + public RisksValue build() { + return new RisksValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsWidget.java b/src/main/java/me/didit/types/ApplicationAnalyticsWidget.java new file mode 100644 index 0000000..1ad9961 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsWidget.java @@ -0,0 +1,967 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One dashboard widget. Every widget has kind and title; the other fields depend on kind. The session widgets reuse the summary objects (data_points, request_breakdown, workflow_funnel_stats and so on); list widgets put their entries in items; transaction metrics use value. + */ +public final class ApplicationAnalyticsWidget extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("kind", "title", "grouping_mode", "data_points", "request_breakdown", "previous_data_points", "previous_request_breakdown", "conversion_rate", "workflow_funnel_stats", "verification_time_stats", "verification_time_time_series", "warning_stats", "resubmission_stats", "resubmission_time_series", "original_status_stats", "original_status_time_series", "demographic_stats", "demographic_time_series", "device_details", "device_time_series", "items", "previous_items", "value", "currency", "thresholds", "search"); + + private ApplicationAnalyticsWidget(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsWidget} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsWidget fromJson(JsonNode json) { + return new ApplicationAnalyticsWidget(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Widget type. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsWidget.Kind kind() { + return Json.required(json, "kind", Conv.enumeration(ApplicationAnalyticsWidget.Kind::of)); + } + + /** + * Display title, for example Verification volume. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String title() { + return Json.required(json, "title", Conv.STRING); + } + + /** + * How the *_time_series lists are grouped: day for up to 14 days, week up to 90, month beyond. Measured from the first day with sessions in the range. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional groupingMode() { + return Json.optional(json, "grouping_mode", Conv.enumeration(ApplicationAnalyticsWidget.GroupingMode::of)); + } + + /** + * The data_points field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> dataPoints() { + return Json.optional(json, "data_points", Conv.list(Conv.model(ApplicationAnalyticsDataPoint::fromJson))); + } + + /** + * Status totals for the range. The previous-period breakdown carries at least the required fields. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional requestBreakdown() { + return Json.optional(json, "request_breakdown", Conv.model(ApplicationAnalyticsRequestBreakdown::fromJson)); + } + + /** + * The previous_data_points field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> previousDataPoints() { + return Json.optional(json, "previous_data_points", Conv.list(Conv.model(ApplicationAnalyticsDataPoint::fromJson))); + } + + /** + * Status totals for the range. The previous-period breakdown carries at least the required fields. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previousRequestBreakdown() { + return Json.optional(json, "previous_request_breakdown", Conv.model(ApplicationAnalyticsRequestBreakdown::fromJson)); + } + + /** + * The conversion_rate field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional conversionRate() { + return Json.optional(json, "conversion_rate", Conv.DOUBLE); + } + + /** + * Keyed by workflow ID. total is the sessions started on that workflow; steps counts the sessions that reached each step (OCR, LIVENESS, FACE_MATCH, AML, POA, PHONE, EMAIL, NFC, DATABASE_VALIDATION, QUESTIONNAIRE, IP_ANALYSIS, AGE_ESTIMATION, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE). Reaching a step is not passing it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> workflowFunnelStats() { + return Json.optional(json, "workflow_funnel_stats", Conv.map(Conv.model(ApplicationAnalyticsWorkflowFunnelValue::fromJson))); + } + + /** + * Active time users spent verifying, ignoring idle gaps. An empty object when there is no data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verificationTimeStats() { + return Json.optional(json, "verification_time_stats", Conv.model(ApplicationAnalyticsVerificationTimeStats::fromJson)); + } + + /** + * The verification_time_time_series field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> verificationTimeTimeSeries() { + return Json.optional(json, "verification_time_time_series", Conv.list(Conv.model(ApplicationAnalyticsVerificationTimePoint::fromJson))); + } + + /** + * Keyed by feature (ID_VERIFICATION, LIVENESS, FACEMATCH, AML, PROOF_OF_ADDRESS, PHONE, EMAIL, NFC, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE and the KYB features). total is the sessions that reached the feature; risks counts sessions per warning code and level. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> warningStats() { + return Json.optional(json, "warning_stats", Conv.map(Conv.model(ApplicationAnalyticsWarningStatsValue::fromJson))); + } + + /** + * The resubmission_stats field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional resubmissionStats() { + return Json.optional(json, "resubmission_stats", Conv.model(ApplicationAnalyticsResubmissionStats::fromJson)); + } + + /** + * The resubmission_time_series field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> resubmissionTimeSeries() { + return Json.optional(json, "resubmission_time_series", Conv.list(Conv.model(ApplicationAnalyticsResubmissionPoint::fromJson))); + } + + /** + * The workflow's automatic verdict, before any reviewer changed it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originalStatusStats() { + return Json.optional(json, "original_status_stats", Conv.model(ApplicationAnalyticsOriginalStatusStats::fromJson)); + } + + /** + * The original_status_time_series field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> originalStatusTimeSeries() { + return Json.optional(json, "original_status_time_series", Conv.list(Conv.model(ApplicationAnalyticsOriginalStatusPoint::fromJson))); + } + + /** + * The demographic_stats field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional demographicStats() { + return Json.optional(json, "demographic_stats", Conv.model(ApplicationAnalyticsDemographicStats::fromJson)); + } + + /** + * The demographic_time_series field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> demographicTimeSeries() { + return Json.optional(json, "demographic_time_series", Conv.list(Conv.model(ApplicationAnalyticsDemographicPoint::fromJson))); + } + + /** + * Top five of each, most sessions first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deviceDetails() { + return Json.optional(json, "device_details", Conv.model(ApplicationAnalyticsDeviceDetails::fromJson)); + } + + /** + * The device_time_series field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> deviceTimeSeries() { + return Json.optional(json, "device_time_series", Conv.list(Conv.model(ApplicationAnalyticsDevicePoint::fromJson))); + } + + /** + * Entries of a list widget. country_distribution (KYC): country, country_code, session_count. conversion_by_country: country, country_code, finished, approved. time_series: date, value. status_time_series: date, approved, declined, flagged. breakdown: transaction_type or action_type with count and volume, or tag_id, tag_name, count. leaderboard: rule_title, trigger_count or assignee, handled_count, average_processing_seconds, each with approved_percentage, declined_percentage, flagged_percentage. histogram: score, count. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional>> items() { + return Json.optional(json, "items", Conv.list(Conv.OBJECT)); + } + + /** + * Transaction time series for the period of the same length right before start_date. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional>> previousItems() { + return Json.optional(json, "previous_items", Conv.list(Conv.OBJECT)); + } + + /** + * metric widgets: the figure for the range. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional value() { + return Json.optional(json, "value", Conv.DOUBLE); + } + + /** + * Transaction volume and amount widgets: the application's default transaction currency. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currency() { + return Json.optional(json, "currency", Conv.STRING); + } + + /** + * histogram widget: the application's review and decline score thresholds. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional thresholds() { + return Json.optional(json, "thresholds", Conv.model(ApplicationAnalyticsWidget.Thresholds::fromJson)); + } + + /** + * kyb_registry_coverage widget: registry search totals. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional search() { + return Json.optional(json, "search", Conv.model(ApplicationAnalyticsWidget.Search::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsWidget}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsWidget from) { + super(from); + } + + /** + * Widget type. + * + * @param kind the value + * @return this builder + */ + public Builder kind(ApplicationAnalyticsWidget.Kind kind) { + return set("kind", Conv.enumeration(ApplicationAnalyticsWidget.Kind::of), kind); + } + + /** + * Display title, for example Verification volume. + * + * @param title the value + * @return this builder + */ + public Builder title(String title) { + return set("title", Conv.STRING, title); + } + + /** + * How the *_time_series lists are grouped: day for up to 14 days, week up to 90, month beyond. Measured from the first day with sessions in the range. + * + * @param groupingMode the value + * @return this builder + */ + public Builder groupingMode(ApplicationAnalyticsWidget.GroupingMode groupingMode) { + return set("grouping_mode", Conv.enumeration(ApplicationAnalyticsWidget.GroupingMode::of), groupingMode); + } + + /** + * The data_points field. + * + * @param dataPoints the value + * @return this builder + */ + public Builder dataPoints(List dataPoints) { + return set("data_points", Conv.list(Conv.model(ApplicationAnalyticsDataPoint::fromJson)), dataPoints); + } + + /** + * Status totals for the range. The previous-period breakdown carries at least the required fields. + * + * @param requestBreakdown the value + * @return this builder + */ + public Builder requestBreakdown(ApplicationAnalyticsRequestBreakdown requestBreakdown) { + return set("request_breakdown", Conv.model(ApplicationAnalyticsRequestBreakdown::fromJson), requestBreakdown); + } + + /** + * The previous_data_points field. + * + * @param previousDataPoints the value + * @return this builder + */ + public Builder previousDataPoints(List previousDataPoints) { + return set("previous_data_points", Conv.list(Conv.model(ApplicationAnalyticsDataPoint::fromJson)), previousDataPoints); + } + + /** + * Status totals for the range. The previous-period breakdown carries at least the required fields. + * + * @param previousRequestBreakdown the value + * @return this builder + */ + public Builder previousRequestBreakdown(ApplicationAnalyticsRequestBreakdown previousRequestBreakdown) { + return set("previous_request_breakdown", Conv.model(ApplicationAnalyticsRequestBreakdown::fromJson), previousRequestBreakdown); + } + + /** + * The conversion_rate field. + * + * @param conversionRate the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder conversionRate(@Nullable Double conversionRate) { + return setNullable("conversion_rate", Conv.DOUBLE, conversionRate); + } + + /** + * Keyed by workflow ID. total is the sessions started on that workflow; steps counts the sessions that reached each step (OCR, LIVENESS, FACE_MATCH, AML, POA, PHONE, EMAIL, NFC, DATABASE_VALIDATION, QUESTIONNAIRE, IP_ANALYSIS, AGE_ESTIMATION, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE). Reaching a step is not passing it. + * + * @param workflowFunnelStats the value + * @return this builder + */ + public Builder workflowFunnelStats(Map workflowFunnelStats) { + return set("workflow_funnel_stats", Conv.map(Conv.model(ApplicationAnalyticsWorkflowFunnelValue::fromJson)), workflowFunnelStats); + } + + /** + * Active time users spent verifying, ignoring idle gaps. An empty object when there is no data. + * + * @param verificationTimeStats the value + * @return this builder + */ + public Builder verificationTimeStats(ApplicationAnalyticsVerificationTimeStats verificationTimeStats) { + return set("verification_time_stats", Conv.model(ApplicationAnalyticsVerificationTimeStats::fromJson), verificationTimeStats); + } + + /** + * The verification_time_time_series field. + * + * @param verificationTimeTimeSeries the value + * @return this builder + */ + public Builder verificationTimeTimeSeries(List verificationTimeTimeSeries) { + return set("verification_time_time_series", Conv.list(Conv.model(ApplicationAnalyticsVerificationTimePoint::fromJson)), verificationTimeTimeSeries); + } + + /** + * Keyed by feature (ID_VERIFICATION, LIVENESS, FACEMATCH, AML, PROOF_OF_ADDRESS, PHONE, EMAIL, NFC, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE and the KYB features). total is the sessions that reached the feature; risks counts sessions per warning code and level. + * + * @param warningStats the value + * @return this builder + */ + public Builder warningStats(Map warningStats) { + return set("warning_stats", Conv.map(Conv.model(ApplicationAnalyticsWarningStatsValue::fromJson)), warningStats); + } + + /** + * The resubmission_stats field. + * + * @param resubmissionStats the value + * @return this builder + */ + public Builder resubmissionStats(ApplicationAnalyticsResubmissionStats resubmissionStats) { + return set("resubmission_stats", Conv.model(ApplicationAnalyticsResubmissionStats::fromJson), resubmissionStats); + } + + /** + * The resubmission_time_series field. + * + * @param resubmissionTimeSeries the value + * @return this builder + */ + public Builder resubmissionTimeSeries(List resubmissionTimeSeries) { + return set("resubmission_time_series", Conv.list(Conv.model(ApplicationAnalyticsResubmissionPoint::fromJson)), resubmissionTimeSeries); + } + + /** + * The workflow's automatic verdict, before any reviewer changed it. + * + * @param originalStatusStats the value + * @return this builder + */ + public Builder originalStatusStats(ApplicationAnalyticsOriginalStatusStats originalStatusStats) { + return set("original_status_stats", Conv.model(ApplicationAnalyticsOriginalStatusStats::fromJson), originalStatusStats); + } + + /** + * The original_status_time_series field. + * + * @param originalStatusTimeSeries the value + * @return this builder + */ + public Builder originalStatusTimeSeries(List originalStatusTimeSeries) { + return set("original_status_time_series", Conv.list(Conv.model(ApplicationAnalyticsOriginalStatusPoint::fromJson)), originalStatusTimeSeries); + } + + /** + * The demographic_stats field. + * + * @param demographicStats the value + * @return this builder + */ + public Builder demographicStats(ApplicationAnalyticsDemographicStats demographicStats) { + return set("demographic_stats", Conv.model(ApplicationAnalyticsDemographicStats::fromJson), demographicStats); + } + + /** + * The demographic_time_series field. + * + * @param demographicTimeSeries the value + * @return this builder + */ + public Builder demographicTimeSeries(List demographicTimeSeries) { + return set("demographic_time_series", Conv.list(Conv.model(ApplicationAnalyticsDemographicPoint::fromJson)), demographicTimeSeries); + } + + /** + * Top five of each, most sessions first. + * + * @param deviceDetails the value + * @return this builder + */ + public Builder deviceDetails(ApplicationAnalyticsDeviceDetails deviceDetails) { + return set("device_details", Conv.model(ApplicationAnalyticsDeviceDetails::fromJson), deviceDetails); + } + + /** + * The device_time_series field. + * + * @param deviceTimeSeries the value + * @return this builder + */ + public Builder deviceTimeSeries(List deviceTimeSeries) { + return set("device_time_series", Conv.list(Conv.model(ApplicationAnalyticsDevicePoint::fromJson)), deviceTimeSeries); + } + + /** + * Entries of a list widget. country_distribution (KYC): country, country_code, session_count. conversion_by_country: country, country_code, finished, approved. time_series: date, value. status_time_series: date, approved, declined, flagged. breakdown: transaction_type or action_type with count and volume, or tag_id, tag_name, count. leaderboard: rule_title, trigger_count or assignee, handled_count, average_processing_seconds, each with approved_percentage, declined_percentage, flagged_percentage. histogram: score, count. + * + * @param items the value + * @return this builder + */ + public Builder items(List> items) { + return setAny("items", items); + } + + /** + * Transaction time series for the period of the same length right before start_date. + * + * @param previousItems the value + * @return this builder + */ + public Builder previousItems(List> previousItems) { + return setAny("previous_items", previousItems); + } + + /** + * metric widgets: the figure for the range. + * + * @param value the value + * @return this builder + */ + public Builder value(double value) { + return set("value", Conv.DOUBLE, value); + } + + /** + * Transaction volume and amount widgets: the application's default transaction currency. + * + * @param currency the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder currency(@Nullable String currency) { + return setNullable("currency", Conv.STRING, currency); + } + + /** + * histogram widget: the application's review and decline score thresholds. + * + * @param thresholds the value + * @return this builder + */ + public Builder thresholds(ApplicationAnalyticsWidget.Thresholds thresholds) { + return set("thresholds", Conv.model(ApplicationAnalyticsWidget.Thresholds::fromJson), thresholds); + } + + /** + * kyb_registry_coverage widget: registry search totals. + * + * @param search the value + * @return this builder + */ + public Builder search(ApplicationAnalyticsWidget.Search search) { + return set("search", Conv.model(ApplicationAnalyticsWidget.Search::fromJson), search); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsWidget} + * @throws IllegalStateException when a required field is missing + */ + public ApplicationAnalyticsWidget build() { + require("kind", false); + require("title", false); + return new ApplicationAnalyticsWidget(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Widget type. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "sessions_volume"}. */ + public static final Kind SESSIONS_VOLUME = new Kind("sessions_volume"); + + /** The value {@code "workflow_funnel"}. */ + public static final Kind WORKFLOW_FUNNEL = new Kind("workflow_funnel"); + + /** The value {@code "average_verification_time"}. */ + public static final Kind AVERAGE_VERIFICATION_TIME = new Kind("average_verification_time"); + + /** The value {@code "country_distribution"}. */ + public static final Kind COUNTRY_DISTRIBUTION = new Kind("country_distribution"); + + /** The value {@code "warning_insights"}. */ + public static final Kind WARNING_INSIGHTS = new Kind("warning_insights"); + + /** The value {@code "conversion_rate"}. */ + public static final Kind CONVERSION_RATE = new Kind("conversion_rate"); + + /** The value {@code "conversion_by_country"}. */ + public static final Kind CONVERSION_BY_COUNTRY = new Kind("conversion_by_country"); + + /** The value {@code "resubmission_insights"}. */ + public static final Kind RESUBMISSION_INSIGHTS = new Kind("resubmission_insights"); + + /** The value {@code "original_status_distribution"}. */ + public static final Kind ORIGINAL_STATUS_DISTRIBUTION = new Kind("original_status_distribution"); + + /** The value {@code "database_validation_coverage"}. */ + public static final Kind DATABASE_VALIDATION_COVERAGE = new Kind("database_validation_coverage"); + + /** The value {@code "database_validation_latency"}. */ + public static final Kind DATABASE_VALIDATION_LATENCY = new Kind("database_validation_latency"); + + /** The value {@code "demographics"}. */ + public static final Kind DEMOGRAPHICS = new Kind("demographics"); + + /** The value {@code "device_details"}. */ + public static final Kind DEVICE_DETAILS = new Kind("device_details"); + + /** The value {@code "horizontal_bar"}. */ + public static final Kind HORIZONTAL_BAR = new Kind("horizontal_bar"); + + /** The value {@code "kyb_registry_coverage"}. */ + public static final Kind KYB_REGISTRY_COVERAGE = new Kind("kyb_registry_coverage"); + + /** The value {@code "kyb_registry_data_completeness"}. */ + public static final Kind KYB_REGISTRY_DATA_COMPLETENESS = new Kind("kyb_registry_data_completeness"); + + /** The value {@code "metric"}. */ + public static final Kind METRIC = new Kind("metric"); + + /** The value {@code "time_series"}. */ + public static final Kind TIME_SERIES = new Kind("time_series"); + + /** The value {@code "breakdown"}. */ + public static final Kind BREAKDOWN = new Kind("breakdown"); + + /** The value {@code "status_time_series"}. */ + public static final Kind STATUS_TIME_SERIES = new Kind("status_time_series"); + + /** The value {@code "leaderboard"}. */ + public static final Kind LEADERBOARD = new Kind("leaderboard"); + + /** The value {@code "histogram"}. */ + public static final Kind HISTOGRAM = new Kind("histogram"); + + private static final List KNOWN_VALUES = List.of(SESSIONS_VOLUME, WORKFLOW_FUNNEL, AVERAGE_VERIFICATION_TIME, COUNTRY_DISTRIBUTION, WARNING_INSIGHTS, CONVERSION_RATE, CONVERSION_BY_COUNTRY, RESUBMISSION_INSIGHTS, ORIGINAL_STATUS_DISTRIBUTION, DATABASE_VALIDATION_COVERAGE, DATABASE_VALIDATION_LATENCY, DEMOGRAPHICS, DEVICE_DETAILS, HORIZONTAL_BAR, KYB_REGISTRY_COVERAGE, KYB_REGISTRY_DATA_COMPLETENESS, METRIC, TIME_SERIES, BREAKDOWN, STATUS_TIME_SERIES, LEADERBOARD, HISTOGRAM); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * How the *_time_series lists are grouped: day for up to 14 days, week up to 90, month beyond. Measured from the first day with sessions in the range. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class GroupingMode extends StringEnum { + /** The value {@code "day"}. */ + public static final GroupingMode DAY = new GroupingMode("day"); + + /** The value {@code "week"}. */ + public static final GroupingMode WEEK = new GroupingMode("week"); + + /** The value {@code "month"}. */ + public static final GroupingMode MONTH = new GroupingMode("month"); + + private static final List KNOWN_VALUES = List.of(DAY, WEEK, MONTH); + + private GroupingMode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static GroupingMode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, GroupingMode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** histogram widget: the application's review and decline score thresholds. */ + public static final class Thresholds extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("review", "decline"); + + private Thresholds(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Thresholds} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Thresholds fromJson(JsonNode json) { + return new Thresholds(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The review field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional review() { + return Json.optional(json, "review", Conv.LONG); + } + + /** + * The decline field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decline() { + return Json.optional(json, "decline", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Thresholds}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Thresholds from) { + super(from); + } + + /** + * The review field. + * + * @param review the value + * @return this builder + */ + public Builder review(long review) { + return set("review", Conv.LONG, review); + } + + /** + * The decline field. + * + * @param decline the value + * @return this builder + */ + public Builder decline(long decline) { + return set("decline", Conv.LONG, decline); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Thresholds} + */ + public Thresholds build() { + return new Thresholds(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** kyb_registry_coverage widget: registry search totals. */ + public static final class Search extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("searches_total", "searches_with_no_candidates"); + + private Search(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Search} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Search fromJson(JsonNode json) { + return new Search(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The searches_total field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional searchesTotal() { + return Json.optional(json, "searches_total", Conv.LONG); + } + + /** + * The searches_with_no_candidates field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional searchesWithNoCandidates() { + return Json.optional(json, "searches_with_no_candidates", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Search}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Search from) { + super(from); + } + + /** + * The searches_total field. + * + * @param searchesTotal the value + * @return this builder + */ + public Builder searchesTotal(long searchesTotal) { + return set("searches_total", Conv.LONG, searchesTotal); + } + + /** + * The searches_with_no_candidates field. + * + * @param searchesWithNoCandidates the value + * @return this builder + */ + public Builder searchesWithNoCandidates(long searchesWithNoCandidates) { + return set("searches_with_no_candidates", Conv.LONG, searchesWithNoCandidates); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Search} + */ + public Search build() { + return new Search(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsWidgetResponse.java b/src/main/java/me/didit/types/ApplicationAnalyticsWidgetResponse.java new file mode 100644 index 0000000..588ad9f --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsWidgetResponse.java @@ -0,0 +1,213 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Dashboard widgets, returned when scope or widget_keys is sent. */ +public final class ApplicationAnalyticsWidgetResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("scope", "available_widget_keys", "requested_widget_keys", "widgets"); + + private ApplicationAnalyticsWidgetResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsWidgetResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsWidgetResponse fromJson(JsonNode json) { + return new ApplicationAnalyticsWidgetResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Dashboard tab. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationAnalyticsWidgetResponse.Scope scope() { + return Json.required(json, "scope", Conv.enumeration(ApplicationAnalyticsWidgetResponse.Scope::of)); + } + + /** + * Every widget key this scope accepts. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List availableWidgetKeys() { + return Json.required(json, "available_widget_keys", Conv.list(Conv.STRING)); + } + + /** + * The widget keys answered, in order: your valid widget_keys, or the scope's defaults. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List requestedWidgetKeys() { + return Json.required(json, "requested_widget_keys", Conv.list(Conv.STRING)); + } + + /** + * Keyed by widget key, in the order of requested_widget_keys. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map widgets() { + return Json.required(json, "widgets", Conv.map(Conv.model(ApplicationAnalyticsWidget::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsWidgetResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsWidgetResponse from) { + super(from); + } + + /** + * Dashboard tab. + * + * @param scope the value + * @return this builder + */ + public Builder scope(ApplicationAnalyticsWidgetResponse.Scope scope) { + return set("scope", Conv.enumeration(ApplicationAnalyticsWidgetResponse.Scope::of), scope); + } + + /** + * Every widget key this scope accepts. + * + * @param availableWidgetKeys the value + * @return this builder + */ + public Builder availableWidgetKeys(List availableWidgetKeys) { + return set("available_widget_keys", Conv.list(Conv.STRING), availableWidgetKeys); + } + + /** + * The widget keys answered, in order: your valid widget_keys, or the scope's defaults. + * + * @param requestedWidgetKeys the value + * @return this builder + */ + public Builder requestedWidgetKeys(List requestedWidgetKeys) { + return set("requested_widget_keys", Conv.list(Conv.STRING), requestedWidgetKeys); + } + + /** + * Keyed by widget key, in the order of requested_widget_keys. + * + * @param widgets the value + * @return this builder + */ + public Builder widgets(Map widgets) { + return set("widgets", Conv.map(Conv.model(ApplicationAnalyticsWidget::fromJson)), widgets); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsWidgetResponse} + * @throws IllegalStateException when a required field is missing + */ + public ApplicationAnalyticsWidgetResponse build() { + require("scope", false); + require("available_widget_keys", false); + require("requested_widget_keys", false); + require("widgets", false); + return new ApplicationAnalyticsWidgetResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Dashboard tab. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "home"}. */ + public static final Scope HOME = new Scope("home"); + + /** The value {@code "kyc"}. */ + public static final Scope KYC = new Scope("kyc"); + + /** The value {@code "kyb"}. */ + public static final Scope KYB = new Scope("kyb"); + + /** The value {@code "transactions"}. */ + public static final Scope TRANSACTIONS = new Scope("transactions"); + + private static final List KNOWN_VALUES = List.of(HOME, KYC, KYB, TRANSACTIONS); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationAnalyticsWorkflowFunnelValue.java b/src/main/java/me/didit/types/ApplicationAnalyticsWorkflowFunnelValue.java new file mode 100644 index 0000000..c55a6a7 --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationAnalyticsWorkflowFunnelValue.java @@ -0,0 +1,203 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ApplicationAnalyticsWorkflowFunnelValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("total", "steps"); + + private ApplicationAnalyticsWorkflowFunnelValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationAnalyticsWorkflowFunnelValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationAnalyticsWorkflowFunnelValue fromJson(JsonNode json) { + return new ApplicationAnalyticsWorkflowFunnelValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The total field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional total() { + return Json.optional(json, "total", Conv.LONG); + } + + /** + * The steps field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> steps() { + return Json.optional(json, "steps", Conv.map(Conv.model(ApplicationAnalyticsWorkflowFunnelValue.StepsValue::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationAnalyticsWorkflowFunnelValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationAnalyticsWorkflowFunnelValue from) { + super(from); + } + + /** + * The total field. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * The steps field. + * + * @param steps the value + * @return this builder + */ + public Builder steps(Map steps) { + return set("steps", Conv.map(Conv.model(ApplicationAnalyticsWorkflowFunnelValue.StepsValue::fromJson)), steps); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationAnalyticsWorkflowFunnelValue} + */ + public ApplicationAnalyticsWorkflowFunnelValue build() { + return new ApplicationAnalyticsWorkflowFunnelValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class StepsValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("reached"); + + private StepsValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code StepsValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static StepsValue fromJson(JsonNode json) { + return new StepsValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The reached field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reached() { + return Json.optional(json, "reached", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link StepsValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(StepsValue from) { + super(from); + } + + /** + * The reached field. + * + * @param reached the value + * @return this builder + */ + public Builder reached(long reached) { + return set("reached", Conv.LONG, reached); + } + + /** + * Builds the value. + * + * @return a new immutable {@code StepsValue} + */ + public StepsValue build() { + return new StepsValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationCredentialsSummary.java b/src/main/java/me/didit/types/ApplicationCredentialsSummary.java index 121c676..900b758 100644 --- a/src/main/java/me/didit/types/ApplicationCredentialsSummary.java +++ b/src/main/java/me/didit/types/ApplicationCredentialsSummary.java @@ -82,7 +82,7 @@ public Optional clientId() { } /** - * Use this as x-api-key on every verification API call. + * Use this as x-api-key on every verification API call. New keys start with sk_live_ (live application) or sk_test_ (sandbox application), followed by 43 URL-safe characters (letters, digits, - and _). Keys created before this format have no prefix and keep working unchanged. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -135,7 +135,7 @@ public Builder clientId(String clientId) { } /** - * Use this as x-api-key on every verification API call. + * Use this as x-api-key on every verification API call. New keys start with sk_live_ (live application) or sk_test_ (sandbox application), followed by 43 URL-safe characters (letters, digits, - and _). Keys created before this format have no prefix and keep working unchanged. * * @param apiKey the value * @return this builder diff --git a/src/main/java/me/didit/types/ApplicationProduct.java b/src/main/java/me/didit/types/ApplicationProduct.java new file mode 100644 index 0000000..67048bd --- /dev/null +++ b/src/main/java/me/didit/types/ApplicationProduct.java @@ -0,0 +1,237 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * One product of an application and its state. Read-only: the application endpoints return it and ignore it in a request body. + */ +public final class ApplicationProduct extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("product", "status", "modules"); + + private ApplicationProduct(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ApplicationProduct} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ApplicationProduct fromJson(JsonNode json) { + return new ApplicationProduct(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * identity (Identity verification), transactions (Transaction monitoring) or authentication (Authentication). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationProduct.Product product() { + return Json.required(json, "product", Conv.enumeration(ApplicationProduct.Product::of)); + } + + /** + * Whether the product is on for this application. early_access applies only to authentication, which Didit turns on for you. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ApplicationProduct.Status status() { + return Json.required(json, "status", Conv.enumeration(ApplicationProduct.Status::of)); + } + + /** + * Module switches of the product, each true or false. identity has user_verification, business_verification and case_management. transactions has travel_rule and case_management. authentication has none ({}). case_management is one setting shown under both identity and transactions, so it has the same value on both. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map modules() { + return Json.required(json, "modules", Conv.map(Conv.BOOLEAN)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ApplicationProduct}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ApplicationProduct from) { + super(from); + } + + /** + * identity (Identity verification), transactions (Transaction monitoring) or authentication (Authentication). + * + * @param product the value + * @return this builder + */ + public Builder product(ApplicationProduct.Product product) { + return set("product", Conv.enumeration(ApplicationProduct.Product::of), product); + } + + /** + * Whether the product is on for this application. early_access applies only to authentication, which Didit turns on for you. + * + * @param status the value + * @return this builder + */ + public Builder status(ApplicationProduct.Status status) { + return set("status", Conv.enumeration(ApplicationProduct.Status::of), status); + } + + /** + * Module switches of the product, each true or false. identity has user_verification, business_verification and case_management. transactions has travel_rule and case_management. authentication has none ({}). case_management is one setting shown under both identity and transactions, so it has the same value on both. + * + * @param modules the value + * @return this builder + */ + public Builder modules(Map modules) { + return set("modules", Conv.map(Conv.BOOLEAN), modules); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ApplicationProduct} + * @throws IllegalStateException when a required field is missing + */ + public ApplicationProduct build() { + require("product", false); + require("status", false); + require("modules", false); + return new ApplicationProduct(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * identity (Identity verification), transactions (Transaction monitoring) or authentication (Authentication). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Product extends StringEnum { + /** The value {@code "identity"}. */ + public static final Product IDENTITY = new Product("identity"); + + /** The value {@code "transactions"}. */ + public static final Product TRANSACTIONS = new Product("transactions"); + + /** The value {@code "authentication"}. */ + public static final Product AUTHENTICATION = new Product("authentication"); + + private static final List KNOWN_VALUES = List.of(IDENTITY, TRANSACTIONS, AUTHENTICATION); + + private Product(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Product of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Product::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Whether the product is on for this application. early_access applies only to authentication, which Didit turns on for you. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "enabled"}. */ + public static final Status ENABLED = new Status("enabled"); + + /** The value {@code "early_access"}. */ + public static final Status EARLY_ACCESS = new Status("early_access"); + + /** The value {@code "disabled"}. */ + public static final Status DISABLED = new Status("disabled"); + + private static final List KNOWN_VALUES = List.of(ENABLED, EARLY_ACCESS, DISABLED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/ApplicationUpdateRequest.java b/src/main/java/me/didit/types/ApplicationUpdateRequest.java index 7c83a98..8621253 100644 --- a/src/main/java/me/didit/types/ApplicationUpdateRequest.java +++ b/src/main/java/me/didit/types/ApplicationUpdateRequest.java @@ -13,7 +13,7 @@ import me.didit.core.JsonModel; /** - * PATCH body for application metadata. Send only the fields you want to change; omitted fields are preserved. uuid, client_id, and api_key cannot be updated. + * PATCH body for application metadata. Send only the fields you want to change; omitted fields are preserved. uuid, client_id, and api_key cannot be updated. products is read-only: a products key in the body is ignored. */ public final class ApplicationUpdateRequest extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("name", "website_url", "redirect_uris", "terms_url", "privacy_url", "description"); diff --git a/src/main/java/me/didit/types/BillingRetrieveBalanceResponse.java b/src/main/java/me/didit/types/BillingRetrieveBalanceResponse.java index 9829177..85da277 100644 --- a/src/main/java/me/didit/types/BillingRetrieveBalanceResponse.java +++ b/src/main/java/me/didit/types/BillingRetrieveBalanceResponse.java @@ -60,7 +60,7 @@ public String balance() { } /** - * When true, Didit will automatically charge the saved Stripe payment method to add credits once the balance crosses auto_refill_threshold. Configured from Console → Billing. + * When true, Didit will automatically charge the saved payment method to add credits once the balance crosses auto_refill_threshold. Configured from Console → Billing. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -113,7 +113,7 @@ public Builder balance(String balance) { } /** - * When true, Didit will automatically charge the saved Stripe payment method to add credits once the balance crosses auto_refill_threshold. Configured from Console → Billing. + * When true, Didit will automatically charge the saved payment method to add credits once the balance crosses auto_refill_threshold. Configured from Console → Billing. * * @param autoRefillEnabled the value * @return this builder diff --git a/src/main/java/me/didit/types/BillingTopUpParams.java b/src/main/java/me/didit/types/BillingTopUpParams.java index 2a76ccf..12c6bce 100644 --- a/src/main/java/me/didit/types/BillingTopUpParams.java +++ b/src/main/java/me/didit/types/BillingTopUpParams.java @@ -60,7 +60,7 @@ public double amountInDollars() { } /** - * URL Stripe redirects the customer to after a successful payment. Currently defaults to https://console.didit.me. Pass a working URL explicitly; the default host is unavailable. Append the literal placeholder {CHECKOUT_SESSION_ID} if you want Stripe to substitute the session ID. + * URL the hosted checkout redirects the customer to after a successful payment. Currently defaults to https://console.didit.me. Pass a working URL explicitly; the default host is unavailable. Append the literal placeholder {CHECKOUT_SESSION_ID} if you want the hosted checkout to substitute the session ID. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -70,7 +70,7 @@ public Optional successUrl() { } /** - * URL Stripe redirects the customer to when they abort or close the Checkout window. Currently defaults to https://console.didit.me. Pass a working URL explicitly; the default host is unavailable. + * URL the hosted checkout redirects the customer to when they abort or close the Checkout window. Currently defaults to https://console.didit.me. Pass a working URL explicitly; the default host is unavailable. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -103,7 +103,7 @@ public Builder amountInDollars(double amountInDollars) { } /** - * URL Stripe redirects the customer to after a successful payment. Currently defaults to https://console.didit.me. Pass a working URL explicitly; the default host is unavailable. Append the literal placeholder {CHECKOUT_SESSION_ID} if you want Stripe to substitute the session ID. + * URL the hosted checkout redirects the customer to after a successful payment. Currently defaults to https://console.didit.me. Pass a working URL explicitly; the default host is unavailable. Append the literal placeholder {CHECKOUT_SESSION_ID} if you want the hosted checkout to substitute the session ID. * * @param successUrl the value * @return this builder @@ -113,7 +113,7 @@ public Builder successUrl(String successUrl) { } /** - * URL Stripe redirects the customer to when they abort or close the Checkout window. Currently defaults to https://console.didit.me. Pass a working URL explicitly; the default host is unavailable. + * URL the hosted checkout redirects the customer to when they abort or close the Checkout window. Currently defaults to https://console.didit.me. Pass a working URL explicitly; the default host is unavailable. * * @param cancelUrl the value * @return this builder diff --git a/src/main/java/me/didit/types/BillingTopUpResponse.java b/src/main/java/me/didit/types/BillingTopUpResponse.java index c19df88..30da52b 100644 --- a/src/main/java/me/didit/types/BillingTopUpResponse.java +++ b/src/main/java/me/didit/types/BillingTopUpResponse.java @@ -48,7 +48,7 @@ public Builder toBuilder() { } /** - * Stripe Checkout Session identifier (e.g. cs_test_a1B2c3...). Useful for server-side reconciliation. + * Hosted checkout session identifier (e.g. cs_test_a1B2c3...). Useful for server-side reconciliation. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -58,7 +58,7 @@ public String checkoutSessionId() { } /** - * Single-use Stripe-hosted payment page URL. Redirect the customer here to complete payment. + * Single-use hosted payment page URL. Redirect the customer here to complete payment. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -81,7 +81,7 @@ private Builder(BillingTopUpResponse from) { } /** - * Stripe Checkout Session identifier (e.g. cs_test_a1B2c3...). Useful for server-side reconciliation. + * Hosted checkout session identifier (e.g. cs_test_a1B2c3...). Useful for server-side reconciliation. * * @param checkoutSessionId the value * @return this builder @@ -91,7 +91,7 @@ public Builder checkoutSessionId(String checkoutSessionId) { } /** - * Single-use Stripe-hosted payment page URL. Redirect the customer here to complete payment. + * Single-use hosted payment page URL. Redirect the customer here to complete payment. * * @param checkoutSessionUrl the value * @return this builder diff --git a/src/main/java/me/didit/types/BlocklistAddParams.java b/src/main/java/me/didit/types/BlocklistAddParams.java new file mode 100644 index 0000000..9658c0c --- /dev/null +++ b/src/main/java/me/didit/types/BlocklistAddParams.java @@ -0,0 +1,181 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code blocklist.add}. */ +public final class BlocklistAddParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "blocklist_face", "blocklist_document", "blocklist_phone", "blocklist_email"); + + private BlocklistAddParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BlocklistAddParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BlocklistAddParams fromJson(JsonNode json) { + return new BlocklistAddParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * A person verification session of your application. Deleted sessions are refused. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Blocklist the session's face. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional blocklistFace() { + return Json.optional(json, "blocklist_face", Conv.BOOLEAN); + } + + /** + * Blocklist the session's identity document. The document needs its issuing state, document type, document number and full name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional blocklistDocument() { + return Json.optional(json, "blocklist_document", Conv.BOOLEAN); + } + + /** + * Blocklist the session's phone number. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional blocklistPhone() { + return Json.optional(json, "blocklist_phone", Conv.BOOLEAN); + } + + /** + * Blocklist the session's email address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional blocklistEmail() { + return Json.optional(json, "blocklist_email", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BlocklistAddParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BlocklistAddParams from) { + super(from); + } + + /** + * A person verification session of your application. Deleted sessions are refused. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Blocklist the session's face. + * + * @param blocklistFace the value + * @return this builder + */ + public Builder blocklistFace(boolean blocklistFace) { + return set("blocklist_face", Conv.BOOLEAN, blocklistFace); + } + + /** + * Blocklist the session's identity document. The document needs its issuing state, document type, document number and full name. + * + * @param blocklistDocument the value + * @return this builder + */ + public Builder blocklistDocument(boolean blocklistDocument) { + return set("blocklist_document", Conv.BOOLEAN, blocklistDocument); + } + + /** + * Blocklist the session's phone number. + * + * @param blocklistPhone the value + * @return this builder + */ + public Builder blocklistPhone(boolean blocklistPhone) { + return set("blocklist_phone", Conv.BOOLEAN, blocklistPhone); + } + + /** + * Blocklist the session's email address. + * + * @param blocklistEmail the value + * @return this builder + */ + public Builder blocklistEmail(boolean blocklistEmail) { + return set("blocklist_email", Conv.BOOLEAN, blocklistEmail); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BlocklistAddParams} + * @throws IllegalStateException when a required field is missing + */ + public BlocklistAddParams build() { + require("session_id", false); + return new BlocklistAddParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/BlocklistAddResponse.java b/src/main/java/me/didit/types/BlocklistAddResponse.java new file mode 100644 index 0000000..79987bd --- /dev/null +++ b/src/main/java/me/didit/types/BlocklistAddResponse.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class BlocklistAddResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("message"); + + private BlocklistAddResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BlocklistAddResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BlocklistAddResponse fromJson(JsonNode json) { + return new BlocklistAddResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Always Successfully updated blocklist. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String message() { + return Json.required(json, "message", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BlocklistAddResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BlocklistAddResponse from) { + super(from); + } + + /** + * Always Successfully updated blocklist. + * + * @param message the value + * @return this builder + */ + public Builder message(String message) { + return set("message", Conv.STRING, message); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BlocklistAddResponse} + * @throws IllegalStateException when a required field is missing + */ + public BlocklistAddResponse build() { + require("message", false); + return new BlocklistAddResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/BlocklistListParams.java b/src/main/java/me/didit/types/BlocklistListParams.java new file mode 100644 index 0000000..c311924 --- /dev/null +++ b/src/main/java/me/didit/types/BlocklistListParams.java @@ -0,0 +1,188 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code blocklist.list}. */ +public final class BlocklistListParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("item_type", "limit", "offset"); + + private BlocklistListParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BlocklistListParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BlocklistListParams fromJson(JsonNode json) { + return new BlocklistListParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Only items of this type. When omitted, every type is returned. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional itemType() { + return Json.optional(json, "item_type", Conv.enumeration(BlocklistListParams.ItemType::of)); + } + + /** + * Page size. Defaults to 50. A value above 1000000 returns 400; zero, a negative or a non-numeric value falls back to 50. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Zero-based index of the first item to return. Prefer following next. A value above 1000000 returns 400; a negative or non-numeric value falls back to 0. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BlocklistListParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BlocklistListParams from) { + super(from); + } + + /** + * Only items of this type. When omitted, every type is returned. + * + * @param itemType the value + * @return this builder + */ + public Builder itemType(BlocklistListParams.ItemType itemType) { + return set("item_type", Conv.enumeration(BlocklistListParams.ItemType::of), itemType); + } + + /** + * Page size. Defaults to 50. A value above 1000000 returns 400; zero, a negative or a non-numeric value falls back to 50. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Zero-based index of the first item to return. Prefer following next. A value above 1000000 returns 400; a negative or non-numeric value falls back to 0. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BlocklistListParams} + */ + public BlocklistListParams build() { + return new BlocklistListParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ItemType extends StringEnum { + /** The value {@code "face"}. */ + public static final ItemType FACE = new ItemType("face"); + + /** The value {@code "document"}. */ + public static final ItemType DOCUMENT = new ItemType("document"); + + /** The value {@code "phone"}. */ + public static final ItemType PHONE = new ItemType("phone"); + + /** The value {@code "email"}. */ + public static final ItemType EMAIL = new ItemType("email"); + + private static final List KNOWN_VALUES = List.of(FACE, DOCUMENT, PHONE, EMAIL); + + private ItemType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ItemType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ItemType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/BlocklistListResponse.java b/src/main/java/me/didit/types/BlocklistListResponse.java new file mode 100644 index 0000000..6b9b3c2 --- /dev/null +++ b/src/main/java/me/didit/types/BlocklistListResponse.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class BlocklistListResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private BlocklistListResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BlocklistListResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BlocklistListResponse fromJson(JsonNode json) { + return new BlocklistListResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Total number of blocklisted items matching the filters. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(BlocklistedItem::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BlocklistListResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BlocklistListResponse from) { + super(from); + } + + /** + * Total number of blocklisted items matching the filters. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(BlocklistedItem::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BlocklistListResponse} + * @throws IllegalStateException when a required field is missing + */ + public BlocklistListResponse build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new BlocklistListResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/BlocklistRemoveParams.java b/src/main/java/me/didit/types/BlocklistRemoveParams.java new file mode 100644 index 0000000..0cabf25 --- /dev/null +++ b/src/main/java/me/didit/types/BlocklistRemoveParams.java @@ -0,0 +1,181 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code blocklist.remove}. */ +public final class BlocklistRemoveParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "unblock_face", "unblock_document", "unblock_phone", "unblock_email"); + + private BlocklistRemoveParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BlocklistRemoveParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BlocklistRemoveParams fromJson(JsonNode json) { + return new BlocklistRemoveParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * A person verification session of your application. Deleted sessions are refused. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Lift the block on the session's face. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional unblockFace() { + return Json.optional(json, "unblock_face", Conv.BOOLEAN); + } + + /** + * Lift the block on the session's identity document. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional unblockDocument() { + return Json.optional(json, "unblock_document", Conv.BOOLEAN); + } + + /** + * Lift the block on the session's phone number. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional unblockPhone() { + return Json.optional(json, "unblock_phone", Conv.BOOLEAN); + } + + /** + * Lift the block on the session's email address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional unblockEmail() { + return Json.optional(json, "unblock_email", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BlocklistRemoveParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BlocklistRemoveParams from) { + super(from); + } + + /** + * A person verification session of your application. Deleted sessions are refused. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Lift the block on the session's face. + * + * @param unblockFace the value + * @return this builder + */ + public Builder unblockFace(boolean unblockFace) { + return set("unblock_face", Conv.BOOLEAN, unblockFace); + } + + /** + * Lift the block on the session's identity document. + * + * @param unblockDocument the value + * @return this builder + */ + public Builder unblockDocument(boolean unblockDocument) { + return set("unblock_document", Conv.BOOLEAN, unblockDocument); + } + + /** + * Lift the block on the session's phone number. + * + * @param unblockPhone the value + * @return this builder + */ + public Builder unblockPhone(boolean unblockPhone) { + return set("unblock_phone", Conv.BOOLEAN, unblockPhone); + } + + /** + * Lift the block on the session's email address. + * + * @param unblockEmail the value + * @return this builder + */ + public Builder unblockEmail(boolean unblockEmail) { + return set("unblock_email", Conv.BOOLEAN, unblockEmail); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BlocklistRemoveParams} + * @throws IllegalStateException when a required field is missing + */ + public BlocklistRemoveParams build() { + require("session_id", false); + return new BlocklistRemoveParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/BlocklistRemoveResponse.java b/src/main/java/me/didit/types/BlocklistRemoveResponse.java new file mode 100644 index 0000000..0527be1 --- /dev/null +++ b/src/main/java/me/didit/types/BlocklistRemoveResponse.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class BlocklistRemoveResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("message"); + + private BlocklistRemoveResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BlocklistRemoveResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BlocklistRemoveResponse fromJson(JsonNode json) { + return new BlocklistRemoveResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Always Successfully removed from blocklist. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String message() { + return Json.required(json, "message", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BlocklistRemoveResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BlocklistRemoveResponse from) { + super(from); + } + + /** + * Always Successfully removed from blocklist. + * + * @param message the value + * @return this builder + */ + public Builder message(String message) { + return set("message", Conv.STRING, message); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BlocklistRemoveResponse} + * @throws IllegalStateException when a required field is missing + */ + public BlocklistRemoveResponse build() { + require("message", false); + return new BlocklistRemoveResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/BlocklistedItem.java b/src/main/java/me/didit/types/BlocklistedItem.java new file mode 100644 index 0000000..48e369d --- /dev/null +++ b/src/main/java/me/didit/types/BlocklistedItem.java @@ -0,0 +1,365 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One blocklisted face, identity document, phone number or email address of a verification session. Fields that do not apply to the item's type are null. + */ +public final class BlocklistedItem extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "session_id", "session_number", "blocklisted_at", "face_image", "document_type", "document_number", "front_image", "phone_number", "country_code", "email"); + + private BlocklistedItem(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BlocklistedItem} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BlocklistedItem fromJson(JsonNode json) { + return new BlocklistedItem(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * What was blocklisted. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public BlocklistedItem.Type type() { + return Json.required(json, "type", Conv.enumeration(BlocklistedItem.Type::of)); + } + + /** + * Verification session the item belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Application-scoped number of that session, as shown in the console. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionNumber() { + return Json.optional(json, "session_number", Conv.LONG); + } + + /** + * When the item was last blocklisted. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime blocklistedAt() { + return Json.required(json, "blocklisted_at", Conv.DATE_TIME); + } + + /** + * Face items: short-lived signed URL, issued again on every call; null for a scoped API key without media: read. Also null when no reference image is stored. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional faceImage() { + return Json.optional(json, "face_image", Conv.STRING); + } + + /** + * Document items: document type code, for example P (passport), ID (national ID), DL (driver's license) or RP (residence permit). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentType() { + return Json.optional(json, "document_type", Conv.STRING); + } + + /** + * Document items: the document number, or the personal number when the document has none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentNumber() { + return Json.optional(json, "document_number", Conv.STRING); + } + + /** + * Document items: front image of the document, short-lived signed URL, issued again on every call; null for a scoped API key without media: read. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional frontImage() { + return Json.optional(json, "front_image", Conv.STRING); + } + + /** + * Phone items: the phone number in E.164. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneNumber() { + return Json.optional(json, "phone_number", Conv.STRING); + } + + /** + * Phone items: ISO 3166-1 alpha-2 country of the number. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countryCode() { + return Json.optional(json, "country_code", Conv.STRING); + } + + /** + * Email items: the email address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BlocklistedItem}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BlocklistedItem from) { + super(from); + } + + /** + * What was blocklisted. + * + * @param type the value + * @return this builder + */ + public Builder type(BlocklistedItem.Type type) { + return set("type", Conv.enumeration(BlocklistedItem.Type::of), type); + } + + /** + * Verification session the item belongs to. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Application-scoped number of that session, as shown in the console. + * + * @param sessionNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionNumber(@Nullable Long sessionNumber) { + return setNullable("session_number", Conv.LONG, sessionNumber); + } + + /** + * When the item was last blocklisted. + * + * @param blocklistedAt the value + * @return this builder + */ + public Builder blocklistedAt(OffsetDateTime blocklistedAt) { + return set("blocklisted_at", Conv.DATE_TIME, blocklistedAt); + } + + /** + * Face items: short-lived signed URL, issued again on every call; null for a scoped API key without media: read. Also null when no reference image is stored. + * + * @param faceImage the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder faceImage(@Nullable String faceImage) { + return setNullable("face_image", Conv.STRING, faceImage); + } + + /** + * Document items: document type code, for example P (passport), ID (national ID), DL (driver's license) or RP (residence permit). + * + * @param documentType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder documentType(@Nullable String documentType) { + return setNullable("document_type", Conv.STRING, documentType); + } + + /** + * Document items: the document number, or the personal number when the document has none. + * + * @param documentNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder documentNumber(@Nullable String documentNumber) { + return setNullable("document_number", Conv.STRING, documentNumber); + } + + /** + * Document items: front image of the document, short-lived signed URL, issued again on every call; null for a scoped API key without media: read. + * + * @param frontImage the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder frontImage(@Nullable String frontImage) { + return setNullable("front_image", Conv.STRING, frontImage); + } + + /** + * Phone items: the phone number in E.164. + * + * @param phoneNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder phoneNumber(@Nullable String phoneNumber) { + return setNullable("phone_number", Conv.STRING, phoneNumber); + } + + /** + * Phone items: ISO 3166-1 alpha-2 country of the number. + * + * @param countryCode the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder countryCode(@Nullable String countryCode) { + return setNullable("country_code", Conv.STRING, countryCode); + } + + /** + * Email items: the email address. + * + * @param email the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder email(@Nullable String email) { + return setNullable("email", Conv.STRING, email); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BlocklistedItem} + * @throws IllegalStateException when a required field is missing + */ + public BlocklistedItem build() { + require("type", false); + require("session_id", false); + require("session_number", true); + require("blocklisted_at", false); + require("face_image", true); + require("document_type", true); + require("document_number", true); + require("front_image", true); + require("phone_number", true); + require("country_code", true); + require("email", true); + return new BlocklistedItem(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * What was blocklisted. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "face"}. */ + public static final Type FACE = new Type("face"); + + /** The value {@code "document"}. */ + public static final Type DOCUMENT = new Type("document"); + + /** The value {@code "phone"}. */ + public static final Type PHONE = new Type("phone"); + + /** The value {@code "email"}. */ + public static final Type EMAIL = new Type("email"); + + private static final List KNOWN_VALUES = List.of(FACE, DOCUMENT, PHONE, EMAIL); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/BrandingCustomization.java b/src/main/java/me/didit/types/BrandingCustomization.java index 4e217d2..127680c 100644 --- a/src/main/java/me/didit/types/BrandingCustomization.java +++ b/src/main/java/me/didit/types/BrandingCustomization.java @@ -17,7 +17,7 @@ * White-label branding for an application: colors, fonts, radii, logo URLs and UI/UX options. Logo URLs are read-only (set them by uploading images). Custom domain and white-label email are not included. */ public final class BrandingCustomization extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("color_primary", "color_secondary", "color_background", "color_panel", "color_panel_10", "color_on_panel_1", "color_on_panel_2", "color_on_background", "color_button_1", "color_button_2", "color_button_text_1", "color_button_text_2", "color_pill_text", "border_radius_panel", "border_radius_buttons", "font_family", "font_weight", "font_url", "logo_square", "logo_rectangular", "favicon", "app_public_name", "privacy_policy_url", "skip_welcome_screen", "disable_login_with_didit", "hide_progress_bar", "callback_seconds"); + private static final Set KNOWN_FIELDS = Set.of("color_primary", "color_secondary", "color_background", "color_panel", "color_panel_10", "color_on_panel_1", "color_on_panel_2", "color_on_background", "color_button_1", "color_button_2", "color_button_text_1", "color_button_text_2", "color_pill_text", "border_radius_panel", "border_radius_buttons", "font_family", "font_weight", "font_url", "logo_square", "logo_rectangular", "logo_rectangular_dark", "favicon", "app_public_name", "privacy_policy_url", "skip_welcome_screen", "disable_login_with_didit", "hide_progress_bar", "callback_seconds"); private BrandingCustomization(ObjectNode json, Map> files) { super(json, files); @@ -252,6 +252,16 @@ public Optional logoRectangular() { return Json.optional(json, "logo_rectangular", Conv.STRING); } + /** + * Read-only. Temporary signed URL of the rectangular logo for dark mode, shown on a dark background. null when no dark logo is set or rectangular logos are not shown for your account. Upload it with image_rectangular_dark. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional logoRectangularDark() { + return Json.optional(json, "logo_rectangular_dark", Conv.STRING); + } + /** * Read-only URL of the favicon. Upload via image_favicon. * @@ -535,6 +545,16 @@ public Builder logoRectangular(@Nullable String logoRectangular) { return setNullable("logo_rectangular", Conv.STRING, logoRectangular); } + /** + * Read-only. Temporary signed URL of the rectangular logo for dark mode, shown on a dark background. null when no dark logo is set or rectangular logos are not shown for your account. Upload it with image_rectangular_dark. + * + * @param logoRectangularDark the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder logoRectangularDark(@Nullable String logoRectangularDark) { + return setNullable("logo_rectangular_dark", Conv.STRING, logoRectangularDark); + } + /** * Read-only URL of the favicon. Upload via image_favicon. * diff --git a/src/main/java/me/didit/types/BrandingCustomizationUpdate.java b/src/main/java/me/didit/types/BrandingCustomizationUpdate.java index bf68691..c0e2521 100644 --- a/src/main/java/me/didit/types/BrandingCustomizationUpdate.java +++ b/src/main/java/me/didit/types/BrandingCustomizationUpdate.java @@ -17,7 +17,7 @@ * Partial branding update body. Provide only the fields you want to change. Hex colors accept #RRGGBB or #RRGGBBAA. Custom domain and white-label email are not accepted here. */ public final class BrandingCustomizationUpdate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("color_primary", "color_secondary", "color_background", "color_panel", "color_panel_10", "color_on_panel_1", "color_on_panel_2", "color_on_background", "color_button_1", "color_button_2", "color_button_text_1", "color_button_text_2", "color_pill_text", "border_radius_panel", "border_radius_buttons", "font_family", "font_weight", "app_public_name", "privacy_policy_url", "skip_welcome_screen", "disable_login_with_didit", "hide_progress_bar", "callback_seconds", "delete_logo_square", "delete_logo_rectangular", "delete_favicon"); + private static final Set KNOWN_FIELDS = Set.of("color_primary", "color_secondary", "color_background", "color_panel", "color_panel_10", "color_on_panel_1", "color_on_panel_2", "color_on_background", "color_button_1", "color_button_2", "color_button_text_1", "color_button_text_2", "color_pill_text", "border_radius_panel", "border_radius_buttons", "font_family", "font_weight", "app_public_name", "privacy_policy_url", "skip_welcome_screen", "disable_login_with_didit", "hide_progress_bar", "callback_seconds", "delete_logo_square", "delete_logo_rectangular", "delete_logo_rectangular_dark", "delete_favicon"); private BrandingCustomizationUpdate(ObjectNode json, Map> files) { super(json, files); @@ -302,6 +302,16 @@ public Optional deleteLogoRectangular() { return Json.optional(json, "delete_logo_rectangular", Conv.BOOLEAN); } + /** + * When true, removes the current dark-mode logo, and logo_rectangular_dark then reads null. If the same multipart request also uploads image_rectangular_dark, the upload wins. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deleteLogoRectangularDark() { + return Json.optional(json, "delete_logo_rectangular_dark", Conv.BOOLEAN); + } + /** * When true, removes the current favicon. * @@ -575,6 +585,16 @@ public Builder deleteLogoRectangular(boolean deleteLogoRectangular) { return set("delete_logo_rectangular", Conv.BOOLEAN, deleteLogoRectangular); } + /** + * When true, removes the current dark-mode logo, and logo_rectangular_dark then reads null. If the same multipart request also uploads image_rectangular_dark, the upload wins. + * + * @param deleteLogoRectangularDark the value + * @return this builder + */ + public Builder deleteLogoRectangularDark(boolean deleteLogoRectangularDark) { + return set("delete_logo_rectangular_dark", Conv.BOOLEAN, deleteLogoRectangularDark); + } + /** * When true, removes the current favicon. * diff --git a/src/main/java/me/didit/types/BusinessDataUpdatedEvent.java b/src/main/java/me/didit/types/BusinessDataUpdatedEvent.java new file mode 100644 index 0000000..c8f8705 --- /dev/null +++ b/src/main/java/me/didit/types/BusinessDataUpdatedEvent.java @@ -0,0 +1,584 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Fields of a business changed. Not sent when the business is created. One save can produce both a status and a data event. + */ +public final class BusinessDataUpdatedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "webhook_type", "vendor_business_id", "vendor_data", "status", "changed_fields", "changes"); + + private BusinessDataUpdatedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BusinessDataUpdatedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BusinessDataUpdatedEvent fromJson(JsonNode json) { + return new BusinessDataUpdatedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * Always business.data.updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public BusinessDataUpdatedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(BusinessDataUpdatedEvent.WebhookType::of)); + } + + /** + * The business the event is about. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String vendorBusinessId() { + return Json.required(json, "vendor_business_id", Conv.STRING); + } + + /** + * Your vendor_data for the business, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * Business status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public BusinessDataUpdatedEvent.Status status() { + return Json.required(json, "status", Conv.enumeration(BusinessDataUpdatedEvent.Status::of)); + } + + /** + * Names of the fields that changed. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List changedFields() { + return Json.required(json, "changed_fields", Conv.list(Conv.enumeration(BusinessDataUpdatedEvent.ChangedFields::of))); + } + + /** + * Previous and current value of each changed field, keyed by field name. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map changes() { + return Json.required(json, "changes", Conv.map(Conv.model(BusinessDataUpdatedEvent.ChangesValue::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BusinessDataUpdatedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BusinessDataUpdatedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * Always business.data.updated. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(BusinessDataUpdatedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(BusinessDataUpdatedEvent.WebhookType::of), webhookType); + } + + /** + * The business the event is about. + * + * @param vendorBusinessId the value + * @return this builder + */ + public Builder vendorBusinessId(String vendorBusinessId) { + return set("vendor_business_id", Conv.STRING, vendorBusinessId); + } + + /** + * Your vendor_data for the business, or null. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * Business status after the change. + * + * @param status the value + * @return this builder + */ + public Builder status(BusinessDataUpdatedEvent.Status status) { + return set("status", Conv.enumeration(BusinessDataUpdatedEvent.Status::of), status); + } + + /** + * Names of the fields that changed. + * + * @param changedFields the value + * @return this builder + */ + public Builder changedFields(List changedFields) { + return set("changed_fields", Conv.list(Conv.enumeration(BusinessDataUpdatedEvent.ChangedFields::of)), changedFields); + } + + /** + * Previous and current value of each changed field, keyed by field name. + * + * @param changes the value + * @return this builder + */ + public Builder changes(Map changes) { + return set("changes", Conv.map(Conv.model(BusinessDataUpdatedEvent.ChangesValue::fromJson)), changes); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BusinessDataUpdatedEvent} + * @throws IllegalStateException when a required field is missing + */ + public BusinessDataUpdatedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("webhook_type", false); + require("vendor_business_id", false); + require("vendor_data", true); + require("status", false); + require("changed_fields", false); + require("changes", false); + return new BusinessDataUpdatedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always business.data.updated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "business.data.updated"}. */ + public static final WebhookType BUSINESS_DATA_UPDATED = new WebhookType("business.data.updated"); + + private static final List KNOWN_VALUES = List.of(BUSINESS_DATA_UPDATED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Business status after the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final Status ACTIVE = new Status("ACTIVE"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "FLAGGED"}. */ + public static final Status FLAGGED = new Status("FLAGGED"); + + /** The value {@code "BLOCKED"}. */ + public static final Status BLOCKED = new Status("BLOCKED"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, IN_REVIEW, FLAGGED, BLOCKED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ChangedFields extends StringEnum { + /** The value {@code "vendor_data"}. */ + public static final ChangedFields VENDOR_DATA = new ChangedFields("vendor_data"); + + /** The value {@code "display_name"}. */ + public static final ChangedFields DISPLAY_NAME = new ChangedFields("display_name"); + + /** The value {@code "metadata"}. */ + public static final ChangedFields METADATA = new ChangedFields("metadata"); + + /** The value {@code "legal_name"}. */ + public static final ChangedFields LEGAL_NAME = new ChangedFields("legal_name"); + + /** The value {@code "registration_number"}. */ + public static final ChangedFields REGISTRATION_NUMBER = new ChangedFields("registration_number"); + + /** The value {@code "country_code"}. */ + public static final ChangedFields COUNTRY_CODE = new ChangedFields("country_code"); + + /** The value {@code "session_count"}. */ + public static final ChangedFields SESSION_COUNT = new ChangedFields("session_count"); + + /** The value {@code "approved_count"}. */ + public static final ChangedFields APPROVED_COUNT = new ChangedFields("approved_count"); + + /** The value {@code "declined_count"}. */ + public static final ChangedFields DECLINED_COUNT = new ChangedFields("declined_count"); + + /** The value {@code "in_review_count"}. */ + public static final ChangedFields IN_REVIEW_COUNT = new ChangedFields("in_review_count"); + + /** The value {@code "features"}. */ + public static final ChangedFields FEATURES = new ChangedFields("features"); + + /** The value {@code "first_session_at"}. */ + public static final ChangedFields FIRST_SESSION_AT = new ChangedFields("first_session_at"); + + /** The value {@code "last_session_at"}. */ + public static final ChangedFields LAST_SESSION_AT = new ChangedFields("last_session_at"); + + private static final List KNOWN_VALUES = List.of(VENDOR_DATA, DISPLAY_NAME, METADATA, LEGAL_NAME, REGISTRATION_NUMBER, COUNTRY_CODE, SESSION_COUNT, APPROVED_COUNT, DECLINED_COUNT, IN_REVIEW_COUNT, FEATURES, FIRST_SESSION_AT, LAST_SESSION_AT); + + private ChangedFields(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ChangedFields of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ChangedFields::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class ChangesValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("previous", "current"); + + private ChangesValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ChangesValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ChangesValue fromJson(JsonNode json) { + return new ChangesValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Value before the change. Dates are ISO 8601 strings. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.ANY); + } + + /** + * Value after the change. Dates are ISO 8601 strings. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional current() { + return Json.optional(json, "current", Conv.ANY); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ChangesValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ChangesValue from) { + super(from); + } + + /** + * Value before the change. Dates are ISO 8601 strings. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable Object previous) { + return setAny("previous", previous); + } + + /** + * Value after the change. Dates are ISO 8601 strings. + * + * @param current the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder current(@Nullable Object current) { + return setAny("current", current); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ChangesValue} + */ + public ChangesValue build() { + return new ChangesValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/BusinessSessionListItem.java b/src/main/java/me/didit/types/BusinessSessionListItem.java new file mode 100644 index 0000000..9e15bcf --- /dev/null +++ b/src/main/java/me/didit/types/BusinessSessionListItem.java @@ -0,0 +1,444 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Summary row for a Business Verification (KYB) session. Returned only when session_kind is business or all. + */ +public final class BusinessSessionListItem extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "session_kind", "session_number", "status", "vendor_data", "created_at", "workflow_id", "workflow_type", "workflow_label", "company_name", "registration_number", "country"); + + private BusinessSessionListItem(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BusinessSessionListItem} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BusinessSessionListItem fromJson(JsonNode json) { + return new BusinessSessionListItem(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Unique identifier of the business verification session. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Row discriminator. Always "business" for KYB rows. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public BusinessSessionListItem.SessionKind sessionKind() { + return Json.required(json, "session_kind", Conv.enumeration(BusinessSessionListItem.SessionKind::of)); + } + + /** + * Human-friendly sequential number of the business session within your application. Numbered independently from KYC sessions. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long sessionNumber() { + return Json.required(json, "session_number", Conv.LONG); + } + + /** + * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public BusinessSessionListItem.Status status() { + return Json.required(json, "status", Conv.enumeration(BusinessSessionListItem.Status::of)); + } + + /** + * Your own identifier for the business, echoed back exactly as sent at session creation. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * UTC timestamp when the session was created. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + /** + * Identifier of the workflow this session was created with. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowId() { + return Json.optional(json, "workflow_id", Conv.STRING); + } + + /** + * Workflow type. kyb for business workflows. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowType() { + return Json.optional(json, "workflow_type", Conv.STRING); + } + + /** + * Display name of the workflow. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowLabel() { + return Json.optional(json, "workflow_label", Conv.STRING); + } + + /** + * Name of the company being verified, from the registry company linked to the session. null until a company is selected. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional companyName() { + return Json.optional(json, "company_name", Conv.STRING); + } + + /** + * Company registration number from the registry company linked to the session. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional registrationNumber() { + return Json.optional(json, "registration_number", Conv.STRING); + } + + /** + * Country code of the registry company linked to the session. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BusinessSessionListItem}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BusinessSessionListItem from) { + super(from); + } + + /** + * Unique identifier of the business verification session. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Row discriminator. Always "business" for KYB rows. + * + * @param sessionKind the value + * @return this builder + */ + public Builder sessionKind(BusinessSessionListItem.SessionKind sessionKind) { + return set("session_kind", Conv.enumeration(BusinessSessionListItem.SessionKind::of), sessionKind); + } + + /** + * Human-friendly sequential number of the business session within your application. Numbered independently from KYC sessions. + * + * @param sessionNumber the value + * @return this builder + */ + public Builder sessionNumber(long sessionNumber) { + return set("session_number", Conv.LONG, sessionNumber); + } + + /** + * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page. + * + * @param status the value + * @return this builder + */ + public Builder status(BusinessSessionListItem.Status status) { + return set("status", Conv.enumeration(BusinessSessionListItem.Status::of), status); + } + + /** + * Your own identifier for the business, echoed back exactly as sent at session creation. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * UTC timestamp when the session was created. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Identifier of the workflow this session was created with. + * + * @param workflowId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder workflowId(@Nullable String workflowId) { + return setNullable("workflow_id", Conv.STRING, workflowId); + } + + /** + * Workflow type. kyb for business workflows. + * + * @param workflowType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder workflowType(@Nullable String workflowType) { + return setNullable("workflow_type", Conv.STRING, workflowType); + } + + /** + * Display name of the workflow. + * + * @param workflowLabel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder workflowLabel(@Nullable String workflowLabel) { + return setNullable("workflow_label", Conv.STRING, workflowLabel); + } + + /** + * Name of the company being verified, from the registry company linked to the session. null until a company is selected. + * + * @param companyName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder companyName(@Nullable String companyName) { + return setNullable("company_name", Conv.STRING, companyName); + } + + /** + * Company registration number from the registry company linked to the session. + * + * @param registrationNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder registrationNumber(@Nullable String registrationNumber) { + return setNullable("registration_number", Conv.STRING, registrationNumber); + } + + /** + * Country code of the registry company linked to the session. + * + * @param country the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder country(@Nullable String country) { + return setNullable("country", Conv.STRING, country); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BusinessSessionListItem} + * @throws IllegalStateException when a required field is missing + */ + public BusinessSessionListItem build() { + require("session_id", false); + require("session_kind", false); + require("session_number", false); + require("status", false); + require("vendor_data", true); + require("created_at", false); + require("workflow_id", true); + require("workflow_type", true); + require("workflow_label", true); + require("company_name", true); + require("registration_number", true); + require("country", true); + return new BusinessSessionListItem(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Row discriminator. Always "business" for KYB rows. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SessionKind extends StringEnum { + /** The value {@code "business"}. */ + public static final SessionKind BUSINESS = new SessionKind("business"); + + private static final List KNOWN_VALUES = List.of(BUSINESS); + + private SessionKind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SessionKind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SessionKind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Started"}. */ + public static final Status NOT_STARTED = new Status("Not Started"); + + /** The value {@code "In Progress"}. */ + public static final Status IN_PROGRESS = new Status("In Progress"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Expired"}. */ + public static final Status EXPIRED = new Status("Expired"); + + /** The value {@code "Abandoned"}. */ + public static final Status ABANDONED = new Status("Abandoned"); + + /** The value {@code "Kyc Expired"}. */ + public static final Status KYC_EXPIRED = new Status("Kyc Expired"); + + /** The value {@code "Resubmitted"}. */ + public static final Status RESUBMITTED = new Status("Resubmitted"); + + /** The value {@code "Awaiting User"}. */ + public static final Status AWAITING_USER = new Status("Awaiting User"); + + private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED, KYC_EXPIRED, RESUBMITTED, AWAITING_USER); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/BusinessStatusUpdatedEvent.java b/src/main/java/me/didit/types/BusinessStatusUpdatedEvent.java new file mode 100644 index 0000000..879027c --- /dev/null +++ b/src/main/java/me/didit/types/BusinessStatusUpdatedEvent.java @@ -0,0 +1,430 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** A business changed status. Not sent when the business is created. */ +public final class BusinessStatusUpdatedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "webhook_type", "vendor_business_id", "vendor_data", "status", "previous_status"); + + private BusinessStatusUpdatedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code BusinessStatusUpdatedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static BusinessStatusUpdatedEvent fromJson(JsonNode json) { + return new BusinessStatusUpdatedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * Always business.status.updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public BusinessStatusUpdatedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(BusinessStatusUpdatedEvent.WebhookType::of)); + } + + /** + * The business the event is about. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String vendorBusinessId() { + return Json.required(json, "vendor_business_id", Conv.STRING); + } + + /** + * Your vendor_data for the business, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * Business status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public BusinessStatusUpdatedEvent.Status status() { + return Json.required(json, "status", Conv.enumeration(BusinessStatusUpdatedEvent.Status::of)); + } + + /** + * Status before the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public BusinessStatusUpdatedEvent.PreviousStatus previousStatus() { + return Json.required(json, "previous_status", Conv.enumeration(BusinessStatusUpdatedEvent.PreviousStatus::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link BusinessStatusUpdatedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(BusinessStatusUpdatedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * Always business.status.updated. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(BusinessStatusUpdatedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(BusinessStatusUpdatedEvent.WebhookType::of), webhookType); + } + + /** + * The business the event is about. + * + * @param vendorBusinessId the value + * @return this builder + */ + public Builder vendorBusinessId(String vendorBusinessId) { + return set("vendor_business_id", Conv.STRING, vendorBusinessId); + } + + /** + * Your vendor_data for the business, or null. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * Business status after the change. + * + * @param status the value + * @return this builder + */ + public Builder status(BusinessStatusUpdatedEvent.Status status) { + return set("status", Conv.enumeration(BusinessStatusUpdatedEvent.Status::of), status); + } + + /** + * Status before the change. + * + * @param previousStatus the value + * @return this builder + */ + public Builder previousStatus(BusinessStatusUpdatedEvent.PreviousStatus previousStatus) { + return set("previous_status", Conv.enumeration(BusinessStatusUpdatedEvent.PreviousStatus::of), previousStatus); + } + + /** + * Builds the value. + * + * @return a new immutable {@code BusinessStatusUpdatedEvent} + * @throws IllegalStateException when a required field is missing + */ + public BusinessStatusUpdatedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("webhook_type", false); + require("vendor_business_id", false); + require("vendor_data", true); + require("status", false); + require("previous_status", false); + return new BusinessStatusUpdatedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always business.status.updated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "business.status.updated"}. */ + public static final WebhookType BUSINESS_STATUS_UPDATED = new WebhookType("business.status.updated"); + + private static final List KNOWN_VALUES = List.of(BUSINESS_STATUS_UPDATED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Business status after the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final Status ACTIVE = new Status("ACTIVE"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "FLAGGED"}. */ + public static final Status FLAGGED = new Status("FLAGGED"); + + /** The value {@code "BLOCKED"}. */ + public static final Status BLOCKED = new Status("BLOCKED"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, IN_REVIEW, FLAGGED, BLOCKED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Status before the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class PreviousStatus extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final PreviousStatus ACTIVE = new PreviousStatus("ACTIVE"); + + /** The value {@code "IN_REVIEW"}. */ + public static final PreviousStatus IN_REVIEW = new PreviousStatus("IN_REVIEW"); + + /** The value {@code "FLAGGED"}. */ + public static final PreviousStatus FLAGGED = new PreviousStatus("FLAGGED"); + + /** The value {@code "BLOCKED"}. */ + public static final PreviousStatus BLOCKED = new PreviousStatus("BLOCKED"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, IN_REVIEW, FLAGGED, BLOCKED); + + private PreviousStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static PreviousStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, PreviousStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/BusinessesRetrieveOngoingMonitoringJobResponse.java b/src/main/java/me/didit/types/BusinessesRetrieveOngoingMonitoringJobResponse.java deleted file mode 100644 index d1bde79..0000000 --- a/src/main/java/me/didit/types/BusinessesRetrieveOngoingMonitoringJobResponse.java +++ /dev/null @@ -1,782 +0,0 @@ -// Code generated by codegen from openapi/*.json. DO NOT EDIT. -package me.didit.types; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; -import me.didit.core.StringEnum; -import org.jspecify.annotations.Nullable; - -public final class BusinessesRetrieveOngoingMonitoringJobResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - - private BusinessesRetrieveOngoingMonitoringJobResponse(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code BusinessesRetrieveOngoingMonitoringJobResponse} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static BusinessesRetrieveOngoingMonitoringJobResponse fromJson(JsonNode json) { - return new BusinessesRetrieveOngoingMonitoringJobResponse(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(BusinessesRetrieveOngoingMonitoringJobResponse.Status::of)); - } - - /** - * The is_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isEnabled() { - return Json.optional(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * The total field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * The processed field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(BusinessesRetrieveOngoingMonitoringJobResponse.Outcomes::fromJson)); - } - - /** - * The error field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(BusinessesRetrieveOngoingMonitoringJobResponse.Estimate::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link BusinessesRetrieveOngoingMonitoringJobResponse}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(BusinessesRetrieveOngoingMonitoringJobResponse from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(BusinessesRetrieveOngoingMonitoringJobResponse.Status status) { - return set("status", Conv.enumeration(BusinessesRetrieveOngoingMonitoringJobResponse.Status::of), status); - } - - /** - * The is_enabled field. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * The total field. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * The processed field. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(BusinessesRetrieveOngoingMonitoringJobResponse.Outcomes outcomes) { - return set("outcomes", Conv.model(BusinessesRetrieveOngoingMonitoringJobResponse.Outcomes::fromJson), outcomes); - } - - /** - * The error field. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(BusinessesRetrieveOngoingMonitoringJobResponse.Estimate estimate) { - return set("estimate", Conv.model(BusinessesRetrieveOngoingMonitoringJobResponse.Estimate::fromJson), estimate); - } - - /** - * Builds the value. - * - * @return a new immutable {@code BusinessesRetrieveOngoingMonitoringJobResponse} - */ - public BusinessesRetrieveOngoingMonitoringJobResponse build() { - return new BusinessesRetrieveOngoingMonitoringJobResponse(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening provider call failed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out mid-job. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening provider call failed. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out mid-job. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } -} diff --git a/src/main/java/me/didit/types/BusinessesSetAmlMonitoringResponse.java b/src/main/java/me/didit/types/BusinessesSetAmlMonitoringResponse.java index 94c1114..4ea6f3f 100644 --- a/src/main/java/me/didit/types/BusinessesSetAmlMonitoringResponse.java +++ b/src/main/java/me/didit/types/BusinessesSetAmlMonitoringResponse.java @@ -2,20 +2,9 @@ package me.didit.types; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.time.OffsetDateTime; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; -import me.didit.core.JsonUnion; import me.didit.core.MultiStatusResponse; -import me.didit.core.StringEnum; -import org.jspecify.annotations.Nullable; /** * Response of {@code businesses.set_aml_monitoring}; the shape depends on the HTTP status. @@ -50,8 +39,8 @@ public boolean isOk() { * * @return the body, or empty for another status */ - public Optional asOk() { - return isOk() ? Optional.of(Conv.union(BusinessesSetAmlMonitoringResponse.Ok::fromJson).read(json)) : Optional.empty(); + public Optional asOk() { + return isOk() ? Optional.of(Conv.union(AMLMonitoringToggleResult::fromJson).read(json)) : Optional.empty(); } /** @@ -68,2583 +57,7 @@ public boolean isAccepted() { * * @return the body, or empty for another status */ - public Optional asAccepted() { - return isAccepted() ? Optional.of(Conv.model(BusinessesSetAmlMonitoringResponse.Accepted::fromJson).read(json)) : Optional.empty(); - } - - /** - * - *

One of: {@code CostPreview}, {@code Variant2}. Check with the {@code is...} - * methods and read with the {@code as...} methods; the fields every variant has are readable directly. - */ - public static final class Ok extends JsonUnion { - private Ok(JsonNode json) { - super(json); - } - - /** - * Reads a {@code Ok} from JSON. - * - * @param json any JSON value - * @return the union - */ - public static Ok fromJson(JsonNode json) { - return new Ok(json.deepCopy()); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public List unmatched() { - return Json.required(json, "unmatched", Conv.list(Conv.STRING)); - } - - /** - * Wraps a {@code CostPreview} value. - * - * @param value the value - * @return the union - */ - public static Ok ofCostPreview(BusinessesSetAmlMonitoringResponse.Ok.CostPreview value) { - return new Ok(Conv.model(BusinessesSetAmlMonitoringResponse.Ok.CostPreview::fromJson).write(value)); - } - - /** - * Whether this value is a {@code CostPreview}. - * - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @return true when it matches - */ - public boolean isCostPreview() { - return Json.hasFields(json, "users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient", "unmatched"); - } - - /** - * Reads this value as a {@code CostPreview}. - * - * @return the value, or empty when {@link #isCostPreview()} is false - */ - public Optional asCostPreview() { - return isCostPreview() ? Optional.of(Conv.model(BusinessesSetAmlMonitoringResponse.Ok.CostPreview::fromJson).read(json)) : Optional.empty(); - } - - /** - * Wraps a {@code Variant2} value. - * - * @param value the value - * @return the union - */ - public static Ok ofVariant2(BusinessesSetAmlMonitoringResponse.Ok.Variant2 value) { - return new Ok(Conv.model(BusinessesSetAmlMonitoringResponse.Ok.Variant2::fromJson).write(value)); - } - - /** - * Whether this value is a {@code Variant2}. - * - * @return true when it matches - */ - public boolean isVariant2() { - return Json.hasFields(json, "job_id", "status", "is_enabled", "target_type", "total", "processed", "outcomes", "results", "error", "created_at", "completed_at", "estimate", "unmatched"); - } - - /** - * Reads this value as a {@code Variant2}. - * - * @return the value, or empty when {@link #isVariant2()} is false - */ - public Optional asVariant2() { - return isVariant2() ? Optional.of(Conv.model(BusinessesSetAmlMonitoringResponse.Ok.Variant2::fromJson).read(json)) : Optional.empty(); - } - - /** Cost preview. Returned directly when estimate_only is true (nothing is written). */ - public static final class CostPreview extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient", "unmatched"); - - private CostPreview(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code CostPreview} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static CostPreview fromJson(JsonNode json) { - return new CostPreview(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Profiles matched by the selection. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long usersTotal() { - return Json.required(json, "users_total", Conv.LONG); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long alreadyEnabled() { - return Json.required(json, "already_enabled", Conv.LONG); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long withExistingAml() { - return Json.required(json, "with_existing_aml", Conv.LONG); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long needingScreening() { - return Json.required(json, "needing_screening", Conv.LONG); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long skippedNoIdentityData() { - return Json.required(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long skippedBlocked() { - return Json.required(json, "skipped_blocked", Conv.LONG); - } - - /** - * One-time cost of the new screenings. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public double estimatedScreeningCostUsd() { - return Json.required(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public double estimatedAnnualMonitoringCostUsd() { - return Json.required(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public boolean balanceSufficient() { - return Json.required(json, "balance_sufficient", Conv.BOOLEAN); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public List unmatched() { - return Json.required(json, "unmatched", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link CostPreview}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(CostPreview from) { - super(from); - } - - /** - * Profiles matched by the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * One-time cost of the new screenings. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. - * - * @param unmatched the value - * @return this builder - */ - public Builder unmatched(List unmatched) { - return set("unmatched", Conv.list(Conv.STRING), unmatched); - } - - /** - * Builds the value. - * - * @return a new immutable {@code CostPreview} - * @throws IllegalStateException when a required field is missing - */ - public CostPreview build() { - require("users_total", false); - require("already_enabled", false); - require("with_existing_aml", false); - require("needing_screening", false); - require("skipped_no_identity_data", false); - require("skipped_blocked", false); - require("estimated_screening_cost_usd", false); - require("estimated_annual_monitoring_cost_usd", false); - require("balance_sufficient", false); - require("unmatched", false); - return new CostPreview(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Variant2 extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "target_type", "total", "processed", "outcomes", "results", "error", "created_at", "completed_at", "estimate", "unmatched"); - - private Variant2(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Variant2} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Variant2 fromJson(JsonNode json) { - return new Variant2(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String jobId() { - return Json.required(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public BusinessesSetAmlMonitoringResponse.Ok.Variant2.Status status() { - return Json.required(json, "status", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Status::of)); - } - - /** - * true when the job enables monitoring, false when it disables it. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public boolean isEnabled() { - return Json.required(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * Which profile family the job toggles. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public BusinessesSetAmlMonitoringResponse.Ok.Variant2.TargetType targetType() { - return Json.required(json, "target_type", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Ok.Variant2.TargetType::of)); - } - - /** - * Profiles the job processes. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long total() { - return Json.required(json, "total", Conv.LONG); - } - - /** - * Profiles processed so far. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long processed() { - return Json.required(json, "processed", Conv.LONG); - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public BusinessesSetAmlMonitoringResponse.Ok.Variant2.Outcomes outcomes() { - return Json.required(json, "outcomes", Conv.model(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Outcomes::fromJson)); - } - - /** - * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public List results() { - return Json.required(json, "results", Conv.list(Conv.model(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Results::fromJson))); - } - - /** - * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * The created_at field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public OffsetDateTime createdAt() { - return Json.required(json, "created_at", Conv.DATE_TIME); - } - - /** - * The completed_at field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional completedAt() { - return Json.optional(json, "completed_at", Conv.DATE_TIME); - } - - /** - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public BusinessesSetAmlMonitoringResponse.Ok.Variant2.Estimate estimate() { - return Json.required(json, "estimate", Conv.model(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Estimate::fromJson)); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public List unmatched() { - return Json.required(json, "unmatched", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Variant2}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Variant2 from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Status status) { - return set("status", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Status::of), status); - } - - /** - * true when the job enables monitoring, false when it disables it. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * Which profile family the job toggles. - * - * @param targetType the value - * @return this builder - */ - public Builder targetType(BusinessesSetAmlMonitoringResponse.Ok.Variant2.TargetType targetType) { - return set("target_type", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Ok.Variant2.TargetType::of), targetType); - } - - /** - * Profiles the job processes. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * Profiles processed so far. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Outcomes outcomes) { - return set("outcomes", Conv.model(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Outcomes::fromJson), outcomes); - } - - /** - * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. - * - * @param results the value - * @return this builder - */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Results::fromJson)), results); - } - - /** - * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * The created_at field. - * - * @param createdAt the value - * @return this builder - */ - public Builder createdAt(OffsetDateTime createdAt) { - return set("created_at", Conv.DATE_TIME, createdAt); - } - - /** - * The completed_at field. - * - * @param completedAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder completedAt(@Nullable OffsetDateTime completedAt) { - return setNullable("completed_at", Conv.DATE_TIME, completedAt); - } - - /** - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Estimate estimate) { - return set("estimate", Conv.model(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Estimate::fromJson), estimate); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. - * - * @param unmatched the value - * @return this builder - */ - public Builder unmatched(List unmatched) { - return set("unmatched", Conv.list(Conv.STRING), unmatched); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Variant2} - * @throws IllegalStateException when a required field is missing - */ - public Variant2 build() { - require("job_id", false); - require("status", false); - require("is_enabled", false); - require("target_type", false); - require("total", false); - require("processed", false); - require("outcomes", false); - require("results", false); - require("error", true); - require("created_at", false); - require("completed_at", true); - require("estimate", false); - require("unmatched", false); - return new Variant2(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Which profile family the job toggles. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class TargetType extends StringEnum { - /** The value {@code "user"}. */ - public static final TargetType USER = new TargetType("user"); - - /** The value {@code "business"}. */ - public static final TargetType BUSINESS = new TargetType("business"); - - private static final List KNOWN_VALUES = List.of(USER, BUSINESS); - - private TargetType(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static TargetType of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, TargetType::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled on it. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active; nothing changed and nothing was billed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring turned off across every AML screening of the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application, no screening is created. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening could not be completed; retry later. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out while the job was running. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled on it. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active; nothing changed and nothing was billed. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring turned off across every AML screening of the profile. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application, no screening is created. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening could not be completed; retry later. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out while the job was running. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Results extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("didit_internal_id", "vendor_data", "outcome"); - - private Results(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Results} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Results fromJson(JsonNode json) { - return new Results(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Didit internal id of the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional diditInternalId() { - return Json.optional(json, "didit_internal_id", Conv.STRING); - } - - /** - * Your own identifier for the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); - } - - /** - * The outcome field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcome() { - return Json.optional(json, "outcome", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Results.Outcome::of)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Results}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Results from) { - super(from); - } - - /** - * Didit internal id of the profile. - * - * @param diditInternalId the value - * @return this builder - */ - public Builder diditInternalId(String diditInternalId) { - return set("didit_internal_id", Conv.STRING, diditInternalId); - } - - /** - * Your own identifier for the profile. - * - * @param vendorData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder vendorData(@Nullable String vendorData) { - return setNullable("vendor_data", Conv.STRING, vendorData); - } - - /** - * The outcome field. - * - * @param outcome the value - * @return this builder - */ - public Builder outcome(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Results.Outcome outcome) { - return set("outcome", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Ok.Variant2.Results.Outcome::of), outcome); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Results} - */ - public Results build() { - return new Results(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Outcome extends StringEnum { - /** The value {@code "enabled"}. */ - public static final Outcome ENABLED = new Outcome("enabled"); - - /** The value {@code "screened_and_enabled"}. */ - public static final Outcome SCREENED_AND_ENABLED = new Outcome("screened_and_enabled"); - - /** The value {@code "already_enabled"}. */ - public static final Outcome ALREADY_ENABLED = new Outcome("already_enabled"); - - /** The value {@code "disabled"}. */ - public static final Outcome DISABLED = new Outcome("disabled"); - - /** The value {@code "skipped_no_identity_data"}. */ - public static final Outcome SKIPPED_NO_IDENTITY_DATA = new Outcome("skipped_no_identity_data"); - - /** The value {@code "skipped_blocked"}. */ - public static final Outcome SKIPPED_BLOCKED = new Outcome("skipped_blocked"); - - /** The value {@code "skipped_sandbox"}. */ - public static final Outcome SKIPPED_SANDBOX = new Outcome("skipped_sandbox"); - - /** The value {@code "failed_provider"}. */ - public static final Outcome FAILED_PROVIDER = new Outcome("failed_provider"); - - /** The value {@code "failed_insufficient_balance"}. */ - public static final Outcome FAILED_INSUFFICIENT_BALANCE = new Outcome("failed_insufficient_balance"); - - private static final List KNOWN_VALUES = List.of(ENABLED, SCREENED_AND_ENABLED, ALREADY_ENABLED, DISABLED, SKIPPED_NO_IDENTITY_DATA, SKIPPED_BLOCKED, SKIPPED_SANDBOX, FAILED_PROVIDER, FAILED_INSUFFICIENT_BALANCE); - - private Outcome(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Outcome of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (nothing is written). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Profiles matched by the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * One-time cost of the new screenings. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Profiles matched by the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * One-time cost of the new screenings. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - } - - public static final class Accepted extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "target_type", "total", "processed", "outcomes", "results", "error", "created_at", "completed_at", "estimate", "unmatched"); - - private Accepted(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Accepted} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Accepted fromJson(JsonNode json) { - return new Accepted(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Accepted.Status::of)); - } - - /** - * true when the job enables monitoring, false when it disables it. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isEnabled() { - return Json.optional(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * Which profile family the job toggles. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional targetType() { - return Json.optional(json, "target_type", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Accepted.TargetType::of)); - } - - /** - * Profiles the job processes. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * Profiles processed so far. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(BusinessesSetAmlMonitoringResponse.Accepted.Outcomes::fromJson)); - } - - /** - * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(BusinessesSetAmlMonitoringResponse.Accepted.Results::fromJson))); - } - - /** - * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * The created_at field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional createdAt() { - return Json.optional(json, "created_at", Conv.DATE_TIME); - } - - /** - * The completed_at field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional completedAt() { - return Json.optional(json, "completed_at", Conv.DATE_TIME); - } - - /** - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(BusinessesSetAmlMonitoringResponse.Accepted.Estimate::fromJson)); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> unmatched() { - return Json.optional(json, "unmatched", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Accepted}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Accepted from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(BusinessesSetAmlMonitoringResponse.Accepted.Status status) { - return set("status", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Accepted.Status::of), status); - } - - /** - * true when the job enables monitoring, false when it disables it. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * Which profile family the job toggles. - * - * @param targetType the value - * @return this builder - */ - public Builder targetType(BusinessesSetAmlMonitoringResponse.Accepted.TargetType targetType) { - return set("target_type", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Accepted.TargetType::of), targetType); - } - - /** - * Profiles the job processes. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * Profiles processed so far. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(BusinessesSetAmlMonitoringResponse.Accepted.Outcomes outcomes) { - return set("outcomes", Conv.model(BusinessesSetAmlMonitoringResponse.Accepted.Outcomes::fromJson), outcomes); - } - - /** - * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. - * - * @param results the value - * @return this builder - */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(BusinessesSetAmlMonitoringResponse.Accepted.Results::fromJson)), results); - } - - /** - * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * The created_at field. - * - * @param createdAt the value - * @return this builder - */ - public Builder createdAt(OffsetDateTime createdAt) { - return set("created_at", Conv.DATE_TIME, createdAt); - } - - /** - * The completed_at field. - * - * @param completedAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder completedAt(@Nullable OffsetDateTime completedAt) { - return setNullable("completed_at", Conv.DATE_TIME, completedAt); - } - - /** - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(BusinessesSetAmlMonitoringResponse.Accepted.Estimate estimate) { - return set("estimate", Conv.model(BusinessesSetAmlMonitoringResponse.Accepted.Estimate::fromJson), estimate); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. - * - * @param unmatched the value - * @return this builder - */ - public Builder unmatched(List unmatched) { - return set("unmatched", Conv.list(Conv.STRING), unmatched); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Accepted} - */ - public Accepted build() { - return new Accepted(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Which profile family the job toggles. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class TargetType extends StringEnum { - /** The value {@code "user"}. */ - public static final TargetType USER = new TargetType("user"); - - /** The value {@code "business"}. */ - public static final TargetType BUSINESS = new TargetType("business"); - - private static final List KNOWN_VALUES = List.of(USER, BUSINESS); - - private TargetType(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static TargetType of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, TargetType::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled on it. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active; nothing changed and nothing was billed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring turned off across every AML screening of the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application, no screening is created. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening could not be completed; retry later. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out while the job was running. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled on it. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active; nothing changed and nothing was billed. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring turned off across every AML screening of the profile. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application, no screening is created. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening could not be completed; retry later. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out while the job was running. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Results extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("didit_internal_id", "vendor_data", "outcome"); - - private Results(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Results} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Results fromJson(JsonNode json) { - return new Results(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Didit internal id of the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional diditInternalId() { - return Json.optional(json, "didit_internal_id", Conv.STRING); - } - - /** - * Your own identifier for the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); - } - - /** - * The outcome field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcome() { - return Json.optional(json, "outcome", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Accepted.Results.Outcome::of)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Results}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Results from) { - super(from); - } - - /** - * Didit internal id of the profile. - * - * @param diditInternalId the value - * @return this builder - */ - public Builder diditInternalId(String diditInternalId) { - return set("didit_internal_id", Conv.STRING, diditInternalId); - } - - /** - * Your own identifier for the profile. - * - * @param vendorData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder vendorData(@Nullable String vendorData) { - return setNullable("vendor_data", Conv.STRING, vendorData); - } - - /** - * The outcome field. - * - * @param outcome the value - * @return this builder - */ - public Builder outcome(BusinessesSetAmlMonitoringResponse.Accepted.Results.Outcome outcome) { - return set("outcome", Conv.enumeration(BusinessesSetAmlMonitoringResponse.Accepted.Results.Outcome::of), outcome); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Results} - */ - public Results build() { - return new Results(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Outcome extends StringEnum { - /** The value {@code "enabled"}. */ - public static final Outcome ENABLED = new Outcome("enabled"); - - /** The value {@code "screened_and_enabled"}. */ - public static final Outcome SCREENED_AND_ENABLED = new Outcome("screened_and_enabled"); - - /** The value {@code "already_enabled"}. */ - public static final Outcome ALREADY_ENABLED = new Outcome("already_enabled"); - - /** The value {@code "disabled"}. */ - public static final Outcome DISABLED = new Outcome("disabled"); - - /** The value {@code "skipped_no_identity_data"}. */ - public static final Outcome SKIPPED_NO_IDENTITY_DATA = new Outcome("skipped_no_identity_data"); - - /** The value {@code "skipped_blocked"}. */ - public static final Outcome SKIPPED_BLOCKED = new Outcome("skipped_blocked"); - - /** The value {@code "skipped_sandbox"}. */ - public static final Outcome SKIPPED_SANDBOX = new Outcome("skipped_sandbox"); - - /** The value {@code "failed_provider"}. */ - public static final Outcome FAILED_PROVIDER = new Outcome("failed_provider"); - - /** The value {@code "failed_insufficient_balance"}. */ - public static final Outcome FAILED_INSUFFICIENT_BALANCE = new Outcome("failed_insufficient_balance"); - - private static final List KNOWN_VALUES = List.of(ENABLED, SCREENED_AND_ENABLED, ALREADY_ENABLED, DISABLED, SKIPPED_NO_IDENTITY_DATA, SKIPPED_BLOCKED, SKIPPED_SANDBOX, FAILED_PROVIDER, FAILED_INSUFFICIENT_BALANCE); - - private Outcome(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Outcome of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (nothing is written). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Profiles matched by the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * One-time cost of the new screenings. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Profiles matched by the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * One-time cost of the new screenings. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } + public Optional asAccepted() { + return isAccepted() ? Optional.of(Conv.model(AMLMonitoringJob::fromJson).read(json)) : Optional.empty(); } } diff --git a/src/main/java/me/didit/types/BusinessesSetOngoingMonitoringParams.java b/src/main/java/me/didit/types/BusinessesSetOngoingMonitoringParams.java deleted file mode 100644 index a2d2945..0000000 --- a/src/main/java/me/didit/types/BusinessesSetOngoingMonitoringParams.java +++ /dev/null @@ -1,262 +0,0 @@ -// Code generated by codegen from openapi/*.json. DO NOT EDIT. -package me.didit.types; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.time.LocalDate; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; - -/** Parameters for {@code businesses.set_ongoing_monitoring}. */ -public final class BusinessesSetOngoingMonitoringParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("is_enabled", "estimate_only", "update_all", "filter_search", "filter_status", "filter_country", "filter_date_from", "filter_date_to", "didit_internal_id_list"); - - private BusinessesSetOngoingMonitoringParams(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code BusinessesSetOngoingMonitoringParams} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static BusinessesSetOngoingMonitoringParams fromJson(JsonNode json) { - return new BusinessesSetOngoingMonitoringParams(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * true to enable monitoring, false to disable it. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public boolean isEnabled() { - return Json.required(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * When true, returns the cost estimate and performs no writes. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimateOnly() { - return Json.optional(json, "estimate_only", Conv.BOOLEAN); - } - - /** - * Apply to every entity of the application (scoped by the filter_* params when present). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional updateAll() { - return Json.optional(json, "update_all", Conv.BOOLEAN); - } - - /** - * The filter_search field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional filterSearch() { - return Json.optional(json, "filter_search", Conv.STRING); - } - - /** - * The filter_status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> filterStatus() { - return Json.optional(json, "filter_status", Conv.list(Conv.STRING)); - } - - /** - * The filter_country field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional filterCountry() { - return Json.optional(json, "filter_country", Conv.STRING); - } - - /** - * The filter_date_from field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional filterDateFrom() { - return Json.optional(json, "filter_date_from", Conv.DATE); - } - - /** - * The filter_date_to field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional filterDateTo() { - return Json.optional(json, "filter_date_to", Conv.DATE); - } - - /** - * Entities to toggle. Required unless update_all is true. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> diditInternalIdList() { - return Json.optional(json, "didit_internal_id_list", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link BusinessesSetOngoingMonitoringParams}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(BusinessesSetOngoingMonitoringParams from) { - super(from); - } - - /** - * true to enable monitoring, false to disable it. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * When true, returns the cost estimate and performs no writes. - * - * @param estimateOnly the value - * @return this builder - */ - public Builder estimateOnly(boolean estimateOnly) { - return set("estimate_only", Conv.BOOLEAN, estimateOnly); - } - - /** - * Apply to every entity of the application (scoped by the filter_* params when present). - * - * @param updateAll the value - * @return this builder - */ - public Builder updateAll(boolean updateAll) { - return set("update_all", Conv.BOOLEAN, updateAll); - } - - /** - * The filter_search field. - * - * @param filterSearch the value - * @return this builder - */ - public Builder filterSearch(String filterSearch) { - return set("filter_search", Conv.STRING, filterSearch); - } - - /** - * The filter_status field. - * - * @param filterStatus the value - * @return this builder - */ - public Builder filterStatus(List filterStatus) { - return set("filter_status", Conv.list(Conv.STRING), filterStatus); - } - - /** - * The filter_country field. - * - * @param filterCountry the value - * @return this builder - */ - public Builder filterCountry(String filterCountry) { - return set("filter_country", Conv.STRING, filterCountry); - } - - /** - * The filter_date_from field. - * - * @param filterDateFrom the value - * @return this builder - */ - public Builder filterDateFrom(LocalDate filterDateFrom) { - return set("filter_date_from", Conv.DATE, filterDateFrom); - } - - /** - * The filter_date_to field. - * - * @param filterDateTo the value - * @return this builder - */ - public Builder filterDateTo(LocalDate filterDateTo) { - return set("filter_date_to", Conv.DATE, filterDateTo); - } - - /** - * Entities to toggle. Required unless update_all is true. - * - * @param diditInternalIdList the value - * @return this builder - */ - public Builder diditInternalIdList(List diditInternalIdList) { - return set("didit_internal_id_list", Conv.list(Conv.STRING), diditInternalIdList); - } - - /** - * Builds the value. - * - * @return a new immutable {@code BusinessesSetOngoingMonitoringParams} - * @throws IllegalStateException when a required field is missing - */ - public BusinessesSetOngoingMonitoringParams build() { - require("is_enabled", false); - return new BusinessesSetOngoingMonitoringParams(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } -} diff --git a/src/main/java/me/didit/types/BusinessesSetOngoingMonitoringResponse.java b/src/main/java/me/didit/types/BusinessesSetOngoingMonitoringResponse.java index b0b4e02..5a80162 100644 --- a/src/main/java/me/didit/types/BusinessesSetOngoingMonitoringResponse.java +++ b/src/main/java/me/didit/types/BusinessesSetOngoingMonitoringResponse.java @@ -2,19 +2,9 @@ package me.didit.types; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; -import me.didit.core.JsonUnion; import me.didit.core.MultiStatusResponse; -import me.didit.core.StringEnum; -import org.jspecify.annotations.Nullable; /** * Response of {@code businesses.set_ongoing_monitoring}; the shape depends on the HTTP status. @@ -49,8 +39,8 @@ public boolean isOk() { * * @return the body, or empty for another status */ - public Optional asOk() { - return isOk() ? Optional.of(Conv.union(BusinessesSetOngoingMonitoringResponse.Ok::fromJson).read(json)) : Optional.empty(); + public Optional asOk() { + return isOk() ? Optional.of(Conv.union(OngoingMonitoringToggleResult::fromJson).read(json)) : Optional.empty(); } /** @@ -67,1866 +57,7 @@ public boolean isAccepted() { * * @return the body, or empty for another status */ - public Optional asAccepted() { - return isAccepted() ? Optional.of(Conv.model(BusinessesSetOngoingMonitoringResponse.Accepted::fromJson).read(json)) : Optional.empty(); - } - - /** - * - *

One of: {@code CostPreview}, {@code Variant2}. Check with the {@code is...} - * methods and read with the {@code as...} methods. - */ - public static final class Ok extends JsonUnion { - private Ok(JsonNode json) { - super(json); - } - - /** - * Reads a {@code Ok} from JSON. - * - * @param json any JSON value - * @return the union - */ - public static Ok fromJson(JsonNode json) { - return new Ok(json.deepCopy()); - } - - /** - * Wraps a {@code CostPreview} value. - * - * @param value the value - * @return the union - */ - public static Ok ofCostPreview(BusinessesSetOngoingMonitoringResponse.Ok.CostPreview value) { - return new Ok(Conv.model(BusinessesSetOngoingMonitoringResponse.Ok.CostPreview::fromJson).write(value)); - } - - /** - * Whether this value is a {@code CostPreview}. - * - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return true when it matches - */ - public boolean isCostPreview() { - return Json.hasAnyField(json, "users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - } - - /** - * Reads this value as a {@code CostPreview}. - * - * @return the value, or empty when {@link #isCostPreview()} is false - */ - public Optional asCostPreview() { - return isCostPreview() ? Optional.of(Conv.model(BusinessesSetOngoingMonitoringResponse.Ok.CostPreview::fromJson).read(json)) : Optional.empty(); - } - - /** - * Wraps a {@code Variant2} value. - * - * @param value the value - * @return the union - */ - public static Ok ofVariant2(BusinessesSetOngoingMonitoringResponse.Ok.Variant2 value) { - return new Ok(Conv.model(BusinessesSetOngoingMonitoringResponse.Ok.Variant2::fromJson).write(value)); - } - - /** - * Whether this value is a {@code Variant2}. - * - * @return true when it matches - */ - public boolean isVariant2() { - return Json.hasAnyField(json, "job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - } - - /** - * Reads this value as a {@code Variant2}. - * - * @return the value, or empty when {@link #isVariant2()} is false - */ - public Optional asVariant2() { - return isVariant2() ? Optional.of(Conv.model(BusinessesSetOngoingMonitoringResponse.Ok.Variant2::fromJson).read(json)) : Optional.empty(); - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class CostPreview extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private CostPreview(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code CostPreview} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static CostPreview fromJson(JsonNode json) { - return new CostPreview(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link CostPreview}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(CostPreview from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code CostPreview} - */ - public CostPreview build() { - return new CostPreview(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Variant2 extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - - private Variant2(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Variant2} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Variant2 fromJson(JsonNode json) { - return new Variant2(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(BusinessesSetOngoingMonitoringResponse.Ok.Variant2.Status::of)); - } - - /** - * The is_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isEnabled() { - return Json.optional(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * The total field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * The processed field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(BusinessesSetOngoingMonitoringResponse.Ok.Variant2.Outcomes::fromJson)); - } - - /** - * The error field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(BusinessesSetOngoingMonitoringResponse.Ok.Variant2.Estimate::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Variant2}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Variant2 from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(BusinessesSetOngoingMonitoringResponse.Ok.Variant2.Status status) { - return set("status", Conv.enumeration(BusinessesSetOngoingMonitoringResponse.Ok.Variant2.Status::of), status); - } - - /** - * The is_enabled field. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * The total field. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * The processed field. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(BusinessesSetOngoingMonitoringResponse.Ok.Variant2.Outcomes outcomes) { - return set("outcomes", Conv.model(BusinessesSetOngoingMonitoringResponse.Ok.Variant2.Outcomes::fromJson), outcomes); - } - - /** - * The error field. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(BusinessesSetOngoingMonitoringResponse.Ok.Variant2.Estimate estimate) { - return set("estimate", Conv.model(BusinessesSetOngoingMonitoringResponse.Ok.Variant2.Estimate::fromJson), estimate); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Variant2} - */ - public Variant2 build() { - return new Variant2(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening provider call failed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out mid-job. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening provider call failed. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out mid-job. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - } - - public static final class Accepted extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - - private Accepted(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Accepted} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Accepted fromJson(JsonNode json) { - return new Accepted(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(BusinessesSetOngoingMonitoringResponse.Accepted.Status::of)); - } - - /** - * The is_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isEnabled() { - return Json.optional(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * The total field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * The processed field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(BusinessesSetOngoingMonitoringResponse.Accepted.Outcomes::fromJson)); - } - - /** - * The error field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(BusinessesSetOngoingMonitoringResponse.Accepted.Estimate::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Accepted}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Accepted from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(BusinessesSetOngoingMonitoringResponse.Accepted.Status status) { - return set("status", Conv.enumeration(BusinessesSetOngoingMonitoringResponse.Accepted.Status::of), status); - } - - /** - * The is_enabled field. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * The total field. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * The processed field. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(BusinessesSetOngoingMonitoringResponse.Accepted.Outcomes outcomes) { - return set("outcomes", Conv.model(BusinessesSetOngoingMonitoringResponse.Accepted.Outcomes::fromJson), outcomes); - } - - /** - * The error field. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(BusinessesSetOngoingMonitoringResponse.Accepted.Estimate estimate) { - return set("estimate", Conv.model(BusinessesSetOngoingMonitoringResponse.Accepted.Estimate::fromJson), estimate); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Accepted} - */ - public Accepted build() { - return new Accepted(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening provider call failed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out mid-job. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening provider call failed. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out mid-job. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } + public Optional asAccepted() { + return isAccepted() ? Optional.of(Conv.model(OngoingMonitoringJob::fromJson).read(json)) : Optional.empty(); } } diff --git a/src/main/java/me/didit/types/BusinessesSetSessionOngoingMonitoringResponse.java b/src/main/java/me/didit/types/BusinessesSetSessionOngoingMonitoringResponse.java index de724bc..0597ade 100644 --- a/src/main/java/me/didit/types/BusinessesSetSessionOngoingMonitoringResponse.java +++ b/src/main/java/me/didit/types/BusinessesSetSessionOngoingMonitoringResponse.java @@ -2,19 +2,9 @@ package me.didit.types; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; -import me.didit.core.JsonUnion; import me.didit.core.MultiStatusResponse; -import me.didit.core.StringEnum; -import org.jspecify.annotations.Nullable; /** * Response of {@code businesses.set_session_ongoing_monitoring}; the shape depends on the HTTP status. @@ -49,8 +39,8 @@ public boolean isOk() { * * @return the body, or empty for another status */ - public Optional asOk() { - return isOk() ? Optional.of(Conv.union(BusinessesSetSessionOngoingMonitoringResponse.Ok::fromJson).read(json)) : Optional.empty(); + public Optional asOk() { + return isOk() ? Optional.of(Conv.union(OngoingMonitoringToggleResult::fromJson).read(json)) : Optional.empty(); } /** @@ -67,1866 +57,7 @@ public boolean isAccepted() { * * @return the body, or empty for another status */ - public Optional asAccepted() { - return isAccepted() ? Optional.of(Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Accepted::fromJson).read(json)) : Optional.empty(); - } - - /** - * - *

One of: {@code CostPreview}, {@code Variant2}. Check with the {@code is...} - * methods and read with the {@code as...} methods. - */ - public static final class Ok extends JsonUnion { - private Ok(JsonNode json) { - super(json); - } - - /** - * Reads a {@code Ok} from JSON. - * - * @param json any JSON value - * @return the union - */ - public static Ok fromJson(JsonNode json) { - return new Ok(json.deepCopy()); - } - - /** - * Wraps a {@code CostPreview} value. - * - * @param value the value - * @return the union - */ - public static Ok ofCostPreview(BusinessesSetSessionOngoingMonitoringResponse.Ok.CostPreview value) { - return new Ok(Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Ok.CostPreview::fromJson).write(value)); - } - - /** - * Whether this value is a {@code CostPreview}. - * - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return true when it matches - */ - public boolean isCostPreview() { - return Json.hasAnyField(json, "users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - } - - /** - * Reads this value as a {@code CostPreview}. - * - * @return the value, or empty when {@link #isCostPreview()} is false - */ - public Optional asCostPreview() { - return isCostPreview() ? Optional.of(Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Ok.CostPreview::fromJson).read(json)) : Optional.empty(); - } - - /** - * Wraps a {@code Variant2} value. - * - * @param value the value - * @return the union - */ - public static Ok ofVariant2(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2 value) { - return new Ok(Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2::fromJson).write(value)); - } - - /** - * Whether this value is a {@code Variant2}. - * - * @return true when it matches - */ - public boolean isVariant2() { - return Json.hasAnyField(json, "job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - } - - /** - * Reads this value as a {@code Variant2}. - * - * @return the value, or empty when {@link #isVariant2()} is false - */ - public Optional asVariant2() { - return isVariant2() ? Optional.of(Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2::fromJson).read(json)) : Optional.empty(); - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class CostPreview extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private CostPreview(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code CostPreview} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static CostPreview fromJson(JsonNode json) { - return new CostPreview(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link CostPreview}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(CostPreview from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code CostPreview} - */ - public CostPreview build() { - return new CostPreview(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Variant2 extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - - private Variant2(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Variant2} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Variant2 fromJson(JsonNode json) { - return new Variant2(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2.Status::of)); - } - - /** - * The is_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isEnabled() { - return Json.optional(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * The total field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * The processed field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2.Outcomes::fromJson)); - } - - /** - * The error field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2.Estimate::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Variant2}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Variant2 from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2.Status status) { - return set("status", Conv.enumeration(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2.Status::of), status); - } - - /** - * The is_enabled field. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * The total field. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * The processed field. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2.Outcomes outcomes) { - return set("outcomes", Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2.Outcomes::fromJson), outcomes); - } - - /** - * The error field. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2.Estimate estimate) { - return set("estimate", Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Ok.Variant2.Estimate::fromJson), estimate); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Variant2} - */ - public Variant2 build() { - return new Variant2(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening provider call failed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out mid-job. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening provider call failed. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out mid-job. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - } - - public static final class Accepted extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - - private Accepted(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Accepted} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Accepted fromJson(JsonNode json) { - return new Accepted(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(BusinessesSetSessionOngoingMonitoringResponse.Accepted.Status::of)); - } - - /** - * The is_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isEnabled() { - return Json.optional(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * The total field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * The processed field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Accepted.Outcomes::fromJson)); - } - - /** - * The error field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Accepted.Estimate::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Accepted}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Accepted from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(BusinessesSetSessionOngoingMonitoringResponse.Accepted.Status status) { - return set("status", Conv.enumeration(BusinessesSetSessionOngoingMonitoringResponse.Accepted.Status::of), status); - } - - /** - * The is_enabled field. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * The total field. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * The processed field. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(BusinessesSetSessionOngoingMonitoringResponse.Accepted.Outcomes outcomes) { - return set("outcomes", Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Accepted.Outcomes::fromJson), outcomes); - } - - /** - * The error field. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(BusinessesSetSessionOngoingMonitoringResponse.Accepted.Estimate estimate) { - return set("estimate", Conv.model(BusinessesSetSessionOngoingMonitoringResponse.Accepted.Estimate::fromJson), estimate); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Accepted} - */ - public Accepted build() { - return new Accepted(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening provider call failed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out mid-job. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening provider call failed. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out mid-job. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } + public Optional asAccepted() { + return isAccepted() ? Optional.of(Conv.model(OngoingMonitoringJob::fromJson).read(json)) : Optional.empty(); } } diff --git a/src/main/java/me/didit/types/BusinessesUpdateParams.java b/src/main/java/me/didit/types/BusinessesUpdateParams.java index 7d72bd4..08c8304 100644 --- a/src/main/java/me/didit/types/BusinessesUpdateParams.java +++ b/src/main/java/me/didit/types/BusinessesUpdateParams.java @@ -16,7 +16,7 @@ /** Parameters for {@code businesses.update}. */ public final class BusinessesUpdateParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("display_name", "legal_name", "registration_number", "country_code", "status", "metadata"); + private static final Set KNOWN_FIELDS = Set.of("display_name", "legal_name", "registration_number", "country_code", "status", "metadata", "reason"); private BusinessesUpdateParams(ObjectNode json, Map> files) { super(json, files); @@ -111,6 +111,16 @@ public Optional> metadata() { return Json.optional(json, "metadata", Conv.OBJECT); } + /** + * Why the status changed. Recorded on the status-change entry of the company's Activity in the Business Console, only when the status actually changes. Webhooks do not carry it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -184,6 +194,16 @@ public Builder metadata(@Nullable Map metadata) { return setAny("metadata", metadata); } + /** + * Why the status changed. Recorded on the status-change entry of the company's Activity in the Business Console, only when the status actually changes. Webhooks do not carry it. + * + * @param reason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reason(@Nullable String reason) { + return setNullable("reason", Conv.STRING, reason); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/BusinessesUpdateStatusParams.java b/src/main/java/me/didit/types/BusinessesUpdateStatusParams.java index 3bf590d..7241b85 100644 --- a/src/main/java/me/didit/types/BusinessesUpdateStatusParams.java +++ b/src/main/java/me/didit/types/BusinessesUpdateStatusParams.java @@ -5,16 +5,17 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import me.didit.core.Conv; import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; -import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; /** Parameters for {@code businesses.update_status}. */ public final class BusinessesUpdateStatusParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("status"); + private static final Set KNOWN_FIELDS = Set.of("status", "reason"); private BusinessesUpdateStatusParams(ObjectNode json, Map> files) { super(json, files); @@ -55,8 +56,18 @@ public Builder toBuilder() { * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public BusinessesUpdateStatusParams.Status status() { - return Json.required(json, "status", Conv.enumeration(BusinessesUpdateStatusParams.Status::of)); + public EntityStatusUpdateRequest.Status status() { + return Json.required(json, "status", Conv.enumeration(EntityStatusUpdateRequest.Status::of)); + } + + /** + * Why the status changed. Recorded on the status-change entry of the company's Activity in the Business Console, only when the status actually changes. Webhooks do not carry it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); } @Override @@ -78,8 +89,18 @@ private Builder(BusinessesUpdateStatusParams from) { * @param status the value * @return this builder */ - public Builder status(BusinessesUpdateStatusParams.Status status) { - return set("status", Conv.enumeration(BusinessesUpdateStatusParams.Status::of), status); + public Builder status(EntityStatusUpdateRequest.Status status) { + return set("status", Conv.enumeration(EntityStatusUpdateRequest.Status::of), status); + } + + /** + * Why the status changed. Recorded on the status-change entry of the company's Activity in the Business Console, only when the status actually changes. Webhooks do not carry it. + * + * @param reason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reason(@Nullable String reason) { + return setNullable("reason", Conv.STRING, reason); } /** @@ -98,50 +119,4 @@ protected Builder self() { return this; } } - - /** - * New lifecycle status. BLOCKED also adds the vendor_data to the system blocklist. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "ACTIVE"}. */ - public static final Status ACTIVE = new Status("ACTIVE"); - - /** The value {@code "FLAGGED"}. */ - public static final Status FLAGGED = new Status("FLAGGED"); - - /** The value {@code "BLOCKED"}. */ - public static final Status BLOCKED = new Status("BLOCKED"); - - private static final List KNOWN_VALUES = List.of(ACTIVE, FLAGGED, BLOCKED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } } diff --git a/src/main/java/me/didit/types/TransactionsListNetworksResponse.java b/src/main/java/me/didit/types/CaseBlueprintPresetInstallResult.java similarity index 58% rename from src/main/java/me/didit/types/TransactionsListNetworksResponse.java rename to src/main/java/me/didit/types/CaseBlueprintPresetInstallResult.java index 3dd9201..d0fa92e 100644 --- a/src/main/java/me/didit/types/TransactionsListNetworksResponse.java +++ b/src/main/java/me/didit/types/CaseBlueprintPresetInstallResult.java @@ -12,22 +12,22 @@ import me.didit.core.Json; import me.didit.core.JsonModel; -public final class TransactionsListNetworksResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("results"); +public final class CaseBlueprintPresetInstallResult extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("created"); - private TransactionsListNetworksResponse(ObjectNode json, Map> files) { + private CaseBlueprintPresetInstallResult(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code TransactionsListNetworksResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code CaseBlueprintPresetInstallResult} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static TransactionsListNetworksResponse fromJson(JsonNode json) { - return new TransactionsListNetworksResponse(Json.copyObject(json), Map.of()); + public static CaseBlueprintPresetInstallResult fromJson(JsonNode json) { + return new CaseBlueprintPresetInstallResult(Json.copyObject(json), Map.of()); } /** @@ -49,13 +49,13 @@ public Builder toBuilder() { } /** - * The results field. + * The created field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(EntityNetworkMembership::fromJson))); + public Optional> created() { + return Json.optional(json, "created", Conv.list(Conv.STRING)); } @Override @@ -63,31 +63,31 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link TransactionsListNetworksResponse}. */ + /** Builds a {@link CaseBlueprintPresetInstallResult}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(TransactionsListNetworksResponse from) { + private Builder(CaseBlueprintPresetInstallResult from) { super(from); } /** - * The results field. + * The created field. * - * @param results the value + * @param created the value * @return this builder */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(EntityNetworkMembership::fromJson)), results); + public Builder created(List created) { + return set("created", Conv.list(Conv.STRING), created); } /** * Builds the value. * - * @return a new immutable {@code TransactionsListNetworksResponse} + * @return a new immutable {@code CaseBlueprintPresetInstallResult} */ - public TransactionsListNetworksResponse build() { - return new TransactionsListNetworksResponse(buildJson(), buildFiles()); + public CaseBlueprintPresetInstallResult build() { + return new CaseBlueprintPresetInstallResult(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/CasesBulkAssignResponse.java b/src/main/java/me/didit/types/CaseBulkUpdateResult.java similarity index 83% rename from src/main/java/me/didit/types/CasesBulkAssignResponse.java rename to src/main/java/me/didit/types/CaseBulkUpdateResult.java index b05216b..5011dff 100644 --- a/src/main/java/me/didit/types/CasesBulkAssignResponse.java +++ b/src/main/java/me/didit/types/CaseBulkUpdateResult.java @@ -12,22 +12,22 @@ import me.didit.core.Json; import me.didit.core.JsonModel; -public final class CasesBulkAssignResponse extends JsonModel { +public final class CaseBulkUpdateResult extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("updated", "failures"); - private CasesBulkAssignResponse(ObjectNode json, Map> files) { + private CaseBulkUpdateResult(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code CasesBulkAssignResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code CaseBulkUpdateResult} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static CasesBulkAssignResponse fromJson(JsonNode json) { - return new CasesBulkAssignResponse(Json.copyObject(json), Map.of()); + public static CaseBulkUpdateResult fromJson(JsonNode json) { + return new CaseBulkUpdateResult(Json.copyObject(json), Map.of()); } /** @@ -64,8 +64,8 @@ public Optional updated() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> failures() { - return Json.optional(json, "failures", Conv.list(Conv.model(CasesBulkAssignResponse.Failures::fromJson))); + public Optional> failures() { + return Json.optional(json, "failures", Conv.list(Conv.model(CaseBulkUpdateResult.Failures::fromJson))); } @Override @@ -73,11 +73,11 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link CasesBulkAssignResponse}. */ + /** Builds a {@link CaseBulkUpdateResult}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(CasesBulkAssignResponse from) { + private Builder(CaseBulkUpdateResult from) { super(from); } @@ -97,17 +97,17 @@ public Builder updated(long updated) { * @param failures the value * @return this builder */ - public Builder failures(List failures) { - return set("failures", Conv.list(Conv.model(CasesBulkAssignResponse.Failures::fromJson)), failures); + public Builder failures(List failures) { + return set("failures", Conv.list(Conv.model(CaseBulkUpdateResult.Failures::fromJson)), failures); } /** * Builds the value. * - * @return a new immutable {@code CasesBulkAssignResponse} + * @return a new immutable {@code CaseBulkUpdateResult} */ - public CasesBulkAssignResponse build() { - return new CasesBulkAssignResponse(buildJson(), buildFiles()); + public CaseBulkUpdateResult build() { + return new CaseBulkUpdateResult(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/CaseChecker.java b/src/main/java/me/didit/types/CaseChecker.java new file mode 100644 index 0000000..6e0c3b6 --- /dev/null +++ b/src/main/java/me/didit/types/CaseChecker.java @@ -0,0 +1,142 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** + * The member named to approve or reject the pending resolution. Set only while a four-eyes resolution waits on a named checker. Null when nothing is pending, while the pending resolution waits on the checker queue, and once the resolution is approved, rejected, or returned for a new review because the evidence changed. + */ +public final class CaseChecker extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("member_uuid", "name", "email"); + + private CaseChecker(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code CaseChecker} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static CaseChecker fromJson(JsonNode json) { + return new CaseChecker(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The member's user uuid: the value you pass as checker when you resolve the case. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional memberUuid() { + return Json.optional(json, "member_uuid", Conv.STRING); + } + + /** + * The member's full name, or null when it is not known. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * The email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link CaseChecker}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(CaseChecker from) { + super(from); + } + + /** + * The member's user uuid: the value you pass as checker when you resolve the case. + * + * @param memberUuid the value + * @return this builder + */ + public Builder memberUuid(String memberUuid) { + return set("member_uuid", Conv.STRING, memberUuid); + } + + /** + * The member's full name, or null when it is not known. + * + * @param name the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder name(@Nullable String name) { + return setNullable("name", Conv.STRING, name); + } + + /** + * The email field. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * Builds the value. + * + * @return a new immutable {@code CaseChecker} + */ + public CaseChecker build() { + return new CaseChecker(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/CaseDetail.java b/src/main/java/me/didit/types/CaseDetail.java index cd74a17..53f400b 100644 --- a/src/main/java/me/didit/types/CaseDetail.java +++ b/src/main/java/me/didit/types/CaseDetail.java @@ -16,7 +16,7 @@ import org.jspecify.annotations.Nullable; public final class CaseDetail extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "case_number", "title", "status", "resolution", "resolution_notes", "priority", "source", "tags", "subject", "assigned_to", "assigned_at", "due_at", "resolved_at", "resolved_by_email", "pending_resolution", "metadata", "blueprint", "notes", "events", "links", "links_count", "notes_count", "checklist_items", "created_at", "updated_at"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "case_number", "title", "status", "resolution", "resolution_notes", "priority", "source", "tags", "subject", "assigned_to", "assigned_at", "team", "due_at", "resolved_at", "resolved_by_email", "pending_resolution", "checker", "metadata", "blueprint", "notes", "events", "links", "links_count", "notes_count", "checklist_items", "created_at", "updated_at"); private CaseDetail(ObjectNode json, Map> files) { super(json, files); @@ -171,6 +171,16 @@ public Optional assignedAt() { return Json.optional(json, "assigned_at", Conv.DATE_TIME); } + /** + * The review team that owns the case, or null when no team owns it. Routing sets it when a routing rule places the case with one of its teams. Change or clear it with PATCH .../cases/{case_uuid}/ (team). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional team() { + return Json.optional(json, "team", Conv.model(CaseTeamSummary::fromJson)); + } + /** * The due_at field. * @@ -211,6 +221,16 @@ public Optional pendingResolution() { return Json.optional(json, "pending_resolution", Conv.model(CaseDetail.PendingResolution::fromJson)); } + /** + * The member named to approve or reject the pending resolution. Set only while a four-eyes resolution waits on a named checker. Null when nothing is pending, while the pending resolution waits on the checker queue, and once the resolution is approved, rejected, or returned for a new review because the evidence changed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional checker() { + return Json.optional(json, "checker", Conv.model(CaseChecker::fromJson)); + } + /** * The metadata field. * @@ -444,6 +464,16 @@ public Builder assignedAt(@Nullable OffsetDateTime assignedAt) { return setNullable("assigned_at", Conv.DATE_TIME, assignedAt); } + /** + * The review team that owns the case, or null when no team owns it. Routing sets it when a routing rule places the case with one of its teams. Change or clear it with PATCH .../cases/{case_uuid}/ (team). + * + * @param team the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder team(@Nullable CaseTeamSummary team) { + return setNullable("team", Conv.model(CaseTeamSummary::fromJson), team); + } + /** * The due_at field. * @@ -484,6 +514,16 @@ public Builder pendingResolution(CaseDetail.@Nullable PendingResolution pendingR return setNullable("pending_resolution", Conv.model(CaseDetail.PendingResolution::fromJson), pendingResolution); } + /** + * The member named to approve or reject the pending resolution. Set only while a four-eyes resolution waits on a named checker. Null when nothing is pending, while the pending resolution waits on the checker queue, and once the resolution is approved, rejected, or returned for a new review because the evidence changed. + * + * @param checker the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder checker(@Nullable CaseChecker checker) { + return setNullable("checker", Conv.model(CaseChecker::fromJson), checker); + } + /** * The metadata field. * @@ -655,7 +695,16 @@ public static final class Resolution extends StringEnum { /** The value {@code "VALID_THREAT"}. */ public static final Resolution VALID_THREAT = new Resolution("VALID_THREAT"); - private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT); + /** The value {@code "INCONCLUSIVE"}. */ + public static final Resolution INCONCLUSIVE = new Resolution("INCONCLUSIVE"); + + /** The value {@code "REQUIREMENTS_NOT_MET"}. */ + public static final Resolution REQUIREMENTS_NOT_MET = new Resolution("REQUIREMENTS_NOT_MET"); + + /** The value {@code "REQUIREMENTS_SATISFIED"}. */ + public static final Resolution REQUIREMENTS_SATISFIED = new Resolution("REQUIREMENTS_SATISFIED"); + + private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT, INCONCLUSIVE, REQUIREMENTS_NOT_MET, REQUIREMENTS_SATISFIED); private Resolution(String value) { super(value); @@ -783,7 +832,7 @@ public boolean isKnown() { * Set only while a 4-eyes resolution is staged on the checker blueprint, awaiting approve or reject-approval. */ public static final class PendingResolution extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("resolution", "resolution_notes", "maker_email", "maker_name", "maker_blueprint_id", "staged_at"); + private static final Set KNOWN_FIELDS = Set.of("resolution", "resolution_notes", "maker_email", "maker_name", "maker_user_id", "maker_blueprint_id", "staged_at"); private PendingResolution(ObjectNode json, Map> files) { super(json, files); @@ -839,7 +888,7 @@ public Optional resolutionNotes() { } /** - * The maker_email field. + * Email of the officer who submitted the resolution. Null when an API key submitted it: that resolution has no recorded maker, so approve and reject-approval return 403 for every caller. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -858,6 +907,16 @@ public Optional makerName() { return Json.optional(json, "maker_name", Conv.STRING); } + /** + * Member user uuid of the officer who submitted the resolution. Null when an API key submitted it. That member cannot be named as checker. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional makerUserId() { + return Json.optional(json, "maker_user_id", Conv.STRING); + } + /** * The maker_blueprint_id field. * @@ -912,13 +971,13 @@ public Builder resolutionNotes(@Nullable String resolutionNotes) { } /** - * The maker_email field. + * Email of the officer who submitted the resolution. Null when an API key submitted it: that resolution has no recorded maker, so approve and reject-approval return 403 for every caller. * - * @param makerEmail the value + * @param makerEmail the value, or {@code null} to send JSON null * @return this builder */ - public Builder makerEmail(String makerEmail) { - return set("maker_email", Conv.STRING, makerEmail); + public Builder makerEmail(@Nullable String makerEmail) { + return setNullable("maker_email", Conv.STRING, makerEmail); } /** @@ -931,6 +990,16 @@ public Builder makerName(@Nullable String makerName) { return setNullable("maker_name", Conv.STRING, makerName); } + /** + * Member user uuid of the officer who submitted the resolution. Null when an API key submitted it. That member cannot be named as checker. + * + * @param makerUserId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder makerUserId(@Nullable String makerUserId) { + return setNullable("maker_user_id", Conv.STRING, makerUserId); + } + /** * The maker_blueprint_id field. * @@ -977,7 +1046,16 @@ public static final class Resolution extends StringEnum { /** The value {@code "VALID_THREAT"}. */ public static final Resolution VALID_THREAT = new Resolution("VALID_THREAT"); - private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT); + /** The value {@code "INCONCLUSIVE"}. */ + public static final Resolution INCONCLUSIVE = new Resolution("INCONCLUSIVE"); + + /** The value {@code "REQUIREMENTS_NOT_MET"}. */ + public static final Resolution REQUIREMENTS_NOT_MET = new Resolution("REQUIREMENTS_NOT_MET"); + + /** The value {@code "REQUIREMENTS_SATISFIED"}. */ + public static final Resolution REQUIREMENTS_SATISFIED = new Resolution("REQUIREMENTS_SATISFIED"); + + private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT, INCONCLUSIVE, REQUIREMENTS_NOT_MET, REQUIREMENTS_SATISFIED); private Resolution(String value) { super(value); diff --git a/src/main/java/me/didit/types/CaseEvent.java b/src/main/java/me/didit/types/CaseEvent.java index 7885e51..6d33908 100644 --- a/src/main/java/me/didit/types/CaseEvent.java +++ b/src/main/java/me/didit/types/CaseEvent.java @@ -271,6 +271,12 @@ public static final class EventType extends StringEnum { /** The value {@code "NOTE_ADDED"}. */ public static final EventType NOTE_ADDED = new EventType("NOTE_ADDED"); + /** The value {@code "NOTE_PINNED"}. */ + public static final EventType NOTE_PINNED = new EventType("NOTE_PINNED"); + + /** The value {@code "NOTE_UNPINNED"}. */ + public static final EventType NOTE_UNPINNED = new EventType("NOTE_UNPINNED"); + /** The value {@code "PRIORITY_CHANGED"}. */ public static final EventType PRIORITY_CHANGED = new EventType("PRIORITY_CHANGED"); @@ -313,7 +319,22 @@ public static final class EventType extends StringEnum { /** The value {@code "APPROVAL_INVALIDATED"}. */ public static final EventType APPROVAL_INVALIDATED = new EventType("APPROVAL_INVALIDATED"); - private static final List KNOWN_VALUES = List.of(CREATED, STATUS_CHANGED, ASSIGNED, UNASSIGNED, RESOLVED, REOPENED, LINK_ADDED, LINK_REMOVED, NOTE_ADDED, PRIORITY_CHANGED, DUE_DATE_CHANGED, TAG_ADDED, TAG_REMOVED, ESCALATED, TRANSFERRED, CHECKLIST_COMPLETED, REPORT_CREATED, REPORT_FINALIZED, SUBMITTED_FOR_APPROVAL, APPROVAL_GRANTED, APPROVAL_REJECTED, ENTITY_ACTION, APPROVAL_INVALIDATED); + /** The value {@code "CUSTOMER_REQUEST"}. */ + public static final EventType CUSTOMER_REQUEST = new EventType("CUSTOMER_REQUEST"); + + /** The value {@code "STAGE_CHANGED"}. */ + public static final EventType STAGE_CHANGED = new EventType("STAGE_CHANGED"); + + /** The value {@code "TEAM_CHANGED"}. */ + public static final EventType TEAM_CHANGED = new EventType("TEAM_CHANGED"); + + /** The value {@code "CHECKER_ASSIGNED"}. */ + public static final EventType CHECKER_ASSIGNED = new EventType("CHECKER_ASSIGNED"); + + /** The value {@code "CHECKER_UNASSIGNED"}. */ + public static final EventType CHECKER_UNASSIGNED = new EventType("CHECKER_UNASSIGNED"); + + private static final List KNOWN_VALUES = List.of(CREATED, STATUS_CHANGED, ASSIGNED, UNASSIGNED, RESOLVED, REOPENED, LINK_ADDED, LINK_REMOVED, NOTE_ADDED, NOTE_PINNED, NOTE_UNPINNED, PRIORITY_CHANGED, DUE_DATE_CHANGED, TAG_ADDED, TAG_REMOVED, ESCALATED, TRANSFERRED, CHECKLIST_COMPLETED, REPORT_CREATED, REPORT_FINALIZED, SUBMITTED_FOR_APPROVAL, APPROVAL_GRANTED, APPROVAL_REJECTED, ENTITY_ACTION, APPROVAL_INVALIDATED, CUSTOMER_REQUEST, STAGE_CHANGED, TEAM_CHANGED, CHECKER_ASSIGNED, CHECKER_UNASSIGNED); private EventType(String value) { super(value); diff --git a/src/main/java/me/didit/types/CaseListItem.java b/src/main/java/me/didit/types/CaseListItem.java index d765ab9..c36849b 100644 --- a/src/main/java/me/didit/types/CaseListItem.java +++ b/src/main/java/me/didit/types/CaseListItem.java @@ -16,7 +16,7 @@ import org.jspecify.annotations.Nullable; public final class CaseListItem extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "case_number", "title", "status", "resolution", "priority", "source", "tags", "subject", "assigned_to", "assigned_at", "due_at", "resolved_at", "created_by_email", "created_by_name", "blueprint", "links_count", "notes_count", "created_at", "updated_at", "application_id", "application_name", "organization_id", "organization_name"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "case_number", "title", "status", "resolution", "pending_resolution", "checker", "priority", "source", "tags", "subject", "assigned_to", "assigned_at", "team", "due_at", "resolved_at", "created_by_email", "created_by_name", "blueprint", "links_count", "notes_count", "created_at", "updated_at", "application_id", "application_name", "organization_id", "organization_name"); private CaseListItem(ObjectNode json, Map> files) { super(json, files); @@ -101,6 +101,26 @@ public Optional resolution() { return Json.optional(json, "resolution", Conv.enumeration(CaseListItem.Resolution::of)); } + /** + * Set only while a 4-eyes resolution is staged on the checker blueprint, awaiting approve or reject-approval. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional pendingResolution() { + return Json.optional(json, "pending_resolution", Conv.model(CaseListItem.PendingResolution::fromJson)); + } + + /** + * The member named to approve or reject the pending resolution. Set only while a four-eyes resolution waits on a named checker. Null when nothing is pending, while the pending resolution waits on the checker queue, and once the resolution is approved, rejected, or returned for a new review because the evidence changed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional checker() { + return Json.optional(json, "checker", Conv.model(CaseChecker::fromJson)); + } + /** * The priority field. * @@ -161,6 +181,16 @@ public Optional assignedAt() { return Json.optional(json, "assigned_at", Conv.DATE_TIME); } + /** + * The review team that owns the case, or null when no team owns it. Routing sets it when a routing rule places the case with one of its teams. Change or clear it with PATCH .../cases/{case_uuid}/ (team). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional team() { + return Json.optional(json, "team", Conv.model(CaseTeamSummary::fromJson)); + } + /** * The due_at field. * @@ -354,6 +384,26 @@ public Builder resolution(CaseListItem.@Nullable Resolution resolution) { return setNullable("resolution", Conv.enumeration(CaseListItem.Resolution::of), resolution); } + /** + * Set only while a 4-eyes resolution is staged on the checker blueprint, awaiting approve or reject-approval. + * + * @param pendingResolution the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder pendingResolution(CaseListItem.@Nullable PendingResolution pendingResolution) { + return setNullable("pending_resolution", Conv.model(CaseListItem.PendingResolution::fromJson), pendingResolution); + } + + /** + * The member named to approve or reject the pending resolution. Set only while a four-eyes resolution waits on a named checker. Null when nothing is pending, while the pending resolution waits on the checker queue, and once the resolution is approved, rejected, or returned for a new review because the evidence changed. + * + * @param checker the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder checker(@Nullable CaseChecker checker) { + return setNullable("checker", Conv.model(CaseChecker::fromJson), checker); + } + /** * The priority field. * @@ -414,6 +464,16 @@ public Builder assignedAt(@Nullable OffsetDateTime assignedAt) { return setNullable("assigned_at", Conv.DATE_TIME, assignedAt); } + /** + * The review team that owns the case, or null when no team owns it. Routing sets it when a routing rule places the case with one of its teams. Change or clear it with PATCH .../cases/{case_uuid}/ (team). + * + * @param team the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder team(@Nullable CaseTeamSummary team) { + return setNullable("team", Conv.model(CaseTeamSummary::fromJson), team); + } + /** * The due_at field. * @@ -615,7 +675,16 @@ public static final class Resolution extends StringEnum { /** The value {@code "VALID_THREAT"}. */ public static final Resolution VALID_THREAT = new Resolution("VALID_THREAT"); - private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT); + /** The value {@code "INCONCLUSIVE"}. */ + public static final Resolution INCONCLUSIVE = new Resolution("INCONCLUSIVE"); + + /** The value {@code "REQUIREMENTS_NOT_MET"}. */ + public static final Resolution REQUIREMENTS_NOT_MET = new Resolution("REQUIREMENTS_NOT_MET"); + + /** The value {@code "REQUIREMENTS_SATISFIED"}. */ + public static final Resolution REQUIREMENTS_SATISFIED = new Resolution("REQUIREMENTS_SATISFIED"); + + private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT, INCONCLUSIVE, REQUIREMENTS_NOT_MET, REQUIREMENTS_SATISFIED); private Resolution(String value) { super(value); @@ -646,6 +715,265 @@ public boolean isKnown() { } } + /** + * Set only while a 4-eyes resolution is staged on the checker blueprint, awaiting approve or reject-approval. + */ + public static final class PendingResolution extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("resolution", "resolution_notes", "maker_email", "maker_name", "maker_user_id", "maker_blueprint_id", "staged_at"); + + private PendingResolution(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code PendingResolution} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PendingResolution fromJson(JsonNode json) { + return new PendingResolution(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The resolution field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional resolution() { + return Json.optional(json, "resolution", Conv.enumeration(CaseListItem.PendingResolution.Resolution::of)); + } + + /** + * The resolution_notes field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional resolutionNotes() { + return Json.optional(json, "resolution_notes", Conv.STRING); + } + + /** + * Email of the officer who submitted the resolution. Null when an API key submitted it: that resolution has no recorded maker, so approve and reject-approval return 403 for every caller. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional makerEmail() { + return Json.optional(json, "maker_email", Conv.STRING); + } + + /** + * The maker_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional makerName() { + return Json.optional(json, "maker_name", Conv.STRING); + } + + /** + * Member user uuid of the officer who submitted the resolution. Null when an API key submitted it. That member cannot be named as checker. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional makerUserId() { + return Json.optional(json, "maker_user_id", Conv.STRING); + } + + /** + * The maker_blueprint_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional makerBlueprintId() { + return Json.optional(json, "maker_blueprint_id", Conv.STRING); + } + + /** + * The staged_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional stagedAt() { + return Json.optional(json, "staged_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link PendingResolution}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(PendingResolution from) { + super(from); + } + + /** + * The resolution field. + * + * @param resolution the value + * @return this builder + */ + public Builder resolution(CaseListItem.PendingResolution.Resolution resolution) { + return set("resolution", Conv.enumeration(CaseListItem.PendingResolution.Resolution::of), resolution); + } + + /** + * The resolution_notes field. + * + * @param resolutionNotes the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder resolutionNotes(@Nullable String resolutionNotes) { + return setNullable("resolution_notes", Conv.STRING, resolutionNotes); + } + + /** + * Email of the officer who submitted the resolution. Null when an API key submitted it: that resolution has no recorded maker, so approve and reject-approval return 403 for every caller. + * + * @param makerEmail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder makerEmail(@Nullable String makerEmail) { + return setNullable("maker_email", Conv.STRING, makerEmail); + } + + /** + * The maker_name field. + * + * @param makerName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder makerName(@Nullable String makerName) { + return setNullable("maker_name", Conv.STRING, makerName); + } + + /** + * Member user uuid of the officer who submitted the resolution. Null when an API key submitted it. That member cannot be named as checker. + * + * @param makerUserId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder makerUserId(@Nullable String makerUserId) { + return setNullable("maker_user_id", Conv.STRING, makerUserId); + } + + /** + * The maker_blueprint_id field. + * + * @param makerBlueprintId the value + * @return this builder + */ + public Builder makerBlueprintId(String makerBlueprintId) { + return set("maker_blueprint_id", Conv.STRING, makerBlueprintId); + } + + /** + * The staged_at field. + * + * @param stagedAt the value + * @return this builder + */ + public Builder stagedAt(OffsetDateTime stagedAt) { + return set("staged_at", Conv.DATE_TIME, stagedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code PendingResolution} + */ + public PendingResolution build() { + return new PendingResolution(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Resolution extends StringEnum { + /** The value {@code "FALSE_POSITIVE"}. */ + public static final Resolution FALSE_POSITIVE = new Resolution("FALSE_POSITIVE"); + + /** The value {@code "VALID_THREAT"}. */ + public static final Resolution VALID_THREAT = new Resolution("VALID_THREAT"); + + /** The value {@code "INCONCLUSIVE"}. */ + public static final Resolution INCONCLUSIVE = new Resolution("INCONCLUSIVE"); + + /** The value {@code "REQUIREMENTS_NOT_MET"}. */ + public static final Resolution REQUIREMENTS_NOT_MET = new Resolution("REQUIREMENTS_NOT_MET"); + + /** The value {@code "REQUIREMENTS_SATISFIED"}. */ + public static final Resolution REQUIREMENTS_SATISFIED = new Resolution("REQUIREMENTS_SATISFIED"); + + private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT, INCONCLUSIVE, REQUIREMENTS_NOT_MET, REQUIREMENTS_SATISFIED); + + private Resolution(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Resolution of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Resolution::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + /** * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. diff --git a/src/main/java/me/didit/types/CaseSubject.java b/src/main/java/me/didit/types/CaseSubject.java index f784b73..d4628e6 100644 --- a/src/main/java/me/didit/types/CaseSubject.java +++ b/src/main/java/me/didit/types/CaseSubject.java @@ -16,7 +16,7 @@ /** The single user or business the case is anchored to. */ public final class CaseSubject extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "uuid", "name", "external_id"); + private static final Set KNOWN_FIELDS = Set.of("type", "uuid", "name", "external_id", "country", "portrait"); private CaseSubject(ObjectNode json, Map> files) { super(json, files); @@ -91,6 +91,26 @@ public Optional externalId() { return Json.optional(json, "external_id", Conv.STRING); } + /** + * Three-letter country code of the subject (ISO 3166-1 alpha-3), or null when it is not known. For a business, its country of incorporation. For a person, the issuing country of an identity document from their verifications; a person with documents from several countries shows one of them, and ICAO document codes such as XXA can appear. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + /** + * Short-lived signed URL of the person's portrait image. Always null for a business, and null for a person without a portrait. The URL expires, so download the image when you read the case instead of storing the URL. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional portrait() { + return Json.optional(json, "portrait", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -144,6 +164,26 @@ public Builder externalId(@Nullable String externalId) { return setNullable("external_id", Conv.STRING, externalId); } + /** + * Three-letter country code of the subject (ISO 3166-1 alpha-3), or null when it is not known. For a business, its country of incorporation. For a person, the issuing country of an identity document from their verifications; a person with documents from several countries shows one of them, and ICAO document codes such as XXA can appear. + * + * @param country the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder country(@Nullable String country) { + return setNullable("country", Conv.STRING, country); + } + + /** + * Short-lived signed URL of the person's portrait image. Always null for a business, and null for a person without a portrait. The URL expires, so download the image when you read the case instead of storing the URL. + * + * @param portrait the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder portrait(@Nullable String portrait) { + return setNullable("portrait", Conv.STRING, portrait); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/CaseTeamSummary.java b/src/main/java/me/didit/types/CaseTeamSummary.java new file mode 100644 index 0000000..a220d2f --- /dev/null +++ b/src/main/java/me/didit/types/CaseTeamSummary.java @@ -0,0 +1,121 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** + * The review team that owns the case, or null when no team owns it. Routing sets it when a routing rule places the case with one of its teams. Change or clear it with PATCH .../cases/{case_uuid}/ (team). + */ +public final class CaseTeamSummary extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "name"); + + private CaseTeamSummary(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code CaseTeamSummary} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static CaseTeamSummary fromJson(JsonNode json) { + return new CaseTeamSummary(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link CaseTeamSummary}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(CaseTeamSummary from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Builds the value. + * + * @return a new immutable {@code CaseTeamSummary} + */ + public CaseTeamSummary build() { + return new CaseTeamSummary(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/CasesBlueprintsInstallPresetsResponse.java b/src/main/java/me/didit/types/CasesBlueprintsInstallPresetsResponse.java deleted file mode 100644 index 2da3254..0000000 --- a/src/main/java/me/didit/types/CasesBlueprintsInstallPresetsResponse.java +++ /dev/null @@ -1,240 +0,0 @@ -// Code generated by codegen from openapi/*.json. DO NOT EDIT. -package me.didit.types; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; -import me.didit.core.MultiStatusResponse; - -/** - * Response of {@code cases.blueprints.install_presets}; the shape depends on the HTTP status. - */ -public final class CasesBlueprintsInstallPresetsResponse extends MultiStatusResponse { - private CasesBlueprintsInstallPresetsResponse(int statusCode, JsonNode json) { - super(statusCode, json); - } - - /** - * Reads the response body received with {@code statusCode}. - * - * @param statusCode the HTTP status - * @param json the response body - * @return the response - */ - public static CasesBlueprintsInstallPresetsResponse fromResponse(int statusCode, JsonNode json) { - return new CasesBlueprintsInstallPresetsResponse(statusCode, json.deepCopy()); - } - - /** - * Whether the API answered 200. - * - * @return true for status 200 - */ - public boolean isOk() { - return statusCode() == 200; - } - - /** - * The body of a 200 response. - * - * @return the body, or empty for another status - */ - public Optional asOk() { - return isOk() ? Optional.of(Conv.model(CasesBlueprintsInstallPresetsResponse.Ok::fromJson).read(json)) : Optional.empty(); - } - - /** - * Whether the API answered 201. - * - * @return true for status 201 - */ - public boolean isCreated() { - return statusCode() == 201; - } - - /** - * The body of a 201 response. - * - * @return the body, or empty for another status - */ - public Optional asCreated() { - return isCreated() ? Optional.of(Conv.model(CasesBlueprintsInstallPresetsResponse.Created::fromJson).read(json)) : Optional.empty(); - } - - public static final class Ok extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("created"); - - private Ok(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Ok} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Ok fromJson(JsonNode json) { - return new Ok(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The created field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> created() { - return Json.optional(json, "created", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Ok}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Ok from) { - super(from); - } - - /** - * The created field. - * - * @param created the value - * @return this builder - */ - public Builder created(List created) { - return set("created", Conv.list(Conv.STRING), created); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Ok} - */ - public Ok build() { - return new Ok(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Created extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("created"); - - private Created(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Created} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Created fromJson(JsonNode json) { - return new Created(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The created field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> created() { - return Json.optional(json, "created", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Created}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Created from) { - super(from); - } - - /** - * The created field. - * - * @param created the value - * @return this builder - */ - public Builder created(List created) { - return set("created", Conv.list(Conv.STRING), created); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Created} - */ - public Created build() { - return new Created(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } -} diff --git a/src/main/java/me/didit/types/CasesBulkUpdateStatusParams.java b/src/main/java/me/didit/types/CasesBulkUpdateStatusParams.java index 862d6f0..724d683 100644 --- a/src/main/java/me/didit/types/CasesBulkUpdateStatusParams.java +++ b/src/main/java/me/didit/types/CasesBulkUpdateStatusParams.java @@ -219,7 +219,16 @@ public static final class Resolution extends StringEnum { /** The value {@code "VALID_THREAT"}. */ public static final Resolution VALID_THREAT = new Resolution("VALID_THREAT"); - private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT); + /** The value {@code "INCONCLUSIVE"}. */ + public static final Resolution INCONCLUSIVE = new Resolution("INCONCLUSIVE"); + + /** The value {@code "REQUIREMENTS_NOT_MET"}. */ + public static final Resolution REQUIREMENTS_NOT_MET = new Resolution("REQUIREMENTS_NOT_MET"); + + /** The value {@code "REQUIREMENTS_SATISFIED"}. */ + public static final Resolution REQUIREMENTS_SATISFIED = new Resolution("REQUIREMENTS_SATISFIED"); + + private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT, INCONCLUSIVE, REQUIREMENTS_NOT_MET, REQUIREMENTS_SATISFIED); private Resolution(String value) { super(value); diff --git a/src/main/java/me/didit/types/CasesBulkUpdateStatusResponse.java b/src/main/java/me/didit/types/CasesBulkUpdateStatusResponse.java deleted file mode 100644 index e3c1063..0000000 --- a/src/main/java/me/didit/types/CasesBulkUpdateStatusResponse.java +++ /dev/null @@ -1,223 +0,0 @@ -// Code generated by codegen from openapi/*.json. DO NOT EDIT. -package me.didit.types; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; - -public final class CasesBulkUpdateStatusResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("updated", "failures"); - - private CasesBulkUpdateStatusResponse(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code CasesBulkUpdateStatusResponse} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static CasesBulkUpdateStatusResponse fromJson(JsonNode json) { - return new CasesBulkUpdateStatusResponse(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The updated field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional updated() { - return Json.optional(json, "updated", Conv.LONG); - } - - /** - * The failures field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> failures() { - return Json.optional(json, "failures", Conv.list(Conv.model(CasesBulkUpdateStatusResponse.Failures::fromJson))); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link CasesBulkUpdateStatusResponse}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(CasesBulkUpdateStatusResponse from) { - super(from); - } - - /** - * The updated field. - * - * @param updated the value - * @return this builder - */ - public Builder updated(long updated) { - return set("updated", Conv.LONG, updated); - } - - /** - * The failures field. - * - * @param failures the value - * @return this builder - */ - public Builder failures(List failures) { - return set("failures", Conv.list(Conv.model(CasesBulkUpdateStatusResponse.Failures::fromJson)), failures); - } - - /** - * Builds the value. - * - * @return a new immutable {@code CasesBulkUpdateStatusResponse} - */ - public CasesBulkUpdateStatusResponse build() { - return new CasesBulkUpdateStatusResponse(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - public static final class Failures extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("case_uuid", "detail"); - - private Failures(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Failures} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Failures fromJson(JsonNode json) { - return new Failures(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The case_uuid field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional caseUuid() { - return Json.optional(json, "case_uuid", Conv.STRING); - } - - /** - * The detail field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional detail() { - return Json.optional(json, "detail", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Failures}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Failures from) { - super(from); - } - - /** - * The case_uuid field. - * - * @param caseUuid the value - * @return this builder - */ - public Builder caseUuid(String caseUuid) { - return set("case_uuid", Conv.STRING, caseUuid); - } - - /** - * The detail field. - * - * @param detail the value - * @return this builder - */ - public Builder detail(String detail) { - return set("detail", Conv.STRING, detail); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Failures} - */ - public Failures build() { - return new Failures(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } -} diff --git a/src/main/java/me/didit/types/CasesRequestsRemindResponse.java b/src/main/java/me/didit/types/CasesRequestsRemindResponse.java new file mode 100644 index 0000000..08dfebc --- /dev/null +++ b/src/main/java/me/didit/types/CasesRequestsRemindResponse.java @@ -0,0 +1,210 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class CasesRequestsRemindResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("request", "reminded_at", "delivery_status", "next_reminder_at"); + + private CasesRequestsRemindResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code CasesRequestsRemindResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static CasesRequestsRemindResponse fromJson(JsonNode json) { + return new CasesRequestsRemindResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The request field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public CustomerMessagingCaseRequest request() { + return Json.required(json, "request", Conv.model(CustomerMessagingCaseRequest::fromJson)); + } + + /** + * When this reminder was recorded. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime remindedAt() { + return Json.required(json, "reminded_at", Conv.DATE_TIME); + } + + /** + * How this reminder's delivery ended. SIMULATED: the email was not sent. UNKNOWN: the email might have been accepted; it is not sent again automatically. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public CasesRequestsRemindResponse.DeliveryStatus deliveryStatus() { + return Json.required(json, "delivery_status", Conv.enumeration(CasesRequestsRemindResponse.DeliveryStatus::of)); + } + + /** + * When you can remind this request again: reminded_at plus 24 hours. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime nextReminderAt() { + return Json.required(json, "next_reminder_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link CasesRequestsRemindResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(CasesRequestsRemindResponse from) { + super(from); + } + + /** + * The request field. + * + * @param request the value + * @return this builder + */ + public Builder request(CustomerMessagingCaseRequest request) { + return set("request", Conv.model(CustomerMessagingCaseRequest::fromJson), request); + } + + /** + * When this reminder was recorded. + * + * @param remindedAt the value + * @return this builder + */ + public Builder remindedAt(OffsetDateTime remindedAt) { + return set("reminded_at", Conv.DATE_TIME, remindedAt); + } + + /** + * How this reminder's delivery ended. SIMULATED: the email was not sent. UNKNOWN: the email might have been accepted; it is not sent again automatically. + * + * @param deliveryStatus the value + * @return this builder + */ + public Builder deliveryStatus(CasesRequestsRemindResponse.DeliveryStatus deliveryStatus) { + return set("delivery_status", Conv.enumeration(CasesRequestsRemindResponse.DeliveryStatus::of), deliveryStatus); + } + + /** + * When you can remind this request again: reminded_at plus 24 hours. + * + * @param nextReminderAt the value + * @return this builder + */ + public Builder nextReminderAt(OffsetDateTime nextReminderAt) { + return set("next_reminder_at", Conv.DATE_TIME, nextReminderAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code CasesRequestsRemindResponse} + * @throws IllegalStateException when a required field is missing + */ + public CasesRequestsRemindResponse build() { + require("request", false); + require("reminded_at", false); + require("delivery_status", false); + require("next_reminder_at", false); + return new CasesRequestsRemindResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * How this reminder's delivery ended. SIMULATED: the email was not sent. UNKNOWN: the email might have been accepted; it is not sent again automatically. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DeliveryStatus extends StringEnum { + /** The value {@code "SENT"}. */ + public static final DeliveryStatus SENT = new DeliveryStatus("SENT"); + + /** The value {@code "SIMULATED"}. */ + public static final DeliveryStatus SIMULATED = new DeliveryStatus("SIMULATED"); + + /** The value {@code "UNKNOWN"}. */ + public static final DeliveryStatus UNKNOWN = new DeliveryStatus("UNKNOWN"); + + private static final List KNOWN_VALUES = List.of(SENT, SIMULATED, UNKNOWN); + + private DeliveryStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DeliveryStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DeliveryStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/CasesResolveParams.java b/src/main/java/me/didit/types/CasesResolveParams.java index 44300bd..9a9169a 100644 --- a/src/main/java/me/didit/types/CasesResolveParams.java +++ b/src/main/java/me/didit/types/CasesResolveParams.java @@ -16,7 +16,7 @@ /** Parameters for {@code cases.resolve}. */ public final class CasesResolveParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("resolution", "note"); + private static final Set KNOWN_FIELDS = Set.of("resolution", "note", "checker"); private CasesResolveParams(ObjectNode json, Map> files) { super(json, files); @@ -71,6 +71,16 @@ public Optional note() { return Json.optional(json, "note", Conv.STRING); } + /** + * Four-eyes blueprints only. The user uuid of the member who must approve this resolution (the same id as assigned_to.uuid on a case, or members[].user_id on a review team). The member must have case access to this application and cannot be the officer who submits the resolution. Omit it or send null to leave the resolution to the checker queue. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional checker() { + return Json.optional(json, "checker", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -104,6 +114,16 @@ public Builder note(@Nullable String note) { return setNullable("note", Conv.STRING, note); } + /** + * Four-eyes blueprints only. The user uuid of the member who must approve this resolution (the same id as assigned_to.uuid on a case, or members[].user_id on a review team). The member must have case access to this application and cannot be the officer who submits the resolution. Omit it or send null to leave the resolution to the checker queue. + * + * @param checker the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder checker(@Nullable String checker) { + return setNullable("checker", Conv.STRING, checker); + } + /** * Builds the value. * @@ -132,7 +152,16 @@ public static final class Resolution extends StringEnum { /** The value {@code "VALID_THREAT"}. */ public static final Resolution VALID_THREAT = new Resolution("VALID_THREAT"); - private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT); + /** The value {@code "INCONCLUSIVE"}. */ + public static final Resolution INCONCLUSIVE = new Resolution("INCONCLUSIVE"); + + /** The value {@code "REQUIREMENTS_NOT_MET"}. */ + public static final Resolution REQUIREMENTS_NOT_MET = new Resolution("REQUIREMENTS_NOT_MET"); + + /** The value {@code "REQUIREMENTS_SATISFIED"}. */ + public static final Resolution REQUIREMENTS_SATISFIED = new Resolution("REQUIREMENTS_SATISFIED"); + + private static final List KNOWN_VALUES = List.of(FALSE_POSITIVE, VALID_THREAT, INCONCLUSIVE, REQUIREMENTS_NOT_MET, REQUIREMENTS_SATISFIED); private Resolution(String value) { super(value); diff --git a/src/main/java/me/didit/types/CasesUpdateParams.java b/src/main/java/me/didit/types/CasesUpdateParams.java index 934ed65..89659d1 100644 --- a/src/main/java/me/didit/types/CasesUpdateParams.java +++ b/src/main/java/me/didit/types/CasesUpdateParams.java @@ -17,7 +17,7 @@ /** Parameters for {@code cases.update}. */ public final class CasesUpdateParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("title", "priority", "due_at", "tags"); + private static final Set KNOWN_FIELDS = Set.of("title", "priority", "due_at", "tags", "team"); private CasesUpdateParams(ObjectNode json, Map> files) { super(json, files); @@ -53,7 +53,7 @@ public Builder toBuilder() { } /** - * The title field. + * New case title. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -73,7 +73,7 @@ public Optional priority() { } /** - * The due_at field. + * New deadline. Send null to clear it. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -83,7 +83,7 @@ public Optional dueAt() { } /** - * The tags field. + * The complete tag list. It replaces the current tags. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -92,6 +92,16 @@ public Optional> tags() { return Json.optional(json, "tags", Conv.list(Conv.STRING)); } + /** + * UUID of the review team that owns the case. It must be an active team of this application. Send null to clear the team. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional team() { + return Json.optional(json, "team", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -106,7 +116,7 @@ private Builder(CasesUpdateParams from) { } /** - * The title field. + * New case title. * * @param title the value * @return this builder @@ -126,7 +136,7 @@ public Builder priority(CasesUpdateParams.Priority priority) { } /** - * The due_at field. + * New deadline. Send null to clear it. * * @param dueAt the value, or {@code null} to send JSON null * @return this builder @@ -136,7 +146,7 @@ public Builder dueAt(@Nullable OffsetDateTime dueAt) { } /** - * The tags field. + * The complete tag list. It replaces the current tags. * * @param tags the value * @return this builder @@ -145,6 +155,16 @@ public Builder tags(List tags) { return set("tags", Conv.list(Conv.STRING), tags); } + /** + * UUID of the review team that owns the case. It must be an active team of this application. Send null to clear the team. + * + * @param team the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder team(@Nullable String team) { + return setNullable("team", Conv.STRING, team); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/ComplianceList.java b/src/main/java/me/didit/types/ComplianceList.java new file mode 100644 index 0000000..966a85c --- /dev/null +++ b/src/main/java/me/didit/types/ComplianceList.java @@ -0,0 +1,412 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** A blocklist, allowlist or custom list of one application. */ +public final class ComplianceList extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "name", "description", "list_type", "entry_type", "is_system", "entry_count", "last_entry_added_at", "created_at", "updated_at"); + + private ComplianceList(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ComplianceList} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ComplianceList fromJson(JsonNode json) { + return new ComplianceList(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * List identifier. Use it as list_uuid in the list and entry endpoints, and as the value of a workflow or transaction rule condition with value_type: "list". + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * List name, unique among the application's lists. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String name() { + return Json.required(json, "name", Conv.STRING); + } + + /** + * Free-text description. Empty string when not set. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String description() { + return Json.required(json, "description", Conv.STRING); + } + + /** + * blocklist: a match declines the session or transaction (system lists only, one per entry type). allowlist: matching items are trusted, for example a face allowlist skips the duplicated-face action; never overrides a blocklist match. custom: used in workflow and transaction rule conditions (value_type: "list"). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ComplianceList.ListType listType() { + return Json.required(json, "list_type", Conv.enumeration(ComplianceList.ListType::of)); + } + + /** + * What the list holds: face, document (document number), phone, email, ip_address (IP address or CIDR range), device_fingerprint, wallet_address, bank_account, country (ISO 3166-1 alpha-3), user or business (your vendor_data), or key (any string). It decides how entry values are validated and matched. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ComplianceList.EntryType entryType() { + return Json.required(json, "entry_type", Conv.enumeration(ComplianceList.EntryType::of)); + } + + /** + * true for the lists Didit provisions: one blocklist per entry type (face, document, phone, email, ip_address, device_fingerprint, wallet_address, bank_account, user, business), the High Risk Countries custom list, and system allowlists. System lists cannot be renamed, edited or deleted; you can add and delete their entries. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isSystem() { + return Json.required(json, "is_system", Conv.BOOLEAN); + } + + /** + * Number of entries currently in the list. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long entryCount() { + return Json.required(json, "entry_count", Conv.LONG); + } + + /** + * When the newest current entry was added. null for an empty list. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastEntryAddedAt() { + return Json.optional(json, "last_entry_added_at", Conv.DATE_TIME); + } + + /** + * When the list was created. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + /** + * When the list was last updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime updatedAt() { + return Json.required(json, "updated_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ComplianceList}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ComplianceList from) { + super(from); + } + + /** + * List identifier. Use it as list_uuid in the list and entry endpoints, and as the value of a workflow or transaction rule condition with value_type: "list". + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * List name, unique among the application's lists. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Free-text description. Empty string when not set. + * + * @param description the value + * @return this builder + */ + public Builder description(String description) { + return set("description", Conv.STRING, description); + } + + /** + * blocklist: a match declines the session or transaction (system lists only, one per entry type). allowlist: matching items are trusted, for example a face allowlist skips the duplicated-face action; never overrides a blocklist match. custom: used in workflow and transaction rule conditions (value_type: "list"). + * + * @param listType the value + * @return this builder + */ + public Builder listType(ComplianceList.ListType listType) { + return set("list_type", Conv.enumeration(ComplianceList.ListType::of), listType); + } + + /** + * What the list holds: face, document (document number), phone, email, ip_address (IP address or CIDR range), device_fingerprint, wallet_address, bank_account, country (ISO 3166-1 alpha-3), user or business (your vendor_data), or key (any string). It decides how entry values are validated and matched. + * + * @param entryType the value + * @return this builder + */ + public Builder entryType(ComplianceList.EntryType entryType) { + return set("entry_type", Conv.enumeration(ComplianceList.EntryType::of), entryType); + } + + /** + * true for the lists Didit provisions: one blocklist per entry type (face, document, phone, email, ip_address, device_fingerprint, wallet_address, bank_account, user, business), the High Risk Countries custom list, and system allowlists. System lists cannot be renamed, edited or deleted; you can add and delete their entries. + * + * @param isSystem the value + * @return this builder + */ + public Builder isSystem(boolean isSystem) { + return set("is_system", Conv.BOOLEAN, isSystem); + } + + /** + * Number of entries currently in the list. + * + * @param entryCount the value + * @return this builder + */ + public Builder entryCount(long entryCount) { + return set("entry_count", Conv.LONG, entryCount); + } + + /** + * When the newest current entry was added. null for an empty list. + * + * @param lastEntryAddedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastEntryAddedAt(@Nullable OffsetDateTime lastEntryAddedAt) { + return setNullable("last_entry_added_at", Conv.DATE_TIME, lastEntryAddedAt); + } + + /** + * When the list was created. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * When the list was last updated. + * + * @param updatedAt the value + * @return this builder + */ + public Builder updatedAt(OffsetDateTime updatedAt) { + return set("updated_at", Conv.DATE_TIME, updatedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ComplianceList} + * @throws IllegalStateException when a required field is missing + */ + public ComplianceList build() { + require("uuid", false); + require("name", false); + require("description", false); + require("list_type", false); + require("entry_type", false); + require("is_system", false); + require("entry_count", false); + require("last_entry_added_at", true); + require("created_at", false); + require("updated_at", false); + return new ComplianceList(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * blocklist: a match declines the session or transaction (system lists only, one per entry type). allowlist: matching items are trusted, for example a face allowlist skips the duplicated-face action; never overrides a blocklist match. custom: used in workflow and transaction rule conditions (value_type: "list"). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ListType extends StringEnum { + /** The value {@code "blocklist"}. */ + public static final ListType BLOCKLIST = new ListType("blocklist"); + + /** The value {@code "allowlist"}. */ + public static final ListType ALLOWLIST = new ListType("allowlist"); + + /** The value {@code "custom"}. */ + public static final ListType CUSTOM = new ListType("custom"); + + private static final List KNOWN_VALUES = List.of(BLOCKLIST, ALLOWLIST, CUSTOM); + + private ListType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ListType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ListType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * What the list holds: face, document (document number), phone, email, ip_address (IP address or CIDR range), device_fingerprint, wallet_address, bank_account, country (ISO 3166-1 alpha-3), user or business (your vendor_data), or key (any string). It decides how entry values are validated and matched. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EntryType extends StringEnum { + /** The value {@code "face"}. */ + public static final EntryType FACE = new EntryType("face"); + + /** The value {@code "document"}. */ + public static final EntryType DOCUMENT = new EntryType("document"); + + /** The value {@code "phone"}. */ + public static final EntryType PHONE = new EntryType("phone"); + + /** The value {@code "email"}. */ + public static final EntryType EMAIL = new EntryType("email"); + + /** The value {@code "ip_address"}. */ + public static final EntryType IP_ADDRESS = new EntryType("ip_address"); + + /** The value {@code "device_fingerprint"}. */ + public static final EntryType DEVICE_FINGERPRINT = new EntryType("device_fingerprint"); + + /** The value {@code "wallet_address"}. */ + public static final EntryType WALLET_ADDRESS = new EntryType("wallet_address"); + + /** The value {@code "bank_account"}. */ + public static final EntryType BANK_ACCOUNT = new EntryType("bank_account"); + + /** The value {@code "country"}. */ + public static final EntryType COUNTRY = new EntryType("country"); + + /** The value {@code "user"}. */ + public static final EntryType USER = new EntryType("user"); + + /** The value {@code "business"}. */ + public static final EntryType BUSINESS = new EntryType("business"); + + /** The value {@code "key"}. */ + public static final EntryType KEY = new EntryType("key"); + + private static final List KNOWN_VALUES = List.of(FACE, DOCUMENT, PHONE, EMAIL, IP_ADDRESS, DEVICE_FINGERPRINT, WALLET_ADDRESS, BANK_ACCOUNT, COUNTRY, USER, BUSINESS, KEY); + + private EntryType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EntryType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EntryType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/ComplianceListEntry.java b/src/main/java/me/didit/types/ComplianceListEntry.java new file mode 100644 index 0000000..7e84cb9 --- /dev/null +++ b/src/main/java/me/didit/types/ComplianceListEntry.java @@ -0,0 +1,823 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** One value in a list. */ +public final class ComplianceListEntry extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "value", "display_label", "comment", "added_by", "reference_session_id", "reference_session_number", "reference_session_type", "reference_object_uuid", "metadata", "created_at"); + + private ComplianceListEntry(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ComplianceListEntry} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ComplianceListEntry fromJson(JsonNode json) { + return new ComplianceListEntry(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Entry identifier. Use it as entry_uuid in Delete a list entry. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * The listed value: a phone number in E.164, an email address, a document number, an IP address or CIDR range, a device fingerprint, a wallet address, a bank account, an ISO 3166-1 alpha-3 country code, a vendor_data, any string on a key list, or the UUID of the stored face on a face list. Values you send are stored in lower case, except phone numbers and country codes. Matching is case-insensitive. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String value() { + return Json.required(json, "value", Conv.STRING); + } + + /** + * Human-readable label. Didit fills it for entries taken from a session (for example the holder's name and document number) when you send none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional displayLabel() { + return Json.optional(json, "display_label", Conv.STRING); + } + + /** + * Free-text note. Empty string when not set. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String comment() { + return Json.required(json, "comment", Conv.STRING); + } + + /** + * Who added the entry: api (an API key), a console user's email address or console, system or migration (entries Didit created), or rule (a transaction or monitoring rule action). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional addedBy() { + return Json.optional(json, "added_by", Conv.STRING); + } + + /** + * Verification session the entry was taken from. null when the entry was added by value. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceSessionId() { + return Json.optional(json, "reference_session_id", Conv.STRING); + } + + /** + * Application-scoped number of that session, as shown in the console. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceSessionNumber() { + return Json.optional(json, "reference_session_number", Conv.LONG); + } + + /** + * kyc for a person verification session, kyb for a business verification session, null when there is no session. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceSessionType() { + return Json.optional(json, "reference_session_type", Conv.enumeration(ComplianceListEntry.ReferenceSessionType::of)); + } + + /** + * The record the entry stands for: the face, document, phone or email record read from a session, the face created by Upload a face to a list, or the UUID you sent (for example a transaction UUID or a user's didit_internal_id). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceObjectUuid() { + return Json.optional(json, "reference_object_uuid", Conv.STRING); + } + + /** + * Extra context. Holds the keys Didit sets (listed here) merged with any metadata you sent when creating the entry, returned as stored. null when there is none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional metadata() { + return Json.optional(json, "metadata", Conv.model(ComplianceListEntry.Metadata::fromJson)); + } + + /** + * When the entry was added. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ComplianceListEntry}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ComplianceListEntry from) { + super(from); + } + + /** + * Entry identifier. Use it as entry_uuid in Delete a list entry. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The listed value: a phone number in E.164, an email address, a document number, an IP address or CIDR range, a device fingerprint, a wallet address, a bank account, an ISO 3166-1 alpha-3 country code, a vendor_data, any string on a key list, or the UUID of the stored face on a face list. Values you send are stored in lower case, except phone numbers and country codes. Matching is case-insensitive. + * + * @param value the value + * @return this builder + */ + public Builder value(String value) { + return set("value", Conv.STRING, value); + } + + /** + * Human-readable label. Didit fills it for entries taken from a session (for example the holder's name and document number) when you send none. + * + * @param displayLabel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder displayLabel(@Nullable String displayLabel) { + return setNullable("display_label", Conv.STRING, displayLabel); + } + + /** + * Free-text note. Empty string when not set. + * + * @param comment the value + * @return this builder + */ + public Builder comment(String comment) { + return set("comment", Conv.STRING, comment); + } + + /** + * Who added the entry: api (an API key), a console user's email address or console, system or migration (entries Didit created), or rule (a transaction or monitoring rule action). + * + * @param addedBy the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder addedBy(@Nullable String addedBy) { + return setNullable("added_by", Conv.STRING, addedBy); + } + + /** + * Verification session the entry was taken from. null when the entry was added by value. + * + * @param referenceSessionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder referenceSessionId(@Nullable String referenceSessionId) { + return setNullable("reference_session_id", Conv.STRING, referenceSessionId); + } + + /** + * Application-scoped number of that session, as shown in the console. + * + * @param referenceSessionNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder referenceSessionNumber(@Nullable Long referenceSessionNumber) { + return setNullable("reference_session_number", Conv.LONG, referenceSessionNumber); + } + + /** + * kyc for a person verification session, kyb for a business verification session, null when there is no session. + * + * @param referenceSessionType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder referenceSessionType(ComplianceListEntry.@Nullable ReferenceSessionType referenceSessionType) { + return setNullable("reference_session_type", Conv.enumeration(ComplianceListEntry.ReferenceSessionType::of), referenceSessionType); + } + + /** + * The record the entry stands for: the face, document, phone or email record read from a session, the face created by Upload a face to a list, or the UUID you sent (for example a transaction UUID or a user's didit_internal_id). + * + * @param referenceObjectUuid the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder referenceObjectUuid(@Nullable String referenceObjectUuid) { + return setNullable("reference_object_uuid", Conv.STRING, referenceObjectUuid); + } + + /** + * Extra context. Holds the keys Didit sets (listed here) merged with any metadata you sent when creating the entry, returned as stored. null when there is none. + * + * @param metadata the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder metadata(ComplianceListEntry.@Nullable Metadata metadata) { + return setNullable("metadata", Conv.model(ComplianceListEntry.Metadata::fromJson), metadata); + } + + /** + * When the entry was added. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ComplianceListEntry} + * @throws IllegalStateException when a required field is missing + */ + public ComplianceListEntry build() { + require("uuid", false); + require("value", false); + require("display_label", true); + require("comment", false); + require("added_by", true); + require("reference_session_id", true); + require("reference_session_number", true); + require("reference_session_type", true); + require("reference_object_uuid", true); + require("metadata", true); + require("created_at", false); + return new ComplianceListEntry(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * kyc for a person verification session, kyb for a business verification session, null when there is no session. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ReferenceSessionType extends StringEnum { + /** The value {@code "kyc"}. */ + public static final ReferenceSessionType KYC = new ReferenceSessionType("kyc"); + + /** The value {@code "kyb"}. */ + public static final ReferenceSessionType KYB = new ReferenceSessionType("kyb"); + + private static final List KNOWN_VALUES = List.of(KYC, KYB); + + private ReferenceSessionType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ReferenceSessionType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ReferenceSessionType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Extra context. Holds the keys Didit sets (listed here) merged with any metadata you sent when creating the entry, returned as stored. null when there is none. + */ + public static final class Metadata extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("reference_image_url", "front_image_url", "full_name", "issuing_state", "document_type", "region", "document_number", "face_image_source", "reference_node_id", "fingerprint_tier", "sandbox", "reference_type", "reference_id"); + + private Metadata(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Metadata} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Metadata fromJson(JsonNode json) { + return new Metadata(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Face entries: short-lived signed URL, issued again on every call; null for a scoped API key without media: read. Absent when no image belongs to the entry. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceImageUrl() { + return Json.optional(json, "reference_image_url", Conv.STRING); + } + + /** + * Document entries that carry a document front image: short-lived signed URL, issued again on every call; null for a scoped API key without media: read. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional frontImageUrl() { + return Json.optional(json, "front_image_url", Conv.STRING); + } + + /** + * Document entries taken from a session: holder's full name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fullName() { + return Json.optional(json, "full_name", Conv.STRING); + } + + /** + * Document entries taken from a session: ISO 3166-1 alpha-3 issuing country. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional issuingState() { + return Json.optional(json, "issuing_state", Conv.STRING); + } + + /** + * Document entries taken from a session: document type code, for example P, ID or DL. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentType() { + return Json.optional(json, "document_type", Conv.STRING); + } + + /** + * Document entries taken from a session: issuing state or province of a regionally issued document, empty when not applicable. Not set by Add session items to the blocklist. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional region() { + return Json.optional(json, "region", Conv.STRING); + } + + /** + * Document entries taken from a session: the document number. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentNumber() { + return Json.optional(json, "document_number", Conv.STRING); + } + + /** + * Face entries taken from a document-capture selfie: which selfie. Absent for liveness faces. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional faceImageSource() { + return Json.optional(json, "face_image_source", Conv.enumeration(ComplianceListEntry.Metadata.FaceImageSource::of)); + } + + /** + * Face entries taken from a document-capture selfie: the ID verification node the selfie came from. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceNodeId() { + return Json.optional(json, "reference_node_id", Conv.STRING); + } + + /** + * Device fingerprint entries taken from a session: how the fingerprint was derived. persistent_id identifies one device; composite_hash and legacy_fp can be shared by devices of the same model. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fingerprintTier() { + return Json.optional(json, "fingerprint_tier", Conv.enumeration(ComplianceListEntry.Metadata.FingerprintTier::of)); + } + + /** + * true on a face entry uploaded in a sandbox application; such an entry matches nothing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sandbox() { + return Json.optional(json, "sandbox", Conv.BOOLEAN); + } + + /** + * Set by you: what reference_object_uuid points to, so the console links back to it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceType() { + return Json.optional(json, "reference_type", Conv.enumeration(ComplianceListEntry.Metadata.ReferenceType::of)); + } + + /** + * Set by you: identifier of the source record. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceId() { + return Json.optional(json, "reference_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Metadata}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Metadata from) { + super(from); + } + + /** + * Face entries: short-lived signed URL, issued again on every call; null for a scoped API key without media: read. Absent when no image belongs to the entry. + * + * @param referenceImageUrl the value + * @return this builder + */ + public Builder referenceImageUrl(String referenceImageUrl) { + return set("reference_image_url", Conv.STRING, referenceImageUrl); + } + + /** + * Document entries that carry a document front image: short-lived signed URL, issued again on every call; null for a scoped API key without media: read. + * + * @param frontImageUrl the value + * @return this builder + */ + public Builder frontImageUrl(String frontImageUrl) { + return set("front_image_url", Conv.STRING, frontImageUrl); + } + + /** + * Document entries taken from a session: holder's full name. + * + * @param fullName the value + * @return this builder + */ + public Builder fullName(String fullName) { + return set("full_name", Conv.STRING, fullName); + } + + /** + * Document entries taken from a session: ISO 3166-1 alpha-3 issuing country. + * + * @param issuingState the value + * @return this builder + */ + public Builder issuingState(String issuingState) { + return set("issuing_state", Conv.STRING, issuingState); + } + + /** + * Document entries taken from a session: document type code, for example P, ID or DL. + * + * @param documentType the value + * @return this builder + */ + public Builder documentType(String documentType) { + return set("document_type", Conv.STRING, documentType); + } + + /** + * Document entries taken from a session: issuing state or province of a regionally issued document, empty when not applicable. Not set by Add session items to the blocklist. + * + * @param region the value + * @return this builder + */ + public Builder region(String region) { + return set("region", Conv.STRING, region); + } + + /** + * Document entries taken from a session: the document number. + * + * @param documentNumber the value + * @return this builder + */ + public Builder documentNumber(String documentNumber) { + return set("document_number", Conv.STRING, documentNumber); + } + + /** + * Face entries taken from a document-capture selfie: which selfie. Absent for liveness faces. + * + * @param faceImageSource the value + * @return this builder + */ + public Builder faceImageSource(ComplianceListEntry.Metadata.FaceImageSource faceImageSource) { + return set("face_image_source", Conv.enumeration(ComplianceListEntry.Metadata.FaceImageSource::of), faceImageSource); + } + + /** + * Face entries taken from a document-capture selfie: the ID verification node the selfie came from. + * + * @param referenceNodeId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder referenceNodeId(@Nullable String referenceNodeId) { + return setNullable("reference_node_id", Conv.STRING, referenceNodeId); + } + + /** + * Device fingerprint entries taken from a session: how the fingerprint was derived. persistent_id identifies one device; composite_hash and legacy_fp can be shared by devices of the same model. + * + * @param fingerprintTier the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fingerprintTier(ComplianceListEntry.Metadata.@Nullable FingerprintTier fingerprintTier) { + return setNullable("fingerprint_tier", Conv.enumeration(ComplianceListEntry.Metadata.FingerprintTier::of), fingerprintTier); + } + + /** + * true on a face entry uploaded in a sandbox application; such an entry matches nothing. + * + * @param sandbox the value + * @return this builder + */ + public Builder sandbox(boolean sandbox) { + return set("sandbox", Conv.BOOLEAN, sandbox); + } + + /** + * Set by you: what reference_object_uuid points to, so the console links back to it. + * + * @param referenceType the value + * @return this builder + */ + public Builder referenceType(ComplianceListEntry.Metadata.ReferenceType referenceType) { + return set("reference_type", Conv.enumeration(ComplianceListEntry.Metadata.ReferenceType::of), referenceType); + } + + /** + * Set by you: identifier of the source record. + * + * @param referenceId the value + * @return this builder + */ + public Builder referenceId(String referenceId) { + return set("reference_id", Conv.STRING, referenceId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Metadata} + */ + public Metadata build() { + return new Metadata(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Face entries taken from a document-capture selfie: which selfie. Absent for liveness faces. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class FaceImageSource extends StringEnum { + /** The value {@code "front_image_camera_front"}. */ + public static final FaceImageSource FRONT_IMAGE_CAMERA_FRONT = new FaceImageSource("front_image_camera_front"); + + /** The value {@code "back_image_camera_front"}. */ + public static final FaceImageSource BACK_IMAGE_CAMERA_FRONT = new FaceImageSource("back_image_camera_front"); + + private static final List KNOWN_VALUES = List.of(FRONT_IMAGE_CAMERA_FRONT, BACK_IMAGE_CAMERA_FRONT); + + private FaceImageSource(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static FaceImageSource of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, FaceImageSource::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Device fingerprint entries taken from a session: how the fingerprint was derived. persistent_id identifies one device; composite_hash and legacy_fp can be shared by devices of the same model. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class FingerprintTier extends StringEnum { + /** The value {@code "persistent_id"}. */ + public static final FingerprintTier PERSISTENT_ID = new FingerprintTier("persistent_id"); + + /** The value {@code "composite_hash"}. */ + public static final FingerprintTier COMPOSITE_HASH = new FingerprintTier("composite_hash"); + + /** The value {@code "legacy_fp"}. */ + public static final FingerprintTier LEGACY_FP = new FingerprintTier("legacy_fp"); + + /** The value {@code "unknown"}. */ + public static final FingerprintTier UNKNOWN = new FingerprintTier("unknown"); + + private static final List KNOWN_VALUES = List.of(PERSISTENT_ID, COMPOSITE_HASH, LEGACY_FP, UNKNOWN); + + private FingerprintTier(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static FingerprintTier of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, FingerprintTier::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Set by you: what reference_object_uuid points to, so the console links back to it. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ReferenceType extends StringEnum { + /** The value {@code "transaction"}. */ + public static final ReferenceType TRANSACTION = new ReferenceType("transaction"); + + /** The value {@code "vendor_user"}. */ + public static final ReferenceType VENDOR_USER = new ReferenceType("vendor_user"); + + /** The value {@code "vendor_business"}. */ + public static final ReferenceType VENDOR_BUSINESS = new ReferenceType("vendor_business"); + + private static final List KNOWN_VALUES = List.of(TRANSACTION, VENDOR_USER, VENDOR_BUSINESS); + + private ReferenceType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ReferenceType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ReferenceType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/CustomizationUpdateParams.java b/src/main/java/me/didit/types/CustomizationUpdateParams.java index 4ba76cf..819f611 100644 --- a/src/main/java/me/didit/types/CustomizationUpdateParams.java +++ b/src/main/java/me/didit/types/CustomizationUpdateParams.java @@ -14,7 +14,7 @@ /** Parameters for {@code customization.update}. */ public final class CustomizationUpdateParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("image_square", "image_rectangular", "image_favicon"); + private static final Set KNOWN_FIELDS = Set.of("image_square", "image_rectangular", "image_rectangular_dark", "image_favicon"); private CustomizationUpdateParams(ObjectNode json, Map> files) { super(json, files); @@ -50,7 +50,7 @@ public Builder toBuilder() { } /** - * Square logo (1:1 aspect ratio). Max 1 MB. Allowed: png, jpg, jpeg, webp. + * Square logo (1:1 aspect ratio). PNG, JPG or WebP, at least 512 px on the long side, at most 1 MB. * * @return the file to upload, if set */ @@ -59,7 +59,7 @@ public Optional imageSquare() { } /** - * Rectangular logo (2:1 aspect ratio). Max 1 MB. Allowed: png, jpg, jpeg, webp. + * Rectangular logo (2:1 aspect ratio). PNG, JPG or WebP, at least 512 px on the long side, at most 1 MB. * * @return the file to upload, if set */ @@ -67,6 +67,15 @@ public Optional imageRectangular() { return file("image_rectangular"); } + /** + * Rectangular logo for dark mode (2:1 aspect ratio), shown on a dark background. PNG, JPG or WebP, at least 512 px on the long side, at most 1 MB. Returned as logo_rectangular_dark. + * + * @return the file to upload, if set + */ + public Optional imageRectangularDark() { + return file("image_rectangular_dark"); + } + /** * Favicon image. Max 500 KB. * @@ -90,7 +99,7 @@ private Builder(CustomizationUpdateParams from) { } /** - * Square logo (1:1 aspect ratio). Max 1 MB. Allowed: png, jpg, jpeg, webp. + * Square logo (1:1 aspect ratio). PNG, JPG or WebP, at least 512 px on the long side, at most 1 MB. * * @param imageSquare the value * @return this builder @@ -110,7 +119,7 @@ public Builder imageSquare(Path path) { } /** - * Rectangular logo (2:1 aspect ratio). Max 1 MB. Allowed: png, jpg, jpeg, webp. + * Rectangular logo (2:1 aspect ratio). PNG, JPG or WebP, at least 512 px on the long side, at most 1 MB. * * @param imageRectangular the value * @return this builder @@ -129,6 +138,26 @@ public Builder imageRectangular(Path path) { return setFile("image_rectangular", FileUpload.of(path)); } + /** + * Rectangular logo for dark mode (2:1 aspect ratio), shown on a dark background. PNG, JPG or WebP, at least 512 px on the long side, at most 1 MB. Returned as logo_rectangular_dark. + * + * @param imageRectangularDark the value + * @return this builder + */ + public Builder imageRectangularDark(FileUpload imageRectangularDark) { + return setFile("image_rectangular_dark", imageRectangularDark); + } + + /** + * Uploads the file at {@code path} as {@code image_rectangular_dark}. + * + * @param path the file to read when the request is sent + * @return this builder + */ + public Builder imageRectangularDark(Path path) { + return setFile("image_rectangular_dark", FileUpload.of(path)); + } + /** * Favicon image. Max 500 KB. * diff --git a/src/main/java/me/didit/types/UsersListNetworksResponse.java b/src/main/java/me/didit/types/EntityNetworkMembershipList.java similarity index 73% rename from src/main/java/me/didit/types/UsersListNetworksResponse.java rename to src/main/java/me/didit/types/EntityNetworkMembershipList.java index 75f5c5a..5cca574 100644 --- a/src/main/java/me/didit/types/UsersListNetworksResponse.java +++ b/src/main/java/me/didit/types/EntityNetworkMembershipList.java @@ -12,22 +12,22 @@ import me.didit.core.Json; import me.didit.core.JsonModel; -public final class UsersListNetworksResponse extends JsonModel { +public final class EntityNetworkMembershipList extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("results"); - private UsersListNetworksResponse(ObjectNode json, Map> files) { + private EntityNetworkMembershipList(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code UsersListNetworksResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code EntityNetworkMembershipList} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static UsersListNetworksResponse fromJson(JsonNode json) { - return new UsersListNetworksResponse(Json.copyObject(json), Map.of()); + public static EntityNetworkMembershipList fromJson(JsonNode json) { + return new EntityNetworkMembershipList(Json.copyObject(json), Map.of()); } /** @@ -63,11 +63,11 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link UsersListNetworksResponse}. */ + /** Builds a {@link EntityNetworkMembershipList}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(UsersListNetworksResponse from) { + private Builder(EntityNetworkMembershipList from) { super(from); } @@ -84,10 +84,10 @@ public Builder results(List results) { /** * Builds the value. * - * @return a new immutable {@code UsersListNetworksResponse} + * @return a new immutable {@code EntityNetworkMembershipList} */ - public UsersListNetworksResponse build() { - return new UsersListNetworksResponse(buildJson(), buildFiles()); + public EntityNetworkMembershipList build() { + return new EntityNetworkMembershipList(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/EntityStatusUpdateRequest.java b/src/main/java/me/didit/types/EntityStatusUpdateRequest.java new file mode 100644 index 0000000..48d0c57 --- /dev/null +++ b/src/main/java/me/didit/types/EntityStatusUpdateRequest.java @@ -0,0 +1,146 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class EntityStatusUpdateRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("status"); + + private EntityStatusUpdateRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code EntityStatusUpdateRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static EntityStatusUpdateRequest fromJson(JsonNode json) { + return new EntityStatusUpdateRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * New lifecycle status. BLOCKED also adds the vendor_data to the system blocklist. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public EntityStatusUpdateRequest.Status status() { + return Json.required(json, "status", Conv.enumeration(EntityStatusUpdateRequest.Status::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link EntityStatusUpdateRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(EntityStatusUpdateRequest from) { + super(from); + } + + /** + * New lifecycle status. BLOCKED also adds the vendor_data to the system blocklist. + * + * @param status the value + * @return this builder + */ + public Builder status(EntityStatusUpdateRequest.Status status) { + return set("status", Conv.enumeration(EntityStatusUpdateRequest.Status::of), status); + } + + /** + * Builds the value. + * + * @return a new immutable {@code EntityStatusUpdateRequest} + * @throws IllegalStateException when a required field is missing + */ + public EntityStatusUpdateRequest build() { + require("status", false); + return new EntityStatusUpdateRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * New lifecycle status. BLOCKED also adds the vendor_data to the system blocklist. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final Status ACTIVE = new Status("ACTIVE"); + + /** The value {@code "FLAGGED"}. */ + public static final Status FLAGGED = new Status("FLAGGED"); + + /** The value {@code "BLOCKED"}. */ + public static final Status BLOCKED = new Status("BLOCKED"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, FLAGGED, BLOCKED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/FeatureStatusOverride.java b/src/main/java/me/didit/types/FeatureStatusOverride.java new file mode 100644 index 0000000..f96da27 --- /dev/null +++ b/src/main/java/me/didit/types/FeatureStatusOverride.java @@ -0,0 +1,744 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One override of a check (feature) status made with PATCH /v3/session/{sessionId}/features/{nodeId}/update-status/: what it changes, whether it applied or still waits for the person to finish the flow, who asked and why. + */ +public final class FeatureStatusOverride extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "node_id", "feature_type", "feature_model", "previous_status", "new_status", "state", "forced", "reason", "requested_by", "requested_at", "applied_at", "cancelled_at", "cancel_reason"); + + private FeatureStatusOverride(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code FeatureStatusOverride} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static FeatureStatusOverride fromJson(JsonNode json) { + return new FeatureStatusOverride(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the override. Pass it to the cancel endpoint. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String id() { + return Json.required(json, "id", Conv.STRING); + } + + /** + * The workflow node the request named (default for a step without a node). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String nodeId() { + return Json.required(json, "node_id", Conv.STRING); + } + + /** + * The feature_type the request passed, or an empty string when it passed none. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String featureType() { + return Json.required(json, "feature_type", Conv.STRING); + } + + /** + * The kind of check that was overridden, for example KYC, Face, AML or GeolocationCheck. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String featureModel() { + return Json.required(json, "feature_model", Conv.STRING); + } + + /** + * The check's status when the override was requested. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public FeatureStatusOverride.PreviousStatus previousStatus() { + return Json.required(json, "previous_status", Conv.enumeration(FeatureStatusOverride.PreviousStatus::of)); + } + + /** + * The status the override sets. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public FeatureStatusOverride.NewStatus newStatus() { + return Json.required(json, "new_status", Conv.enumeration(FeatureStatusOverride.NewStatus::of)); + } + + /** + * applied: the check has the new status. pending_until_flow_finishes: the override waits and applies when the person finishes the flow. cancelled: it waited and never applied (see cancel_reason). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public FeatureStatusOverride.State state() { + return Json.required(json, "state", Conv.enumeration(FeatureStatusOverride.State::of)); + } + + /** + * true when the override applied at once while the person was still in the flow. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean forced() { + return Json.required(json, "forced", Conv.BOOLEAN); + } + + /** + * The comment sent with the request, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * Who requested the override, or null when that is not known. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional requestedBy() { + return Json.optional(json, "requested_by", Conv.model(FeatureStatusOverride.RequestedBy::fromJson)); + } + + /** + * When the override was requested. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime requestedAt() { + return Json.required(json, "requested_at", Conv.DATE_TIME); + } + + /** + * When the check took the new status, or null while it waits or once it is cancelled. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional appliedAt() { + return Json.optional(json, "applied_at", Conv.DATE_TIME); + } + + /** + * When a waiting override was cancelled, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional cancelledAt() { + return Json.optional(json, "cancelled_at", Conv.DATE_TIME); + } + + /** + * Why a waiting override never applied, or null. cancelled_by_reviewer: cancelled with the cancel endpoint. replaced: a newer override on the same check replaced it. check_changed: the person redid the check before finishing the flow. session_closed: the session left the flow before the person finished it (abandoned, expired, or decided mid-flow). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional cancelReason() { + return Json.optional(json, "cancel_reason", Conv.enumeration(FeatureStatusOverride.CancelReason::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link FeatureStatusOverride}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(FeatureStatusOverride from) { + super(from); + } + + /** + * Identifier of the override. Pass it to the cancel endpoint. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The workflow node the request named (default for a step without a node). + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * The feature_type the request passed, or an empty string when it passed none. + * + * @param featureType the value + * @return this builder + */ + public Builder featureType(String featureType) { + return set("feature_type", Conv.STRING, featureType); + } + + /** + * The kind of check that was overridden, for example KYC, Face, AML or GeolocationCheck. + * + * @param featureModel the value + * @return this builder + */ + public Builder featureModel(String featureModel) { + return set("feature_model", Conv.STRING, featureModel); + } + + /** + * The check's status when the override was requested. + * + * @param previousStatus the value + * @return this builder + */ + public Builder previousStatus(FeatureStatusOverride.PreviousStatus previousStatus) { + return set("previous_status", Conv.enumeration(FeatureStatusOverride.PreviousStatus::of), previousStatus); + } + + /** + * The status the override sets. + * + * @param newStatus the value + * @return this builder + */ + public Builder newStatus(FeatureStatusOverride.NewStatus newStatus) { + return set("new_status", Conv.enumeration(FeatureStatusOverride.NewStatus::of), newStatus); + } + + /** + * applied: the check has the new status. pending_until_flow_finishes: the override waits and applies when the person finishes the flow. cancelled: it waited and never applied (see cancel_reason). + * + * @param state the value + * @return this builder + */ + public Builder state(FeatureStatusOverride.State state) { + return set("state", Conv.enumeration(FeatureStatusOverride.State::of), state); + } + + /** + * true when the override applied at once while the person was still in the flow. + * + * @param forced the value + * @return this builder + */ + public Builder forced(boolean forced) { + return set("forced", Conv.BOOLEAN, forced); + } + + /** + * The comment sent with the request, or null. + * + * @param reason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reason(@Nullable String reason) { + return setNullable("reason", Conv.STRING, reason); + } + + /** + * Who requested the override, or null when that is not known. + * + * @param requestedBy the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder requestedBy(FeatureStatusOverride.@Nullable RequestedBy requestedBy) { + return setNullable("requested_by", Conv.model(FeatureStatusOverride.RequestedBy::fromJson), requestedBy); + } + + /** + * When the override was requested. + * + * @param requestedAt the value + * @return this builder + */ + public Builder requestedAt(OffsetDateTime requestedAt) { + return set("requested_at", Conv.DATE_TIME, requestedAt); + } + + /** + * When the check took the new status, or null while it waits or once it is cancelled. + * + * @param appliedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder appliedAt(@Nullable OffsetDateTime appliedAt) { + return setNullable("applied_at", Conv.DATE_TIME, appliedAt); + } + + /** + * When a waiting override was cancelled, or null. + * + * @param cancelledAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder cancelledAt(@Nullable OffsetDateTime cancelledAt) { + return setNullable("cancelled_at", Conv.DATE_TIME, cancelledAt); + } + + /** + * Why a waiting override never applied, or null. cancelled_by_reviewer: cancelled with the cancel endpoint. replaced: a newer override on the same check replaced it. check_changed: the person redid the check before finishing the flow. session_closed: the session left the flow before the person finished it (abandoned, expired, or decided mid-flow). + * + * @param cancelReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder cancelReason(FeatureStatusOverride.@Nullable CancelReason cancelReason) { + return setNullable("cancel_reason", Conv.enumeration(FeatureStatusOverride.CancelReason::of), cancelReason); + } + + /** + * Builds the value. + * + * @return a new immutable {@code FeatureStatusOverride} + * @throws IllegalStateException when a required field is missing + */ + public FeatureStatusOverride build() { + require("id", false); + require("node_id", false); + require("feature_type", false); + require("feature_model", false); + require("previous_status", false); + require("new_status", false); + require("state", false); + require("forced", false); + require("reason", true); + require("requested_by", true); + require("requested_at", false); + require("applied_at", true); + require("cancelled_at", true); + require("cancel_reason", true); + return new FeatureStatusOverride(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * The check's status when the override was requested. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class PreviousStatus extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final PreviousStatus NOT_FINISHED = new PreviousStatus("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final PreviousStatus APPROVED = new PreviousStatus("Approved"); + + /** The value {@code "Declined"}. */ + public static final PreviousStatus DECLINED = new PreviousStatus("Declined"); + + /** The value {@code "In Review"}. */ + public static final PreviousStatus IN_REVIEW = new PreviousStatus("In Review"); + + /** The value {@code "Resub Requested"}. */ + public static final PreviousStatus RESUB_REQUESTED = new PreviousStatus("Resub Requested"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private PreviousStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static PreviousStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, PreviousStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The status the override sets. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NewStatus extends StringEnum { + /** The value {@code "Approved"}. */ + public static final NewStatus APPROVED = new NewStatus("Approved"); + + /** The value {@code "Declined"}. */ + public static final NewStatus DECLINED = new NewStatus("Declined"); + + /** The value {@code "In Review"}. */ + public static final NewStatus IN_REVIEW = new NewStatus("In Review"); + + private static final List KNOWN_VALUES = List.of(APPROVED, DECLINED, IN_REVIEW); + + private NewStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NewStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NewStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * applied: the check has the new status. pending_until_flow_finishes: the override waits and applies when the person finishes the flow. cancelled: it waited and never applied (see cancel_reason). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class State extends StringEnum { + /** The value {@code "applied"}. */ + public static final State APPLIED = new State("applied"); + + /** The value {@code "pending_until_flow_finishes"}. */ + public static final State PENDING_UNTIL_FLOW_FINISHES = new State("pending_until_flow_finishes"); + + /** The value {@code "cancelled"}. */ + public static final State CANCELLED = new State("cancelled"); + + private static final List KNOWN_VALUES = List.of(APPLIED, PENDING_UNTIL_FLOW_FINISHES, CANCELLED); + + private State(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static State of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, State::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** Who requested the override, or null when that is not known. */ + public static final class RequestedBy extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("email", "name", "type"); + + private RequestedBy(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code RequestedBy} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static RequestedBy fromJson(JsonNode json) { + return new RequestedBy(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Email of the console user, or null for API keys and the system. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + /** + * Display name of the requester, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * Who made the request: a console user, an API key, or Didit itself. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional type() { + return Json.optional(json, "type", Conv.enumeration(FeatureStatusOverride.RequestedBy.Type::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link RequestedBy}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(RequestedBy from) { + super(from); + } + + /** + * Email of the console user, or null for API keys and the system. + * + * @param email the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder email(@Nullable String email) { + return setNullable("email", Conv.STRING, email); + } + + /** + * Display name of the requester, or null. + * + * @param name the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder name(@Nullable String name) { + return setNullable("name", Conv.STRING, name); + } + + /** + * Who made the request: a console user, an API key, or Didit itself. + * + * @param type the value + * @return this builder + */ + public Builder type(FeatureStatusOverride.RequestedBy.Type type) { + return set("type", Conv.enumeration(FeatureStatusOverride.RequestedBy.Type::of), type); + } + + /** + * Builds the value. + * + * @return a new immutable {@code RequestedBy} + */ + public RequestedBy build() { + return new RequestedBy(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Who made the request: a console user, an API key, or Didit itself. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "CONSOLE_USER"}. */ + public static final Type CONSOLE_USER = new Type("CONSOLE_USER"); + + /** The value {@code "API_KEY"}. */ + public static final Type API_KEY = new Type("API_KEY"); + + /** The value {@code "SYSTEM"}. */ + public static final Type SYSTEM = new Type("SYSTEM"); + + private static final List KNOWN_VALUES = List.of(CONSOLE_USER, API_KEY, SYSTEM); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * Why a waiting override never applied, or null. cancelled_by_reviewer: cancelled with the cancel endpoint. replaced: a newer override on the same check replaced it. check_changed: the person redid the check before finishing the flow. session_closed: the session left the flow before the person finished it (abandoned, expired, or decided mid-flow). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class CancelReason extends StringEnum { + /** The value {@code "cancelled_by_reviewer"}. */ + public static final CancelReason CANCELLED_BY_REVIEWER = new CancelReason("cancelled_by_reviewer"); + + /** The value {@code "replaced"}. */ + public static final CancelReason REPLACED = new CancelReason("replaced"); + + /** The value {@code "check_changed"}. */ + public static final CancelReason CHECK_CHANGED = new CancelReason("check_changed"); + + /** The value {@code "session_closed"}. */ + public static final CancelReason SESSION_CLOSED = new CancelReason("session_closed"); + + private static final List KNOWN_VALUES = List.of(CANCELLED_BY_REVIEWER, REPLACED, CHECK_CHANGED, SESSION_CLOSED); + + private CancelReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static CancelReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, CancelReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorAcceptRequest.java b/src/main/java/me/didit/types/InvestigatorAcceptRequest.java new file mode 100644 index 0000000..3e925c7 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorAcceptRequest.java @@ -0,0 +1,158 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class InvestigatorAcceptRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("narrative", "send_email", "email_address", "email_language"); + + private InvestigatorAcceptRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorAcceptRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorAcceptRequest fromJson(JsonNode json) { + return new InvestigatorAcceptRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * A rewritten narrative. Sending one records the decision as edited and accepted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional narrative() { + return Json.optional(json, "narrative", Conv.STRING); + } + + /** + * Email the person about the decision. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sendEmail() { + return Json.optional(json, "send_email", Conv.BOOLEAN); + } + + /** + * Required when send_email is true. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailAddress() { + return Json.optional(json, "email_address", Conv.STRING); + } + + /** + * The email_language field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailLanguage() { + return Json.optional(json, "email_language", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorAcceptRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorAcceptRequest from) { + super(from); + } + + /** + * A rewritten narrative. Sending one records the decision as edited and accepted. + * + * @param narrative the value + * @return this builder + */ + public Builder narrative(String narrative) { + return set("narrative", Conv.STRING, narrative); + } + + /** + * Email the person about the decision. + * + * @param sendEmail the value + * @return this builder + */ + public Builder sendEmail(boolean sendEmail) { + return set("send_email", Conv.BOOLEAN, sendEmail); + } + + /** + * Required when send_email is true. + * + * @param emailAddress the value + * @return this builder + */ + public Builder emailAddress(String emailAddress) { + return set("email_address", Conv.STRING, emailAddress); + } + + /** + * The email_language field. + * + * @param emailLanguage the value + * @return this builder + */ + public Builder emailLanguage(String emailLanguage) { + return set("email_language", Conv.STRING, emailLanguage); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorAcceptRequest} + */ + public InvestigatorAcceptRequest build() { + return new InvestigatorAcceptRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/BusinessesListNetworksResponse.java b/src/main/java/me/didit/types/InvestigatorBatchAcceptRequest.java similarity index 54% rename from src/main/java/me/didit/types/BusinessesListNetworksResponse.java rename to src/main/java/me/didit/types/InvestigatorBatchAcceptRequest.java index 6d8793f..2762d7f 100644 --- a/src/main/java/me/didit/types/BusinessesListNetworksResponse.java +++ b/src/main/java/me/didit/types/InvestigatorBatchAcceptRequest.java @@ -5,29 +5,28 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import me.didit.core.Conv; import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; -public final class BusinessesListNetworksResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("results"); +public final class InvestigatorBatchAcceptRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("run_ids"); - private BusinessesListNetworksResponse(ObjectNode json, Map> files) { + private InvestigatorBatchAcceptRequest(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code BusinessesListNetworksResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code InvestigatorBatchAcceptRequest} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static BusinessesListNetworksResponse fromJson(JsonNode json) { - return new BusinessesListNetworksResponse(Json.copyObject(json), Map.of()); + public static InvestigatorBatchAcceptRequest fromJson(JsonNode json) { + return new InvestigatorBatchAcceptRequest(Json.copyObject(json), Map.of()); } /** @@ -49,13 +48,13 @@ public Builder toBuilder() { } /** - * The results field. + * The run_ids field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(EntityNetworkMembership::fromJson))); + public List runIds() { + return Json.required(json, "run_ids", Conv.list(Conv.STRING)); } @Override @@ -63,31 +62,33 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link BusinessesListNetworksResponse}. */ + /** Builds a {@link InvestigatorBatchAcceptRequest}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(BusinessesListNetworksResponse from) { + private Builder(InvestigatorBatchAcceptRequest from) { super(from); } /** - * The results field. + * The run_ids field. * - * @param results the value + * @param runIds the value * @return this builder */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(EntityNetworkMembership::fromJson)), results); + public Builder runIds(List runIds) { + return set("run_ids", Conv.list(Conv.STRING), runIds); } /** * Builds the value. * - * @return a new immutable {@code BusinessesListNetworksResponse} + * @return a new immutable {@code InvestigatorBatchAcceptRequest} + * @throws IllegalStateException when a required field is missing */ - public BusinessesListNetworksResponse build() { - return new BusinessesListNetworksResponse(buildJson(), buildFiles()); + public InvestigatorBatchAcceptRequest build() { + require("run_ids", false); + return new InvestigatorBatchAcceptRequest(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/InvestigatorBatchAcceptResponse.java b/src/main/java/me/didit/types/InvestigatorBatchAcceptResponse.java new file mode 100644 index 0000000..750e6a6 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorBatchAcceptResponse.java @@ -0,0 +1,421 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class InvestigatorBatchAcceptResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("accepted", "skipped", "results"); + + private InvestigatorBatchAcceptResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorBatchAcceptResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorBatchAcceptResponse fromJson(JsonNode json) { + return new InvestigatorBatchAcceptResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The accepted field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional accepted() { + return Json.optional(json, "accepted", Conv.LONG); + } + + /** + * The skipped field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional skipped() { + return Json.optional(json, "skipped", Conv.LONG); + } + + /** + * The results field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> results() { + return Json.optional(json, "results", Conv.list(Conv.model(InvestigatorBatchAcceptResponse.Results::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorBatchAcceptResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorBatchAcceptResponse from) { + super(from); + } + + /** + * The accepted field. + * + * @param accepted the value + * @return this builder + */ + public Builder accepted(long accepted) { + return set("accepted", Conv.LONG, accepted); + } + + /** + * The skipped field. + * + * @param skipped the value + * @return this builder + */ + public Builder skipped(long skipped) { + return set("skipped", Conv.LONG, skipped); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(InvestigatorBatchAcceptResponse.Results::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorBatchAcceptResponse} + */ + public InvestigatorBatchAcceptResponse build() { + return new InvestigatorBatchAcceptResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Results extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("run_id", "result", "reason", "detail", "action", "final_status"); + + private Results(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Results} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Results fromJson(JsonNode json) { + return new Results(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The run_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional runId() { + return Json.optional(json, "run_id", Conv.STRING); + } + + /** + * The result field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional result() { + return Json.optional(json, "result", Conv.enumeration(InvestigatorBatchAcceptResponse.Results.Result::of)); + } + + /** + * The reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.enumeration(InvestigatorBatchAcceptResponse.Results.Reason::of)); + } + + /** + * The detail field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional detail() { + return Json.optional(json, "detail", Conv.ANY); + } + + /** + * The action field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional action() { + return Json.optional(json, "action", Conv.STRING); + } + + /** + * The final_status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional finalStatus() { + return Json.optional(json, "final_status", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Results}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Results from) { + super(from); + } + + /** + * The run_id field. + * + * @param runId the value + * @return this builder + */ + public Builder runId(String runId) { + return set("run_id", Conv.STRING, runId); + } + + /** + * The result field. + * + * @param result the value + * @return this builder + */ + public Builder result(InvestigatorBatchAcceptResponse.Results.Result result) { + return set("result", Conv.enumeration(InvestigatorBatchAcceptResponse.Results.Result::of), result); + } + + /** + * The reason field. + * + * @param reason the value + * @return this builder + */ + public Builder reason(InvestigatorBatchAcceptResponse.Results.Reason reason) { + return set("reason", Conv.enumeration(InvestigatorBatchAcceptResponse.Results.Reason::of), reason); + } + + /** + * The detail field. + * + * @param detail the value + * @return this builder + */ + public Builder detail(@Nullable Object detail) { + return setAny("detail", detail); + } + + /** + * The action field. + * + * @param action the value + * @return this builder + */ + public Builder action(String action) { + return set("action", Conv.STRING, action); + } + + /** + * The final_status field. + * + * @param finalStatus the value + * @return this builder + */ + public Builder finalStatus(String finalStatus) { + return set("final_status", Conv.STRING, finalStatus); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Results} + */ + public Results build() { + return new Results(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Result extends StringEnum { + /** The value {@code "accepted"}. */ + public static final Result ACCEPTED = new Result("accepted"); + + /** The value {@code "skipped"}. */ + public static final Result SKIPPED = new Result("skipped"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, SKIPPED); + + private Result(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Result of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Result::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Reason extends StringEnum { + /** The value {@code "not_found"}. */ + public static final Reason NOT_FOUND = new Reason("not_found"); + + /** The value {@code "forbidden"}. */ + public static final Reason FORBIDDEN = new Reason("forbidden"); + + /** The value {@code "already_decided"}. */ + public static final Reason ALREADY_DECIDED = new Reason("already_decided"); + + /** The value {@code "not_ready"}. */ + public static final Reason NOT_READY = new Reason("not_ready"); + + /** The value {@code "guardrail"}. */ + public static final Reason GUARDRAIL = new Reason("guardrail"); + + /** The value {@code "invalid"}. */ + public static final Reason INVALID = new Reason("invalid"); + + private static final List KNOWN_VALUES = List.of(NOT_FOUND, FORBIDDEN, ALREADY_DECIDED, NOT_READY, GUARDRAIL, INVALID); + + private Reason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Reason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Reason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorBlock.java b/src/main/java/me/didit/types/InvestigatorBlock.java new file mode 100644 index 0000000..87c2468 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorBlock.java @@ -0,0 +1,637 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * The item's latest Investigator review, or null when it never had one. Carried by the V3 session and business session decision (and so by status.updated webhooks). + */ +public final class InvestigatorBlock extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("run_id", "status", "stage", "queue", "is_open", "recommendation", "recommendation_detail", "confidence", "summary", "ready_to_accept", "prepared_at", "automated", "decided_by", "decision"); + + private InvestigatorBlock(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorBlock} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorBlock fromJson(JsonNode json) { + return new InvestigatorBlock(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The run_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional runId() { + return Json.optional(json, "run_id", Conv.STRING); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(InvestigatorBlock.Status::of)); + } + + /** + * The stage field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional stage() { + return Json.optional(json, "stage", Conv.enumeration(InvestigatorBlock.Stage::of)); + } + + /** + * The queue field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional queue() { + return Json.optional(json, "queue", Conv.enumeration(InvestigatorBlock.Queue::of)); + } + + /** + * The is_open field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isOpen() { + return Json.optional(json, "is_open", Conv.BOOLEAN); + } + + /** + * The recommendation field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional recommendation() { + return Json.optional(json, "recommendation", Conv.enumeration(InvestigatorBlock.Recommendation::of)); + } + + /** + * The recommendation_detail field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional recommendationDetail() { + return Json.optional(json, "recommendation_detail", Conv.STRING); + } + + /** + * The confidence field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional confidence() { + return Json.optional(json, "confidence", Conv.enumeration(InvestigatorBlock.Confidence::of)); + } + + /** + * The summary field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional summary() { + return Json.optional(json, "summary", Conv.STRING); + } + + /** + * The ready_to_accept field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional readyToAccept() { + return Json.optional(json, "ready_to_accept", Conv.BOOLEAN); + } + + /** + * The prepared_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional preparedAt() { + return Json.optional(json, "prepared_at", Conv.DATE_TIME); + } + + /** + * The Investigator decided on its own, even if the decision was later overridden. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional automated() { + return Json.optional(json, "automated", Conv.BOOLEAN); + } + + /** + * The decided_by field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decidedBy() { + return Json.optional(json, "decided_by", Conv.enumeration(InvestigatorBlock.DecidedBy::of)); + } + + /** + * The decision field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decision() { + return Json.optional(json, "decision", Conv.model(InvestigatorDecision::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorBlock}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorBlock from) { + super(from); + } + + /** + * The run_id field. + * + * @param runId the value + * @return this builder + */ + public Builder runId(String runId) { + return set("run_id", Conv.STRING, runId); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(InvestigatorBlock.Status status) { + return set("status", Conv.enumeration(InvestigatorBlock.Status::of), status); + } + + /** + * The stage field. + * + * @param stage the value + * @return this builder + */ + public Builder stage(InvestigatorBlock.Stage stage) { + return set("stage", Conv.enumeration(InvestigatorBlock.Stage::of), stage); + } + + /** + * The queue field. + * + * @param queue the value + * @return this builder + */ + public Builder queue(InvestigatorBlock.Queue queue) { + return set("queue", Conv.enumeration(InvestigatorBlock.Queue::of), queue); + } + + /** + * The is_open field. + * + * @param isOpen the value + * @return this builder + */ + public Builder isOpen(boolean isOpen) { + return set("is_open", Conv.BOOLEAN, isOpen); + } + + /** + * The recommendation field. + * + * @param recommendation the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder recommendation(InvestigatorBlock.@Nullable Recommendation recommendation) { + return setNullable("recommendation", Conv.enumeration(InvestigatorBlock.Recommendation::of), recommendation); + } + + /** + * The recommendation_detail field. + * + * @param recommendationDetail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder recommendationDetail(@Nullable String recommendationDetail) { + return setNullable("recommendation_detail", Conv.STRING, recommendationDetail); + } + + /** + * The confidence field. + * + * @param confidence the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder confidence(InvestigatorBlock.@Nullable Confidence confidence) { + return setNullable("confidence", Conv.enumeration(InvestigatorBlock.Confidence::of), confidence); + } + + /** + * The summary field. + * + * @param summary the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder summary(@Nullable String summary) { + return setNullable("summary", Conv.STRING, summary); + } + + /** + * The ready_to_accept field. + * + * @param readyToAccept the value + * @return this builder + */ + public Builder readyToAccept(boolean readyToAccept) { + return set("ready_to_accept", Conv.BOOLEAN, readyToAccept); + } + + /** + * The prepared_at field. + * + * @param preparedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder preparedAt(@Nullable OffsetDateTime preparedAt) { + return setNullable("prepared_at", Conv.DATE_TIME, preparedAt); + } + + /** + * The Investigator decided on its own, even if the decision was later overridden. + * + * @param automated the value + * @return this builder + */ + public Builder automated(boolean automated) { + return set("automated", Conv.BOOLEAN, automated); + } + + /** + * The decided_by field. + * + * @param decidedBy the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder decidedBy(InvestigatorBlock.@Nullable DecidedBy decidedBy) { + return setNullable("decided_by", Conv.enumeration(InvestigatorBlock.DecidedBy::of), decidedBy); + } + + /** + * The decision field. + * + * @param decision the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder decision(@Nullable InvestigatorDecision decision) { + return setNullable("decision", Conv.model(InvestigatorDecision::fromJson), decision); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorBlock} + */ + public InvestigatorBlock build() { + return new InvestigatorBlock(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "running"}. */ + public static final Status RUNNING = new Status("running"); + + /** The value {@code "completed"}. */ + public static final Status COMPLETED = new Status("completed"); + + /** The value {@code "failed"}. */ + public static final Status FAILED = new Status("failed"); + + private static final List KNOWN_VALUES = List.of(RUNNING, COMPLETED, FAILED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Stage extends StringEnum { + /** The value {@code "shadow"}. */ + public static final Stage SHADOW = new Stage("shadow"); + + /** The value {@code "live"}. */ + public static final Stage LIVE = new Stage("live"); + + private static final List KNOWN_VALUES = List.of(SHADOW, LIVE); + + private Stage(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Stage of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Stage::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Queue extends StringEnum { + /** The value {@code "kyc"}. */ + public static final Queue KYC = new Queue("kyc"); + + /** The value {@code "kyb"}. */ + public static final Queue KYB = new Queue("kyb"); + + /** The value {@code "aml"}. */ + public static final Queue AML = new Queue("aml"); + + /** The value {@code "transactions"}. */ + public static final Queue TRANSACTIONS = new Queue("transactions"); + + /** The value {@code "cases"}. */ + public static final Queue CASES = new Queue("cases"); + + private static final List KNOWN_VALUES = List.of(KYC, KYB, AML, TRANSACTIONS, CASES); + + private Queue(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Queue of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Queue::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Recommendation extends StringEnum { + /** The value {@code "approve"}. */ + public static final Recommendation APPROVE = new Recommendation("approve"); + + /** The value {@code "decline"}. */ + public static final Recommendation DECLINE = new Recommendation("decline"); + + /** The value {@code "escalate"}. */ + public static final Recommendation ESCALATE = new Recommendation("escalate"); + + /** The value {@code "request"}. */ + public static final Recommendation REQUEST = new Recommendation("request"); + + private static final List KNOWN_VALUES = List.of(APPROVE, DECLINE, ESCALATE, REQUEST); + + private Recommendation(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Recommendation of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Recommendation::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Confidence extends StringEnum { + /** The value {@code "high"}. */ + public static final Confidence HIGH = new Confidence("high"); + + /** The value {@code "moderate"}. */ + public static final Confidence MODERATE = new Confidence("moderate"); + + /** The value {@code "low"}. */ + public static final Confidence LOW = new Confidence("low"); + + private static final List KNOWN_VALUES = List.of(HIGH, MODERATE, LOW); + + private Confidence(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Confidence of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Confidence::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DecidedBy extends StringEnum { + /** The value {@code "investigator"}. */ + public static final DecidedBy INVESTIGATOR = new DecidedBy("investigator"); + + /** The value {@code "member"}. */ + public static final DecidedBy MEMBER = new DecidedBy("member"); + + private static final List KNOWN_VALUES = List.of(INVESTIGATOR, MEMBER); + + private DecidedBy(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DecidedBy of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DecidedBy::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorDecision.java b/src/main/java/me/didit/types/InvestigatorDecision.java new file mode 100644 index 0000000..4928149 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorDecision.java @@ -0,0 +1,913 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * How the item left the Investigator queue. decided_by, original_outcome and overrides are returned by the accept and override endpoints and the report; the investigator block of a session decision carries the other fields. + */ +public final class InvestigatorDecision extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("outcome", "action", "actor_email", "actor_name", "override_category", "override_reason", "final_status", "agreed", "decided_at", "billed", "decided_by", "original_outcome", "overrides"); + + private InvestigatorDecision(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorDecision} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorDecision fromJson(JsonNode json) { + return new InvestigatorDecision(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * accepted or edited took the recommendation (edited with a rewritten narrative), overridden took another action, decided_elsewhere is a decision taken outside the Investigator while the report was open, automated is a decision the Investigator took on its own. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional outcome() { + return Json.optional(json, "outcome", Conv.enumeration(InvestigatorDecision.Outcome::of)); + } + + /** + * The action field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional action() { + return Json.optional(json, "action", Conv.enumeration(InvestigatorDecision.Action::of)); + } + + /** + * The actor_email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actorEmail() { + return Json.optional(json, "actor_email", Conv.STRING); + } + + /** + * The actor_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actorName() { + return Json.optional(json, "actor_name", Conv.STRING); + } + + /** + * The override_category field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional overrideCategory() { + return Json.optional(json, "override_category", Conv.enumeration(InvestigatorDecision.OverrideCategory::of)); + } + + /** + * The override_reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional overrideReason() { + return Json.optional(json, "override_reason", Conv.STRING); + } + + /** + * The item's status after the decision. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional finalStatus() { + return Json.optional(json, "final_status", Conv.STRING); + } + + /** + * Whether the decision matched the recommendation. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional agreed() { + return Json.optional(json, "agreed", Conv.BOOLEAN); + } + + /** + * The decided_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decidedAt() { + return Json.optional(json, "decided_at", Conv.DATE_TIME); + } + + /** + * The review was charged. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional billed() { + return Json.optional(json, "billed", Conv.BOOLEAN); + } + + /** + * The decided_by field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decidedBy() { + return Json.optional(json, "decided_by", Conv.enumeration(InvestigatorDecision.DecidedBy::of)); + } + + /** + * The decision the Investigator took on its own, unchanged by later overrides. Null for a decision the team took. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originalOutcome() { + return Json.optional(json, "original_outcome", Conv.model(InvestigatorDecision.OriginalOutcome::fromJson)); + } + + /** + * Every override of a decision the Investigator took on its own, oldest first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> overrides() { + return Json.optional(json, "overrides", Conv.list(Conv.model(InvestigatorDecision.Overrides::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorDecision}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorDecision from) { + super(from); + } + + /** + * accepted or edited took the recommendation (edited with a rewritten narrative), overridden took another action, decided_elsewhere is a decision taken outside the Investigator while the report was open, automated is a decision the Investigator took on its own. + * + * @param outcome the value + * @return this builder + */ + public Builder outcome(InvestigatorDecision.Outcome outcome) { + return set("outcome", Conv.enumeration(InvestigatorDecision.Outcome::of), outcome); + } + + /** + * The action field. + * + * @param action the value + * @return this builder + */ + public Builder action(InvestigatorDecision.Action action) { + return set("action", Conv.enumeration(InvestigatorDecision.Action::of), action); + } + + /** + * The actor_email field. + * + * @param actorEmail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actorEmail(@Nullable String actorEmail) { + return setNullable("actor_email", Conv.STRING, actorEmail); + } + + /** + * The actor_name field. + * + * @param actorName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actorName(@Nullable String actorName) { + return setNullable("actor_name", Conv.STRING, actorName); + } + + /** + * The override_category field. + * + * @param overrideCategory the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder overrideCategory(InvestigatorDecision.@Nullable OverrideCategory overrideCategory) { + return setNullable("override_category", Conv.enumeration(InvestigatorDecision.OverrideCategory::of), overrideCategory); + } + + /** + * The override_reason field. + * + * @param overrideReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder overrideReason(@Nullable String overrideReason) { + return setNullable("override_reason", Conv.STRING, overrideReason); + } + + /** + * The item's status after the decision. + * + * @param finalStatus the value + * @return this builder + */ + public Builder finalStatus(String finalStatus) { + return set("final_status", Conv.STRING, finalStatus); + } + + /** + * Whether the decision matched the recommendation. + * + * @param agreed the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder agreed(@Nullable Boolean agreed) { + return setNullable("agreed", Conv.BOOLEAN, agreed); + } + + /** + * The decided_at field. + * + * @param decidedAt the value + * @return this builder + */ + public Builder decidedAt(OffsetDateTime decidedAt) { + return set("decided_at", Conv.DATE_TIME, decidedAt); + } + + /** + * The review was charged. + * + * @param billed the value + * @return this builder + */ + public Builder billed(boolean billed) { + return set("billed", Conv.BOOLEAN, billed); + } + + /** + * The decided_by field. + * + * @param decidedBy the value + * @return this builder + */ + public Builder decidedBy(InvestigatorDecision.DecidedBy decidedBy) { + return set("decided_by", Conv.enumeration(InvestigatorDecision.DecidedBy::of), decidedBy); + } + + /** + * The decision the Investigator took on its own, unchanged by later overrides. Null for a decision the team took. + * + * @param originalOutcome the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder originalOutcome(InvestigatorDecision.@Nullable OriginalOutcome originalOutcome) { + return setNullable("original_outcome", Conv.model(InvestigatorDecision.OriginalOutcome::fromJson), originalOutcome); + } + + /** + * Every override of a decision the Investigator took on its own, oldest first. + * + * @param overrides the value + * @return this builder + */ + public Builder overrides(List overrides) { + return set("overrides", Conv.list(Conv.model(InvestigatorDecision.Overrides::fromJson)), overrides); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorDecision} + */ + public InvestigatorDecision build() { + return new InvestigatorDecision(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * accepted or edited took the recommendation (edited with a rewritten narrative), overridden took another action, decided_elsewhere is a decision taken outside the Investigator while the report was open, automated is a decision the Investigator took on its own. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Outcome extends StringEnum { + /** The value {@code "accepted"}. */ + public static final Outcome ACCEPTED = new Outcome("accepted"); + + /** The value {@code "edited"}. */ + public static final Outcome EDITED = new Outcome("edited"); + + /** The value {@code "overridden"}. */ + public static final Outcome OVERRIDDEN = new Outcome("overridden"); + + /** The value {@code "decided_elsewhere"}. */ + public static final Outcome DECIDED_ELSEWHERE = new Outcome("decided_elsewhere"); + + /** The value {@code "automated"}. */ + public static final Outcome AUTOMATED = new Outcome("automated"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, EDITED, OVERRIDDEN, DECIDED_ELSEWHERE, AUTOMATED); + + private Outcome(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Outcome of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Action extends StringEnum { + /** The value {@code "approve"}. */ + public static final Action APPROVE = new Action("approve"); + + /** The value {@code "decline"}. */ + public static final Action DECLINE = new Action("decline"); + + /** The value {@code "escalate"}. */ + public static final Action ESCALATE = new Action("escalate"); + + /** The value {@code "request"}. */ + public static final Action REQUEST = new Action("request"); + + private static final List KNOWN_VALUES = List.of(APPROVE, DECLINE, ESCALATE, REQUEST); + + private Action(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Action of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Action::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class OverrideCategory extends StringEnum { + /** The value {@code "missing_context"}. */ + public static final OverrideCategory MISSING_CONTEXT = new OverrideCategory("missing_context"); + + /** The value {@code "needs_information"}. */ + public static final OverrideCategory NEEDS_INFORMATION = new OverrideCategory("needs_information"); + + /** The value {@code "policy_differs"}. */ + public static final OverrideCategory POLICY_DIFFERS = new OverrideCategory("policy_differs"); + + /** The value {@code "evidence_misread"}. */ + public static final OverrideCategory EVIDENCE_MISREAD = new OverrideCategory("evidence_misread"); + + /** The value {@code "other"}. */ + public static final OverrideCategory OTHER = new OverrideCategory("other"); + + /** The value {@code ""}. */ + public static final OverrideCategory EMPTY = new OverrideCategory(""); + + private static final List KNOWN_VALUES = List.of(MISSING_CONTEXT, NEEDS_INFORMATION, POLICY_DIFFERS, EVIDENCE_MISREAD, OTHER, EMPTY); + + private OverrideCategory(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static OverrideCategory of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, OverrideCategory::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DecidedBy extends StringEnum { + /** The value {@code "investigator"}. */ + public static final DecidedBy INVESTIGATOR = new DecidedBy("investigator"); + + /** The value {@code "member"}. */ + public static final DecidedBy MEMBER = new DecidedBy("member"); + + private static final List KNOWN_VALUES = List.of(INVESTIGATOR, MEMBER); + + private DecidedBy(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DecidedBy of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DecidedBy::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The decision the Investigator took on its own, unchanged by later overrides. Null for a decision the team took. + */ + public static final class OriginalOutcome extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("action", "final_status", "decided_at"); + + private OriginalOutcome(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code OriginalOutcome} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static OriginalOutcome fromJson(JsonNode json) { + return new OriginalOutcome(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The action field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional action() { + return Json.optional(json, "action", Conv.STRING); + } + + /** + * The final_status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional finalStatus() { + return Json.optional(json, "final_status", Conv.STRING); + } + + /** + * The decided_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decidedAt() { + return Json.optional(json, "decided_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link OriginalOutcome}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(OriginalOutcome from) { + super(from); + } + + /** + * The action field. + * + * @param action the value + * @return this builder + */ + public Builder action(String action) { + return set("action", Conv.STRING, action); + } + + /** + * The final_status field. + * + * @param finalStatus the value + * @return this builder + */ + public Builder finalStatus(String finalStatus) { + return set("final_status", Conv.STRING, finalStatus); + } + + /** + * The decided_at field. + * + * @param decidedAt the value + * @return this builder + */ + public Builder decidedAt(OffsetDateTime decidedAt) { + return set("decided_at", Conv.DATE_TIME, decidedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code OriginalOutcome} + */ + public OriginalOutcome build() { + return new OriginalOutcome(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Overrides extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("action", "actor_email", "actor_name", "override_category", "override_reason", "previous_status", "final_status", "agreed", "overridden_at"); + + private Overrides(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Overrides} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Overrides fromJson(JsonNode json) { + return new Overrides(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The action field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional action() { + return Json.optional(json, "action", Conv.STRING); + } + + /** + * The actor_email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actorEmail() { + return Json.optional(json, "actor_email", Conv.STRING); + } + + /** + * The actor_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actorName() { + return Json.optional(json, "actor_name", Conv.STRING); + } + + /** + * The override_category field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional overrideCategory() { + return Json.optional(json, "override_category", Conv.STRING); + } + + /** + * The override_reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional overrideReason() { + return Json.optional(json, "override_reason", Conv.STRING); + } + + /** + * The previous_status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previousStatus() { + return Json.optional(json, "previous_status", Conv.STRING); + } + + /** + * The final_status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional finalStatus() { + return Json.optional(json, "final_status", Conv.STRING); + } + + /** + * The agreed field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional agreed() { + return Json.optional(json, "agreed", Conv.BOOLEAN); + } + + /** + * The overridden_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional overriddenAt() { + return Json.optional(json, "overridden_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Overrides}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Overrides from) { + super(from); + } + + /** + * The action field. + * + * @param action the value + * @return this builder + */ + public Builder action(String action) { + return set("action", Conv.STRING, action); + } + + /** + * The actor_email field. + * + * @param actorEmail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actorEmail(@Nullable String actorEmail) { + return setNullable("actor_email", Conv.STRING, actorEmail); + } + + /** + * The actor_name field. + * + * @param actorName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actorName(@Nullable String actorName) { + return setNullable("actor_name", Conv.STRING, actorName); + } + + /** + * The override_category field. + * + * @param overrideCategory the value + * @return this builder + */ + public Builder overrideCategory(String overrideCategory) { + return set("override_category", Conv.STRING, overrideCategory); + } + + /** + * The override_reason field. + * + * @param overrideReason the value + * @return this builder + */ + public Builder overrideReason(String overrideReason) { + return set("override_reason", Conv.STRING, overrideReason); + } + + /** + * The previous_status field. + * + * @param previousStatus the value + * @return this builder + */ + public Builder previousStatus(String previousStatus) { + return set("previous_status", Conv.STRING, previousStatus); + } + + /** + * The final_status field. + * + * @param finalStatus the value + * @return this builder + */ + public Builder finalStatus(String finalStatus) { + return set("final_status", Conv.STRING, finalStatus); + } + + /** + * The agreed field. + * + * @param agreed the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder agreed(@Nullable Boolean agreed) { + return setNullable("agreed", Conv.BOOLEAN, agreed); + } + + /** + * The overridden_at field. + * + * @param overriddenAt the value + * @return this builder + */ + public Builder overriddenAt(OffsetDateTime overriddenAt) { + return set("overridden_at", Conv.DATE_TIME, overriddenAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Overrides} + */ + public Overrides build() { + return new Overrides(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorExportActivityParams.java b/src/main/java/me/didit/types/InvestigatorExportActivityParams.java new file mode 100644 index 0000000..89c5335 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorExportActivityParams.java @@ -0,0 +1,274 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code investigator.export_activity}. */ +public final class InvestigatorExportActivityParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("scope", "group", "actor", "since", "until"); + + private InvestigatorExportActivityParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorExportActivityParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorExportActivityParams fromJson(JsonNode json) { + return new InvestigatorExportActivityParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(InvestigatorExportActivityParams.Scope::of)); + } + + /** + * The group field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional group() { + return Json.optional(json, "group", Conv.enumeration(InvestigatorExportActivityParams.Group::of)); + } + + /** + * agent for the Investigator, or a person's email. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actor() { + return Json.optional(json, "actor", Conv.STRING); + } + + /** + * The since field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional since() { + return Json.optional(json, "since", Conv.DATE_TIME); + } + + /** + * The until field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional until() { + return Json.optional(json, "until", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorExportActivityParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorExportActivityParams from) { + super(from); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @param scope the value + * @return this builder + */ + public Builder scope(InvestigatorExportActivityParams.Scope scope) { + return set("scope", Conv.enumeration(InvestigatorExportActivityParams.Scope::of), scope); + } + + /** + * The group field. + * + * @param group the value + * @return this builder + */ + public Builder group(InvestigatorExportActivityParams.Group group) { + return set("group", Conv.enumeration(InvestigatorExportActivityParams.Group::of), group); + } + + /** + * agent for the Investigator, or a person's email. + * + * @param actor the value + * @return this builder + */ + public Builder actor(String actor) { + return set("actor", Conv.STRING, actor); + } + + /** + * The since field. + * + * @param since the value + * @return this builder + */ + public Builder since(OffsetDateTime since) { + return set("since", Conv.DATE_TIME, since); + } + + /** + * The until field. + * + * @param until the value + * @return this builder + */ + public Builder until(OffsetDateTime until) { + return set("until", Conv.DATE_TIME, until); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorExportActivityParams} + */ + public InvestigatorExportActivityParams build() { + return new InvestigatorExportActivityParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "all"}. */ + public static final Scope ALL = new Scope("all"); + + /** The value {@code "identity"}. */ + public static final Scope IDENTITY = new Scope("identity"); + + /** The value {@code "transactions"}. */ + public static final Scope TRANSACTIONS = new Scope("transactions"); + + private static final List KNOWN_VALUES = List.of(ALL, IDENTITY, TRANSACTIONS); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Group extends StringEnum { + /** The value {@code "agent"}. */ + public static final Group AGENT = new Group("agent"); + + /** The value {@code "decisions"}. */ + public static final Group DECISIONS = new Group("decisions"); + + /** The value {@code "overrides"}. */ + public static final Group OVERRIDES = new Group("overrides"); + + /** The value {@code "autonomy"}. */ + public static final Group AUTONOMY = new Group("autonomy"); + + private static final List KNOWN_VALUES = List.of(AGENT, DECISIONS, OVERRIDES, AUTONOMY); + + private Group(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Group of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Group::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorListActivityParams.java b/src/main/java/me/didit/types/InvestigatorListActivityParams.java new file mode 100644 index 0000000..f3dab6a --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorListActivityParams.java @@ -0,0 +1,314 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code investigator.list_activity}. */ +public final class InvestigatorListActivityParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("scope", "group", "actor", "since", "until", "limit", "offset"); + + private InvestigatorListActivityParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorListActivityParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorListActivityParams fromJson(JsonNode json) { + return new InvestigatorListActivityParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(InvestigatorListActivityParams.Scope::of)); + } + + /** + * The group field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional group() { + return Json.optional(json, "group", Conv.enumeration(InvestigatorListActivityParams.Group::of)); + } + + /** + * agent for the Investigator, or a person's email. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actor() { + return Json.optional(json, "actor", Conv.STRING); + } + + /** + * The since field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional since() { + return Json.optional(json, "since", Conv.DATE_TIME); + } + + /** + * The until field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional until() { + return Json.optional(json, "until", Conv.DATE_TIME); + } + + /** + * The limit field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * The offset field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorListActivityParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorListActivityParams from) { + super(from); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @param scope the value + * @return this builder + */ + public Builder scope(InvestigatorListActivityParams.Scope scope) { + return set("scope", Conv.enumeration(InvestigatorListActivityParams.Scope::of), scope); + } + + /** + * The group field. + * + * @param group the value + * @return this builder + */ + public Builder group(InvestigatorListActivityParams.Group group) { + return set("group", Conv.enumeration(InvestigatorListActivityParams.Group::of), group); + } + + /** + * agent for the Investigator, or a person's email. + * + * @param actor the value + * @return this builder + */ + public Builder actor(String actor) { + return set("actor", Conv.STRING, actor); + } + + /** + * The since field. + * + * @param since the value + * @return this builder + */ + public Builder since(OffsetDateTime since) { + return set("since", Conv.DATE_TIME, since); + } + + /** + * The until field. + * + * @param until the value + * @return this builder + */ + public Builder until(OffsetDateTime until) { + return set("until", Conv.DATE_TIME, until); + } + + /** + * The limit field. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * The offset field. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorListActivityParams} + */ + public InvestigatorListActivityParams build() { + return new InvestigatorListActivityParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "all"}. */ + public static final Scope ALL = new Scope("all"); + + /** The value {@code "identity"}. */ + public static final Scope IDENTITY = new Scope("identity"); + + /** The value {@code "transactions"}. */ + public static final Scope TRANSACTIONS = new Scope("transactions"); + + private static final List KNOWN_VALUES = List.of(ALL, IDENTITY, TRANSACTIONS); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Group extends StringEnum { + /** The value {@code "agent"}. */ + public static final Group AGENT = new Group("agent"); + + /** The value {@code "decisions"}. */ + public static final Group DECISIONS = new Group("decisions"); + + /** The value {@code "overrides"}. */ + public static final Group OVERRIDES = new Group("overrides"); + + /** The value {@code "autonomy"}. */ + public static final Group AUTONOMY = new Group("autonomy"); + + private static final List KNOWN_VALUES = List.of(AGENT, DECISIONS, OVERRIDES, AUTONOMY); + + private Group(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Group of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Group::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorListActivityResponse.java b/src/main/java/me/didit/types/InvestigatorListActivityResponse.java new file mode 100644 index 0000000..45761fb --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorListActivityResponse.java @@ -0,0 +1,199 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class InvestigatorListActivityResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "counts", "actors", "results"); + + private InvestigatorListActivityResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorListActivityResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorListActivityResponse fromJson(JsonNode json) { + return new InvestigatorListActivityResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional count() { + return Json.optional(json, "count", Conv.LONG); + } + + /** + * The next field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * The previous field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The counts field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> counts() { + return Json.optional(json, "counts", Conv.OBJECT); + } + + /** + * The actors field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> actors() { + return Json.optional(json, "actors", Conv.list(Conv.ANY)); + } + + /** + * The results field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional>> results() { + return Json.optional(json, "results", Conv.list(Conv.OBJECT)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorListActivityResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorListActivityResponse from) { + super(from); + } + + /** + * The count field. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * The next field. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * The previous field. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The counts field. + * + * @param counts the value + * @return this builder + */ + public Builder counts(Map counts) { + return setAny("counts", counts); + } + + /** + * The actors field. + * + * @param actors the value + * @return this builder + */ + public Builder actors(List actors) { + return setAny("actors", actors); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List> results) { + return setAny("results", results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorListActivityResponse} + */ + public InvestigatorListActivityResponse build() { + return new InvestigatorListActivityResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorListGuardrailsResponse.java b/src/main/java/me/didit/types/InvestigatorListGuardrailsResponse.java new file mode 100644 index 0000000..e43435d --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorListGuardrailsResponse.java @@ -0,0 +1,295 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class InvestigatorListGuardrailsResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("results"); + + private InvestigatorListGuardrailsResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorListGuardrailsResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorListGuardrailsResponse fromJson(JsonNode json) { + return new InvestigatorListGuardrailsResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The results field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> results() { + return Json.optional(json, "results", Conv.list(Conv.model(InvestigatorListGuardrailsResponse.Results::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorListGuardrailsResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorListGuardrailsResponse from) { + super(from); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(InvestigatorListGuardrailsResponse.Results::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorListGuardrailsResponse} + */ + public InvestigatorListGuardrailsResponse build() { + return new InvestigatorListGuardrailsResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Results extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("key", "title", "description", "locked"); + + private Results(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Results} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Results fromJson(JsonNode json) { + return new Results(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The key field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional key() { + return Json.optional(json, "key", Conv.enumeration(InvestigatorListGuardrailsResponse.Results.Key::of)); + } + + /** + * The title field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional title() { + return Json.optional(json, "title", Conv.STRING); + } + + /** + * The description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } + + /** + * The locked field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional locked() { + return Json.optional(json, "locked", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Results}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Results from) { + super(from); + } + + /** + * The key field. + * + * @param key the value + * @return this builder + */ + public Builder key(InvestigatorListGuardrailsResponse.Results.Key key) { + return set("key", Conv.enumeration(InvestigatorListGuardrailsResponse.Results.Key::of), key); + } + + /** + * The title field. + * + * @param title the value + * @return this builder + */ + public Builder title(String title) { + return set("title", Conv.STRING, title); + } + + /** + * The description field. + * + * @param description the value + * @return this builder + */ + public Builder description(String description) { + return set("description", Conv.STRING, description); + } + + /** + * The locked field. + * + * @param locked the value + * @return this builder + */ + public Builder locked(boolean locked) { + return set("locked", Conv.BOOLEAN, locked); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Results} + */ + public Results build() { + return new Results(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Key extends StringEnum { + /** The value {@code "sanctions_to_person"}. */ + public static final Key SANCTIONS_TO_PERSON = new Key("sanctions_to_person"); + + /** The value {@code "no_tipping_off"}. */ + public static final Key NO_TIPPING_OFF = new Key("no_tipping_off"); + + /** The value {@code "no_protected_characteristics"}. */ + public static final Key NO_PROTECTED_CHARACTERISTICS = new Key("no_protected_characteristics"); + + /** The value {@code "never_files_reports"}. */ + public static final Key NEVER_FILES_REPORTS = new Key("never_files_reports"); + + /** The value {@code "every_decision_logged"}. */ + public static final Key EVERY_DECISION_LOGGED = new Key("every_decision_logged"); + + private static final List KNOWN_VALUES = List.of(SANCTIONS_TO_PERSON, NO_TIPPING_OFF, NO_PROTECTED_CHARACTERISTICS, NEVER_FILES_REPORTS, EVERY_DECISION_LOGGED); + + private Key(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Key of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Key::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorListQueueParams.java b/src/main/java/me/didit/types/InvestigatorListQueueParams.java new file mode 100644 index 0000000..ce9411e --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorListQueueParams.java @@ -0,0 +1,449 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code investigator.list_queue}. */ +public final class InvestigatorListQueueParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("scope", "kind", "queue", "recommendation", "confidence", "ready", "search", "limit", "offset"); + + private InvestigatorListQueueParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorListQueueParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorListQueueParams fromJson(JsonNode json) { + return new InvestigatorListQueueParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(InvestigatorListQueueParams.Scope::of)); + } + + /** + * session, business_session, transaction or case, or several separated by commas. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.STRING); + } + + /** + * The queue field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional queue() { + return Json.optional(json, "queue", Conv.enumeration(InvestigatorListQueueParams.Queue::of)); + } + + /** + * The recommendation field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional recommendation() { + return Json.optional(json, "recommendation", Conv.enumeration(InvestigatorListQueueParams.Recommendation::of)); + } + + /** + * The confidence field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional confidence() { + return Json.optional(json, "confidence", Conv.enumeration(InvestigatorListQueueParams.Confidence::of)); + } + + /** + * The ready field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ready() { + return Json.optional(json, "ready", Conv.BOOLEAN); + } + + /** + * Name, company, case title, number, transaction ID or item ID. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional search() { + return Json.optional(json, "search", Conv.STRING); + } + + /** + * The limit field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * The offset field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorListQueueParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorListQueueParams from) { + super(from); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @param scope the value + * @return this builder + */ + public Builder scope(InvestigatorListQueueParams.Scope scope) { + return set("scope", Conv.enumeration(InvestigatorListQueueParams.Scope::of), scope); + } + + /** + * session, business_session, transaction or case, or several separated by commas. + * + * @param kind the value + * @return this builder + */ + public Builder kind(String kind) { + return set("kind", Conv.STRING, kind); + } + + /** + * The queue field. + * + * @param queue the value + * @return this builder + */ + public Builder queue(InvestigatorListQueueParams.Queue queue) { + return set("queue", Conv.enumeration(InvestigatorListQueueParams.Queue::of), queue); + } + + /** + * The recommendation field. + * + * @param recommendation the value + * @return this builder + */ + public Builder recommendation(InvestigatorListQueueParams.Recommendation recommendation) { + return set("recommendation", Conv.enumeration(InvestigatorListQueueParams.Recommendation::of), recommendation); + } + + /** + * The confidence field. + * + * @param confidence the value + * @return this builder + */ + public Builder confidence(InvestigatorListQueueParams.Confidence confidence) { + return set("confidence", Conv.enumeration(InvestigatorListQueueParams.Confidence::of), confidence); + } + + /** + * The ready field. + * + * @param ready the value + * @return this builder + */ + public Builder ready(boolean ready) { + return set("ready", Conv.BOOLEAN, ready); + } + + /** + * Name, company, case title, number, transaction ID or item ID. + * + * @param search the value + * @return this builder + */ + public Builder search(String search) { + return set("search", Conv.STRING, search); + } + + /** + * The limit field. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * The offset field. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorListQueueParams} + */ + public InvestigatorListQueueParams build() { + return new InvestigatorListQueueParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "all"}. */ + public static final Scope ALL = new Scope("all"); + + /** The value {@code "identity"}. */ + public static final Scope IDENTITY = new Scope("identity"); + + /** The value {@code "transactions"}. */ + public static final Scope TRANSACTIONS = new Scope("transactions"); + + private static final List KNOWN_VALUES = List.of(ALL, IDENTITY, TRANSACTIONS); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Queue extends StringEnum { + /** The value {@code "kyc"}. */ + public static final Queue KYC = new Queue("kyc"); + + /** The value {@code "kyb"}. */ + public static final Queue KYB = new Queue("kyb"); + + /** The value {@code "aml"}. */ + public static final Queue AML = new Queue("aml"); + + /** The value {@code "transactions"}. */ + public static final Queue TRANSACTIONS = new Queue("transactions"); + + /** The value {@code "cases"}. */ + public static final Queue CASES = new Queue("cases"); + + private static final List KNOWN_VALUES = List.of(KYC, KYB, AML, TRANSACTIONS, CASES); + + private Queue(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Queue of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Queue::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Recommendation extends StringEnum { + /** The value {@code "approve"}. */ + public static final Recommendation APPROVE = new Recommendation("approve"); + + /** The value {@code "decline"}. */ + public static final Recommendation DECLINE = new Recommendation("decline"); + + /** The value {@code "escalate"}. */ + public static final Recommendation ESCALATE = new Recommendation("escalate"); + + /** The value {@code "request"}. */ + public static final Recommendation REQUEST = new Recommendation("request"); + + private static final List KNOWN_VALUES = List.of(APPROVE, DECLINE, ESCALATE, REQUEST); + + private Recommendation(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Recommendation of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Recommendation::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Confidence extends StringEnum { + /** The value {@code "high"}. */ + public static final Confidence HIGH = new Confidence("high"); + + /** The value {@code "moderate"}. */ + public static final Confidence MODERATE = new Confidence("moderate"); + + /** The value {@code "low"}. */ + public static final Confidence LOW = new Confidence("low"); + + private static final List KNOWN_VALUES = List.of(HIGH, MODERATE, LOW); + + private Confidence(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Confidence of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Confidence::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorListQueueResponse.java b/src/main/java/me/didit/types/InvestigatorListQueueResponse.java new file mode 100644 index 0000000..86f8d73 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorListQueueResponse.java @@ -0,0 +1,179 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class InvestigatorListQueueResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "counts", "results"); + + private InvestigatorListQueueResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorListQueueResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorListQueueResponse fromJson(JsonNode json) { + return new InvestigatorListQueueResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional count() { + return Json.optional(json, "count", Conv.LONG); + } + + /** + * The next field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * The previous field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The counts field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> counts() { + return Json.optional(json, "counts", Conv.OBJECT); + } + + /** + * The results field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> results() { + return Json.optional(json, "results", Conv.list(Conv.model(InvestigatorQueueRow::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorListQueueResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorListQueueResponse from) { + super(from); + } + + /** + * The count field. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * The next field. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * The previous field. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The counts field. + * + * @param counts the value + * @return this builder + */ + public Builder counts(Map counts) { + return setAny("counts", counts); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(InvestigatorQueueRow::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorListQueueResponse} + */ + public InvestigatorListQueueResponse build() { + return new InvestigatorListQueueResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorOverrideRequest.java b/src/main/java/me/didit/types/InvestigatorOverrideRequest.java new file mode 100644 index 0000000..0c461c3 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorOverrideRequest.java @@ -0,0 +1,302 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class InvestigatorOverrideRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("action", "category", "reason", "send_email", "email_address", "email_language"); + + private InvestigatorOverrideRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorOverrideRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorOverrideRequest fromJson(JsonNode json) { + return new InvestigatorOverrideRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The action field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public InvestigatorOverrideRequest.Action action() { + return Json.required(json, "action", Conv.enumeration(InvestigatorOverrideRequest.Action::of)); + } + + /** + * The category field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public InvestigatorOverrideRequest.Category category() { + return Json.required(json, "category", Conv.enumeration(InvestigatorOverrideRequest.Category::of)); + } + + /** + * The reason field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String reason() { + return Json.required(json, "reason", Conv.STRING); + } + + /** + * Email the person about the decision. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sendEmail() { + return Json.optional(json, "send_email", Conv.BOOLEAN); + } + + /** + * Required when send_email is true. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailAddress() { + return Json.optional(json, "email_address", Conv.STRING); + } + + /** + * The email_language field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailLanguage() { + return Json.optional(json, "email_language", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorOverrideRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorOverrideRequest from) { + super(from); + } + + /** + * The action field. + * + * @param action the value + * @return this builder + */ + public Builder action(InvestigatorOverrideRequest.Action action) { + return set("action", Conv.enumeration(InvestigatorOverrideRequest.Action::of), action); + } + + /** + * The category field. + * + * @param category the value + * @return this builder + */ + public Builder category(InvestigatorOverrideRequest.Category category) { + return set("category", Conv.enumeration(InvestigatorOverrideRequest.Category::of), category); + } + + /** + * The reason field. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * Email the person about the decision. + * + * @param sendEmail the value + * @return this builder + */ + public Builder sendEmail(boolean sendEmail) { + return set("send_email", Conv.BOOLEAN, sendEmail); + } + + /** + * Required when send_email is true. + * + * @param emailAddress the value + * @return this builder + */ + public Builder emailAddress(String emailAddress) { + return set("email_address", Conv.STRING, emailAddress); + } + + /** + * The email_language field. + * + * @param emailLanguage the value + * @return this builder + */ + public Builder emailLanguage(String emailLanguage) { + return set("email_language", Conv.STRING, emailLanguage); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorOverrideRequest} + * @throws IllegalStateException when a required field is missing + */ + public InvestigatorOverrideRequest build() { + require("action", false); + require("category", false); + require("reason", false); + return new InvestigatorOverrideRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Action extends StringEnum { + /** The value {@code "approve"}. */ + public static final Action APPROVE = new Action("approve"); + + /** The value {@code "decline"}. */ + public static final Action DECLINE = new Action("decline"); + + /** The value {@code "escalate"}. */ + public static final Action ESCALATE = new Action("escalate"); + + /** The value {@code "request"}. */ + public static final Action REQUEST = new Action("request"); + + private static final List KNOWN_VALUES = List.of(APPROVE, DECLINE, ESCALATE, REQUEST); + + private Action(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Action of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Action::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Category extends StringEnum { + /** The value {@code "missing_context"}. */ + public static final Category MISSING_CONTEXT = new Category("missing_context"); + + /** The value {@code "needs_information"}. */ + public static final Category NEEDS_INFORMATION = new Category("needs_information"); + + /** The value {@code "policy_differs"}. */ + public static final Category POLICY_DIFFERS = new Category("policy_differs"); + + /** The value {@code "evidence_misread"}. */ + public static final Category EVIDENCE_MISREAD = new Category("evidence_misread"); + + /** The value {@code "other"}. */ + public static final Category OTHER = new Category("other"); + + private static final List KNOWN_VALUES = List.of(MISSING_CONTEXT, NEEDS_INFORMATION, POLICY_DIFFERS, EVIDENCE_MISREAD, OTHER); + + private Category(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Category of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Category::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorQueueRow.java b/src/main/java/me/didit/types/InvestigatorQueueRow.java new file mode 100644 index 0000000..61a2325 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorQueueRow.java @@ -0,0 +1,615 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class InvestigatorQueueRow extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("run_id", "kind", "queue", "stage", "subject", "recommendation", "recommendation_detail", "confidence", "summary", "sources_count", "ready_to_accept", "guardrails", "waiting_since", "due_at", "prepared_at"); + + private InvestigatorQueueRow(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorQueueRow} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorQueueRow fromJson(JsonNode json) { + return new InvestigatorQueueRow(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The run_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional runId() { + return Json.optional(json, "run_id", Conv.STRING); + } + + /** + * The kind field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(InvestigatorQueueRow.Kind::of)); + } + + /** + * The queue field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional queue() { + return Json.optional(json, "queue", Conv.enumeration(InvestigatorQueueRow.Queue::of)); + } + + /** + * The stage field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional stage() { + return Json.optional(json, "stage", Conv.enumeration(InvestigatorQueueRow.Stage::of)); + } + + /** + * The item under review: id, number, status, name and, depending on the kind, country, workflow, vendor_data, txn_id, amount, currency, direction, title or priority. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> subject() { + return Json.optional(json, "subject", Conv.OBJECT); + } + + /** + * The recommendation field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional recommendation() { + return Json.optional(json, "recommendation", Conv.enumeration(InvestigatorQueueRow.Recommendation::of)); + } + + /** + * The recommendation_detail field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional recommendationDetail() { + return Json.optional(json, "recommendation_detail", Conv.STRING); + } + + /** + * The confidence field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional confidence() { + return Json.optional(json, "confidence", Conv.enumeration(InvestigatorQueueRow.Confidence::of)); + } + + /** + * The summary field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional summary() { + return Json.optional(json, "summary", Conv.STRING); + } + + /** + * The sources_count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourcesCount() { + return Json.optional(json, "sources_count", Conv.LONG); + } + + /** + * The ready_to_accept field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional readyToAccept() { + return Json.optional(json, "ready_to_accept", Conv.BOOLEAN); + } + + /** + * The locked guardrails that hold this item, if any. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional guardrails() { + return Json.optional(json, "guardrails", Conv.ANY); + } + + /** + * The waiting_since field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional waitingSince() { + return Json.optional(json, "waiting_since", Conv.DATE_TIME); + } + + /** + * The due_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dueAt() { + return Json.optional(json, "due_at", Conv.DATE_TIME); + } + + /** + * The prepared_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional preparedAt() { + return Json.optional(json, "prepared_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorQueueRow}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorQueueRow from) { + super(from); + } + + /** + * The run_id field. + * + * @param runId the value + * @return this builder + */ + public Builder runId(String runId) { + return set("run_id", Conv.STRING, runId); + } + + /** + * The kind field. + * + * @param kind the value + * @return this builder + */ + public Builder kind(InvestigatorQueueRow.Kind kind) { + return set("kind", Conv.enumeration(InvestigatorQueueRow.Kind::of), kind); + } + + /** + * The queue field. + * + * @param queue the value + * @return this builder + */ + public Builder queue(InvestigatorQueueRow.Queue queue) { + return set("queue", Conv.enumeration(InvestigatorQueueRow.Queue::of), queue); + } + + /** + * The stage field. + * + * @param stage the value + * @return this builder + */ + public Builder stage(InvestigatorQueueRow.Stage stage) { + return set("stage", Conv.enumeration(InvestigatorQueueRow.Stage::of), stage); + } + + /** + * The item under review: id, number, status, name and, depending on the kind, country, workflow, vendor_data, txn_id, amount, currency, direction, title or priority. + * + * @param subject the value + * @return this builder + */ + public Builder subject(Map subject) { + return setAny("subject", subject); + } + + /** + * The recommendation field. + * + * @param recommendation the value + * @return this builder + */ + public Builder recommendation(InvestigatorQueueRow.Recommendation recommendation) { + return set("recommendation", Conv.enumeration(InvestigatorQueueRow.Recommendation::of), recommendation); + } + + /** + * The recommendation_detail field. + * + * @param recommendationDetail the value + * @return this builder + */ + public Builder recommendationDetail(String recommendationDetail) { + return set("recommendation_detail", Conv.STRING, recommendationDetail); + } + + /** + * The confidence field. + * + * @param confidence the value + * @return this builder + */ + public Builder confidence(InvestigatorQueueRow.Confidence confidence) { + return set("confidence", Conv.enumeration(InvestigatorQueueRow.Confidence::of), confidence); + } + + /** + * The summary field. + * + * @param summary the value + * @return this builder + */ + public Builder summary(String summary) { + return set("summary", Conv.STRING, summary); + } + + /** + * The sources_count field. + * + * @param sourcesCount the value + * @return this builder + */ + public Builder sourcesCount(long sourcesCount) { + return set("sources_count", Conv.LONG, sourcesCount); + } + + /** + * The ready_to_accept field. + * + * @param readyToAccept the value + * @return this builder + */ + public Builder readyToAccept(boolean readyToAccept) { + return set("ready_to_accept", Conv.BOOLEAN, readyToAccept); + } + + /** + * The locked guardrails that hold this item, if any. + * + * @param guardrails the value + * @return this builder + */ + public Builder guardrails(@Nullable Object guardrails) { + return setAny("guardrails", guardrails); + } + + /** + * The waiting_since field. + * + * @param waitingSince the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder waitingSince(@Nullable OffsetDateTime waitingSince) { + return setNullable("waiting_since", Conv.DATE_TIME, waitingSince); + } + + /** + * The due_at field. + * + * @param dueAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dueAt(@Nullable OffsetDateTime dueAt) { + return setNullable("due_at", Conv.DATE_TIME, dueAt); + } + + /** + * The prepared_at field. + * + * @param preparedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder preparedAt(@Nullable OffsetDateTime preparedAt) { + return setNullable("prepared_at", Conv.DATE_TIME, preparedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorQueueRow} + */ + public InvestigatorQueueRow build() { + return new InvestigatorQueueRow(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "session"}. */ + public static final Kind SESSION = new Kind("session"); + + /** The value {@code "business_session"}. */ + public static final Kind BUSINESS_SESSION = new Kind("business_session"); + + /** The value {@code "transaction"}. */ + public static final Kind TRANSACTION = new Kind("transaction"); + + /** The value {@code "case"}. */ + public static final Kind CASE = new Kind("case"); + + private static final List KNOWN_VALUES = List.of(SESSION, BUSINESS_SESSION, TRANSACTION, CASE); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Queue extends StringEnum { + /** The value {@code "kyc"}. */ + public static final Queue KYC = new Queue("kyc"); + + /** The value {@code "kyb"}. */ + public static final Queue KYB = new Queue("kyb"); + + /** The value {@code "aml"}. */ + public static final Queue AML = new Queue("aml"); + + /** The value {@code "transactions"}. */ + public static final Queue TRANSACTIONS = new Queue("transactions"); + + /** The value {@code "cases"}. */ + public static final Queue CASES = new Queue("cases"); + + private static final List KNOWN_VALUES = List.of(KYC, KYB, AML, TRANSACTIONS, CASES); + + private Queue(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Queue of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Queue::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Stage extends StringEnum { + /** The value {@code "shadow"}. */ + public static final Stage SHADOW = new Stage("shadow"); + + /** The value {@code "live"}. */ + public static final Stage LIVE = new Stage("live"); + + private static final List KNOWN_VALUES = List.of(SHADOW, LIVE); + + private Stage(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Stage of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Stage::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Recommendation extends StringEnum { + /** The value {@code "approve"}. */ + public static final Recommendation APPROVE = new Recommendation("approve"); + + /** The value {@code "decline"}. */ + public static final Recommendation DECLINE = new Recommendation("decline"); + + /** The value {@code "escalate"}. */ + public static final Recommendation ESCALATE = new Recommendation("escalate"); + + /** The value {@code "request"}. */ + public static final Recommendation REQUEST = new Recommendation("request"); + + private static final List KNOWN_VALUES = List.of(APPROVE, DECLINE, ESCALATE, REQUEST); + + private Recommendation(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Recommendation of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Recommendation::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Confidence extends StringEnum { + /** The value {@code "high"}. */ + public static final Confidence HIGH = new Confidence("high"); + + /** The value {@code "moderate"}. */ + public static final Confidence MODERATE = new Confidence("moderate"); + + /** The value {@code "low"}. */ + public static final Confidence LOW = new Confidence("low"); + + private static final List KNOWN_VALUES = List.of(HIGH, MODERATE, LOW); + + private Confidence(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Confidence of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Confidence::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorRetrieveDashboardParams.java b/src/main/java/me/didit/types/InvestigatorRetrieveDashboardParams.java new file mode 100644 index 0000000..f19b227 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorRetrieveDashboardParams.java @@ -0,0 +1,145 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code investigator.retrieve_dashboard}. */ +public final class InvestigatorRetrieveDashboardParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("scope"); + + private InvestigatorRetrieveDashboardParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorRetrieveDashboardParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorRetrieveDashboardParams fromJson(JsonNode json) { + return new InvestigatorRetrieveDashboardParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(InvestigatorRetrieveDashboardParams.Scope::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorRetrieveDashboardParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorRetrieveDashboardParams from) { + super(from); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @param scope the value + * @return this builder + */ + public Builder scope(InvestigatorRetrieveDashboardParams.Scope scope) { + return set("scope", Conv.enumeration(InvestigatorRetrieveDashboardParams.Scope::of), scope); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorRetrieveDashboardParams} + */ + public InvestigatorRetrieveDashboardParams build() { + return new InvestigatorRetrieveDashboardParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "all"}. */ + public static final Scope ALL = new Scope("all"); + + /** The value {@code "identity"}. */ + public static final Scope IDENTITY = new Scope("identity"); + + /** The value {@code "transactions"}. */ + public static final Scope TRANSACTIONS = new Scope("transactions"); + + private static final List KNOWN_VALUES = List.of(ALL, IDENTITY, TRANSACTIONS); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorRetrieveDashboardResponse.java b/src/main/java/me/didit/types/InvestigatorRetrieveDashboardResponse.java new file mode 100644 index 0000000..8b7bfe5 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorRetrieveDashboardResponse.java @@ -0,0 +1,219 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class InvestigatorRetrieveDashboardResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("stage", "window_days", "reviews", "decided", "automated", "automation_rate", "hours_saved"); + + private InvestigatorRetrieveDashboardResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorRetrieveDashboardResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorRetrieveDashboardResponse fromJson(JsonNode json) { + return new InvestigatorRetrieveDashboardResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The stage field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional stage() { + return Json.optional(json, "stage", Conv.STRING); + } + + /** + * The window_days field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional windowDays() { + return Json.optional(json, "window_days", Conv.LONG); + } + + /** + * The reviews field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reviews() { + return Json.optional(json, "reviews", Conv.LONG); + } + + /** + * The decided field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decided() { + return Json.optional(json, "decided", Conv.LONG); + } + + /** + * The automated field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional automated() { + return Json.optional(json, "automated", Conv.LONG); + } + + /** + * The automation_rate field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional automationRate() { + return Json.optional(json, "automation_rate", Conv.DOUBLE); + } + + /** + * The hours_saved field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional hoursSaved() { + return Json.optional(json, "hours_saved", Conv.DOUBLE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorRetrieveDashboardResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorRetrieveDashboardResponse from) { + super(from); + } + + /** + * The stage field. + * + * @param stage the value + * @return this builder + */ + public Builder stage(String stage) { + return set("stage", Conv.STRING, stage); + } + + /** + * The window_days field. + * + * @param windowDays the value + * @return this builder + */ + public Builder windowDays(long windowDays) { + return set("window_days", Conv.LONG, windowDays); + } + + /** + * The reviews field. + * + * @param reviews the value + * @return this builder + */ + public Builder reviews(long reviews) { + return set("reviews", Conv.LONG, reviews); + } + + /** + * The decided field. + * + * @param decided the value + * @return this builder + */ + public Builder decided(long decided) { + return set("decided", Conv.LONG, decided); + } + + /** + * The automated field. + * + * @param automated the value + * @return this builder + */ + public Builder automated(long automated) { + return set("automated", Conv.LONG, automated); + } + + /** + * The automation_rate field. + * + * @param automationRate the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder automationRate(@Nullable Double automationRate) { + return setNullable("automation_rate", Conv.DOUBLE, automationRate); + } + + /** + * The hours_saved field. + * + * @param hoursSaved the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder hoursSaved(@Nullable Double hoursSaved) { + return setNullable("hours_saved", Conv.DOUBLE, hoursSaved); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorRetrieveDashboardResponse} + */ + public InvestigatorRetrieveDashboardResponse build() { + return new InvestigatorRetrieveDashboardResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorRetrievePerformanceParams.java b/src/main/java/me/didit/types/InvestigatorRetrievePerformanceParams.java new file mode 100644 index 0000000..58538bb --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorRetrievePerformanceParams.java @@ -0,0 +1,210 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code investigator.retrieve_performance}. */ +public final class InvestigatorRetrievePerformanceParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("scope", "period"); + + private InvestigatorRetrievePerformanceParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorRetrievePerformanceParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorRetrievePerformanceParams fromJson(JsonNode json) { + return new InvestigatorRetrievePerformanceParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(InvestigatorRetrievePerformanceParams.Scope::of)); + } + + /** + * Days, ending today. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional period() { + return Json.optional(json, "period", Conv.enumeration(InvestigatorRetrievePerformanceParams.Period::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorRetrievePerformanceParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorRetrievePerformanceParams from) { + super(from); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @param scope the value + * @return this builder + */ + public Builder scope(InvestigatorRetrievePerformanceParams.Scope scope) { + return set("scope", Conv.enumeration(InvestigatorRetrievePerformanceParams.Scope::of), scope); + } + + /** + * Days, ending today. + * + * @param period the value + * @return this builder + */ + public Builder period(InvestigatorRetrievePerformanceParams.Period period) { + return set("period", Conv.enumeration(InvestigatorRetrievePerformanceParams.Period::of), period); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorRetrievePerformanceParams} + */ + public InvestigatorRetrievePerformanceParams build() { + return new InvestigatorRetrievePerformanceParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "all"}. */ + public static final Scope ALL = new Scope("all"); + + /** The value {@code "identity"}. */ + public static final Scope IDENTITY = new Scope("identity"); + + /** The value {@code "transactions"}. */ + public static final Scope TRANSACTIONS = new Scope("transactions"); + + private static final List KNOWN_VALUES = List.of(ALL, IDENTITY, TRANSACTIONS); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Period extends StringEnum { + /** The value {@code "7"}. */ + public static final Period _7 = new Period("7"); + + /** The value {@code "30"}. */ + public static final Period _30 = new Period("30"); + + /** The value {@code "90"}. */ + public static final Period _90 = new Period("90"); + + private static final List KNOWN_VALUES = List.of(_7, _30, _90); + + private Period(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Period of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Period::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorRetrievePerformanceResponse.java b/src/main/java/me/didit/types/InvestigatorRetrievePerformanceResponse.java new file mode 100644 index 0000000..15eb795 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorRetrievePerformanceResponse.java @@ -0,0 +1,159 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class InvestigatorRetrievePerformanceResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("period", "start", "end", "investigated"); + + private InvestigatorRetrievePerformanceResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorRetrievePerformanceResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorRetrievePerformanceResponse fromJson(JsonNode json) { + return new InvestigatorRetrievePerformanceResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The period field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional period() { + return Json.optional(json, "period", Conv.LONG); + } + + /** + * The start field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional start() { + return Json.optional(json, "start", Conv.DATE); + } + + /** + * The end field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional end() { + return Json.optional(json, "end", Conv.DATE); + } + + /** + * The investigated field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional investigated() { + return Json.optional(json, "investigated", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorRetrievePerformanceResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorRetrievePerformanceResponse from) { + super(from); + } + + /** + * The period field. + * + * @param period the value + * @return this builder + */ + public Builder period(long period) { + return set("period", Conv.LONG, period); + } + + /** + * The start field. + * + * @param start the value + * @return this builder + */ + public Builder start(LocalDate start) { + return set("start", Conv.DATE, start); + } + + /** + * The end field. + * + * @param end the value + * @return this builder + */ + public Builder end(LocalDate end) { + return set("end", Conv.DATE, end); + } + + /** + * The investigated field. + * + * @param investigated the value + * @return this builder + */ + public Builder investigated(long investigated) { + return set("investigated", Conv.LONG, investigated); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorRetrievePerformanceResponse} + */ + public InvestigatorRetrievePerformanceResponse build() { + return new InvestigatorRetrievePerformanceResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorRetrieveSummaryParams.java b/src/main/java/me/didit/types/InvestigatorRetrieveSummaryParams.java new file mode 100644 index 0000000..c36eaf4 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorRetrieveSummaryParams.java @@ -0,0 +1,165 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code investigator.retrieve_summary}. */ +public final class InvestigatorRetrieveSummaryParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("scope", "timezone"); + + private InvestigatorRetrieveSummaryParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorRetrieveSummaryParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorRetrieveSummaryParams fromJson(JsonNode json) { + return new InvestigatorRetrieveSummaryParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(InvestigatorRetrieveSummaryParams.Scope::of)); + } + + /** + * IANA time zone (for example Europe/Madrid) that "today" counts from. UTC by default. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional timezone() { + return Json.optional(json, "timezone", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorRetrieveSummaryParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorRetrieveSummaryParams from) { + super(from); + } + + /** + * identity (sessions, and cases not opened by a transaction rule) or transactions (transaction alerts, and cases a transaction rule opened). + * + * @param scope the value + * @return this builder + */ + public Builder scope(InvestigatorRetrieveSummaryParams.Scope scope) { + return set("scope", Conv.enumeration(InvestigatorRetrieveSummaryParams.Scope::of), scope); + } + + /** + * IANA time zone (for example Europe/Madrid) that "today" counts from. UTC by default. + * + * @param timezone the value + * @return this builder + */ + public Builder timezone(String timezone) { + return set("timezone", Conv.STRING, timezone); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorRetrieveSummaryParams} + */ + public InvestigatorRetrieveSummaryParams build() { + return new InvestigatorRetrieveSummaryParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "all"}. */ + public static final Scope ALL = new Scope("all"); + + /** The value {@code "identity"}. */ + public static final Scope IDENTITY = new Scope("identity"); + + /** The value {@code "transactions"}. */ + public static final Scope TRANSACTIONS = new Scope("transactions"); + + private static final List KNOWN_VALUES = List.of(ALL, IDENTITY, TRANSACTIONS); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorRetrieveSummaryResponse.java b/src/main/java/me/didit/types/InvestigatorRetrieveSummaryResponse.java new file mode 100644 index 0000000..2c9a6b2 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorRetrieveSummaryResponse.java @@ -0,0 +1,240 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class InvestigatorRetrieveSummaryResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("stage", "last_report_at", "prepared_today", "ready_to_accept", "open", "decided_today", "automation_rate_month", "preparing"); + + private InvestigatorRetrieveSummaryResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorRetrieveSummaryResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorRetrieveSummaryResponse fromJson(JsonNode json) { + return new InvestigatorRetrieveSummaryResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The stage field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional stage() { + return Json.optional(json, "stage", Conv.STRING); + } + + /** + * The last_report_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastReportAt() { + return Json.optional(json, "last_report_at", Conv.DATE_TIME); + } + + /** + * The prepared_today field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional preparedToday() { + return Json.optional(json, "prepared_today", Conv.LONG); + } + + /** + * The ready_to_accept field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional readyToAccept() { + return Json.optional(json, "ready_to_accept", Conv.LONG); + } + + /** + * The open field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional open() { + return Json.optional(json, "open", Conv.LONG); + } + + /** + * The decided_today field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decidedToday() { + return Json.optional(json, "decided_today", Conv.LONG); + } + + /** + * The automation_rate_month field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional automationRateMonth() { + return Json.optional(json, "automation_rate_month", Conv.DOUBLE); + } + + /** + * The preparing field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional preparing() { + return Json.optional(json, "preparing", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorRetrieveSummaryResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorRetrieveSummaryResponse from) { + super(from); + } + + /** + * The stage field. + * + * @param stage the value + * @return this builder + */ + public Builder stage(String stage) { + return set("stage", Conv.STRING, stage); + } + + /** + * The last_report_at field. + * + * @param lastReportAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastReportAt(@Nullable OffsetDateTime lastReportAt) { + return setNullable("last_report_at", Conv.DATE_TIME, lastReportAt); + } + + /** + * The prepared_today field. + * + * @param preparedToday the value + * @return this builder + */ + public Builder preparedToday(long preparedToday) { + return set("prepared_today", Conv.LONG, preparedToday); + } + + /** + * The ready_to_accept field. + * + * @param readyToAccept the value + * @return this builder + */ + public Builder readyToAccept(long readyToAccept) { + return set("ready_to_accept", Conv.LONG, readyToAccept); + } + + /** + * The open field. + * + * @param open the value + * @return this builder + */ + public Builder open(long open) { + return set("open", Conv.LONG, open); + } + + /** + * The decided_today field. + * + * @param decidedToday the value + * @return this builder + */ + public Builder decidedToday(long decidedToday) { + return set("decided_today", Conv.LONG, decidedToday); + } + + /** + * The automation_rate_month field. + * + * @param automationRateMonth the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder automationRateMonth(@Nullable Double automationRateMonth) { + return setNullable("automation_rate_month", Conv.DOUBLE, automationRateMonth); + } + + /** + * The preparing field. + * + * @param preparing the value + * @return this builder + */ + public Builder preparing(long preparing) { + return set("preparing", Conv.LONG, preparing); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorRetrieveSummaryResponse} + */ + public InvestigatorRetrieveSummaryResponse build() { + return new InvestigatorRetrieveSummaryResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorRetrieveTransactionResponse.java b/src/main/java/me/didit/types/InvestigatorRetrieveTransactionResponse.java new file mode 100644 index 0000000..f500d21 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorRetrieveTransactionResponse.java @@ -0,0 +1,119 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class InvestigatorRetrieveTransactionResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("transaction_id", "investigator"); + + private InvestigatorRetrieveTransactionResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorRetrieveTransactionResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorRetrieveTransactionResponse fromJson(JsonNode json) { + return new InvestigatorRetrieveTransactionResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The transaction_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionId() { + return Json.optional(json, "transaction_id", Conv.STRING); + } + + /** + * The item's latest Investigator review, or null when it never had one. Carried by the V3 session and business session decision (and so by status.updated webhooks). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional investigator() { + return Json.optional(json, "investigator", Conv.model(InvestigatorBlock::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorRetrieveTransactionResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorRetrieveTransactionResponse from) { + super(from); + } + + /** + * The transaction_id field. + * + * @param transactionId the value + * @return this builder + */ + public Builder transactionId(String transactionId) { + return set("transaction_id", Conv.STRING, transactionId); + } + + /** + * The item's latest Investigator review, or null when it never had one. Carried by the V3 session and business session decision (and so by status.updated webhooks). + * + * @param investigator the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder investigator(@Nullable InvestigatorBlock investigator) { + return setNullable("investigator", Conv.model(InvestigatorBlock::fromJson), investigator); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorRetrieveTransactionResponse} + */ + public InvestigatorRetrieveTransactionResponse build() { + return new InvestigatorRetrieveTransactionResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorRunDetail.java b/src/main/java/me/didit/types/InvestigatorRunDetail.java new file mode 100644 index 0000000..eeabb0f --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorRunDetail.java @@ -0,0 +1,580 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class InvestigatorRunDetail extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("run_id", "kind", "queue", "stage", "subject", "recommendation", "recommendation_detail", "confidence", "summary", "sources_count", "ready_to_accept", "guardrails", "waiting_since", "due_at", "prepared_at", "status", "is_open", "lookups", "duration_ms", "agent_version", "prepared_before_opened", "report", "trail", "decision", "autonomy"); + + private InvestigatorRunDetail(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorRunDetail} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorRunDetail fromJson(JsonNode json) { + return new InvestigatorRunDetail(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The run_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional runId() { + return Json.optional(json, "run_id", Conv.STRING); + } + + /** + * The kind field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(InvestigatorQueueRow.Kind::of)); + } + + /** + * The queue field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional queue() { + return Json.optional(json, "queue", Conv.enumeration(InvestigatorQueueRow.Queue::of)); + } + + /** + * The stage field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional stage() { + return Json.optional(json, "stage", Conv.enumeration(InvestigatorQueueRow.Stage::of)); + } + + /** + * The item under review: id, number, status, name and, depending on the kind, country, workflow, vendor_data, txn_id, amount, currency, direction, title or priority. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> subject() { + return Json.optional(json, "subject", Conv.OBJECT); + } + + /** + * The recommendation field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional recommendation() { + return Json.optional(json, "recommendation", Conv.enumeration(InvestigatorQueueRow.Recommendation::of)); + } + + /** + * The recommendation_detail field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional recommendationDetail() { + return Json.optional(json, "recommendation_detail", Conv.STRING); + } + + /** + * The confidence field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional confidence() { + return Json.optional(json, "confidence", Conv.enumeration(InvestigatorQueueRow.Confidence::of)); + } + + /** + * The summary field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional summary() { + return Json.optional(json, "summary", Conv.STRING); + } + + /** + * The sources_count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourcesCount() { + return Json.optional(json, "sources_count", Conv.LONG); + } + + /** + * The ready_to_accept field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional readyToAccept() { + return Json.optional(json, "ready_to_accept", Conv.BOOLEAN); + } + + /** + * The locked guardrails that hold this item, if any. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional guardrails() { + return Json.optional(json, "guardrails", Conv.ANY); + } + + /** + * The waiting_since field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional waitingSince() { + return Json.optional(json, "waiting_since", Conv.DATE_TIME); + } + + /** + * The due_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dueAt() { + return Json.optional(json, "due_at", Conv.DATE_TIME); + } + + /** + * The prepared_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional preparedAt() { + return Json.optional(json, "prepared_at", Conv.DATE_TIME); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * The is_open field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isOpen() { + return Json.optional(json, "is_open", Conv.BOOLEAN); + } + + /** + * How many sections and linked records it read. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lookups() { + return Json.optional(json, "lookups", Conv.LONG); + } + + /** + * The duration_ms field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional durationMs() { + return Json.optional(json, "duration_ms", Conv.LONG); + } + + /** + * The agent_version field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional agentVersion() { + return Json.optional(json, "agent_version", Conv.STRING); + } + + /** + * The prepared_before_opened field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional preparedBeforeOpened() { + return Json.optional(json, "prepared_before_opened", Conv.BOOLEAN); + } + + /** + * The report: reasoning, evidence points with their verdict (ok, risk, look, explained, context) and cited sources, screening hit verdicts (same, not_same, unclear) and suggested next steps. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> report() { + return Json.optional(json, "report", Conv.OBJECT); + } + + /** + * What I checked: the overview, sections and records it read, in order. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional trail() { + return Json.optional(json, "trail", Conv.ANY); + } + + /** + * The decision field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decision() { + return Json.optional(json, "decision", Conv.model(InvestigatorDecision::fromJson)); + } + + /** + * What autonomy made of the report (result, reason, tier, policy_version, rule, agreement, decisions, threshold, at). Null when the queue is not automated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> autonomy() { + return Json.optional(json, "autonomy", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorRunDetail}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorRunDetail from) { + super(from); + } + + /** + * The run_id field. + * + * @param runId the value + * @return this builder + */ + public Builder runId(String runId) { + return set("run_id", Conv.STRING, runId); + } + + /** + * The kind field. + * + * @param kind the value + * @return this builder + */ + public Builder kind(InvestigatorQueueRow.Kind kind) { + return set("kind", Conv.enumeration(InvestigatorQueueRow.Kind::of), kind); + } + + /** + * The queue field. + * + * @param queue the value + * @return this builder + */ + public Builder queue(InvestigatorQueueRow.Queue queue) { + return set("queue", Conv.enumeration(InvestigatorQueueRow.Queue::of), queue); + } + + /** + * The stage field. + * + * @param stage the value + * @return this builder + */ + public Builder stage(InvestigatorQueueRow.Stage stage) { + return set("stage", Conv.enumeration(InvestigatorQueueRow.Stage::of), stage); + } + + /** + * The item under review: id, number, status, name and, depending on the kind, country, workflow, vendor_data, txn_id, amount, currency, direction, title or priority. + * + * @param subject the value + * @return this builder + */ + public Builder subject(Map subject) { + return setAny("subject", subject); + } + + /** + * The recommendation field. + * + * @param recommendation the value + * @return this builder + */ + public Builder recommendation(InvestigatorQueueRow.Recommendation recommendation) { + return set("recommendation", Conv.enumeration(InvestigatorQueueRow.Recommendation::of), recommendation); + } + + /** + * The recommendation_detail field. + * + * @param recommendationDetail the value + * @return this builder + */ + public Builder recommendationDetail(String recommendationDetail) { + return set("recommendation_detail", Conv.STRING, recommendationDetail); + } + + /** + * The confidence field. + * + * @param confidence the value + * @return this builder + */ + public Builder confidence(InvestigatorQueueRow.Confidence confidence) { + return set("confidence", Conv.enumeration(InvestigatorQueueRow.Confidence::of), confidence); + } + + /** + * The summary field. + * + * @param summary the value + * @return this builder + */ + public Builder summary(String summary) { + return set("summary", Conv.STRING, summary); + } + + /** + * The sources_count field. + * + * @param sourcesCount the value + * @return this builder + */ + public Builder sourcesCount(long sourcesCount) { + return set("sources_count", Conv.LONG, sourcesCount); + } + + /** + * The ready_to_accept field. + * + * @param readyToAccept the value + * @return this builder + */ + public Builder readyToAccept(boolean readyToAccept) { + return set("ready_to_accept", Conv.BOOLEAN, readyToAccept); + } + + /** + * The locked guardrails that hold this item, if any. + * + * @param guardrails the value + * @return this builder + */ + public Builder guardrails(@Nullable Object guardrails) { + return setAny("guardrails", guardrails); + } + + /** + * The waiting_since field. + * + * @param waitingSince the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder waitingSince(@Nullable OffsetDateTime waitingSince) { + return setNullable("waiting_since", Conv.DATE_TIME, waitingSince); + } + + /** + * The due_at field. + * + * @param dueAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dueAt(@Nullable OffsetDateTime dueAt) { + return setNullable("due_at", Conv.DATE_TIME, dueAt); + } + + /** + * The prepared_at field. + * + * @param preparedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder preparedAt(@Nullable OffsetDateTime preparedAt) { + return setNullable("prepared_at", Conv.DATE_TIME, preparedAt); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * The is_open field. + * + * @param isOpen the value + * @return this builder + */ + public Builder isOpen(boolean isOpen) { + return set("is_open", Conv.BOOLEAN, isOpen); + } + + /** + * How many sections and linked records it read. + * + * @param lookups the value + * @return this builder + */ + public Builder lookups(long lookups) { + return set("lookups", Conv.LONG, lookups); + } + + /** + * The duration_ms field. + * + * @param durationMs the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder durationMs(@Nullable Long durationMs) { + return setNullable("duration_ms", Conv.LONG, durationMs); + } + + /** + * The agent_version field. + * + * @param agentVersion the value + * @return this builder + */ + public Builder agentVersion(String agentVersion) { + return set("agent_version", Conv.STRING, agentVersion); + } + + /** + * The prepared_before_opened field. + * + * @param preparedBeforeOpened the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder preparedBeforeOpened(@Nullable Boolean preparedBeforeOpened) { + return setNullable("prepared_before_opened", Conv.BOOLEAN, preparedBeforeOpened); + } + + /** + * The report: reasoning, evidence points with their verdict (ok, risk, look, explained, context) and cited sources, screening hit verdicts (same, not_same, unclear) and suggested next steps. + * + * @param report the value + * @return this builder + */ + public Builder report(Map report) { + return setAny("report", report); + } + + /** + * What I checked: the overview, sections and records it read, in order. + * + * @param trail the value + * @return this builder + */ + public Builder trail(@Nullable Object trail) { + return setAny("trail", trail); + } + + /** + * The decision field. + * + * @param decision the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder decision(@Nullable InvestigatorDecision decision) { + return setNullable("decision", Conv.model(InvestigatorDecision::fromJson), decision); + } + + /** + * What autonomy made of the report (result, reason, tier, policy_version, rule, agreement, decisions, threshold, at). Null when the queue is not automated. + * + * @param autonomy the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder autonomy(@Nullable Map autonomy) { + return setAny("autonomy", autonomy); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorRunDetail} + */ + public InvestigatorRunDetail build() { + return new InvestigatorRunDetail(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/InvestigatorSettings.java b/src/main/java/me/didit/types/InvestigatorSettings.java new file mode 100644 index 0000000..0c881b4 --- /dev/null +++ b/src/main/java/me/didit/types/InvestigatorSettings.java @@ -0,0 +1,431 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class InvestigatorSettings extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("stage", "stage_changed_at", "stage_changed_by", "monthly_spend_limit", "spend_limit"); + + private InvestigatorSettings(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InvestigatorSettings} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InvestigatorSettings fromJson(JsonNode json) { + return new InvestigatorSettings(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The stage field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional stage() { + return Json.optional(json, "stage", Conv.enumeration(InvestigatorSettings.Stage::of)); + } + + /** + * The stage_changed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional stageChangedAt() { + return Json.optional(json, "stage_changed_at", Conv.DATE_TIME); + } + + /** + * The stage_changed_by field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional stageChangedBy() { + return Json.optional(json, "stage_changed_by", Conv.STRING); + } + + /** + * Monthly limit in your billing currency, 0.01 to 1,000,000. Null turns it off. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional monthlySpendLimit() { + return Json.optional(json, "monthly_spend_limit", Conv.STRING); + } + + /** + * The spend_limit field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional spendLimit() { + return Json.optional(json, "spend_limit", Conv.model(InvestigatorSettings.SpendLimit::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InvestigatorSettings}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InvestigatorSettings from) { + super(from); + } + + /** + * The stage field. + * + * @param stage the value + * @return this builder + */ + public Builder stage(InvestigatorSettings.Stage stage) { + return set("stage", Conv.enumeration(InvestigatorSettings.Stage::of), stage); + } + + /** + * The stage_changed_at field. + * + * @param stageChangedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder stageChangedAt(@Nullable OffsetDateTime stageChangedAt) { + return setNullable("stage_changed_at", Conv.DATE_TIME, stageChangedAt); + } + + /** + * The stage_changed_by field. + * + * @param stageChangedBy the value + * @return this builder + */ + public Builder stageChangedBy(String stageChangedBy) { + return set("stage_changed_by", Conv.STRING, stageChangedBy); + } + + /** + * Monthly limit in your billing currency, 0.01 to 1,000,000. Null turns it off. + * + * @param monthlySpendLimit the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder monthlySpendLimit(@Nullable String monthlySpendLimit) { + return setNullable("monthly_spend_limit", Conv.STRING, monthlySpendLimit); + } + + /** + * The spend_limit field. + * + * @param spendLimit the value + * @return this builder + */ + public Builder spendLimit(InvestigatorSettings.SpendLimit spendLimit) { + return set("spend_limit", Conv.model(InvestigatorSettings.SpendLimit::fromJson), spendLimit); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InvestigatorSettings} + */ + public InvestigatorSettings build() { + return new InvestigatorSettings(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Stage extends StringEnum { + /** The value {@code "none"}. */ + public static final Stage NONE = new Stage("none"); + + /** The value {@code "shadow"}. */ + public static final Stage SHADOW = new Stage("shadow"); + + /** The value {@code "live"}. */ + public static final Stage LIVE = new Stage("live"); + + private static final List KNOWN_VALUES = List.of(NONE, SHADOW, LIVE); + + private Stage(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Stage of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Stage::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class SpendLimit extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("enabled", "limit", "spent", "reviews", "reserved", "currency", "paused"); + + private SpendLimit(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SpendLimit} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SpendLimit fromJson(JsonNode json) { + return new SpendLimit(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The enabled field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional enabled() { + return Json.optional(json, "enabled", Conv.BOOLEAN); + } + + /** + * The limit field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.STRING); + } + + /** + * The spent field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional spent() { + return Json.optional(json, "spent", Conv.STRING); + } + + /** + * The reviews field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reviews() { + return Json.optional(json, "reviews", Conv.LONG); + } + + /** + * The price of every open live report, held back because accepting it charges it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reserved() { + return Json.optional(json, "reserved", Conv.STRING); + } + + /** + * The currency field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currency() { + return Json.optional(json, "currency", Conv.STRING); + } + + /** + * At the limit: no new review starts. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional paused() { + return Json.optional(json, "paused", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SpendLimit}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SpendLimit from) { + super(from); + } + + /** + * The enabled field. + * + * @param enabled the value + * @return this builder + */ + public Builder enabled(boolean enabled) { + return set("enabled", Conv.BOOLEAN, enabled); + } + + /** + * The limit field. + * + * @param limit the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder limit(@Nullable String limit) { + return setNullable("limit", Conv.STRING, limit); + } + + /** + * The spent field. + * + * @param spent the value + * @return this builder + */ + public Builder spent(String spent) { + return set("spent", Conv.STRING, spent); + } + + /** + * The reviews field. + * + * @param reviews the value + * @return this builder + */ + public Builder reviews(long reviews) { + return set("reviews", Conv.LONG, reviews); + } + + /** + * The price of every open live report, held back because accepting it charges it. + * + * @param reserved the value + * @return this builder + */ + public Builder reserved(String reserved) { + return set("reserved", Conv.STRING, reserved); + } + + /** + * The currency field. + * + * @param currency the value + * @return this builder + */ + public Builder currency(String currency) { + return set("currency", Conv.STRING, currency); + } + + /** + * At the limit: no new review starts. + * + * @param paused the value + * @return this builder + */ + public Builder paused(boolean paused) { + return set("paused", Conv.BOOLEAN, paused); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SpendLimit} + */ + public SpendLimit build() { + return new SpendLimit(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/KYBKeyPeopleParty.java b/src/main/java/me/didit/types/KYBKeyPeopleParty.java new file mode 100644 index 0000000..d8b28c3 --- /dev/null +++ b/src/main/java/me/didit/types/KYBKeyPeopleParty.java @@ -0,0 +1,1321 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * A Key People party of a Business Verification (KYB) session, with its child verification. The same party appears in key_people_checks[].submitted.parties[] of the business session decision. + */ +public final class KYBKeyPeopleParty extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "entity_type", "source", "name", "first_name", "last_name", "company_name", "registration_number", "email", "nationality", "phone_number", "date_of_birth", "position", "custom_fields", "is_active", "is_skipped", "requires_verification", "roles", "kyc_session_id", "kyc_session_status", "kyc_session_url", "kyc_session_deleted", "kyb_sub_session_id", "kyb_sub_session_status", "kyb_sub_session_url", "kyb_sub_session_deleted", "verification_workflow_type", "verification_workflow_id", "verification_workflow_label", "verification_workflow_features", "didit_internal_id"); + + private KYBKeyPeopleParty(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code KYBKeyPeopleParty} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static KYBKeyPeopleParty fromJson(JsonNode json) { + return new KYBKeyPeopleParty(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Party identifier. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * Whether the party is an individual or a company. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public KYBKeyPeopleParty.EntityType entityType() { + return Json.required(json, "entity_type", Conv.enumeration(KYBKeyPeopleParty.EntityType::of)); + } + + /** + * REGISTRY: taken from the company registry. USER: entered in the Key People screen. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public KYBKeyPeopleParty.Source source() { + return Json.required(json, "source", Conv.enumeration(KYBKeyPeopleParty.Source::of)); + } + + /** + * Display name of the party. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String name() { + return Json.required(json, "name", Conv.STRING); + } + + /** + * First name (persons). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional firstName() { + return Json.optional(json, "first_name", Conv.STRING); + } + + /** + * Last name (persons). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastName() { + return Json.optional(json, "last_name", Conv.STRING); + } + + /** + * Company name (company parties). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional companyName() { + return Json.optional(json, "company_name", Conv.STRING); + } + + /** + * Company registration number (company parties). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional registrationNumber() { + return Json.optional(json, "registration_number", Conv.STRING); + } + + /** + * Email the verification link is sent to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + /** + * Nationality for persons, country of incorporation for companies. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nationality() { + return Json.optional(json, "nationality", Conv.STRING); + } + + /** + * Phone number collected for the party. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneNumber() { + return Json.optional(json, "phone_number", Conv.STRING); + } + + /** + * Date of birth (persons). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateOfBirth() { + return Json.optional(json, "date_of_birth", Conv.DATE); + } + + /** + * Free-text job title (persons). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional position() { + return Json.optional(json, "position", Conv.STRING); + } + + /** + * Workflow-defined custom fields collected for the party, keyed by field key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> customFields() { + return Json.optional(json, "custom_fields", Conv.OBJECT); + } + + /** + * Whether the party is active. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isActive() { + return Json.required(json, "is_active", Conv.BOOLEAN); + } + + /** + * Whether the party is exempt from verification. Regenerating clears it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isSkipped() { + return Json.required(json, "is_skipped", Conv.BOOLEAN); + } + + /** + * Whether the role and ownership rules require a verification for this party. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean requiresVerification() { + return Json.required(json, "requires_verification", Conv.BOOLEAN); + } + + /** + * Roles the party holds in the company. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List roles() { + return Json.required(json, "roles", Conv.list(Conv.model(KYBKeyPeopleParty.Roles::fromJson))); + } + + /** + * Child individual KYC session (persons). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kycSessionId() { + return Json.optional(json, "kyc_session_id", Conv.STRING); + } + + /** + * Status of the child KYC session. A deleted session keeps the status it had when it was deleted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kycSessionStatus() { + return Json.optional(json, "kyc_session_status", Conv.enumeration(KYBKeyPeopleParty.KycSessionStatus::of)); + } + + /** + * Hosted link of the child KYC session. null when there is none or it was deleted, and for a scoped API key without sessions: write. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kycSessionUrl() { + return Json.optional(json, "kyc_session_url", Conv.STRING); + } + + /** + * The child KYC session was deleted, so it can never settle. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean kycSessionDeleted() { + return Json.required(json, "kyc_session_deleted", Conv.BOOLEAN); + } + + /** + * Child KYB session (company parties). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kybSubSessionId() { + return Json.optional(json, "kyb_sub_session_id", Conv.STRING); + } + + /** + * Status of the child KYB session. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kybSubSessionStatus() { + return Json.optional(json, "kyb_sub_session_status", Conv.enumeration(KYBKeyPeopleParty.KybSubSessionStatus::of)); + } + + /** + * Hosted link of the child KYB session, with the same null rules as kyc_session_url. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kybSubSessionUrl() { + return Json.optional(json, "kyb_sub_session_url", Conv.STRING); + } + + /** + * Company-party counterpart of kyc_session_deleted. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean kybSubSessionDeleted() { + return Json.required(json, "kyb_sub_session_deleted", Conv.BOOLEAN); + } + + /** + * Type of the workflow the child session runs. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verificationWorkflowType() { + return Json.optional(json, "verification_workflow_type", Conv.enumeration(KYBKeyPeopleParty.VerificationWorkflowType::of)); + } + + /** + * Workflow the child session runs. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verificationWorkflowId() { + return Json.optional(json, "verification_workflow_id", Conv.STRING); + } + + /** + * Label of that workflow. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verificationWorkflowLabel() { + return Json.optional(json, "verification_workflow_label", Conv.STRING); + } + + /** + * Features of the child session's workflow with their current status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> verificationWorkflowFeatures() { + return Json.optional(json, "verification_workflow_features", Conv.list(Conv.model(KYBKeyPeopleParty.VerificationWorkflowFeatures::fromJson))); + } + + /** + * Didit's identifier of the verified person, once known. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional diditInternalId() { + return Json.optional(json, "didit_internal_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link KYBKeyPeopleParty}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(KYBKeyPeopleParty from) { + super(from); + } + + /** + * Party identifier. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Whether the party is an individual or a company. + * + * @param entityType the value + * @return this builder + */ + public Builder entityType(KYBKeyPeopleParty.EntityType entityType) { + return set("entity_type", Conv.enumeration(KYBKeyPeopleParty.EntityType::of), entityType); + } + + /** + * REGISTRY: taken from the company registry. USER: entered in the Key People screen. + * + * @param source the value + * @return this builder + */ + public Builder source(KYBKeyPeopleParty.Source source) { + return set("source", Conv.enumeration(KYBKeyPeopleParty.Source::of), source); + } + + /** + * Display name of the party. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * First name (persons). + * + * @param firstName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder firstName(@Nullable String firstName) { + return setNullable("first_name", Conv.STRING, firstName); + } + + /** + * Last name (persons). + * + * @param lastName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastName(@Nullable String lastName) { + return setNullable("last_name", Conv.STRING, lastName); + } + + /** + * Company name (company parties). + * + * @param companyName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder companyName(@Nullable String companyName) { + return setNullable("company_name", Conv.STRING, companyName); + } + + /** + * Company registration number (company parties). + * + * @param registrationNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder registrationNumber(@Nullable String registrationNumber) { + return setNullable("registration_number", Conv.STRING, registrationNumber); + } + + /** + * Email the verification link is sent to. + * + * @param email the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder email(@Nullable String email) { + return setNullable("email", Conv.STRING, email); + } + + /** + * Nationality for persons, country of incorporation for companies. + * + * @param nationality the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nationality(@Nullable String nationality) { + return setNullable("nationality", Conv.STRING, nationality); + } + + /** + * Phone number collected for the party. + * + * @param phoneNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder phoneNumber(@Nullable String phoneNumber) { + return setNullable("phone_number", Conv.STRING, phoneNumber); + } + + /** + * Date of birth (persons). + * + * @param dateOfBirth the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dateOfBirth(@Nullable LocalDate dateOfBirth) { + return setNullable("date_of_birth", Conv.DATE, dateOfBirth); + } + + /** + * Free-text job title (persons). + * + * @param position the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder position(@Nullable String position) { + return setNullable("position", Conv.STRING, position); + } + + /** + * Workflow-defined custom fields collected for the party, keyed by field key. + * + * @param customFields the value + * @return this builder + */ + public Builder customFields(Map customFields) { + return setAny("custom_fields", customFields); + } + + /** + * Whether the party is active. + * + * @param isActive the value + * @return this builder + */ + public Builder isActive(boolean isActive) { + return set("is_active", Conv.BOOLEAN, isActive); + } + + /** + * Whether the party is exempt from verification. Regenerating clears it. + * + * @param isSkipped the value + * @return this builder + */ + public Builder isSkipped(boolean isSkipped) { + return set("is_skipped", Conv.BOOLEAN, isSkipped); + } + + /** + * Whether the role and ownership rules require a verification for this party. + * + * @param requiresVerification the value + * @return this builder + */ + public Builder requiresVerification(boolean requiresVerification) { + return set("requires_verification", Conv.BOOLEAN, requiresVerification); + } + + /** + * Roles the party holds in the company. + * + * @param roles the value + * @return this builder + */ + public Builder roles(List roles) { + return set("roles", Conv.list(Conv.model(KYBKeyPeopleParty.Roles::fromJson)), roles); + } + + /** + * Child individual KYC session (persons). + * + * @param kycSessionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder kycSessionId(@Nullable String kycSessionId) { + return setNullable("kyc_session_id", Conv.STRING, kycSessionId); + } + + /** + * Status of the child KYC session. A deleted session keeps the status it had when it was deleted. + * + * @param kycSessionStatus the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder kycSessionStatus(KYBKeyPeopleParty.@Nullable KycSessionStatus kycSessionStatus) { + return setNullable("kyc_session_status", Conv.enumeration(KYBKeyPeopleParty.KycSessionStatus::of), kycSessionStatus); + } + + /** + * Hosted link of the child KYC session. null when there is none or it was deleted, and for a scoped API key without sessions: write. + * + * @param kycSessionUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder kycSessionUrl(@Nullable String kycSessionUrl) { + return setNullable("kyc_session_url", Conv.STRING, kycSessionUrl); + } + + /** + * The child KYC session was deleted, so it can never settle. + * + * @param kycSessionDeleted the value + * @return this builder + */ + public Builder kycSessionDeleted(boolean kycSessionDeleted) { + return set("kyc_session_deleted", Conv.BOOLEAN, kycSessionDeleted); + } + + /** + * Child KYB session (company parties). + * + * @param kybSubSessionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder kybSubSessionId(@Nullable String kybSubSessionId) { + return setNullable("kyb_sub_session_id", Conv.STRING, kybSubSessionId); + } + + /** + * Status of the child KYB session. + * + * @param kybSubSessionStatus the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder kybSubSessionStatus(KYBKeyPeopleParty.@Nullable KybSubSessionStatus kybSubSessionStatus) { + return setNullable("kyb_sub_session_status", Conv.enumeration(KYBKeyPeopleParty.KybSubSessionStatus::of), kybSubSessionStatus); + } + + /** + * Hosted link of the child KYB session, with the same null rules as kyc_session_url. + * + * @param kybSubSessionUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder kybSubSessionUrl(@Nullable String kybSubSessionUrl) { + return setNullable("kyb_sub_session_url", Conv.STRING, kybSubSessionUrl); + } + + /** + * Company-party counterpart of kyc_session_deleted. + * + * @param kybSubSessionDeleted the value + * @return this builder + */ + public Builder kybSubSessionDeleted(boolean kybSubSessionDeleted) { + return set("kyb_sub_session_deleted", Conv.BOOLEAN, kybSubSessionDeleted); + } + + /** + * Type of the workflow the child session runs. + * + * @param verificationWorkflowType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verificationWorkflowType(KYBKeyPeopleParty.@Nullable VerificationWorkflowType verificationWorkflowType) { + return setNullable("verification_workflow_type", Conv.enumeration(KYBKeyPeopleParty.VerificationWorkflowType::of), verificationWorkflowType); + } + + /** + * Workflow the child session runs. + * + * @param verificationWorkflowId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verificationWorkflowId(@Nullable String verificationWorkflowId) { + return setNullable("verification_workflow_id", Conv.STRING, verificationWorkflowId); + } + + /** + * Label of that workflow. + * + * @param verificationWorkflowLabel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verificationWorkflowLabel(@Nullable String verificationWorkflowLabel) { + return setNullable("verification_workflow_label", Conv.STRING, verificationWorkflowLabel); + } + + /** + * Features of the child session's workflow with their current status. + * + * @param verificationWorkflowFeatures the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verificationWorkflowFeatures(@Nullable List verificationWorkflowFeatures) { + return setNullable("verification_workflow_features", Conv.list(Conv.model(KYBKeyPeopleParty.VerificationWorkflowFeatures::fromJson)), verificationWorkflowFeatures); + } + + /** + * Didit's identifier of the verified person, once known. + * + * @param diditInternalId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder diditInternalId(@Nullable String diditInternalId) { + return setNullable("didit_internal_id", Conv.STRING, diditInternalId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code KYBKeyPeopleParty} + * @throws IllegalStateException when a required field is missing + */ + public KYBKeyPeopleParty build() { + require("uuid", false); + require("entity_type", false); + require("source", false); + require("name", false); + require("is_active", false); + require("is_skipped", false); + require("requires_verification", false); + require("roles", false); + require("kyc_session_deleted", false); + require("kyb_sub_session_deleted", false); + return new KYBKeyPeopleParty(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Whether the party is an individual or a company. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EntityType extends StringEnum { + /** The value {@code "person"}. */ + public static final EntityType PERSON = new EntityType("person"); + + /** The value {@code "company"}. */ + public static final EntityType COMPANY = new EntityType("company"); + + private static final List KNOWN_VALUES = List.of(PERSON, COMPANY); + + private EntityType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EntityType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EntityType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * REGISTRY: taken from the company registry. USER: entered in the Key People screen. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "REGISTRY"}. */ + public static final Source REGISTRY = new Source("REGISTRY"); + + /** The value {@code "USER"}. */ + public static final Source USER = new Source("USER"); + + private static final List KNOWN_VALUES = List.of(REGISTRY, USER); + + private Source(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class Roles extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("role", "ownership_percent", "voting_percent"); + + private Roles(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Roles} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Roles fromJson(JsonNode json) { + return new Roles(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Role code. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public KYBKeyPeopleParty.Roles.Role role() { + return Json.required(json, "role", Conv.enumeration(KYBKeyPeopleParty.Roles.Role::of)); + } + + /** + * Ownership percentage for this role, when known. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ownershipPercent() { + return Json.optional(json, "ownership_percent", Conv.DOUBLE); + } + + /** + * Voting-rights percentage for this role, when known. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional votingPercent() { + return Json.optional(json, "voting_percent", Conv.DOUBLE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Roles}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Roles from) { + super(from); + } + + /** + * Role code. + * + * @param role the value + * @return this builder + */ + public Builder role(KYBKeyPeopleParty.Roles.Role role) { + return set("role", Conv.enumeration(KYBKeyPeopleParty.Roles.Role::of), role); + } + + /** + * Ownership percentage for this role, when known. + * + * @param ownershipPercent the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ownershipPercent(@Nullable Double ownershipPercent) { + return setNullable("ownership_percent", Conv.DOUBLE, ownershipPercent); + } + + /** + * Voting-rights percentage for this role, when known. + * + * @param votingPercent the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder votingPercent(@Nullable Double votingPercent) { + return setNullable("voting_percent", Conv.DOUBLE, votingPercent); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Roles} + * @throws IllegalStateException when a required field is missing + */ + public Roles build() { + require("role", false); + require("ownership_percent", true); + require("voting_percent", true); + return new Roles(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Role code. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Role extends StringEnum { + /** The value {@code "director"}. */ + public static final Role DIRECTOR = new Role("director"); + + /** The value {@code "non_executive_director"}. */ + public static final Role NON_EXECUTIVE_DIRECTOR = new Role("non_executive_director"); + + /** The value {@code "secretary"}. */ + public static final Role SECRETARY = new Role("secretary"); + + /** The value {@code "chairman"}. */ + public static final Role CHAIRMAN = new Role("chairman"); + + /** The value {@code "ubo"}. */ + public static final Role UBO = new Role("ubo"); + + /** The value {@code "shareholder"}. */ + public static final Role SHAREHOLDER = new Role("shareholder"); + + /** The value {@code "representative"}. */ + public static final Role REPRESENTATIVE = new Role("representative"); + + /** The value {@code "founder"}. */ + public static final Role FOUNDER = new Role("founder"); + + /** The value {@code "legal_advisor"}. */ + public static final Role LEGAL_ADVISOR = new Role("legal_advisor"); + + /** The value {@code "authorized_signatory"}. */ + public static final Role AUTHORIZED_SIGNATORY = new Role("authorized_signatory"); + + /** The value {@code "trustee"}. */ + public static final Role TRUSTEE = new Role("trustee"); + + /** The value {@code "beneficiary"}. */ + public static final Role BENEFICIARY = new Role("beneficiary"); + + /** The value {@code "settlor"}. */ + public static final Role SETTLOR = new Role("settlor"); + + /** The value {@code "protector"}. */ + public static final Role PROTECTOR = new Role("protector"); + + /** The value {@code "company_officer"}. */ + public static final Role COMPANY_OFFICER = new Role("company_officer"); + + /** The value {@code "investor"}. */ + public static final Role INVESTOR = new Role("investor"); + + /** The value {@code "other"}. */ + public static final Role OTHER = new Role("other"); + + private static final List KNOWN_VALUES = List.of(DIRECTOR, NON_EXECUTIVE_DIRECTOR, SECRETARY, CHAIRMAN, UBO, SHAREHOLDER, REPRESENTATIVE, FOUNDER, LEGAL_ADVISOR, AUTHORIZED_SIGNATORY, TRUSTEE, BENEFICIARY, SETTLOR, PROTECTOR, COMPANY_OFFICER, INVESTOR, OTHER); + + private Role(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Role of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Role::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * Status of the child KYC session. A deleted session keeps the status it had when it was deleted. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class KycSessionStatus extends StringEnum { + /** The value {@code "Not Started"}. */ + public static final KycSessionStatus NOT_STARTED = new KycSessionStatus("Not Started"); + + /** The value {@code "In Progress"}. */ + public static final KycSessionStatus IN_PROGRESS = new KycSessionStatus("In Progress"); + + /** The value {@code "Approved"}. */ + public static final KycSessionStatus APPROVED = new KycSessionStatus("Approved"); + + /** The value {@code "Declined"}. */ + public static final KycSessionStatus DECLINED = new KycSessionStatus("Declined"); + + /** The value {@code "In Review"}. */ + public static final KycSessionStatus IN_REVIEW = new KycSessionStatus("In Review"); + + /** The value {@code "Expired"}. */ + public static final KycSessionStatus EXPIRED = new KycSessionStatus("Expired"); + + /** The value {@code "Abandoned"}. */ + public static final KycSessionStatus ABANDONED = new KycSessionStatus("Abandoned"); + + /** The value {@code "Kyc Expired"}. */ + public static final KycSessionStatus KYC_EXPIRED = new KycSessionStatus("Kyc Expired"); + + /** The value {@code "Resubmitted"}. */ + public static final KycSessionStatus RESUBMITTED = new KycSessionStatus("Resubmitted"); + + /** The value {@code "Awaiting User"}. */ + public static final KycSessionStatus AWAITING_USER = new KycSessionStatus("Awaiting User"); + + private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED, KYC_EXPIRED, RESUBMITTED, AWAITING_USER); + + private KycSessionStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static KycSessionStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, KycSessionStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Status of the child KYB session. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class KybSubSessionStatus extends StringEnum { + /** The value {@code "Not Started"}. */ + public static final KybSubSessionStatus NOT_STARTED = new KybSubSessionStatus("Not Started"); + + /** The value {@code "In Progress"}. */ + public static final KybSubSessionStatus IN_PROGRESS = new KybSubSessionStatus("In Progress"); + + /** The value {@code "Approved"}. */ + public static final KybSubSessionStatus APPROVED = new KybSubSessionStatus("Approved"); + + /** The value {@code "Declined"}. */ + public static final KybSubSessionStatus DECLINED = new KybSubSessionStatus("Declined"); + + /** The value {@code "In Review"}. */ + public static final KybSubSessionStatus IN_REVIEW = new KybSubSessionStatus("In Review"); + + /** The value {@code "Expired"}. */ + public static final KybSubSessionStatus EXPIRED = new KybSubSessionStatus("Expired"); + + /** The value {@code "Abandoned"}. */ + public static final KybSubSessionStatus ABANDONED = new KybSubSessionStatus("Abandoned"); + + /** The value {@code "Kyc Expired"}. */ + public static final KybSubSessionStatus KYC_EXPIRED = new KybSubSessionStatus("Kyc Expired"); + + /** The value {@code "Resubmitted"}. */ + public static final KybSubSessionStatus RESUBMITTED = new KybSubSessionStatus("Resubmitted"); + + /** The value {@code "Awaiting User"}. */ + public static final KybSubSessionStatus AWAITING_USER = new KybSubSessionStatus("Awaiting User"); + + private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED, KYC_EXPIRED, RESUBMITTED, AWAITING_USER); + + private KybSubSessionStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static KybSubSessionStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, KybSubSessionStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Type of the workflow the child session runs. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class VerificationWorkflowType extends StringEnum { + /** The value {@code "kyc"}. */ + public static final VerificationWorkflowType KYC = new VerificationWorkflowType("kyc"); + + /** The value {@code "kyb"}. */ + public static final VerificationWorkflowType KYB = new VerificationWorkflowType("kyb"); + + /** The value {@code "adaptive_age_verification"}. */ + public static final VerificationWorkflowType ADAPTIVE_AGE_VERIFICATION = new VerificationWorkflowType("adaptive_age_verification"); + + /** The value {@code "biometric_authentication"}. */ + public static final VerificationWorkflowType BIOMETRIC_AUTHENTICATION = new VerificationWorkflowType("biometric_authentication"); + + /** The value {@code "address_verification"}. */ + public static final VerificationWorkflowType ADDRESS_VERIFICATION = new VerificationWorkflowType("address_verification"); + + /** The value {@code "questionnaire_verification"}. */ + public static final VerificationWorkflowType QUESTIONNAIRE_VERIFICATION = new VerificationWorkflowType("questionnaire_verification"); + + /** The value {@code "email_verification"}. */ + public static final VerificationWorkflowType EMAIL_VERIFICATION = new VerificationWorkflowType("email_verification"); + + /** The value {@code "phone_verification"}. */ + public static final VerificationWorkflowType PHONE_VERIFICATION = new VerificationWorkflowType("phone_verification"); + + private static final List KNOWN_VALUES = List.of(KYC, KYB, ADAPTIVE_AGE_VERIFICATION, BIOMETRIC_AUTHENTICATION, ADDRESS_VERIFICATION, QUESTIONNAIRE_VERIFICATION, EMAIL_VERIFICATION, PHONE_VERIFICATION); + + private VerificationWorkflowType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static VerificationWorkflowType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, VerificationWorkflowType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class VerificationWorkflowFeatures extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("feature", "status"); + + private VerificationWorkflowFeatures(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code VerificationWorkflowFeatures} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static VerificationWorkflowFeatures fromJson(JsonNode json) { + return new VerificationWorkflowFeatures(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Feature code, for example ID_VERIFICATION or LIVENESS. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String feature() { + return Json.required(json, "feature", Conv.STRING); + } + + /** + * Feature status, Not Finished until it runs. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String status() { + return Json.required(json, "status", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link VerificationWorkflowFeatures}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(VerificationWorkflowFeatures from) { + super(from); + } + + /** + * Feature code, for example ID_VERIFICATION or LIVENESS. + * + * @param feature the value + * @return this builder + */ + public Builder feature(String feature) { + return set("feature", Conv.STRING, feature); + } + + /** + * Feature status, Not Finished until it runs. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * Builds the value. + * + * @return a new immutable {@code VerificationWorkflowFeatures} + * @throws IllegalStateException when a required field is missing + */ + public VerificationWorkflowFeatures build() { + require("feature", false); + require("status", false); + return new VerificationWorkflowFeatures(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/KybListUbosParams.java b/src/main/java/me/didit/types/KYBRegistryCandidateRequest.java similarity index 86% rename from src/main/java/me/didit/types/KybListUbosParams.java rename to src/main/java/me/didit/types/KYBRegistryCandidateRequest.java index 2bcd42a..0c37b8e 100644 --- a/src/main/java/me/didit/types/KybListUbosParams.java +++ b/src/main/java/me/didit/types/KYBRegistryCandidateRequest.java @@ -13,23 +13,22 @@ import me.didit.core.JsonModel; import org.jspecify.annotations.Nullable; -/** Parameters for {@code kyb.list_ubos}. */ -public final class KybListUbosParams extends JsonModel { +public final class KYBRegistryCandidateRequest extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("kyb_response_id", "vendor_data", "metadata", "save_api_request"); - private KybListUbosParams(ObjectNode json, Map> files) { + private KYBRegistryCandidateRequest(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code KybListUbosParams} from JSON. Unknown fields are kept and readable + * Reads a {@code KYBRegistryCandidateRequest} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static KybListUbosParams fromJson(JsonNode json) { - return new KybListUbosParams(Json.copyObject(json), Map.of()); + public static KYBRegistryCandidateRequest fromJson(JsonNode json) { + return new KYBRegistryCandidateRequest(Json.copyObject(json), Map.of()); } /** @@ -95,11 +94,11 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link KybListUbosParams}. */ + /** Builds a {@link KYBRegistryCandidateRequest}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(KybListUbosParams from) { + private Builder(KYBRegistryCandidateRequest from) { super(from); } @@ -146,12 +145,12 @@ public Builder saveApiRequest(boolean saveApiRequest) { /** * Builds the value. * - * @return a new immutable {@code KybListUbosParams} + * @return a new immutable {@code KYBRegistryCandidateRequest} * @throws IllegalStateException when a required field is missing */ - public KybListUbosParams build() { + public KYBRegistryCandidateRequest build() { require("kyb_response_id", false); - return new KybListUbosParams(buildJson(), buildFiles()); + return new KYBRegistryCandidateRequest(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/KYBRegistrySearchResolvedCallback.java b/src/main/java/me/didit/types/KYBRegistrySearchResolvedCallback.java new file mode 100644 index 0000000..8c09d99 --- /dev/null +++ b/src/main/java/me/didit/types/KYBRegistrySearchResolvedCallback.java @@ -0,0 +1,334 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Body of the callback sent to the webhook_url of POST /v3/kyb/search/ once the registry search resolves. It is not signed: the request carries X-Didit-Unsigned-Callback: true and none of the signature headers, so confirm it by matching request_id against a search you started. Uses event_type, not webhook_type. + */ +public final class KYBRegistrySearchResolvedCallback extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "event_type", "request_id", "vendor_data", "metadata", "search_status", "search_resolved", "kyb_registry", "timestamp", "created_at"); + + private KYBRegistrySearchResolvedCallback(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code KYBRegistrySearchResolvedCallback} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static KYBRegistrySearchResolvedCallback fromJson(JsonNode json) { + return new KYBRegistrySearchResolvedCallback(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the callback. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Always kyb.registry_search.resolved. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public KYBRegistrySearchResolvedCallback.EventType eventType() { + return Json.required(json, "event_type", Conv.enumeration(KYBRegistrySearchResolvedCallback.EventType::of)); + } + + /** + * The request_id returned by POST /v3/kyb/search/. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String requestId() { + return Json.required(json, "request_id", Conv.STRING); + } + + /** + * Echo of the vendor_data you sent. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * Echo of the metadata you sent. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> metadata() { + return Json.optional(json, "metadata", Conv.OBJECT); + } + + /** + * Always resolved today. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String searchStatus() { + return Json.required(json, "search_status", Conv.STRING); + } + + /** + * true when search_status is resolved. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean searchResolved() { + return Json.required(json, "search_resolved", Conv.BOOLEAN); + } + + /** + * The kyb_registry field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public KYBRegistrySearchResults kybRegistry() { + return Json.required(json, "kyb_registry", Conv.model(KYBRegistrySearchResults::fromJson)); + } + + /** + * Unix time in seconds at which the callback was queued. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds at which the callback was queued. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link KYBRegistrySearchResolvedCallback}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(KYBRegistrySearchResolvedCallback from) { + super(from); + } + + /** + * Identifier of the callback. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Always kyb.registry_search.resolved. + * + * @param eventType the value + * @return this builder + */ + public Builder eventType(KYBRegistrySearchResolvedCallback.EventType eventType) { + return set("event_type", Conv.enumeration(KYBRegistrySearchResolvedCallback.EventType::of), eventType); + } + + /** + * The request_id returned by POST /v3/kyb/search/. + * + * @param requestId the value + * @return this builder + */ + public Builder requestId(String requestId) { + return set("request_id", Conv.STRING, requestId); + } + + /** + * Echo of the vendor_data you sent. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * Echo of the metadata you sent. + * + * @param metadata the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder metadata(@Nullable Map metadata) { + return setAny("metadata", metadata); + } + + /** + * Always resolved today. + * + * @param searchStatus the value + * @return this builder + */ + public Builder searchStatus(String searchStatus) { + return set("search_status", Conv.STRING, searchStatus); + } + + /** + * true when search_status is resolved. + * + * @param searchResolved the value + * @return this builder + */ + public Builder searchResolved(boolean searchResolved) { + return set("search_resolved", Conv.BOOLEAN, searchResolved); + } + + /** + * The kyb_registry field. + * + * @param kybRegistry the value + * @return this builder + */ + public Builder kybRegistry(KYBRegistrySearchResults kybRegistry) { + return set("kyb_registry", Conv.model(KYBRegistrySearchResults::fromJson), kybRegistry); + } + + /** + * Unix time in seconds at which the callback was queued. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds at which the callback was queued. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code KYBRegistrySearchResolvedCallback} + * @throws IllegalStateException when a required field is missing + */ + public KYBRegistrySearchResolvedCallback build() { + require("event_id", false); + require("event_type", false); + require("request_id", false); + require("vendor_data", true); + require("metadata", true); + require("search_status", false); + require("search_resolved", false); + require("kyb_registry", false); + require("timestamp", false); + require("created_at", false); + return new KYBRegistrySearchResolvedCallback(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always kyb.registry_search.resolved. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EventType extends StringEnum { + /** The value {@code "kyb.registry_search.resolved"}. */ + public static final EventType KYB_REGISTRY_SEARCH_RESOLVED = new EventType("kyb.registry_search.resolved"); + + private static final List KNOWN_VALUES = List.of(KYB_REGISTRY_SEARCH_RESOLVED); + + private EventType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EventType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EventType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/KYBRegistrySearchResults.java b/src/main/java/me/didit/types/KYBRegistrySearchResults.java new file mode 100644 index 0000000..2168ebb --- /dev/null +++ b/src/main/java/me/didit/types/KYBRegistrySearchResults.java @@ -0,0 +1,553 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class KYBRegistrySearchResults extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("companies", "pagination", "search_status", "search_resolved"); + + private KYBRegistrySearchResults(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code KYBRegistrySearchResults} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static KYBRegistrySearchResults fromJson(JsonNode json) { + return new KYBRegistrySearchResults(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Deduplicated candidate list. Empty while search_resolved=false (webhook mode) or when nothing matched. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> companies() { + return Json.optional(json, "companies", Conv.list(Conv.model(KYBRegistrySearchResults.Companies::fromJson))); + } + + /** + * The pagination field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional pagination() { + return Json.optional(json, "pagination", Conv.model(KYBRegistrySearchResults.Pagination::fromJson)); + } + + /** + * Registry search lifecycle status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional searchStatus() { + return Json.optional(json, "search_status", Conv.enumeration(KYBRegistrySearchResults.SearchStatus::of)); + } + + /** + * true once the candidate list is final. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional searchResolved() { + return Json.optional(json, "search_resolved", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link KYBRegistrySearchResults}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(KYBRegistrySearchResults from) { + super(from); + } + + /** + * Deduplicated candidate list. Empty while search_resolved=false (webhook mode) or when nothing matched. + * + * @param companies the value + * @return this builder + */ + public Builder companies(List companies) { + return set("companies", Conv.list(Conv.model(KYBRegistrySearchResults.Companies::fromJson)), companies); + } + + /** + * The pagination field. + * + * @param pagination the value + * @return this builder + */ + public Builder pagination(KYBRegistrySearchResults.Pagination pagination) { + return set("pagination", Conv.model(KYBRegistrySearchResults.Pagination::fromJson), pagination); + } + + /** + * Registry search lifecycle status. + * + * @param searchStatus the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder searchStatus(KYBRegistrySearchResults.@Nullable SearchStatus searchStatus) { + return setNullable("search_status", Conv.enumeration(KYBRegistrySearchResults.SearchStatus::of), searchStatus); + } + + /** + * true once the candidate list is final. + * + * @param searchResolved the value + * @return this builder + */ + public Builder searchResolved(boolean searchResolved) { + return set("search_resolved", Conv.BOOLEAN, searchResolved); + } + + /** + * Builds the value. + * + * @return a new immutable {@code KYBRegistrySearchResults} + */ + public KYBRegistrySearchResults build() { + return new KYBRegistrySearchResults(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Companies extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("kyb_response_id", "name", "registration_number", "status", "type", "registered_address", "risk_level", "fetch_status"); + + private Companies(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Companies} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Companies fromJson(JsonNode json) { + return new Companies(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Handle for this candidate — pass it to POST /v3/kyb/select/ to retrieve the full profile. Ephemeral and scoped to this search: do not store it long-term; re-search to get a fresh one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kybResponseId() { + return Json.optional(json, "kyb_response_id", Conv.STRING); + } + + /** + * Registered company name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * Registry registration number. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional registrationNumber() { + return Json.optional(json, "registration_number", Conv.STRING); + } + + /** + * Normalized registry status of the company (e.g. active, dissolved, inactive, struck off). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * Legal form as reported by the registry. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional type() { + return Json.optional(json, "type", Conv.STRING); + } + + /** + * Registered address of the candidate, when the registry returns one. Use it, with the registration number, to tell same-named companies apart: in the United States a candidate can be listed under the state where the company operates rather than the state you searched. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional registeredAddress() { + return Json.optional(json, "registered_address", Conv.STRING); + } + + /** + * Registry-reported risk level, when available. Usually null at search time. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional riskLevel() { + return Json.optional(json, "risk_level", Conv.STRING); + } + + /** + * Always pending for search candidates — the full company profile is only fetched (and billed) when you select the candidate. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fetchStatus() { + return Json.optional(json, "fetch_status", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Companies}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Companies from) { + super(from); + } + + /** + * Handle for this candidate — pass it to POST /v3/kyb/select/ to retrieve the full profile. Ephemeral and scoped to this search: do not store it long-term; re-search to get a fresh one. + * + * @param kybResponseId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder kybResponseId(@Nullable String kybResponseId) { + return setNullable("kyb_response_id", Conv.STRING, kybResponseId); + } + + /** + * Registered company name. + * + * @param name the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder name(@Nullable String name) { + return setNullable("name", Conv.STRING, name); + } + + /** + * Registry registration number. + * + * @param registrationNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder registrationNumber(@Nullable String registrationNumber) { + return setNullable("registration_number", Conv.STRING, registrationNumber); + } + + /** + * Normalized registry status of the company (e.g. active, dissolved, inactive, struck off). + * + * @param status the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder status(@Nullable String status) { + return setNullable("status", Conv.STRING, status); + } + + /** + * Legal form as reported by the registry. + * + * @param type the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder type(@Nullable String type) { + return setNullable("type", Conv.STRING, type); + } + + /** + * Registered address of the candidate, when the registry returns one. Use it, with the registration number, to tell same-named companies apart: in the United States a candidate can be listed under the state where the company operates rather than the state you searched. + * + * @param registeredAddress the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder registeredAddress(@Nullable String registeredAddress) { + return setNullable("registered_address", Conv.STRING, registeredAddress); + } + + /** + * Registry-reported risk level, when available. Usually null at search time. + * + * @param riskLevel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder riskLevel(@Nullable String riskLevel) { + return setNullable("risk_level", Conv.STRING, riskLevel); + } + + /** + * Always pending for search candidates — the full company profile is only fetched (and billed) when you select the candidate. + * + * @param fetchStatus the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fetchStatus(@Nullable String fetchStatus) { + return setNullable("fetch_status", Conv.STRING, fetchStatus); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Companies} + */ + public Companies build() { + return new Companies(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Pagination extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("total", "page", "per_page"); + + private Pagination(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Pagination} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Pagination fromJson(JsonNode json) { + return new Pagination(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Number of candidates returned. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional total() { + return Json.optional(json, "total", Conv.LONG); + } + + /** + * The page field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional page() { + return Json.optional(json, "page", Conv.LONG); + } + + /** + * The per_page field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional perPage() { + return Json.optional(json, "per_page", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Pagination}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Pagination from) { + super(from); + } + + /** + * Number of candidates returned. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * The page field. + * + * @param page the value + * @return this builder + */ + public Builder page(long page) { + return set("page", Conv.LONG, page); + } + + /** + * The per_page field. + * + * @param perPage the value + * @return this builder + */ + public Builder perPage(long perPage) { + return set("per_page", Conv.LONG, perPage); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Pagination} + */ + public Pagination build() { + return new Pagination(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * Registry search lifecycle status. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SearchStatus extends StringEnum { + /** The value {@code "pending"}. */ + public static final SearchStatus PENDING = new SearchStatus("pending"); + + /** The value {@code "resolved"}. */ + public static final SearchStatus RESOLVED = new SearchStatus("resolved"); + + private static final List KNOWN_VALUES = List.of(PENDING, RESOLVED); + + private SearchStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SearchStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SearchStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/KybListShareholdersParams.java b/src/main/java/me/didit/types/KybListShareholdersParams.java deleted file mode 100644 index 070f33e..0000000 --- a/src/main/java/me/didit/types/KybListShareholdersParams.java +++ /dev/null @@ -1,162 +0,0 @@ -// Code generated by codegen from openapi/*.json. DO NOT EDIT. -package me.didit.types; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; -import org.jspecify.annotations.Nullable; - -/** Parameters for {@code kyb.list_shareholders}. */ -public final class KybListShareholdersParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("kyb_response_id", "vendor_data", "metadata", "save_api_request"); - - private KybListShareholdersParams(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code KybListShareholdersParams} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static KybListShareholdersParams fromJson(JsonNode json) { - return new KybListShareholdersParams(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Candidate handle from POST /v3/kyb/search/ (kyb_registry.companies[].kyb_response_id). Ephemeral — use it shortly after the search that produced it. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String kybResponseId() { - return Json.required(json, "kyb_response_id", Conv.STRING); - } - - /** - * Your identifier for this company; echoed back and stored with the business session. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); - } - - /** - * Free-form JSON stored with the business session and echoed back. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> metadata() { - return Json.optional(json, "metadata", Conv.OBJECT); - } - - /** - * Must be true (the default). KYB registry selects are always persisted so billing and retrieval can be tracked; false returns 400. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional saveApiRequest() { - return Json.optional(json, "save_api_request", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link KybListShareholdersParams}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(KybListShareholdersParams from) { - super(from); - } - - /** - * Candidate handle from POST /v3/kyb/search/ (kyb_registry.companies[].kyb_response_id). Ephemeral — use it shortly after the search that produced it. - * - * @param kybResponseId the value - * @return this builder - */ - public Builder kybResponseId(String kybResponseId) { - return set("kyb_response_id", Conv.STRING, kybResponseId); - } - - /** - * Your identifier for this company; echoed back and stored with the business session. - * - * @param vendorData the value - * @return this builder - */ - public Builder vendorData(String vendorData) { - return set("vendor_data", Conv.STRING, vendorData); - } - - /** - * Free-form JSON stored with the business session and echoed back. - * - * @param metadata the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder metadata(@Nullable Map metadata) { - return setAny("metadata", metadata); - } - - /** - * Must be true (the default). KYB registry selects are always persisted so billing and retrieval can be tracked; false returns 400. - * - * @param saveApiRequest the value - * @return this builder - */ - public Builder saveApiRequest(boolean saveApiRequest) { - return set("save_api_request", Conv.BOOLEAN, saveApiRequest); - } - - /** - * Builds the value. - * - * @return a new immutable {@code KybListShareholdersParams} - * @throws IllegalStateException when a required field is missing - */ - public KybListShareholdersParams build() { - require("kyb_response_id", false); - return new KybListShareholdersParams(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } -} diff --git a/src/main/java/me/didit/types/KybSearchParams.java b/src/main/java/me/didit/types/KybSearchParams.java index 1fc7cf8..41df603 100644 --- a/src/main/java/me/didit/types/KybSearchParams.java +++ b/src/main/java/me/didit/types/KybSearchParams.java @@ -16,7 +16,7 @@ /** Parameters for {@code kyb.search}. */ public final class KybSearchParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("country_code", "name", "registration_number", "search_type", "vendor_data", "metadata", "webhook_url"); + private static final Set KNOWN_FIELDS = Set.of("country_code", "name", "registration_number", "tier", "search_type", "vendor_data", "metadata", "webhook_url"); private KybSearchParams(ObjectNode json, Map> files) { super(json, files); @@ -82,7 +82,17 @@ public Optional registrationNumber() { } /** - * Name-matching strategy applied by the registry search. + * Registry tier whose provider chain this search runs on. basic (default) searches the country's Lite chain; shareholders and ubo search that tier's ownership chain, which can be a different source, so an empty Lite search does not prove the registry lacks the company. A country that does not offer the named tier is searched at the highest tier it offers up to that one. Retrieve the candidate from the endpoint that sells the tier you searched: POST /v3/kyb/select/ (Lite), POST /v3/kyb/shareholders/ or POST /v3/kyb/ubo/. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional tier() { + return Json.optional(json, "tier", Conv.enumeration(KybSearchParams.Tier::of)); + } + + /** + * Name-matching strategy applied by the registry search. contains is the only accepted value: no registry source behind the search offers a prefix or fuzzy mode, so start_with and fuzzy are rejected with 400 (not a valid choice). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -165,7 +175,17 @@ public Builder registrationNumber(String registrationNumber) { } /** - * Name-matching strategy applied by the registry search. + * Registry tier whose provider chain this search runs on. basic (default) searches the country's Lite chain; shareholders and ubo search that tier's ownership chain, which can be a different source, so an empty Lite search does not prove the registry lacks the company. A country that does not offer the named tier is searched at the highest tier it offers up to that one. Retrieve the candidate from the endpoint that sells the tier you searched: POST /v3/kyb/select/ (Lite), POST /v3/kyb/shareholders/ or POST /v3/kyb/ubo/. + * + * @param tier the value + * @return this builder + */ + public Builder tier(KybSearchParams.Tier tier) { + return set("tier", Conv.enumeration(KybSearchParams.Tier::of), tier); + } + + /** + * Name-matching strategy applied by the registry search. contains is the only accepted value: no registry source behind the search offers a prefix or fuzzy mode, so start_with and fuzzy are rejected with 400 (not a valid choice). * * @param searchType the value * @return this builder @@ -222,7 +242,53 @@ protected Builder self() { } /** - * Name-matching strategy applied by the registry search. + * Registry tier whose provider chain this search runs on. basic (default) searches the country's Lite chain; shareholders and ubo search that tier's ownership chain, which can be a different source, so an empty Lite search does not prove the registry lacks the company. A country that does not offer the named tier is searched at the highest tier it offers up to that one. Retrieve the candidate from the endpoint that sells the tier you searched: POST /v3/kyb/select/ (Lite), POST /v3/kyb/shareholders/ or POST /v3/kyb/ubo/. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Tier extends StringEnum { + /** The value {@code "basic"}. */ + public static final Tier BASIC = new Tier("basic"); + + /** The value {@code "shareholders"}. */ + public static final Tier SHAREHOLDERS = new Tier("shareholders"); + + /** The value {@code "ubo"}. */ + public static final Tier UBO = new Tier("ubo"); + + private static final List KNOWN_VALUES = List.of(BASIC, SHAREHOLDERS, UBO); + + private Tier(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Tier of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Tier::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Name-matching strategy applied by the registry search. contains is the only accepted value: no registry source behind the search offers a prefix or fuzzy mode, so start_with and fuzzy are rejected with 400 (not a valid choice). * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -230,13 +296,7 @@ public static final class SearchType extends StringEnum { /** The value {@code "contains"}. */ public static final SearchType CONTAINS = new SearchType("contains"); - /** The value {@code "start_with"}. */ - public static final SearchType START_WITH = new SearchType("start_with"); - - /** The value {@code "fuzzy"}. */ - public static final SearchType FUZZY = new SearchType("fuzzy"); - - private static final List KNOWN_VALUES = List.of(CONTAINS, START_WITH, FUZZY); + private static final List KNOWN_VALUES = List.of(CONTAINS); private SearchType(String value) { super(value); diff --git a/src/main/java/me/didit/types/KybSearchResponse.java b/src/main/java/me/didit/types/KybSearchResponse.java index 1eea28c..4dd991e 100644 --- a/src/main/java/me/didit/types/KybSearchResponse.java +++ b/src/main/java/me/didit/types/KybSearchResponse.java @@ -12,7 +12,6 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; -import me.didit.core.StringEnum; import org.jspecify.annotations.Nullable; public final class KybSearchResponse extends JsonModel { @@ -67,8 +66,8 @@ public Optional requestId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional kybRegistry() { - return Json.optional(json, "kyb_registry", Conv.model(KybSearchResponse.KybRegistry::fromJson)); + public Optional kybRegistry() { + return Json.optional(json, "kyb_registry", Conv.model(KYBRegistrySearchResults::fromJson)); } /** @@ -130,8 +129,8 @@ public Builder requestId(String requestId) { * @param kybRegistry the value * @return this builder */ - public Builder kybRegistry(KybSearchResponse.KybRegistry kybRegistry) { - return set("kyb_registry", Conv.model(KybSearchResponse.KybRegistry::fromJson), kybRegistry); + public Builder kybRegistry(KYBRegistrySearchResults kybRegistry) { + return set("kyb_registry", Conv.model(KYBRegistrySearchResults::fromJson), kybRegistry); } /** @@ -178,522 +177,4 @@ protected Builder self() { return this; } } - - public static final class KybRegistry extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("companies", "pagination", "search_status", "search_resolved"); - - private KybRegistry(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code KybRegistry} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static KybRegistry fromJson(JsonNode json) { - return new KybRegistry(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Deduplicated candidate list. Empty while search_resolved=false (webhook mode) or when nothing matched. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> companies() { - return Json.optional(json, "companies", Conv.list(Conv.model(KybSearchResponse.KybRegistry.Companies::fromJson))); - } - - /** - * The pagination field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional pagination() { - return Json.optional(json, "pagination", Conv.model(KybSearchResponse.KybRegistry.Pagination::fromJson)); - } - - /** - * Registry search lifecycle status. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional searchStatus() { - return Json.optional(json, "search_status", Conv.enumeration(KybSearchResponse.KybRegistry.SearchStatus::of)); - } - - /** - * true once the candidate list is final. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional searchResolved() { - return Json.optional(json, "search_resolved", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link KybRegistry}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(KybRegistry from) { - super(from); - } - - /** - * Deduplicated candidate list. Empty while search_resolved=false (webhook mode) or when nothing matched. - * - * @param companies the value - * @return this builder - */ - public Builder companies(List companies) { - return set("companies", Conv.list(Conv.model(KybSearchResponse.KybRegistry.Companies::fromJson)), companies); - } - - /** - * The pagination field. - * - * @param pagination the value - * @return this builder - */ - public Builder pagination(KybSearchResponse.KybRegistry.Pagination pagination) { - return set("pagination", Conv.model(KybSearchResponse.KybRegistry.Pagination::fromJson), pagination); - } - - /** - * Registry search lifecycle status. - * - * @param searchStatus the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder searchStatus(KybSearchResponse.KybRegistry.@Nullable SearchStatus searchStatus) { - return setNullable("search_status", Conv.enumeration(KybSearchResponse.KybRegistry.SearchStatus::of), searchStatus); - } - - /** - * true once the candidate list is final. - * - * @param searchResolved the value - * @return this builder - */ - public Builder searchResolved(boolean searchResolved) { - return set("search_resolved", Conv.BOOLEAN, searchResolved); - } - - /** - * Builds the value. - * - * @return a new immutable {@code KybRegistry} - */ - public KybRegistry build() { - return new KybRegistry(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - public static final class Companies extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("kyb_response_id", "name", "registration_number", "status", "type", "risk_level", "fetch_status"); - - private Companies(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Companies} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Companies fromJson(JsonNode json) { - return new Companies(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Handle for this candidate — pass it to POST /v3/kyb/select/ to retrieve the full profile. Ephemeral and scoped to this search: do not store it long-term; re-search to get a fresh one. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional kybResponseId() { - return Json.optional(json, "kyb_response_id", Conv.STRING); - } - - /** - * Registered company name. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional name() { - return Json.optional(json, "name", Conv.STRING); - } - - /** - * Registry registration number. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional registrationNumber() { - return Json.optional(json, "registration_number", Conv.STRING); - } - - /** - * Normalized registry status of the company (e.g. active, dissolved, inactive, struck off). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.STRING); - } - - /** - * Legal form as reported by the registry. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional type() { - return Json.optional(json, "type", Conv.STRING); - } - - /** - * Registry-reported risk level, when available. Usually null at search time. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional riskLevel() { - return Json.optional(json, "risk_level", Conv.STRING); - } - - /** - * Always pending for search candidates — the full company profile is only fetched (and billed) when you select the candidate. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional fetchStatus() { - return Json.optional(json, "fetch_status", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Companies}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Companies from) { - super(from); - } - - /** - * Handle for this candidate — pass it to POST /v3/kyb/select/ to retrieve the full profile. Ephemeral and scoped to this search: do not store it long-term; re-search to get a fresh one. - * - * @param kybResponseId the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder kybResponseId(@Nullable String kybResponseId) { - return setNullable("kyb_response_id", Conv.STRING, kybResponseId); - } - - /** - * Registered company name. - * - * @param name the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder name(@Nullable String name) { - return setNullable("name", Conv.STRING, name); - } - - /** - * Registry registration number. - * - * @param registrationNumber the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder registrationNumber(@Nullable String registrationNumber) { - return setNullable("registration_number", Conv.STRING, registrationNumber); - } - - /** - * Normalized registry status of the company (e.g. active, dissolved, inactive, struck off). - * - * @param status the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder status(@Nullable String status) { - return setNullable("status", Conv.STRING, status); - } - - /** - * Legal form as reported by the registry. - * - * @param type the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder type(@Nullable String type) { - return setNullable("type", Conv.STRING, type); - } - - /** - * Registry-reported risk level, when available. Usually null at search time. - * - * @param riskLevel the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder riskLevel(@Nullable String riskLevel) { - return setNullable("risk_level", Conv.STRING, riskLevel); - } - - /** - * Always pending for search candidates — the full company profile is only fetched (and billed) when you select the candidate. - * - * @param fetchStatus the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder fetchStatus(@Nullable String fetchStatus) { - return setNullable("fetch_status", Conv.STRING, fetchStatus); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Companies} - */ - public Companies build() { - return new Companies(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Pagination extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("total", "page", "per_page"); - - private Pagination(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Pagination} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Pagination fromJson(JsonNode json) { - return new Pagination(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Number of candidates returned. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * The page field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional page() { - return Json.optional(json, "page", Conv.LONG); - } - - /** - * The per_page field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional perPage() { - return Json.optional(json, "per_page", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Pagination}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Pagination from) { - super(from); - } - - /** - * Number of candidates returned. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * The page field. - * - * @param page the value - * @return this builder - */ - public Builder page(long page) { - return set("page", Conv.LONG, page); - } - - /** - * The per_page field. - * - * @param perPage the value - * @return this builder - */ - public Builder perPage(long perPage) { - return set("per_page", Conv.LONG, perPage); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Pagination} - */ - public Pagination build() { - return new Pagination(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** - * Registry search lifecycle status. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class SearchStatus extends StringEnum { - /** The value {@code "pending"}. */ - public static final SearchStatus PENDING = new SearchStatus("pending"); - - /** The value {@code "resolved"}. */ - public static final SearchStatus RESOLVED = new SearchStatus("resolved"); - - private static final List KNOWN_VALUES = List.of(PENDING, RESOLVED); - - private SearchStatus(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static SearchStatus of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, SearchStatus::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } } diff --git a/src/main/java/me/didit/types/KybSelectParams.java b/src/main/java/me/didit/types/KybSelectParams.java deleted file mode 100644 index 2c11159..0000000 --- a/src/main/java/me/didit/types/KybSelectParams.java +++ /dev/null @@ -1,162 +0,0 @@ -// Code generated by codegen from openapi/*.json. DO NOT EDIT. -package me.didit.types; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; -import org.jspecify.annotations.Nullable; - -/** Parameters for {@code kyb.select}. */ -public final class KybSelectParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("kyb_response_id", "vendor_data", "metadata", "save_api_request"); - - private KybSelectParams(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code KybSelectParams} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static KybSelectParams fromJson(JsonNode json) { - return new KybSelectParams(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Candidate handle from POST /v3/kyb/search/ (kyb_registry.companies[].kyb_response_id). Ephemeral — use it shortly after the search that produced it. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String kybResponseId() { - return Json.required(json, "kyb_response_id", Conv.STRING); - } - - /** - * Your identifier for this company; echoed back and stored with the business session. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); - } - - /** - * Free-form JSON stored with the business session and echoed back. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> metadata() { - return Json.optional(json, "metadata", Conv.OBJECT); - } - - /** - * Must be true (the default). KYB registry selects are always persisted so billing and retrieval can be tracked; false returns 400. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional saveApiRequest() { - return Json.optional(json, "save_api_request", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link KybSelectParams}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(KybSelectParams from) { - super(from); - } - - /** - * Candidate handle from POST /v3/kyb/search/ (kyb_registry.companies[].kyb_response_id). Ephemeral — use it shortly after the search that produced it. - * - * @param kybResponseId the value - * @return this builder - */ - public Builder kybResponseId(String kybResponseId) { - return set("kyb_response_id", Conv.STRING, kybResponseId); - } - - /** - * Your identifier for this company; echoed back and stored with the business session. - * - * @param vendorData the value - * @return this builder - */ - public Builder vendorData(String vendorData) { - return set("vendor_data", Conv.STRING, vendorData); - } - - /** - * Free-form JSON stored with the business session and echoed back. - * - * @param metadata the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder metadata(@Nullable Map metadata) { - return setAny("metadata", metadata); - } - - /** - * Must be true (the default). KYB registry selects are always persisted so billing and retrieval can be tracked; false returns 400. - * - * @param saveApiRequest the value - * @return this builder - */ - public Builder saveApiRequest(boolean saveApiRequest) { - return set("save_api_request", Conv.BOOLEAN, saveApiRequest); - } - - /** - * Builds the value. - * - * @return a new immutable {@code KybSelectParams} - * @throws IllegalStateException when a required field is missing - */ - public KybSelectParams build() { - require("kyb_response_id", false); - return new KybSelectParams(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } -} diff --git a/src/main/java/me/didit/types/ListsCreateParams.java b/src/main/java/me/didit/types/ListsCreateParams.java new file mode 100644 index 0000000..af946b3 --- /dev/null +++ b/src/main/java/me/didit/types/ListsCreateParams.java @@ -0,0 +1,280 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code lists.create}. */ +public final class ListsCreateParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "description", "list_type", "entry_type"); + + private ListsCreateParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ListsCreateParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ListsCreateParams fromJson(JsonNode json) { + return new ListsCreateParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * List name. Must differ from the name of every other list of the application, system lists included. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String name() { + return Json.required(json, "name", Conv.STRING); + } + + /** + * Optional free-text description. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } + + /** + * allowlist or custom. blocklist is refused with 400. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ListsCreateParams.ListType listType() { + return Json.required(json, "list_type", Conv.enumeration(ListsCreateParams.ListType::of)); + } + + /** + * What the list holds: face, document (document number), phone, email, ip_address (IP address or CIDR range), device_fingerprint, wallet_address, bank_account, country (ISO 3166-1 alpha-3), user or business (your vendor_data), or key (any string). It decides how entry values are validated and matched. Cannot be changed later. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ListsCreateParams.EntryType entryType() { + return Json.required(json, "entry_type", Conv.enumeration(ListsCreateParams.EntryType::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ListsCreateParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ListsCreateParams from) { + super(from); + } + + /** + * List name. Must differ from the name of every other list of the application, system lists included. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Optional free-text description. + * + * @param description the value + * @return this builder + */ + public Builder description(String description) { + return set("description", Conv.STRING, description); + } + + /** + * allowlist or custom. blocklist is refused with 400. + * + * @param listType the value + * @return this builder + */ + public Builder listType(ListsCreateParams.ListType listType) { + return set("list_type", Conv.enumeration(ListsCreateParams.ListType::of), listType); + } + + /** + * What the list holds: face, document (document number), phone, email, ip_address (IP address or CIDR range), device_fingerprint, wallet_address, bank_account, country (ISO 3166-1 alpha-3), user or business (your vendor_data), or key (any string). It decides how entry values are validated and matched. Cannot be changed later. + * + * @param entryType the value + * @return this builder + */ + public Builder entryType(ListsCreateParams.EntryType entryType) { + return set("entry_type", Conv.enumeration(ListsCreateParams.EntryType::of), entryType); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ListsCreateParams} + * @throws IllegalStateException when a required field is missing + */ + public ListsCreateParams build() { + require("name", false); + require("list_type", false); + require("entry_type", false); + return new ListsCreateParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * allowlist or custom. blocklist is refused with 400. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ListType extends StringEnum { + /** The value {@code "allowlist"}. */ + public static final ListType ALLOWLIST = new ListType("allowlist"); + + /** The value {@code "custom"}. */ + public static final ListType CUSTOM = new ListType("custom"); + + private static final List KNOWN_VALUES = List.of(ALLOWLIST, CUSTOM); + + private ListType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ListType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ListType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * What the list holds: face, document (document number), phone, email, ip_address (IP address or CIDR range), device_fingerprint, wallet_address, bank_account, country (ISO 3166-1 alpha-3), user or business (your vendor_data), or key (any string). It decides how entry values are validated and matched. Cannot be changed later. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EntryType extends StringEnum { + /** The value {@code "face"}. */ + public static final EntryType FACE = new EntryType("face"); + + /** The value {@code "document"}. */ + public static final EntryType DOCUMENT = new EntryType("document"); + + /** The value {@code "phone"}. */ + public static final EntryType PHONE = new EntryType("phone"); + + /** The value {@code "email"}. */ + public static final EntryType EMAIL = new EntryType("email"); + + /** The value {@code "ip_address"}. */ + public static final EntryType IP_ADDRESS = new EntryType("ip_address"); + + /** The value {@code "device_fingerprint"}. */ + public static final EntryType DEVICE_FINGERPRINT = new EntryType("device_fingerprint"); + + /** The value {@code "wallet_address"}. */ + public static final EntryType WALLET_ADDRESS = new EntryType("wallet_address"); + + /** The value {@code "bank_account"}. */ + public static final EntryType BANK_ACCOUNT = new EntryType("bank_account"); + + /** The value {@code "country"}. */ + public static final EntryType COUNTRY = new EntryType("country"); + + /** The value {@code "user"}. */ + public static final EntryType USER = new EntryType("user"); + + /** The value {@code "business"}. */ + public static final EntryType BUSINESS = new EntryType("business"); + + /** The value {@code "key"}. */ + public static final EntryType KEY = new EntryType("key"); + + private static final List KNOWN_VALUES = List.of(FACE, DOCUMENT, PHONE, EMAIL, IP_ADDRESS, DEVICE_FINGERPRINT, WALLET_ADDRESS, BANK_ACCOUNT, COUNTRY, USER, BUSINESS, KEY); + + private EntryType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EntryType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EntryType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/ListsEntriesCreateParams.java b/src/main/java/me/didit/types/ListsEntriesCreateParams.java new file mode 100644 index 0000000..59e0962 --- /dev/null +++ b/src/main/java/me/didit/types/ListsEntriesCreateParams.java @@ -0,0 +1,441 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** Parameters for {@code lists.entries.create}. */ +public final class ListsEntriesCreateParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("value", "reference_session_id", "reference_object_uuid", "face_image_source", "reference_node_id", "display_label", "comment", "metadata"); + + private ListsEntriesCreateParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ListsEntriesCreateParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ListsEntriesCreateParams fromJson(JsonNode json) { + return new ListsEntriesCreateParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The value to list, validated against the list's entry_type: email a valid email address; phone a valid international number, stored in E.164; ip_address an IP address or CIDR range (for example 10.0.0.0/8); device_fingerprint at least 8 characters among letters, digits, -, _, : and . (a blocklist refuses a fingerprint that several distinct devices have reported); country an ISO 3166-1 alpha-2 or alpha-3 code, stored as alpha-3; user and business the vendor_data; any other type a non-empty string. Stored in lower case except phone numbers and country codes, and a list holds each value once (case-insensitive). Required unless reference_session_id is sent; must be left empty with a document-capture face_image_source. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional value() { + return Json.optional(json, "value", Conv.STRING); + } + + /** + * A person or business verification session of your application to take the value from. Didit reads the session's most recent record of the list's entry type (face, document, phone, email, ip_address or device_fingerprint), links the entry to the session and to that record, and fills display_label and metadata for documents. For a device_fingerprint blocklist, a session that only has a device-class fingerprint, or was completed on more than one device, is refused: send the exact fingerprint as value. For other entry types, or when the session has no such record, send value too; your value is then stored and the entry is still linked to the session. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceSessionId() { + return Json.optional(json, "reference_session_id", Conv.STRING); + } + + /** + * UUID of the record the entry stands for when it does not come from a session: a transaction UUID, or the didit_internal_id of a user or business. Pair it with metadata.reference_type. Replaced by the session's record when reference_session_id provides one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceObjectUuid() { + return Json.optional(json, "reference_object_uuid", Conv.STRING); + } + + /** + * Face lists with reference_session_id: which of the session's images the face is taken from. liveness (default) is the liveness selfie. front_image_camera_front and back_image_camera_front are the selfies the front camera took while the user photographed the front or back of their document: accepted only on the system face blocklist, with reference_session_id and an empty value. Each selfie can be added once and must show one clear face. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional faceImageSource() { + return Json.optional(json, "face_image_source", Conv.enumeration(ListsEntriesCreateParams.FaceImageSource::of)); + } + + /** + * With a document-capture face_image_source: the ID verification node whose selfie to use, for workflows with more than one. Defaults to the most recent ID verification that has that image. Refused with liveness. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceNodeId() { + return Json.optional(json, "reference_node_id", Conv.STRING); + } + + /** + * Human-readable label. When empty, Didit derives one from the session where it can (for example the holder's name and document number). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional displayLabel() { + return Json.optional(json, "display_label", Conv.STRING); + } + + /** + * Free-text note, such as the reason for listing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional comment() { + return Json.optional(json, "comment", Conv.STRING); + } + + /** + * Free-form JSON stored with the entry and returned as stored. Use reference_type and reference_id so the console links back to the source. reference_image_url and front_image_url are set by Didit only and are dropped if you send them. For an entry read from a session, Didit's keys are merged with yours. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional metadata() { + return Json.optional(json, "metadata", Conv.model(ListsEntriesCreateParams.Metadata::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ListsEntriesCreateParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ListsEntriesCreateParams from) { + super(from); + } + + /** + * The value to list, validated against the list's entry_type: email a valid email address; phone a valid international number, stored in E.164; ip_address an IP address or CIDR range (for example 10.0.0.0/8); device_fingerprint at least 8 characters among letters, digits, -, _, : and . (a blocklist refuses a fingerprint that several distinct devices have reported); country an ISO 3166-1 alpha-2 or alpha-3 code, stored as alpha-3; user and business the vendor_data; any other type a non-empty string. Stored in lower case except phone numbers and country codes, and a list holds each value once (case-insensitive). Required unless reference_session_id is sent; must be left empty with a document-capture face_image_source. + * + * @param value the value + * @return this builder + */ + public Builder value(String value) { + return set("value", Conv.STRING, value); + } + + /** + * A person or business verification session of your application to take the value from. Didit reads the session's most recent record of the list's entry type (face, document, phone, email, ip_address or device_fingerprint), links the entry to the session and to that record, and fills display_label and metadata for documents. For a device_fingerprint blocklist, a session that only has a device-class fingerprint, or was completed on more than one device, is refused: send the exact fingerprint as value. For other entry types, or when the session has no such record, send value too; your value is then stored and the entry is still linked to the session. + * + * @param referenceSessionId the value + * @return this builder + */ + public Builder referenceSessionId(String referenceSessionId) { + return set("reference_session_id", Conv.STRING, referenceSessionId); + } + + /** + * UUID of the record the entry stands for when it does not come from a session: a transaction UUID, or the didit_internal_id of a user or business. Pair it with metadata.reference_type. Replaced by the session's record when reference_session_id provides one. + * + * @param referenceObjectUuid the value + * @return this builder + */ + public Builder referenceObjectUuid(String referenceObjectUuid) { + return set("reference_object_uuid", Conv.STRING, referenceObjectUuid); + } + + /** + * Face lists with reference_session_id: which of the session's images the face is taken from. liveness (default) is the liveness selfie. front_image_camera_front and back_image_camera_front are the selfies the front camera took while the user photographed the front or back of their document: accepted only on the system face blocklist, with reference_session_id and an empty value. Each selfie can be added once and must show one clear face. + * + * @param faceImageSource the value + * @return this builder + */ + public Builder faceImageSource(ListsEntriesCreateParams.FaceImageSource faceImageSource) { + return set("face_image_source", Conv.enumeration(ListsEntriesCreateParams.FaceImageSource::of), faceImageSource); + } + + /** + * With a document-capture face_image_source: the ID verification node whose selfie to use, for workflows with more than one. Defaults to the most recent ID verification that has that image. Refused with liveness. + * + * @param referenceNodeId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder referenceNodeId(@Nullable String referenceNodeId) { + return setNullable("reference_node_id", Conv.STRING, referenceNodeId); + } + + /** + * Human-readable label. When empty, Didit derives one from the session where it can (for example the holder's name and document number). + * + * @param displayLabel the value + * @return this builder + */ + public Builder displayLabel(String displayLabel) { + return set("display_label", Conv.STRING, displayLabel); + } + + /** + * Free-text note, such as the reason for listing. + * + * @param comment the value + * @return this builder + */ + public Builder comment(String comment) { + return set("comment", Conv.STRING, comment); + } + + /** + * Free-form JSON stored with the entry and returned as stored. Use reference_type and reference_id so the console links back to the source. reference_image_url and front_image_url are set by Didit only and are dropped if you send them. For an entry read from a session, Didit's keys are merged with yours. + * + * @param metadata the value + * @return this builder + */ + public Builder metadata(ListsEntriesCreateParams.Metadata metadata) { + return set("metadata", Conv.model(ListsEntriesCreateParams.Metadata::fromJson), metadata); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ListsEntriesCreateParams} + */ + public ListsEntriesCreateParams build() { + return new ListsEntriesCreateParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Face lists with reference_session_id: which of the session's images the face is taken from. liveness (default) is the liveness selfie. front_image_camera_front and back_image_camera_front are the selfies the front camera took while the user photographed the front or back of their document: accepted only on the system face blocklist, with reference_session_id and an empty value. Each selfie can be added once and must show one clear face. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class FaceImageSource extends StringEnum { + /** The value {@code "liveness"}. */ + public static final FaceImageSource LIVENESS = new FaceImageSource("liveness"); + + /** The value {@code "front_image_camera_front"}. */ + public static final FaceImageSource FRONT_IMAGE_CAMERA_FRONT = new FaceImageSource("front_image_camera_front"); + + /** The value {@code "back_image_camera_front"}. */ + public static final FaceImageSource BACK_IMAGE_CAMERA_FRONT = new FaceImageSource("back_image_camera_front"); + + private static final List KNOWN_VALUES = List.of(LIVENESS, FRONT_IMAGE_CAMERA_FRONT, BACK_IMAGE_CAMERA_FRONT); + + private FaceImageSource(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static FaceImageSource of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, FaceImageSource::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Free-form JSON stored with the entry and returned as stored. Use reference_type and reference_id so the console links back to the source. reference_image_url and front_image_url are set by Didit only and are dropped if you send them. For an entry read from a session, Didit's keys are merged with yours. + */ + public static final class Metadata extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("reference_type", "reference_id"); + + private Metadata(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Metadata} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Metadata fromJson(JsonNode json) { + return new Metadata(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * What reference_object_uuid points to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceType() { + return Json.optional(json, "reference_type", Conv.enumeration(ListsEntriesCreateParams.Metadata.ReferenceType::of)); + } + + /** + * Identifier of the source record. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceId() { + return Json.optional(json, "reference_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Metadata}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Metadata from) { + super(from); + } + + /** + * What reference_object_uuid points to. + * + * @param referenceType the value + * @return this builder + */ + public Builder referenceType(ListsEntriesCreateParams.Metadata.ReferenceType referenceType) { + return set("reference_type", Conv.enumeration(ListsEntriesCreateParams.Metadata.ReferenceType::of), referenceType); + } + + /** + * Identifier of the source record. + * + * @param referenceId the value + * @return this builder + */ + public Builder referenceId(String referenceId) { + return set("reference_id", Conv.STRING, referenceId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Metadata} + */ + public Metadata build() { + return new Metadata(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * What reference_object_uuid points to. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ReferenceType extends StringEnum { + /** The value {@code "transaction"}. */ + public static final ReferenceType TRANSACTION = new ReferenceType("transaction"); + + /** The value {@code "vendor_user"}. */ + public static final ReferenceType VENDOR_USER = new ReferenceType("vendor_user"); + + /** The value {@code "vendor_business"}. */ + public static final ReferenceType VENDOR_BUSINESS = new ReferenceType("vendor_business"); + + private static final List KNOWN_VALUES = List.of(TRANSACTION, VENDOR_USER, VENDOR_BUSINESS); + + private ReferenceType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ReferenceType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ReferenceType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/ListsEntriesListParams.java b/src/main/java/me/didit/types/ListsEntriesListParams.java new file mode 100644 index 0000000..dad2eca --- /dev/null +++ b/src/main/java/me/didit/types/ListsEntriesListParams.java @@ -0,0 +1,159 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code lists.entries.list}. */ +public final class ListsEntriesListParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("search", "reference_session_id", "limit", "offset"); + + private ListsEntriesListParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ListsEntriesListParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ListsEntriesListParams fromJson(JsonNode json) { + return new ListsEntriesListParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Case-insensitive text that the entry's value or display_label must contain. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional search() { + return Json.optional(json, "search", Conv.STRING); + } + + /** + * Only the entries taken from this person verification session, for example to see which of its faces are already listed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceSessionId() { + return Json.optional(json, "reference_session_id", Conv.STRING); + } + + /** + * Page size. Defaults to 50. A value above 1000000 returns 400; zero, a negative or a non-numeric value falls back to 50. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Zero-based index of the first item to return. Prefer following next. A value above 1000000 returns 400; a negative or non-numeric value falls back to 0. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ListsEntriesListParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ListsEntriesListParams from) { + super(from); + } + + /** + * Case-insensitive text that the entry's value or display_label must contain. + * + * @param search the value + * @return this builder + */ + public Builder search(String search) { + return set("search", Conv.STRING, search); + } + + /** + * Only the entries taken from this person verification session, for example to see which of its faces are already listed. + * + * @param referenceSessionId the value + * @return this builder + */ + public Builder referenceSessionId(String referenceSessionId) { + return set("reference_session_id", Conv.STRING, referenceSessionId); + } + + /** + * Page size. Defaults to 50. A value above 1000000 returns 400; zero, a negative or a non-numeric value falls back to 50. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Zero-based index of the first item to return. Prefer following next. A value above 1000000 returns 400; a negative or non-numeric value falls back to 0. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ListsEntriesListParams} + */ + public ListsEntriesListParams build() { + return new ListsEntriesListParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ListsEntriesListResponse.java b/src/main/java/me/didit/types/ListsEntriesListResponse.java new file mode 100644 index 0000000..38fe396 --- /dev/null +++ b/src/main/java/me/didit/types/ListsEntriesListResponse.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class ListsEntriesListResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private ListsEntriesListResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ListsEntriesListResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ListsEntriesListResponse fromJson(JsonNode json) { + return new ListsEntriesListResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Total number of entries matching the filters. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(ComplianceListEntry::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ListsEntriesListResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ListsEntriesListResponse from) { + super(from); + } + + /** + * Total number of entries matching the filters. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(ComplianceListEntry::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ListsEntriesListResponse} + * @throws IllegalStateException when a required field is missing + */ + public ListsEntriesListResponse build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new ListsEntriesListResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ListsEntriesUploadFaceParams.java b/src/main/java/me/didit/types/ListsEntriesUploadFaceParams.java new file mode 100644 index 0000000..2ef05aa --- /dev/null +++ b/src/main/java/me/didit/types/ListsEntriesUploadFaceParams.java @@ -0,0 +1,121 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code lists.entries.upload_face}. */ +public final class ListsEntriesUploadFaceParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("image", "comment"); + + private ListsEntriesUploadFaceParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ListsEntriesUploadFaceParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ListsEntriesUploadFaceParams fromJson(JsonNode json) { + return new ListsEntriesUploadFaceParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Base64-encoded JPEG or PNG, at most 2 MB once decoded, showing a clearly visible face. Send the bare base64 string, without a data:image/...;base64, prefix. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String image() { + return Json.required(json, "image", Conv.STRING); + } + + /** + * Optional free-text note, such as the reason for listing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional comment() { + return Json.optional(json, "comment", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ListsEntriesUploadFaceParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ListsEntriesUploadFaceParams from) { + super(from); + } + + /** + * Base64-encoded JPEG or PNG, at most 2 MB once decoded, showing a clearly visible face. Send the bare base64 string, without a data:image/...;base64, prefix. + * + * @param image the value + * @return this builder + */ + public Builder image(String image) { + return set("image", Conv.STRING, image); + } + + /** + * Optional free-text note, such as the reason for listing. + * + * @param comment the value + * @return this builder + */ + public Builder comment(String comment) { + return set("comment", Conv.STRING, comment); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ListsEntriesUploadFaceParams} + * @throws IllegalStateException when a required field is missing + */ + public ListsEntriesUploadFaceParams build() { + require("image", false); + return new ListsEntriesUploadFaceParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ListsListParams.java b/src/main/java/me/didit/types/ListsListParams.java new file mode 100644 index 0000000..cac6511 --- /dev/null +++ b/src/main/java/me/didit/types/ListsListParams.java @@ -0,0 +1,339 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code lists.list}. */ +public final class ListsListParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("list_type", "entry_type", "ordering", "limit", "offset"); + + private ListsListParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ListsListParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ListsListParams fromJson(JsonNode json) { + return new ListsListParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Only lists of this type. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional listType() { + return Json.optional(json, "list_type", Conv.enumeration(ListsListParams.ListType::of)); + } + + /** + * Only lists of this entry type. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entryType() { + return Json.optional(json, "entry_type", Conv.enumeration(ListsListParams.EntryType::of)); + } + + /** + * Sort order: -created_at (newest first, the default) or created_at (oldest first). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ordering() { + return Json.optional(json, "ordering", Conv.enumeration(ListsListParams.Ordering::of)); + } + + /** + * Page size. Defaults to 50. A value above 1000000 returns 400; zero, a negative or a non-numeric value falls back to 50. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Zero-based index of the first item to return. Prefer following next. A value above 1000000 returns 400; a negative or non-numeric value falls back to 0. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ListsListParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ListsListParams from) { + super(from); + } + + /** + * Only lists of this type. + * + * @param listType the value + * @return this builder + */ + public Builder listType(ListsListParams.ListType listType) { + return set("list_type", Conv.enumeration(ListsListParams.ListType::of), listType); + } + + /** + * Only lists of this entry type. + * + * @param entryType the value + * @return this builder + */ + public Builder entryType(ListsListParams.EntryType entryType) { + return set("entry_type", Conv.enumeration(ListsListParams.EntryType::of), entryType); + } + + /** + * Sort order: -created_at (newest first, the default) or created_at (oldest first). + * + * @param ordering the value + * @return this builder + */ + public Builder ordering(ListsListParams.Ordering ordering) { + return set("ordering", Conv.enumeration(ListsListParams.Ordering::of), ordering); + } + + /** + * Page size. Defaults to 50. A value above 1000000 returns 400; zero, a negative or a non-numeric value falls back to 50. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Zero-based index of the first item to return. Prefer following next. A value above 1000000 returns 400; a negative or non-numeric value falls back to 0. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ListsListParams} + */ + public ListsListParams build() { + return new ListsListParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ListType extends StringEnum { + /** The value {@code "blocklist"}. */ + public static final ListType BLOCKLIST = new ListType("blocklist"); + + /** The value {@code "allowlist"}. */ + public static final ListType ALLOWLIST = new ListType("allowlist"); + + /** The value {@code "custom"}. */ + public static final ListType CUSTOM = new ListType("custom"); + + private static final List KNOWN_VALUES = List.of(BLOCKLIST, ALLOWLIST, CUSTOM); + + private ListType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ListType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ListType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EntryType extends StringEnum { + /** The value {@code "face"}. */ + public static final EntryType FACE = new EntryType("face"); + + /** The value {@code "document"}. */ + public static final EntryType DOCUMENT = new EntryType("document"); + + /** The value {@code "phone"}. */ + public static final EntryType PHONE = new EntryType("phone"); + + /** The value {@code "email"}. */ + public static final EntryType EMAIL = new EntryType("email"); + + /** The value {@code "ip_address"}. */ + public static final EntryType IP_ADDRESS = new EntryType("ip_address"); + + /** The value {@code "device_fingerprint"}. */ + public static final EntryType DEVICE_FINGERPRINT = new EntryType("device_fingerprint"); + + /** The value {@code "wallet_address"}. */ + public static final EntryType WALLET_ADDRESS = new EntryType("wallet_address"); + + /** The value {@code "bank_account"}. */ + public static final EntryType BANK_ACCOUNT = new EntryType("bank_account"); + + /** The value {@code "country"}. */ + public static final EntryType COUNTRY = new EntryType("country"); + + /** The value {@code "user"}. */ + public static final EntryType USER = new EntryType("user"); + + /** The value {@code "business"}. */ + public static final EntryType BUSINESS = new EntryType("business"); + + /** The value {@code "key"}. */ + public static final EntryType KEY = new EntryType("key"); + + private static final List KNOWN_VALUES = List.of(FACE, DOCUMENT, PHONE, EMAIL, IP_ADDRESS, DEVICE_FINGERPRINT, WALLET_ADDRESS, BANK_ACCOUNT, COUNTRY, USER, BUSINESS, KEY); + + private EntryType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EntryType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EntryType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Ordering extends StringEnum { + /** The value {@code "-created_at"}. */ + public static final Ordering CREATED_AT = new Ordering("-created_at"); + + /** The value {@code "created_at"}. */ + public static final Ordering CREATED_AT_2 = new Ordering("created_at"); + + private static final List KNOWN_VALUES = List.of(CREATED_AT, CREATED_AT_2); + + private Ordering(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Ordering of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Ordering::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/ListsListResponse.java b/src/main/java/me/didit/types/ListsListResponse.java new file mode 100644 index 0000000..ac16799 --- /dev/null +++ b/src/main/java/me/didit/types/ListsListResponse.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class ListsListResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private ListsListResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ListsListResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ListsListResponse fromJson(JsonNode json) { + return new ListsListResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Total number of lists matching the filters. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(ComplianceList::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ListsListResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ListsListResponse from) { + super(from); + } + + /** + * Total number of lists matching the filters. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(ComplianceList::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ListsListResponse} + * @throws IllegalStateException when a required field is missing + */ + public ListsListResponse build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new ListsListResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ListsUpdateParams.java b/src/main/java/me/didit/types/ListsUpdateParams.java new file mode 100644 index 0000000..9d3ab59 --- /dev/null +++ b/src/main/java/me/didit/types/ListsUpdateParams.java @@ -0,0 +1,119 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code lists.update}. */ +public final class ListsUpdateParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "description"); + + private ListsUpdateParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ListsUpdateParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ListsUpdateParams fromJson(JsonNode json) { + return new ListsUpdateParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * New name. Must differ from the name of every other list of the application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * New description. Send an empty string to clear it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ListsUpdateParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ListsUpdateParams from) { + super(from); + } + + /** + * New name. Must differ from the name of every other list of the application. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * New description. Send an empty string to clear it. + * + * @param description the value + * @return this builder + */ + public Builder description(String description) { + return set("description", Conv.STRING, description); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ListsUpdateParams} + */ + public ListsUpdateParams build() { + return new ListsUpdateParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/ListsUpdateResponse.java b/src/main/java/me/didit/types/ListsUpdateResponse.java new file mode 100644 index 0000000..3d88fb1 --- /dev/null +++ b/src/main/java/me/didit/types/ListsUpdateResponse.java @@ -0,0 +1,120 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class ListsUpdateResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "description"); + + private ListsUpdateResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ListsUpdateResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ListsUpdateResponse fromJson(JsonNode json) { + return new ListsUpdateResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The list's name after the update. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String name() { + return Json.required(json, "name", Conv.STRING); + } + + /** + * The list's description after the update. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String description() { + return Json.required(json, "description", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ListsUpdateResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ListsUpdateResponse from) { + super(from); + } + + /** + * The list's name after the update. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * The list's description after the update. + * + * @param description the value + * @return this builder + */ + public Builder description(String description) { + return set("description", Conv.STRING, description); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ListsUpdateResponse} + * @throws IllegalStateException when a required field is missing + */ + public ListsUpdateResponse build() { + require("name", false); + require("description", false); + return new ListsUpdateResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEvent.java b/src/main/java/me/didit/types/MonitoringEvent.java new file mode 100644 index 0000000..9b0bd2f --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEvent.java @@ -0,0 +1,817 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One stored monitoring event as returned by the list and subject-timeline endpoints. Restricted evidence (payload, device context, rule evidence) is only returned by GET /v3/events/{event_uuid}/. + */ +public final class MonitoringEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_uuid", "event_id", "event_category", "action_type", "occurred_at", "received_at", "status", "score", "subject_vendor_data", "subject_entity_type", "subject_uuid", "counterparty_vendor_data", "session_id", "device_fingerprint", "ip_address", "ip_country", "amount", "currency", "game_id", "round_id", "bonus_id", "affiliate_code", "payment_reference_id", "retention_class", "rules_matched_count", "correlation_pending", "is_droppable"); + + private MonitoringEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEvent fromJson(JsonNode json) { + return new MonitoringEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Didit identifier of the event. Use it as {event_uuid} in the retrieve and delete calls. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventUuid() { + return Json.required(json, "event_uuid", Conv.STRING); + } + + /** + * Your own identifier, the idempotency key you sent. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Event category. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEvent.EventCategory eventCategory() { + return Json.required(json, "event_category", Conv.enumeration(MonitoringEvent.EventCategory::of)); + } + + /** + * What happened within the category, as you sent it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String actionType() { + return Json.required(json, "action_type", Conv.STRING); + } + + /** + * When the event happened on your side. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime occurredAt() { + return Json.required(json, "occurred_at", Conv.DATE_TIME); + } + + /** + * When Didit accepted the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime receivedAt() { + return Json.required(json, "received_at", Conv.DATE_TIME); + } + + /** + * ACCEPTED: stored, no matched rule changed its standing (the normal answer for most events, and always the answer in a sandbox application, where no rules run). APPROVED: a rule matched and the score stayed under your review threshold. IN_REVIEW: a rule pushed the score over the review threshold or set the status. DECLINED: a rule declined it or the score crossed your decline threshold. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEvent.Status status() { + return Json.required(json, "status", Conv.enumeration(MonitoringEvent.Status::of)); + } + + /** + * Accumulated rule score. Starts at 0; rule add_score actions move it (negative deltas are allowed). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long score() { + return Json.required(json, "score", Conv.LONG); + } + + /** + * subject.vendor_data of the event: your id for the user or company. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String subjectVendorData() { + return Json.required(json, "subject_vendor_data", Conv.STRING); + } + + /** + * individual or company: which kind of subject subject_vendor_data names. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String subjectEntityType() { + return Json.required(json, "subject_entity_type", Conv.STRING); + } + + /** + * UUID of the user (individual) or company the event resolved to, or null when that record no longer exists. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectUuid() { + return Json.optional(json, "subject_uuid", Conv.STRING); + } + + /** + * The other player of a P2P event (promoted from payload.counterparty_vendor_data). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyVendorData() { + return Json.optional(json, "counterparty_vendor_data", Conv.STRING); + } + + /** + * session.session_id sent with the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * device_context.fingerprint sent with the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deviceFingerprint() { + return Json.optional(json, "device_fingerprint", Conv.STRING); + } + + /** + * IP address of the event. On the server lane this is device_context.network_context.ip_address as you sent it; on the device lane it is the IP of the request. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipAddress() { + return Json.optional(json, "ip_address", Conv.STRING); + } + + /** + * Country code of ip_address from IP enrichment, or null when the address was not enriched (missing, private, or low-value telemetry whose address was not already known). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipCountry() { + return Json.optional(json, "ip_country", Conv.STRING); + } + + /** + * Amount as a decimal string with 18 decimal places, from transaction_context.amount, payload.amount or payload.stake_amount, in that order. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional amount() { + return Json.optional(json, "amount", Conv.STRING); + } + + /** + * Currency from transaction_context.currency or payload.currency. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currency() { + return Json.optional(json, "currency", Conv.STRING); + } + + /** + * Promoted from payload.game_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional gameId() { + return Json.optional(json, "game_id", Conv.STRING); + } + + /** + * Promoted from payload.round_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional roundId() { + return Json.optional(json, "round_id", Conv.STRING); + } + + /** + * Promoted from payload.bonus_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bonusId() { + return Json.optional(json, "bonus_id", Conv.STRING); + } + + /** + * Promoted from payload.affiliate_code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional affiliateCode() { + return Json.optional(json, "affiliate_code", Conv.STRING); + } + + /** + * Payment reference from transaction_context.payment_reference_id or payload.payment_reference_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional paymentReferenceId() { + return Json.optional(json, "payment_reference_id", Conv.STRING); + } + + /** + * Retention class. Payment and dispute events are monetary_or_compliance; security and responsible-gambling events are risk_relevant; session.page_view, session.sdk_heartbeat, session.sdk_loaded, session.device_seen, session.bot_signal_observed, bonus.promotion_viewed and affiliate.referral_clicked are low_value; everything else is standard. An event an active rule matched is promoted to risk_relevant (never demoted). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEvent.RetentionClass retentionClass() { + return Json.required(json, "retention_class", Conv.enumeration(MonitoringEvent.RetentionClass::of)); + } + + /** + * Number of rules that matched the event, including rules in TEST mode (which record evidence but apply no actions). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long rulesMatchedCount() { + return Json.required(json, "rules_matched_count", Conv.LONG); + } + + /** + * true while an asynchronous rule (cross-subject correlation, or a sequence longer than 24 hours) still has to run for this event. It resolves within minutes and status, score and rules_matched_count update in place. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean correlationPending() { + return Json.required(json, "correlation_pending", Conv.BOOLEAN); + } + + /** + * Whether an SDK under backpressure may drop events of this kind. false for every security, payment, dispute and responsible_gambling event and for gameplay.bet_placed, gameplay.bet_settled and gameplay.p2p_settlement. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isDroppable() { + return Json.required(json, "is_droppable", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEvent from) { + super(from); + } + + /** + * Didit identifier of the event. Use it as {event_uuid} in the retrieve and delete calls. + * + * @param eventUuid the value + * @return this builder + */ + public Builder eventUuid(String eventUuid) { + return set("event_uuid", Conv.STRING, eventUuid); + } + + /** + * Your own identifier, the idempotency key you sent. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Event category. + * + * @param eventCategory the value + * @return this builder + */ + public Builder eventCategory(MonitoringEvent.EventCategory eventCategory) { + return set("event_category", Conv.enumeration(MonitoringEvent.EventCategory::of), eventCategory); + } + + /** + * What happened within the category, as you sent it. + * + * @param actionType the value + * @return this builder + */ + public Builder actionType(String actionType) { + return set("action_type", Conv.STRING, actionType); + } + + /** + * When the event happened on your side. + * + * @param occurredAt the value + * @return this builder + */ + public Builder occurredAt(OffsetDateTime occurredAt) { + return set("occurred_at", Conv.DATE_TIME, occurredAt); + } + + /** + * When Didit accepted the event. + * + * @param receivedAt the value + * @return this builder + */ + public Builder receivedAt(OffsetDateTime receivedAt) { + return set("received_at", Conv.DATE_TIME, receivedAt); + } + + /** + * ACCEPTED: stored, no matched rule changed its standing (the normal answer for most events, and always the answer in a sandbox application, where no rules run). APPROVED: a rule matched and the score stayed under your review threshold. IN_REVIEW: a rule pushed the score over the review threshold or set the status. DECLINED: a rule declined it or the score crossed your decline threshold. + * + * @param status the value + * @return this builder + */ + public Builder status(MonitoringEvent.Status status) { + return set("status", Conv.enumeration(MonitoringEvent.Status::of), status); + } + + /** + * Accumulated rule score. Starts at 0; rule add_score actions move it (negative deltas are allowed). + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * subject.vendor_data of the event: your id for the user or company. + * + * @param subjectVendorData the value + * @return this builder + */ + public Builder subjectVendorData(String subjectVendorData) { + return set("subject_vendor_data", Conv.STRING, subjectVendorData); + } + + /** + * individual or company: which kind of subject subject_vendor_data names. + * + * @param subjectEntityType the value + * @return this builder + */ + public Builder subjectEntityType(String subjectEntityType) { + return set("subject_entity_type", Conv.STRING, subjectEntityType); + } + + /** + * UUID of the user (individual) or company the event resolved to, or null when that record no longer exists. + * + * @param subjectUuid the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder subjectUuid(@Nullable String subjectUuid) { + return setNullable("subject_uuid", Conv.STRING, subjectUuid); + } + + /** + * The other player of a P2P event (promoted from payload.counterparty_vendor_data). + * + * @param counterpartyVendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder counterpartyVendorData(@Nullable String counterpartyVendorData) { + return setNullable("counterparty_vendor_data", Conv.STRING, counterpartyVendorData); + } + + /** + * session.session_id sent with the event. + * + * @param sessionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionId(@Nullable String sessionId) { + return setNullable("session_id", Conv.STRING, sessionId); + } + + /** + * device_context.fingerprint sent with the event. + * + * @param deviceFingerprint the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder deviceFingerprint(@Nullable String deviceFingerprint) { + return setNullable("device_fingerprint", Conv.STRING, deviceFingerprint); + } + + /** + * IP address of the event. On the server lane this is device_context.network_context.ip_address as you sent it; on the device lane it is the IP of the request. + * + * @param ipAddress the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ipAddress(@Nullable String ipAddress) { + return setNullable("ip_address", Conv.STRING, ipAddress); + } + + /** + * Country code of ip_address from IP enrichment, or null when the address was not enriched (missing, private, or low-value telemetry whose address was not already known). + * + * @param ipCountry the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ipCountry(@Nullable String ipCountry) { + return setNullable("ip_country", Conv.STRING, ipCountry); + } + + /** + * Amount as a decimal string with 18 decimal places, from transaction_context.amount, payload.amount or payload.stake_amount, in that order. + * + * @param amount the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder amount(@Nullable String amount) { + return setNullable("amount", Conv.STRING, amount); + } + + /** + * Currency from transaction_context.currency or payload.currency. + * + * @param currency the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder currency(@Nullable String currency) { + return setNullable("currency", Conv.STRING, currency); + } + + /** + * Promoted from payload.game_id. + * + * @param gameId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder gameId(@Nullable String gameId) { + return setNullable("game_id", Conv.STRING, gameId); + } + + /** + * Promoted from payload.round_id. + * + * @param roundId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder roundId(@Nullable String roundId) { + return setNullable("round_id", Conv.STRING, roundId); + } + + /** + * Promoted from payload.bonus_id. + * + * @param bonusId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder bonusId(@Nullable String bonusId) { + return setNullable("bonus_id", Conv.STRING, bonusId); + } + + /** + * Promoted from payload.affiliate_code. + * + * @param affiliateCode the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder affiliateCode(@Nullable String affiliateCode) { + return setNullable("affiliate_code", Conv.STRING, affiliateCode); + } + + /** + * Payment reference from transaction_context.payment_reference_id or payload.payment_reference_id. + * + * @param paymentReferenceId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder paymentReferenceId(@Nullable String paymentReferenceId) { + return setNullable("payment_reference_id", Conv.STRING, paymentReferenceId); + } + + /** + * Retention class. Payment and dispute events are monetary_or_compliance; security and responsible-gambling events are risk_relevant; session.page_view, session.sdk_heartbeat, session.sdk_loaded, session.device_seen, session.bot_signal_observed, bonus.promotion_viewed and affiliate.referral_clicked are low_value; everything else is standard. An event an active rule matched is promoted to risk_relevant (never demoted). + * + * @param retentionClass the value + * @return this builder + */ + public Builder retentionClass(MonitoringEvent.RetentionClass retentionClass) { + return set("retention_class", Conv.enumeration(MonitoringEvent.RetentionClass::of), retentionClass); + } + + /** + * Number of rules that matched the event, including rules in TEST mode (which record evidence but apply no actions). + * + * @param rulesMatchedCount the value + * @return this builder + */ + public Builder rulesMatchedCount(long rulesMatchedCount) { + return set("rules_matched_count", Conv.LONG, rulesMatchedCount); + } + + /** + * true while an asynchronous rule (cross-subject correlation, or a sequence longer than 24 hours) still has to run for this event. It resolves within minutes and status, score and rules_matched_count update in place. + * + * @param correlationPending the value + * @return this builder + */ + public Builder correlationPending(boolean correlationPending) { + return set("correlation_pending", Conv.BOOLEAN, correlationPending); + } + + /** + * Whether an SDK under backpressure may drop events of this kind. false for every security, payment, dispute and responsible_gambling event and for gameplay.bet_placed, gameplay.bet_settled and gameplay.p2p_settlement. + * + * @param isDroppable the value + * @return this builder + */ + public Builder isDroppable(boolean isDroppable) { + return set("is_droppable", Conv.BOOLEAN, isDroppable); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEvent} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEvent build() { + require("event_uuid", false); + require("event_id", false); + require("event_category", false); + require("action_type", false); + require("occurred_at", false); + require("received_at", false); + require("status", false); + require("score", false); + require("subject_vendor_data", false); + require("subject_entity_type", false); + require("subject_uuid", true); + require("counterparty_vendor_data", true); + require("session_id", true); + require("device_fingerprint", true); + require("ip_address", true); + require("ip_country", true); + require("amount", true); + require("currency", true); + require("game_id", true); + require("round_id", true); + require("bonus_id", true); + require("affiliate_code", true); + require("payment_reference_id", true); + require("retention_class", false); + require("rules_matched_count", false); + require("correlation_pending", false); + require("is_droppable", false); + return new MonitoringEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Event category. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EventCategory extends StringEnum { + /** The value {@code "auth"}. */ + public static final EventCategory AUTH = new EventCategory("auth"); + + /** The value {@code "session"}. */ + public static final EventCategory SESSION = new EventCategory("session"); + + /** The value {@code "gameplay"}. */ + public static final EventCategory GAMEPLAY = new EventCategory("gameplay"); + + /** The value {@code "bonus"}. */ + public static final EventCategory BONUS = new EventCategory("bonus"); + + /** The value {@code "affiliate"}. */ + public static final EventCategory AFFILIATE = new EventCategory("affiliate"); + + /** The value {@code "payment"}. */ + public static final EventCategory PAYMENT = new EventCategory("payment"); + + /** The value {@code "dispute"}. */ + public static final EventCategory DISPUTE = new EventCategory("dispute"); + + /** The value {@code "security"}. */ + public static final EventCategory SECURITY = new EventCategory("security"); + + /** The value {@code "responsible_gambling"}. */ + public static final EventCategory RESPONSIBLE_GAMBLING = new EventCategory("responsible_gambling"); + + /** The value {@code "custom"}. */ + public static final EventCategory CUSTOM = new EventCategory("custom"); + + private static final List KNOWN_VALUES = List.of(AUTH, SESSION, GAMEPLAY, BONUS, AFFILIATE, PAYMENT, DISPUTE, SECURITY, RESPONSIBLE_GAMBLING, CUSTOM); + + private EventCategory(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EventCategory of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EventCategory::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * ACCEPTED: stored, no matched rule changed its standing (the normal answer for most events, and always the answer in a sandbox application, where no rules run). APPROVED: a rule matched and the score stayed under your review threshold. IN_REVIEW: a rule pushed the score over the review threshold or set the status. DECLINED: a rule declined it or the score crossed your decline threshold. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACCEPTED"}. */ + public static final Status ACCEPTED = new Status("ACCEPTED"); + + /** The value {@code "APPROVED"}. */ + public static final Status APPROVED = new Status("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Status DECLINED = new Status("DECLINED"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, APPROVED, IN_REVIEW, DECLINED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Retention class. Payment and dispute events are monetary_or_compliance; security and responsible-gambling events are risk_relevant; session.page_view, session.sdk_heartbeat, session.sdk_loaded, session.device_seen, session.bot_signal_observed, bonus.promotion_viewed and affiliate.referral_clicked are low_value; everything else is standard. An event an active rule matched is promoted to risk_relevant (never demoted). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class RetentionClass extends StringEnum { + /** The value {@code "low_value"}. */ + public static final RetentionClass LOW_VALUE = new RetentionClass("low_value"); + + /** The value {@code "standard"}. */ + public static final RetentionClass STANDARD = new RetentionClass("standard"); + + /** The value {@code "risk_relevant"}. */ + public static final RetentionClass RISK_RELEVANT = new RetentionClass("risk_relevant"); + + /** The value {@code "monetary_or_compliance"}. */ + public static final RetentionClass MONETARY_OR_COMPLIANCE = new RetentionClass("monetary_or_compliance"); + + private static final List KNOWN_VALUES = List.of(LOW_VALUE, STANDARD, RISK_RELEVANT, MONETARY_OR_COMPLIANCE); + + private RetentionClass(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static RetentionClass of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RetentionClass::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventCreateRequest.java b/src/main/java/me/didit/types/MonitoringEventCreateRequest.java new file mode 100644 index 0000000..a4df605 --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventCreateRequest.java @@ -0,0 +1,1308 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.JsonUnion; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * The event envelope, the same for every category. camelCase aliases are accepted: eventId, eventCategory (or category), actionType, occurredAt, timeZone (or zoneId), deviceContext (or device), transactionContext, customValues (or props); inside subject: vendorData, entityType, fullName; inside session: sessionId, pageUrl; inside transaction_context: paymentReferenceId. + */ +public final class MonitoringEventCreateRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "event_category", "action_type", "occurred_at", "time_zone", "subject", "business", "session", "device_context", "transaction_context", "payload", "custom_values"); + + private MonitoringEventCreateRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventCreateRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventCreateRequest fromJson(JsonNode json) { + return new MonitoringEventCreateRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Your identifier for the event, unique per application. It is the idempotency key: resending the same event_category, action_type, occurred_at, subject.vendor_data and payload under it is a replay, anything different is a conflict. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Event category. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventCreateRequest.EventCategory eventCategory() { + return Json.required(json, "event_category", Conv.enumeration(MonitoringEventCreateRequest.EventCategory::of)); + } + + /** + * What happened within the category. Open vocabulary; the managed actions listed in Event ingestion (for example login, deposit_completed, chargeback_opened) unlock typed validation and managed rule fields. Leading and trailing whitespace is trimmed. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String actionType() { + return Json.required(json, "action_type", Conv.STRING); + } + + /** + * When the event happened on your side, ISO 8601 (a value without an offset is read as UTC). Must be no more than 7 days in the past and no more than 5 minutes in the future. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime occurredAt() { + return Json.required(json, "occurred_at", Conv.DATE_TIME); + } + + /** + * Time zone of the occurrence on your side, for example Europe/Madrid. Stored with the event; not returned by the read endpoints. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional timeZone() { + return Json.optional(json, "time_zone", Conv.STRING); + } + + /** + * The user or company the event belongs to. It resolves to the user (or company) with this vendor_data in your application; one is created when none exists. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventCreateRequest.Subject subject() { + return Json.required(json, "subject", Conv.model(MonitoringEventCreateRequest.Subject::fromJson)); + } + + /** + * The business the event belongs to, when it differs from the subject. Only vendor_data is read. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional business() { + return Json.optional(json, "business", Conv.model(MonitoringEventCreateRequest.Business::fromJson)); + } + + /** + * Your session context for the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional session() { + return Json.optional(json, "session", Conv.model(MonitoringEventCreateRequest.Session::fromJson)); + } + + /** + * Device and network context, stored as sent and returned by GET /v3/events/{event_uuid}/. On this server lane Didit does not see the end user's connection, so the IP enriched and used by rules is network_context.ip_address as you send it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deviceContext() { + return Json.optional(json, "device_context", Conv.model(MonitoringEventCreateRequest.DeviceContext::fromJson)); + } + + /** + * Money context. Non-empty values here also satisfy the required payload keys of the action. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionContext() { + return Json.optional(json, "transaction_context", Conv.model(MonitoringEventCreateRequest.TransactionContext::fromJson)); + } + + /** + * The category's domain fields, at most 16384 bytes once JSON-encoded. Some actions require keys (in payload or transaction_context): gameplay.p2p_settlement needs game_id, counterparty_vendor_data, stake_amount, net_result_amount, role and one of hand_id, round_id, match_id; gameplay.bet_placed and gameplay.bet_settled need game_id; payment.deposit_initiated, payment.deposit_completed, payment.withdrawal_requested and payment.withdrawal_completed need payment_reference_id, amount, currency, payment_method, status; payment.withdrawal_address_changed needs withdrawal_address_hash; dispute.chargeback_opened, dispute.chargeback_won and dispute.chargeback_lost need payment_reference_id and one of dispute_id, chargeback_id; session.bot_signal_observed needs telemetry_schema_version and one of bot_score, actions_per_minute, timing_variance_ms, pointer_entropy, keystroke_entropy, reaction_time_ms_p50; responsible_gambling.self_exclusion_match needs registry_source, exclusion_status, identity_match_ref. Every security action ending in _changed needs changed_field, plus previous_reference_hash or new_reference_hash when the field is password, mfa_method, mfa, email, phone, withdrawal_address, payout_method or api_key. Send hashes and references, never a card number, a full withdrawal address, a password hash or an MFA secret. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> payload() { + return Json.optional(json, "payload", Conv.OBJECT); + } + + /** + * Up to 100 keys of your own, addressable from rules as custom_values.<key>. Values must be scalars (string, number, boolean or null); nested objects and arrays are rejected. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> customValues() { + return Json.optional(json, "custom_values", Conv.map(Conv.union(MonitoringEventCreateRequest.CustomValuesValue::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventCreateRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventCreateRequest from) { + super(from); + } + + /** + * Your identifier for the event, unique per application. It is the idempotency key: resending the same event_category, action_type, occurred_at, subject.vendor_data and payload under it is a replay, anything different is a conflict. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Event category. + * + * @param eventCategory the value + * @return this builder + */ + public Builder eventCategory(MonitoringEventCreateRequest.EventCategory eventCategory) { + return set("event_category", Conv.enumeration(MonitoringEventCreateRequest.EventCategory::of), eventCategory); + } + + /** + * What happened within the category. Open vocabulary; the managed actions listed in Event ingestion (for example login, deposit_completed, chargeback_opened) unlock typed validation and managed rule fields. Leading and trailing whitespace is trimmed. + * + * @param actionType the value + * @return this builder + */ + public Builder actionType(String actionType) { + return set("action_type", Conv.STRING, actionType); + } + + /** + * When the event happened on your side, ISO 8601 (a value without an offset is read as UTC). Must be no more than 7 days in the past and no more than 5 minutes in the future. + * + * @param occurredAt the value + * @return this builder + */ + public Builder occurredAt(OffsetDateTime occurredAt) { + return set("occurred_at", Conv.DATE_TIME, occurredAt); + } + + /** + * Time zone of the occurrence on your side, for example Europe/Madrid. Stored with the event; not returned by the read endpoints. + * + * @param timeZone the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder timeZone(@Nullable String timeZone) { + return setNullable("time_zone", Conv.STRING, timeZone); + } + + /** + * The user or company the event belongs to. It resolves to the user (or company) with this vendor_data in your application; one is created when none exists. + * + * @param subject the value + * @return this builder + */ + public Builder subject(MonitoringEventCreateRequest.Subject subject) { + return set("subject", Conv.model(MonitoringEventCreateRequest.Subject::fromJson), subject); + } + + /** + * The business the event belongs to, when it differs from the subject. Only vendor_data is read. + * + * @param business the value + * @return this builder + */ + public Builder business(MonitoringEventCreateRequest.Business business) { + return set("business", Conv.model(MonitoringEventCreateRequest.Business::fromJson), business); + } + + /** + * Your session context for the event. + * + * @param session the value + * @return this builder + */ + public Builder session(MonitoringEventCreateRequest.Session session) { + return set("session", Conv.model(MonitoringEventCreateRequest.Session::fromJson), session); + } + + /** + * Device and network context, stored as sent and returned by GET /v3/events/{event_uuid}/. On this server lane Didit does not see the end user's connection, so the IP enriched and used by rules is network_context.ip_address as you send it. + * + * @param deviceContext the value + * @return this builder + */ + public Builder deviceContext(MonitoringEventCreateRequest.DeviceContext deviceContext) { + return set("device_context", Conv.model(MonitoringEventCreateRequest.DeviceContext::fromJson), deviceContext); + } + + /** + * Money context. Non-empty values here also satisfy the required payload keys of the action. + * + * @param transactionContext the value + * @return this builder + */ + public Builder transactionContext(MonitoringEventCreateRequest.TransactionContext transactionContext) { + return set("transaction_context", Conv.model(MonitoringEventCreateRequest.TransactionContext::fromJson), transactionContext); + } + + /** + * The category's domain fields, at most 16384 bytes once JSON-encoded. Some actions require keys (in payload or transaction_context): gameplay.p2p_settlement needs game_id, counterparty_vendor_data, stake_amount, net_result_amount, role and one of hand_id, round_id, match_id; gameplay.bet_placed and gameplay.bet_settled need game_id; payment.deposit_initiated, payment.deposit_completed, payment.withdrawal_requested and payment.withdrawal_completed need payment_reference_id, amount, currency, payment_method, status; payment.withdrawal_address_changed needs withdrawal_address_hash; dispute.chargeback_opened, dispute.chargeback_won and dispute.chargeback_lost need payment_reference_id and one of dispute_id, chargeback_id; session.bot_signal_observed needs telemetry_schema_version and one of bot_score, actions_per_minute, timing_variance_ms, pointer_entropy, keystroke_entropy, reaction_time_ms_p50; responsible_gambling.self_exclusion_match needs registry_source, exclusion_status, identity_match_ref. Every security action ending in _changed needs changed_field, plus previous_reference_hash or new_reference_hash when the field is password, mfa_method, mfa, email, phone, withdrawal_address, payout_method or api_key. Send hashes and references, never a card number, a full withdrawal address, a password hash or an MFA secret. + * + * @param payload the value + * @return this builder + */ + public Builder payload(Map payload) { + return setAny("payload", payload); + } + + /** + * Up to 100 keys of your own, addressable from rules as custom_values.<key>. Values must be scalars (string, number, boolean or null); nested objects and arrays are rejected. + * + * @param customValues the value + * @return this builder + */ + public Builder customValues(Map customValues) { + return set("custom_values", Conv.map(Conv.union(MonitoringEventCreateRequest.CustomValuesValue::fromJson)), customValues); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventCreateRequest} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEventCreateRequest build() { + require("event_id", false); + require("event_category", false); + require("action_type", false); + require("occurred_at", false); + require("subject", false); + return new MonitoringEventCreateRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Event category. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EventCategory extends StringEnum { + /** The value {@code "auth"}. */ + public static final EventCategory AUTH = new EventCategory("auth"); + + /** The value {@code "session"}. */ + public static final EventCategory SESSION = new EventCategory("session"); + + /** The value {@code "gameplay"}. */ + public static final EventCategory GAMEPLAY = new EventCategory("gameplay"); + + /** The value {@code "bonus"}. */ + public static final EventCategory BONUS = new EventCategory("bonus"); + + /** The value {@code "affiliate"}. */ + public static final EventCategory AFFILIATE = new EventCategory("affiliate"); + + /** The value {@code "payment"}. */ + public static final EventCategory PAYMENT = new EventCategory("payment"); + + /** The value {@code "dispute"}. */ + public static final EventCategory DISPUTE = new EventCategory("dispute"); + + /** The value {@code "security"}. */ + public static final EventCategory SECURITY = new EventCategory("security"); + + /** The value {@code "responsible_gambling"}. */ + public static final EventCategory RESPONSIBLE_GAMBLING = new EventCategory("responsible_gambling"); + + /** The value {@code "custom"}. */ + public static final EventCategory CUSTOM = new EventCategory("custom"); + + private static final List KNOWN_VALUES = List.of(AUTH, SESSION, GAMEPLAY, BONUS, AFFILIATE, PAYMENT, DISPUTE, SECURITY, RESPONSIBLE_GAMBLING, CUSTOM); + + private EventCategory(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EventCategory of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EventCategory::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The user or company the event belongs to. It resolves to the user (or company) with this vendor_data in your application; one is created when none exists. + */ + public static final class Subject extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("vendor_data", "entity_type", "full_name", "email", "phone", "country"); + + private Subject(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Subject} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Subject fromJson(JsonNode json) { + return new Subject(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Your id for the subject. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String vendorData() { + return Json.required(json, "vendor_data", Conv.STRING); + } + + /** + * company resolves the subject to a company; any other value (default individual) resolves it to an individual. The same vendor_data can name one of each. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.STRING); + } + + /** + * Display name, used only when this event creates the user or company. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fullName() { + return Json.optional(json, "full_name", Conv.STRING); + } + + /** + * Validated as an email address; not stored on the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + /** + * Validated; not stored on the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phone() { + return Json.optional(json, "phone", Conv.STRING); + } + + /** + * Validated; not stored on the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Subject}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Subject from) { + super(from); + } + + /** + * Your id for the subject. + * + * @param vendorData the value + * @return this builder + */ + public Builder vendorData(String vendorData) { + return set("vendor_data", Conv.STRING, vendorData); + } + + /** + * company resolves the subject to a company; any other value (default individual) resolves it to an individual. The same vendor_data can name one of each. + * + * @param entityType the value + * @return this builder + */ + public Builder entityType(String entityType) { + return set("entity_type", Conv.STRING, entityType); + } + + /** + * Display name, used only when this event creates the user or company. + * + * @param fullName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fullName(@Nullable String fullName) { + return setNullable("full_name", Conv.STRING, fullName); + } + + /** + * Validated as an email address; not stored on the event. + * + * @param email the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder email(@Nullable String email) { + return setNullable("email", Conv.STRING, email); + } + + /** + * Validated; not stored on the event. + * + * @param phone the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder phone(@Nullable String phone) { + return setNullable("phone", Conv.STRING, phone); + } + + /** + * Validated; not stored on the event. + * + * @param country the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder country(@Nullable String country) { + return setNullable("country", Conv.STRING, country); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Subject} + * @throws IllegalStateException when a required field is missing + */ + public Subject build() { + require("vendor_data", false); + return new Subject(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * The business the event belongs to, when it differs from the subject. Only vendor_data is read. + */ + public static final class Business extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("vendor_data"); + + private Business(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Business} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Business fromJson(JsonNode json) { + return new Business(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Your id for the business. Filterable as business_vendor_data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Business}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Business from) { + super(from); + } + + /** + * Your id for the business. Filterable as business_vendor_data. + * + * @param vendorData the value + * @return this builder + */ + public Builder vendorData(String vendorData) { + return set("vendor_data", Conv.STRING, vendorData); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Business} + */ + public Business build() { + return new Business(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Your session context for the event. */ + public static final class Session extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "sequence", "page_url", "referrer"); + + private Session(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Session} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Session fromJson(JsonNode json) { + return new Session(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Your session identifier. Filterable and returned as session_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * Position of the event within the session. Returned as session_sequence. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sequence() { + return Json.optional(json, "sequence", Conv.LONG); + } + + /** + * Validated; not stored on the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional pageUrl() { + return Json.optional(json, "page_url", Conv.STRING); + } + + /** + * Validated; not stored on the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referrer() { + return Json.optional(json, "referrer", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Session}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Session from) { + super(from); + } + + /** + * Your session identifier. Filterable and returned as session_id. + * + * @param sessionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionId(@Nullable String sessionId) { + return setNullable("session_id", Conv.STRING, sessionId); + } + + /** + * Position of the event within the session. Returned as session_sequence. + * + * @param sequence the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sequence(@Nullable Long sequence) { + return setNullable("sequence", Conv.LONG, sequence); + } + + /** + * Validated; not stored on the event. + * + * @param pageUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder pageUrl(@Nullable String pageUrl) { + return setNullable("page_url", Conv.STRING, pageUrl); + } + + /** + * Validated; not stored on the event. + * + * @param referrer the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder referrer(@Nullable String referrer) { + return setNullable("referrer", Conv.STRING, referrer); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Session} + */ + public Session build() { + return new Session(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * Device and network context, stored as sent and returned by GET /v3/events/{event_uuid}/. On this server lane Didit does not see the end user's connection, so the IP enriched and used by rules is network_context.ip_address as you send it. + */ + public static final class DeviceContext extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("fingerprint", "persistent_device_id", "bot_score", "network_context"); + + private DeviceContext(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code DeviceContext} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DeviceContext fromJson(JsonNode json) { + return new DeviceContext(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Device fingerprint. Filterable as device_fingerprint. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fingerprint() { + return Json.optional(json, "fingerprint", Conv.STRING); + } + + /** + * Your persistent device identifier. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional persistentDeviceId() { + return Json.optional(json, "persistent_device_id", Conv.STRING); + } + + /** + * Bot score from your own detection, as an integer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional botScore() { + return Json.optional(json, "bot_score", Conv.LONG); + } + + /** + * The network_context field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional networkContext() { + return Json.optional(json, "network_context", Conv.model(MonitoringEventCreateRequest.DeviceContext.NetworkContext::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link DeviceContext}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(DeviceContext from) { + super(from); + } + + /** + * Device fingerprint. Filterable as device_fingerprint. + * + * @param fingerprint the value + * @return this builder + */ + public Builder fingerprint(String fingerprint) { + return set("fingerprint", Conv.STRING, fingerprint); + } + + /** + * Your persistent device identifier. + * + * @param persistentDeviceId the value + * @return this builder + */ + public Builder persistentDeviceId(String persistentDeviceId) { + return set("persistent_device_id", Conv.STRING, persistentDeviceId); + } + + /** + * Bot score from your own detection, as an integer. + * + * @param botScore the value + * @return this builder + */ + public Builder botScore(long botScore) { + return set("bot_score", Conv.LONG, botScore); + } + + /** + * The network_context field. + * + * @param networkContext the value + * @return this builder + */ + public Builder networkContext(MonitoringEventCreateRequest.DeviceContext.NetworkContext networkContext) { + return set("network_context", Conv.model(MonitoringEventCreateRequest.DeviceContext.NetworkContext::fromJson), networkContext); + } + + /** + * Builds the value. + * + * @return a new immutable {@code DeviceContext} + */ + public DeviceContext build() { + return new DeviceContext(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class NetworkContext extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("ip_address"); + + private NetworkContext(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkContext} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkContext fromJson(JsonNode json) { + return new NetworkContext(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * End user's IP address. Public addresses are enriched once per unique address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipAddress() { + return Json.optional(json, "ip_address", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkContext}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkContext from) { + super(from); + } + + /** + * End user's IP address. Public addresses are enriched once per unique address. + * + * @param ipAddress the value + * @return this builder + */ + public Builder ipAddress(String ipAddress) { + return set("ip_address", Conv.STRING, ipAddress); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkContext} + */ + public NetworkContext build() { + return new NetworkContext(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } + + /** + * Money context. Non-empty values here also satisfy the required payload keys of the action. + */ + public static final class TransactionContext extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("amount", "currency", "direction", "payment_reference_id"); + + private TransactionContext(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionContext} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionContext fromJson(JsonNode json) { + return new TransactionContext(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Amount as a decimal string (a number is also accepted). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional amount() { + return Json.optional(json, "amount", Conv.STRING); + } + + /** + * Currency code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currency() { + return Json.optional(json, "currency", Conv.STRING); + } + + /** + * Direction of the money movement, for example inbound or outbound. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional direction() { + return Json.optional(json, "direction", Conv.STRING); + } + + /** + * Your payment reference. Filterable as payment_reference_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional paymentReferenceId() { + return Json.optional(json, "payment_reference_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionContext}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionContext from) { + super(from); + } + + /** + * Amount as a decimal string (a number is also accepted). + * + * @param amount the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder amount(@Nullable String amount) { + return setNullable("amount", Conv.STRING, amount); + } + + /** + * Currency code. + * + * @param currency the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder currency(@Nullable String currency) { + return setNullable("currency", Conv.STRING, currency); + } + + /** + * Direction of the money movement, for example inbound or outbound. + * + * @param direction the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder direction(@Nullable String direction) { + return setNullable("direction", Conv.STRING, direction); + } + + /** + * Your payment reference. Filterable as payment_reference_id. + * + * @param paymentReferenceId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder paymentReferenceId(@Nullable String paymentReferenceId) { + return setNullable("payment_reference_id", Conv.STRING, paymentReferenceId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionContext} + */ + public TransactionContext build() { + return new TransactionContext(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * + *

One of: {@code String}, {@code Double}, {@code Boolean}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class CustomValuesValue extends JsonUnion { + private CustomValuesValue(JsonNode json) { + super(json); + } + + /** + * Reads a {@code CustomValuesValue} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static CustomValuesValue fromJson(JsonNode json) { + return new CustomValuesValue(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static CustomValuesValue ofString(String value) { + return new CustomValuesValue(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code Double} value. + * + * @param value the value + * @return the union + */ + public static CustomValuesValue ofDouble(Double value) { + return new CustomValuesValue(Conv.DOUBLE.write(value)); + } + + /** + * Whether this value is a {@code Double}. + * + * @return true when it matches + */ + public boolean isDouble() { + return json.isNumber(); + } + + /** + * Reads this value as a {@code Double}. + * + * @return the value, or empty when {@link #isDouble()} is false + */ + public Optional asDouble() { + return isDouble() ? Optional.of(Conv.DOUBLE.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code Boolean} value. + * + * @param value the value + * @return the union + */ + public static CustomValuesValue ofBoolean(Boolean value) { + return new CustomValuesValue(Conv.BOOLEAN.write(value)); + } + + /** + * Whether this value is a {@code Boolean}. + * + * @return true when it matches + */ + public boolean isBoolean() { + return json.isBoolean(); + } + + /** + * Reads this value as a {@code Boolean}. + * + * @return the value, or empty when {@link #isBoolean()} is false + */ + public Optional asBoolean() { + return isBoolean() ? Optional.of(Conv.BOOLEAN.read(json)) : Optional.empty(); + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventDetail.java b/src/main/java/me/didit/types/MonitoringEventDetail.java new file mode 100644 index 0000000..b0dae1d --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventDetail.java @@ -0,0 +1,1822 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One monitoring event with its full envelope, enrichment and rule evidence. Every field of MonitoringEvent plus the fields below. + */ +public final class MonitoringEventDetail extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_uuid", "event_id", "event_category", "action_type", "occurred_at", "received_at", "status", "score", "subject_vendor_data", "subject_entity_type", "subject_uuid", "counterparty_vendor_data", "session_id", "device_fingerprint", "ip_address", "ip_country", "amount", "currency", "game_id", "round_id", "bonus_id", "affiliate_code", "payment_reference_id", "retention_class", "rules_matched_count", "correlation_pending", "is_droppable", "payload", "custom_values", "device_context", "field_sources", "session_sequence", "table_id", "hand_id", "match_id", "market_id", "side", "role", "net_result_amount", "payment_fingerprint", "instrument_reference_hash", "withdrawal_address_hash", "dispute_id", "chargeback_id", "changed_field", "identity_match_ref", "schema_version", "expires_at", "rules_evaluated_count", "rule_runs", "ip_enrichment", "subject_profile", "tags"); + + private MonitoringEventDetail(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventDetail} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventDetail fromJson(JsonNode json) { + return new MonitoringEventDetail(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Didit identifier of the event. Use it as {event_uuid} in the retrieve and delete calls. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventUuid() { + return Json.required(json, "event_uuid", Conv.STRING); + } + + /** + * Your own identifier, the idempotency key you sent. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Event category. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEvent.EventCategory eventCategory() { + return Json.required(json, "event_category", Conv.enumeration(MonitoringEvent.EventCategory::of)); + } + + /** + * What happened within the category, as you sent it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String actionType() { + return Json.required(json, "action_type", Conv.STRING); + } + + /** + * When the event happened on your side. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime occurredAt() { + return Json.required(json, "occurred_at", Conv.DATE_TIME); + } + + /** + * When Didit accepted the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime receivedAt() { + return Json.required(json, "received_at", Conv.DATE_TIME); + } + + /** + * ACCEPTED: stored, no matched rule changed its standing (the normal answer for most events, and always the answer in a sandbox application, where no rules run). APPROVED: a rule matched and the score stayed under your review threshold. IN_REVIEW: a rule pushed the score over the review threshold or set the status. DECLINED: a rule declined it or the score crossed your decline threshold. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEvent.Status status() { + return Json.required(json, "status", Conv.enumeration(MonitoringEvent.Status::of)); + } + + /** + * Accumulated rule score. Starts at 0; rule add_score actions move it (negative deltas are allowed). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long score() { + return Json.required(json, "score", Conv.LONG); + } + + /** + * subject.vendor_data of the event: your id for the user or company. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String subjectVendorData() { + return Json.required(json, "subject_vendor_data", Conv.STRING); + } + + /** + * individual or company: which kind of subject subject_vendor_data names. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String subjectEntityType() { + return Json.required(json, "subject_entity_type", Conv.STRING); + } + + /** + * UUID of the user (individual) or company the event resolved to, or null when that record no longer exists. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectUuid() { + return Json.optional(json, "subject_uuid", Conv.STRING); + } + + /** + * The other player of a P2P event (promoted from payload.counterparty_vendor_data). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyVendorData() { + return Json.optional(json, "counterparty_vendor_data", Conv.STRING); + } + + /** + * session.session_id sent with the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * device_context.fingerprint sent with the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deviceFingerprint() { + return Json.optional(json, "device_fingerprint", Conv.STRING); + } + + /** + * IP address of the event. On the server lane this is device_context.network_context.ip_address as you sent it; on the device lane it is the IP of the request. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipAddress() { + return Json.optional(json, "ip_address", Conv.STRING); + } + + /** + * Country code of ip_address from IP enrichment, or null when the address was not enriched (missing, private, or low-value telemetry whose address was not already known). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipCountry() { + return Json.optional(json, "ip_country", Conv.STRING); + } + + /** + * Amount as a decimal string with 18 decimal places, from transaction_context.amount, payload.amount or payload.stake_amount, in that order. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional amount() { + return Json.optional(json, "amount", Conv.STRING); + } + + /** + * Currency from transaction_context.currency or payload.currency. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currency() { + return Json.optional(json, "currency", Conv.STRING); + } + + /** + * Promoted from payload.game_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional gameId() { + return Json.optional(json, "game_id", Conv.STRING); + } + + /** + * Promoted from payload.round_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional roundId() { + return Json.optional(json, "round_id", Conv.STRING); + } + + /** + * Promoted from payload.bonus_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bonusId() { + return Json.optional(json, "bonus_id", Conv.STRING); + } + + /** + * Promoted from payload.affiliate_code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional affiliateCode() { + return Json.optional(json, "affiliate_code", Conv.STRING); + } + + /** + * Payment reference from transaction_context.payment_reference_id or payload.payment_reference_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional paymentReferenceId() { + return Json.optional(json, "payment_reference_id", Conv.STRING); + } + + /** + * Retention class. Payment and dispute events are monetary_or_compliance; security and responsible-gambling events are risk_relevant; session.page_view, session.sdk_heartbeat, session.sdk_loaded, session.device_seen, session.bot_signal_observed, bonus.promotion_viewed and affiliate.referral_clicked are low_value; everything else is standard. An event an active rule matched is promoted to risk_relevant (never demoted). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEvent.RetentionClass retentionClass() { + return Json.required(json, "retention_class", Conv.enumeration(MonitoringEvent.RetentionClass::of)); + } + + /** + * Number of rules that matched the event, including rules in TEST mode (which record evidence but apply no actions). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long rulesMatchedCount() { + return Json.required(json, "rules_matched_count", Conv.LONG); + } + + /** + * true while an asynchronous rule (cross-subject correlation, or a sequence longer than 24 hours) still has to run for this event. It resolves within minutes and status, score and rules_matched_count update in place. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean correlationPending() { + return Json.required(json, "correlation_pending", Conv.BOOLEAN); + } + + /** + * Whether an SDK under backpressure may drop events of this kind. false for every security, payment, dispute and responsible_gambling event and for gameplay.bet_placed, gameplay.bet_settled and gameplay.p2p_settlement. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isDroppable() { + return Json.required(json, "is_droppable", Conv.BOOLEAN); + } + + /** + * The category fields you sent in payload. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map payload() { + return Json.required(json, "payload", Conv.OBJECT); + } + + /** + * The scalar custom_values you sent. Didit adds vpn_or_proxy_detected: true or is_data_center: true when IP enrichment flags the address and you did not set them. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map customValues() { + return Json.required(json, "custom_values", Conv.OBJECT); + } + + /** + * The device context as stored: your device_context (including fingerprint, persistent_device_id and bot_score when sent) with network_context completed by IP enrichment (ip_country, ip_country_code, ip_city, asn_number, asn_organization, is_vpn_or_tor, is_data_center, proxy_type, connection_type) for keys you left empty. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map deviceContext() { + return Json.required(json, "device_context", Conv.OBJECT); + } + + /** + * Field path to provenance: customer (sent by your backend), sdk (sent through the device lane) or server (derived by Didit, such as received_at and enriched network fields). Check it before acting on a match. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map fieldSources() { + return Json.required(json, "field_sources", Conv.map(Conv.enumeration(MonitoringEventDetail.FieldSourcesValue::of))); + } + + /** + * session.sequence sent with the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionSequence() { + return Json.optional(json, "session_sequence", Conv.LONG); + } + + /** + * Promoted from payload.table_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional tableId() { + return Json.optional(json, "table_id", Conv.STRING); + } + + /** + * Promoted from payload.hand_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional handId() { + return Json.optional(json, "hand_id", Conv.STRING); + } + + /** + * Promoted from payload.match_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchId() { + return Json.optional(json, "match_id", Conv.STRING); + } + + /** + * Promoted from payload.market_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional marketId() { + return Json.optional(json, "market_id", Conv.STRING); + } + + /** + * Promoted from payload.side. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional side() { + return Json.optional(json, "side", Conv.STRING); + } + + /** + * Promoted from payload.role. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional role() { + return Json.optional(json, "role", Conv.STRING); + } + + /** + * Promoted from payload.net_result_amount, as a decimal string with 18 decimal places. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional netResultAmount() { + return Json.optional(json, "net_result_amount", Conv.STRING); + } + + /** + * Promoted from payload.payment_fingerprint. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional paymentFingerprint() { + return Json.optional(json, "payment_fingerprint", Conv.STRING); + } + + /** + * Promoted from payload.instrument_reference_hash. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional instrumentReferenceHash() { + return Json.optional(json, "instrument_reference_hash", Conv.STRING); + } + + /** + * Promoted from payload.withdrawal_address_hash (or payload.destination_reference_hash). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional withdrawalAddressHash() { + return Json.optional(json, "withdrawal_address_hash", Conv.STRING); + } + + /** + * Promoted from payload.dispute_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional disputeId() { + return Json.optional(json, "dispute_id", Conv.STRING); + } + + /** + * Promoted from payload.chargeback_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional chargebackId() { + return Json.optional(json, "chargeback_id", Conv.STRING); + } + + /** + * Promoted from payload.changed_field (security change events). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional changedField() { + return Json.optional(json, "changed_field", Conv.STRING); + } + + /** + * Promoted from payload.identity_match_ref (responsible-gambling registry matches). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional identityMatchRef() { + return Json.optional(json, "identity_match_ref", Conv.STRING); + } + + /** + * Version of the event envelope schema. Currently 1. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long schemaVersion() { + return Json.required(json, "schema_version", Conv.LONG); + } + + /** + * When the event expires under its retention class and is deleted: occurred_at plus the class period (14, 30 or 90 days by default, adjustable per application). Null for monetary_or_compliance events, which follow transaction retention. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expiresAt() { + return Json.optional(json, "expires_at", Conv.DATE_TIME); + } + + /** + * Number of event rules evaluated for this event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long rulesEvaluatedCount() { + return Json.required(json, "rules_evaluated_count", Conv.LONG); + } + + /** + * Every rule that matched the event, newest first. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List ruleRuns() { + return Json.required(json, "rule_runs", Conv.list(Conv.model(MonitoringEventRuleRun::fromJson))); + } + + /** + * Enrichment of the event's IP address, or null when the address was not enriched. See IP enrichment. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipEnrichment() { + return Json.optional(json, "ip_enrichment", Conv.model(MonitoringEventDetail.IpEnrichment::fromJson)); + } + + /** + * Cached email and phone risk signals of the subject, or null when no profile exists for the subject. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectProfile() { + return Json.optional(json, "subject_profile", Conv.model(MonitoringEventDetail.SubjectProfile::fromJson)); + } + + /** + * Names of the tags attached to the event (rule add_tags actions). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List tags() { + return Json.required(json, "tags", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventDetail}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventDetail from) { + super(from); + } + + /** + * Didit identifier of the event. Use it as {event_uuid} in the retrieve and delete calls. + * + * @param eventUuid the value + * @return this builder + */ + public Builder eventUuid(String eventUuid) { + return set("event_uuid", Conv.STRING, eventUuid); + } + + /** + * Your own identifier, the idempotency key you sent. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Event category. + * + * @param eventCategory the value + * @return this builder + */ + public Builder eventCategory(MonitoringEvent.EventCategory eventCategory) { + return set("event_category", Conv.enumeration(MonitoringEvent.EventCategory::of), eventCategory); + } + + /** + * What happened within the category, as you sent it. + * + * @param actionType the value + * @return this builder + */ + public Builder actionType(String actionType) { + return set("action_type", Conv.STRING, actionType); + } + + /** + * When the event happened on your side. + * + * @param occurredAt the value + * @return this builder + */ + public Builder occurredAt(OffsetDateTime occurredAt) { + return set("occurred_at", Conv.DATE_TIME, occurredAt); + } + + /** + * When Didit accepted the event. + * + * @param receivedAt the value + * @return this builder + */ + public Builder receivedAt(OffsetDateTime receivedAt) { + return set("received_at", Conv.DATE_TIME, receivedAt); + } + + /** + * ACCEPTED: stored, no matched rule changed its standing (the normal answer for most events, and always the answer in a sandbox application, where no rules run). APPROVED: a rule matched and the score stayed under your review threshold. IN_REVIEW: a rule pushed the score over the review threshold or set the status. DECLINED: a rule declined it or the score crossed your decline threshold. + * + * @param status the value + * @return this builder + */ + public Builder status(MonitoringEvent.Status status) { + return set("status", Conv.enumeration(MonitoringEvent.Status::of), status); + } + + /** + * Accumulated rule score. Starts at 0; rule add_score actions move it (negative deltas are allowed). + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * subject.vendor_data of the event: your id for the user or company. + * + * @param subjectVendorData the value + * @return this builder + */ + public Builder subjectVendorData(String subjectVendorData) { + return set("subject_vendor_data", Conv.STRING, subjectVendorData); + } + + /** + * individual or company: which kind of subject subject_vendor_data names. + * + * @param subjectEntityType the value + * @return this builder + */ + public Builder subjectEntityType(String subjectEntityType) { + return set("subject_entity_type", Conv.STRING, subjectEntityType); + } + + /** + * UUID of the user (individual) or company the event resolved to, or null when that record no longer exists. + * + * @param subjectUuid the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder subjectUuid(@Nullable String subjectUuid) { + return setNullable("subject_uuid", Conv.STRING, subjectUuid); + } + + /** + * The other player of a P2P event (promoted from payload.counterparty_vendor_data). + * + * @param counterpartyVendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder counterpartyVendorData(@Nullable String counterpartyVendorData) { + return setNullable("counterparty_vendor_data", Conv.STRING, counterpartyVendorData); + } + + /** + * session.session_id sent with the event. + * + * @param sessionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionId(@Nullable String sessionId) { + return setNullable("session_id", Conv.STRING, sessionId); + } + + /** + * device_context.fingerprint sent with the event. + * + * @param deviceFingerprint the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder deviceFingerprint(@Nullable String deviceFingerprint) { + return setNullable("device_fingerprint", Conv.STRING, deviceFingerprint); + } + + /** + * IP address of the event. On the server lane this is device_context.network_context.ip_address as you sent it; on the device lane it is the IP of the request. + * + * @param ipAddress the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ipAddress(@Nullable String ipAddress) { + return setNullable("ip_address", Conv.STRING, ipAddress); + } + + /** + * Country code of ip_address from IP enrichment, or null when the address was not enriched (missing, private, or low-value telemetry whose address was not already known). + * + * @param ipCountry the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ipCountry(@Nullable String ipCountry) { + return setNullable("ip_country", Conv.STRING, ipCountry); + } + + /** + * Amount as a decimal string with 18 decimal places, from transaction_context.amount, payload.amount or payload.stake_amount, in that order. + * + * @param amount the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder amount(@Nullable String amount) { + return setNullable("amount", Conv.STRING, amount); + } + + /** + * Currency from transaction_context.currency or payload.currency. + * + * @param currency the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder currency(@Nullable String currency) { + return setNullable("currency", Conv.STRING, currency); + } + + /** + * Promoted from payload.game_id. + * + * @param gameId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder gameId(@Nullable String gameId) { + return setNullable("game_id", Conv.STRING, gameId); + } + + /** + * Promoted from payload.round_id. + * + * @param roundId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder roundId(@Nullable String roundId) { + return setNullable("round_id", Conv.STRING, roundId); + } + + /** + * Promoted from payload.bonus_id. + * + * @param bonusId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder bonusId(@Nullable String bonusId) { + return setNullable("bonus_id", Conv.STRING, bonusId); + } + + /** + * Promoted from payload.affiliate_code. + * + * @param affiliateCode the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder affiliateCode(@Nullable String affiliateCode) { + return setNullable("affiliate_code", Conv.STRING, affiliateCode); + } + + /** + * Payment reference from transaction_context.payment_reference_id or payload.payment_reference_id. + * + * @param paymentReferenceId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder paymentReferenceId(@Nullable String paymentReferenceId) { + return setNullable("payment_reference_id", Conv.STRING, paymentReferenceId); + } + + /** + * Retention class. Payment and dispute events are monetary_or_compliance; security and responsible-gambling events are risk_relevant; session.page_view, session.sdk_heartbeat, session.sdk_loaded, session.device_seen, session.bot_signal_observed, bonus.promotion_viewed and affiliate.referral_clicked are low_value; everything else is standard. An event an active rule matched is promoted to risk_relevant (never demoted). + * + * @param retentionClass the value + * @return this builder + */ + public Builder retentionClass(MonitoringEvent.RetentionClass retentionClass) { + return set("retention_class", Conv.enumeration(MonitoringEvent.RetentionClass::of), retentionClass); + } + + /** + * Number of rules that matched the event, including rules in TEST mode (which record evidence but apply no actions). + * + * @param rulesMatchedCount the value + * @return this builder + */ + public Builder rulesMatchedCount(long rulesMatchedCount) { + return set("rules_matched_count", Conv.LONG, rulesMatchedCount); + } + + /** + * true while an asynchronous rule (cross-subject correlation, or a sequence longer than 24 hours) still has to run for this event. It resolves within minutes and status, score and rules_matched_count update in place. + * + * @param correlationPending the value + * @return this builder + */ + public Builder correlationPending(boolean correlationPending) { + return set("correlation_pending", Conv.BOOLEAN, correlationPending); + } + + /** + * Whether an SDK under backpressure may drop events of this kind. false for every security, payment, dispute and responsible_gambling event and for gameplay.bet_placed, gameplay.bet_settled and gameplay.p2p_settlement. + * + * @param isDroppable the value + * @return this builder + */ + public Builder isDroppable(boolean isDroppable) { + return set("is_droppable", Conv.BOOLEAN, isDroppable); + } + + /** + * The category fields you sent in payload. + * + * @param payload the value + * @return this builder + */ + public Builder payload(Map payload) { + return setAny("payload", payload); + } + + /** + * The scalar custom_values you sent. Didit adds vpn_or_proxy_detected: true or is_data_center: true when IP enrichment flags the address and you did not set them. + * + * @param customValues the value + * @return this builder + */ + public Builder customValues(Map customValues) { + return setAny("custom_values", customValues); + } + + /** + * The device context as stored: your device_context (including fingerprint, persistent_device_id and bot_score when sent) with network_context completed by IP enrichment (ip_country, ip_country_code, ip_city, asn_number, asn_organization, is_vpn_or_tor, is_data_center, proxy_type, connection_type) for keys you left empty. + * + * @param deviceContext the value + * @return this builder + */ + public Builder deviceContext(Map deviceContext) { + return setAny("device_context", deviceContext); + } + + /** + * Field path to provenance: customer (sent by your backend), sdk (sent through the device lane) or server (derived by Didit, such as received_at and enriched network fields). Check it before acting on a match. + * + * @param fieldSources the value + * @return this builder + */ + public Builder fieldSources(Map fieldSources) { + return set("field_sources", Conv.map(Conv.enumeration(MonitoringEventDetail.FieldSourcesValue::of)), fieldSources); + } + + /** + * session.sequence sent with the event. + * + * @param sessionSequence the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionSequence(@Nullable Long sessionSequence) { + return setNullable("session_sequence", Conv.LONG, sessionSequence); + } + + /** + * Promoted from payload.table_id. + * + * @param tableId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder tableId(@Nullable String tableId) { + return setNullable("table_id", Conv.STRING, tableId); + } + + /** + * Promoted from payload.hand_id. + * + * @param handId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder handId(@Nullable String handId) { + return setNullable("hand_id", Conv.STRING, handId); + } + + /** + * Promoted from payload.match_id. + * + * @param matchId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder matchId(@Nullable String matchId) { + return setNullable("match_id", Conv.STRING, matchId); + } + + /** + * Promoted from payload.market_id. + * + * @param marketId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder marketId(@Nullable String marketId) { + return setNullable("market_id", Conv.STRING, marketId); + } + + /** + * Promoted from payload.side. + * + * @param side the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder side(@Nullable String side) { + return setNullable("side", Conv.STRING, side); + } + + /** + * Promoted from payload.role. + * + * @param role the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder role(@Nullable String role) { + return setNullable("role", Conv.STRING, role); + } + + /** + * Promoted from payload.net_result_amount, as a decimal string with 18 decimal places. + * + * @param netResultAmount the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder netResultAmount(@Nullable String netResultAmount) { + return setNullable("net_result_amount", Conv.STRING, netResultAmount); + } + + /** + * Promoted from payload.payment_fingerprint. + * + * @param paymentFingerprint the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder paymentFingerprint(@Nullable String paymentFingerprint) { + return setNullable("payment_fingerprint", Conv.STRING, paymentFingerprint); + } + + /** + * Promoted from payload.instrument_reference_hash. + * + * @param instrumentReferenceHash the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder instrumentReferenceHash(@Nullable String instrumentReferenceHash) { + return setNullable("instrument_reference_hash", Conv.STRING, instrumentReferenceHash); + } + + /** + * Promoted from payload.withdrawal_address_hash (or payload.destination_reference_hash). + * + * @param withdrawalAddressHash the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder withdrawalAddressHash(@Nullable String withdrawalAddressHash) { + return setNullable("withdrawal_address_hash", Conv.STRING, withdrawalAddressHash); + } + + /** + * Promoted from payload.dispute_id. + * + * @param disputeId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder disputeId(@Nullable String disputeId) { + return setNullable("dispute_id", Conv.STRING, disputeId); + } + + /** + * Promoted from payload.chargeback_id. + * + * @param chargebackId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder chargebackId(@Nullable String chargebackId) { + return setNullable("chargeback_id", Conv.STRING, chargebackId); + } + + /** + * Promoted from payload.changed_field (security change events). + * + * @param changedField the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder changedField(@Nullable String changedField) { + return setNullable("changed_field", Conv.STRING, changedField); + } + + /** + * Promoted from payload.identity_match_ref (responsible-gambling registry matches). + * + * @param identityMatchRef the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder identityMatchRef(@Nullable String identityMatchRef) { + return setNullable("identity_match_ref", Conv.STRING, identityMatchRef); + } + + /** + * Version of the event envelope schema. Currently 1. + * + * @param schemaVersion the value + * @return this builder + */ + public Builder schemaVersion(long schemaVersion) { + return set("schema_version", Conv.LONG, schemaVersion); + } + + /** + * When the event expires under its retention class and is deleted: occurred_at plus the class period (14, 30 or 90 days by default, adjustable per application). Null for monetary_or_compliance events, which follow transaction retention. + * + * @param expiresAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expiresAt(@Nullable OffsetDateTime expiresAt) { + return setNullable("expires_at", Conv.DATE_TIME, expiresAt); + } + + /** + * Number of event rules evaluated for this event. + * + * @param rulesEvaluatedCount the value + * @return this builder + */ + public Builder rulesEvaluatedCount(long rulesEvaluatedCount) { + return set("rules_evaluated_count", Conv.LONG, rulesEvaluatedCount); + } + + /** + * Every rule that matched the event, newest first. + * + * @param ruleRuns the value + * @return this builder + */ + public Builder ruleRuns(List ruleRuns) { + return set("rule_runs", Conv.list(Conv.model(MonitoringEventRuleRun::fromJson)), ruleRuns); + } + + /** + * Enrichment of the event's IP address, or null when the address was not enriched. See IP enrichment. + * + * @param ipEnrichment the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ipEnrichment(MonitoringEventDetail.@Nullable IpEnrichment ipEnrichment) { + return setNullable("ip_enrichment", Conv.model(MonitoringEventDetail.IpEnrichment::fromJson), ipEnrichment); + } + + /** + * Cached email and phone risk signals of the subject, or null when no profile exists for the subject. + * + * @param subjectProfile the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder subjectProfile(MonitoringEventDetail.@Nullable SubjectProfile subjectProfile) { + return setNullable("subject_profile", Conv.model(MonitoringEventDetail.SubjectProfile::fromJson), subjectProfile); + } + + /** + * Names of the tags attached to the event (rule add_tags actions). + * + * @param tags the value + * @return this builder + */ + public Builder tags(List tags) { + return set("tags", Conv.list(Conv.STRING), tags); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventDetail} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEventDetail build() { + require("event_uuid", false); + require("event_id", false); + require("event_category", false); + require("action_type", false); + require("occurred_at", false); + require("received_at", false); + require("status", false); + require("score", false); + require("subject_vendor_data", false); + require("subject_entity_type", false); + require("subject_uuid", true); + require("counterparty_vendor_data", true); + require("session_id", true); + require("device_fingerprint", true); + require("ip_address", true); + require("ip_country", true); + require("amount", true); + require("currency", true); + require("game_id", true); + require("round_id", true); + require("bonus_id", true); + require("affiliate_code", true); + require("payment_reference_id", true); + require("retention_class", false); + require("rules_matched_count", false); + require("correlation_pending", false); + require("is_droppable", false); + require("payload", false); + require("custom_values", false); + require("device_context", false); + require("field_sources", false); + require("session_sequence", true); + require("table_id", true); + require("hand_id", true); + require("match_id", true); + require("market_id", true); + require("side", true); + require("role", true); + require("net_result_amount", true); + require("payment_fingerprint", true); + require("instrument_reference_hash", true); + require("withdrawal_address_hash", true); + require("dispute_id", true); + require("chargeback_id", true); + require("changed_field", true); + require("identity_match_ref", true); + require("schema_version", false); + require("expires_at", true); + require("rules_evaluated_count", false); + require("rule_runs", false); + require("ip_enrichment", true); + require("subject_profile", true); + require("tags", false); + return new MonitoringEventDetail(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class FieldSourcesValue extends StringEnum { + /** The value {@code "customer"}. */ + public static final FieldSourcesValue CUSTOMER = new FieldSourcesValue("customer"); + + /** The value {@code "sdk"}. */ + public static final FieldSourcesValue SDK = new FieldSourcesValue("sdk"); + + /** The value {@code "server"}. */ + public static final FieldSourcesValue SERVER = new FieldSourcesValue("server"); + + private static final List KNOWN_VALUES = List.of(CUSTOMER, SDK, SERVER); + + private FieldSourcesValue(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static FieldSourcesValue of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, FieldSourcesValue::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Enrichment of the event's IP address, or null when the address was not enriched. See IP enrichment. + */ + public static final class IpEnrichment extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("ip_country_code", "ip_city", "asn_number", "asn_organization", "is_vpn_or_tor", "is_data_center", "proxy_type", "last_enriched_at"); + + private IpEnrichment(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code IpEnrichment} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static IpEnrichment fromJson(JsonNode json) { + return new IpEnrichment(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Country code of the address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipCountryCode() { + return Json.optional(json, "ip_country_code", Conv.STRING); + } + + /** + * City of the address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipCity() { + return Json.optional(json, "ip_city", Conv.STRING); + } + + /** + * Autonomous system number. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional asnNumber() { + return Json.optional(json, "asn_number", Conv.LONG); + } + + /** + * Organization that operates the autonomous system. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional asnOrganization() { + return Json.optional(json, "asn_organization", Conv.STRING); + } + + /** + * Whether the address is a known VPN or Tor exit. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isVpnOrTor() { + return Json.required(json, "is_vpn_or_tor", Conv.BOOLEAN); + } + + /** + * Whether the address belongs to a hosting or data-centre network. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isDataCenter() { + return Json.required(json, "is_data_center", Conv.BOOLEAN); + } + + /** + * Proxy classification of the address as an opaque string, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional proxyType() { + return Json.optional(json, "proxy_type", Conv.STRING); + } + + /** + * When the address was last enriched. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastEnrichedAt() { + return Json.optional(json, "last_enriched_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link IpEnrichment}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(IpEnrichment from) { + super(from); + } + + /** + * Country code of the address. + * + * @param ipCountryCode the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ipCountryCode(@Nullable String ipCountryCode) { + return setNullable("ip_country_code", Conv.STRING, ipCountryCode); + } + + /** + * City of the address. + * + * @param ipCity the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ipCity(@Nullable String ipCity) { + return setNullable("ip_city", Conv.STRING, ipCity); + } + + /** + * Autonomous system number. + * + * @param asnNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder asnNumber(@Nullable Long asnNumber) { + return setNullable("asn_number", Conv.LONG, asnNumber); + } + + /** + * Organization that operates the autonomous system. + * + * @param asnOrganization the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder asnOrganization(@Nullable String asnOrganization) { + return setNullable("asn_organization", Conv.STRING, asnOrganization); + } + + /** + * Whether the address is a known VPN or Tor exit. + * + * @param isVpnOrTor the value + * @return this builder + */ + public Builder isVpnOrTor(boolean isVpnOrTor) { + return set("is_vpn_or_tor", Conv.BOOLEAN, isVpnOrTor); + } + + /** + * Whether the address belongs to a hosting or data-centre network. + * + * @param isDataCenter the value + * @return this builder + */ + public Builder isDataCenter(boolean isDataCenter) { + return set("is_data_center", Conv.BOOLEAN, isDataCenter); + } + + /** + * Proxy classification of the address as an opaque string, or null. + * + * @param proxyType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder proxyType(@Nullable String proxyType) { + return setNullable("proxy_type", Conv.STRING, proxyType); + } + + /** + * When the address was last enriched. + * + * @param lastEnrichedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastEnrichedAt(@Nullable OffsetDateTime lastEnrichedAt) { + return setNullable("last_enriched_at", Conv.DATE_TIME, lastEnrichedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code IpEnrichment} + * @throws IllegalStateException when a required field is missing + */ + public IpEnrichment build() { + require("ip_country_code", true); + require("ip_city", true); + require("asn_number", true); + require("asn_organization", true); + require("is_vpn_or_tor", false); + require("is_data_center", false); + require("proxy_type", true); + require("last_enriched_at", true); + return new IpEnrichment(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * Cached email and phone risk signals of the subject, or null when no profile exists for the subject. + */ + public static final class SubjectProfile extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("email_risk_score", "email_deliverability", "email_is_disposable", "email_is_breached", "email_social_count", "email_social_providers", "phone_risk_score", "phone_line_type", "phone_carrier", "phone_social_count", "phone_social_providers", "source_provider", "last_enriched_at"); + + private SubjectProfile(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SubjectProfile} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SubjectProfile fromJson(JsonNode json) { + return new SubjectProfile(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Email risk score. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailRiskScore() { + return Json.optional(json, "email_risk_score", Conv.LONG); + } + + /** + * Email deliverability verdict. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailDeliverability() { + return Json.optional(json, "email_deliverability", Conv.STRING); + } + + /** + * Whether the email address is disposable. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailIsDisposable() { + return Json.optional(json, "email_is_disposable", Conv.BOOLEAN); + } + + /** + * Whether the email address appears in known data breaches. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailIsBreached() { + return Json.optional(json, "email_is_breached", Conv.BOOLEAN); + } + + /** + * Number of online accounts linked to the email address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailSocialCount() { + return Json.optional(json, "email_social_count", Conv.LONG); + } + + /** + * Names of the online services where an account linked to the email address was found. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List emailSocialProviders() { + return Json.required(json, "email_social_providers", Conv.list(Conv.STRING)); + } + + /** + * Phone risk score. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneRiskScore() { + return Json.optional(json, "phone_risk_score", Conv.LONG); + } + + /** + * Phone line type. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneLineType() { + return Json.optional(json, "phone_line_type", Conv.STRING); + } + + /** + * Phone carrier name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneCarrier() { + return Json.optional(json, "phone_carrier", Conv.STRING); + } + + /** + * Number of online accounts linked to the phone number. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneSocialCount() { + return Json.optional(json, "phone_social_count", Conv.LONG); + } + + /** + * Names of the online services where an account linked to the phone number was found. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List phoneSocialProviders() { + return Json.required(json, "phone_social_providers", Conv.list(Conv.STRING)); + } + + /** + * Opaque identifier of the source of the profile. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceProvider() { + return Json.optional(json, "source_provider", Conv.STRING); + } + + /** + * When the profile was last refreshed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastEnrichedAt() { + return Json.optional(json, "last_enriched_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SubjectProfile}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SubjectProfile from) { + super(from); + } + + /** + * Email risk score. + * + * @param emailRiskScore the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder emailRiskScore(@Nullable Long emailRiskScore) { + return setNullable("email_risk_score", Conv.LONG, emailRiskScore); + } + + /** + * Email deliverability verdict. + * + * @param emailDeliverability the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder emailDeliverability(@Nullable String emailDeliverability) { + return setNullable("email_deliverability", Conv.STRING, emailDeliverability); + } + + /** + * Whether the email address is disposable. + * + * @param emailIsDisposable the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder emailIsDisposable(@Nullable Boolean emailIsDisposable) { + return setNullable("email_is_disposable", Conv.BOOLEAN, emailIsDisposable); + } + + /** + * Whether the email address appears in known data breaches. + * + * @param emailIsBreached the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder emailIsBreached(@Nullable Boolean emailIsBreached) { + return setNullable("email_is_breached", Conv.BOOLEAN, emailIsBreached); + } + + /** + * Number of online accounts linked to the email address. + * + * @param emailSocialCount the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder emailSocialCount(@Nullable Long emailSocialCount) { + return setNullable("email_social_count", Conv.LONG, emailSocialCount); + } + + /** + * Names of the online services where an account linked to the email address was found. + * + * @param emailSocialProviders the value + * @return this builder + */ + public Builder emailSocialProviders(List emailSocialProviders) { + return set("email_social_providers", Conv.list(Conv.STRING), emailSocialProviders); + } + + /** + * Phone risk score. + * + * @param phoneRiskScore the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder phoneRiskScore(@Nullable Long phoneRiskScore) { + return setNullable("phone_risk_score", Conv.LONG, phoneRiskScore); + } + + /** + * Phone line type. + * + * @param phoneLineType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder phoneLineType(@Nullable String phoneLineType) { + return setNullable("phone_line_type", Conv.STRING, phoneLineType); + } + + /** + * Phone carrier name. + * + * @param phoneCarrier the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder phoneCarrier(@Nullable String phoneCarrier) { + return setNullable("phone_carrier", Conv.STRING, phoneCarrier); + } + + /** + * Number of online accounts linked to the phone number. + * + * @param phoneSocialCount the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder phoneSocialCount(@Nullable Long phoneSocialCount) { + return setNullable("phone_social_count", Conv.LONG, phoneSocialCount); + } + + /** + * Names of the online services where an account linked to the phone number was found. + * + * @param phoneSocialProviders the value + * @return this builder + */ + public Builder phoneSocialProviders(List phoneSocialProviders) { + return set("phone_social_providers", Conv.list(Conv.STRING), phoneSocialProviders); + } + + /** + * Opaque identifier of the source of the profile. + * + * @param sourceProvider the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sourceProvider(@Nullable String sourceProvider) { + return setNullable("source_provider", Conv.STRING, sourceProvider); + } + + /** + * When the profile was last refreshed. + * + * @param lastEnrichedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastEnrichedAt(@Nullable OffsetDateTime lastEnrichedAt) { + return setNullable("last_enriched_at", Conv.DATE_TIME, lastEnrichedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SubjectProfile} + * @throws IllegalStateException when a required field is missing + */ + public SubjectProfile build() { + require("email_risk_score", true); + require("email_deliverability", true); + require("email_is_disposable", true); + require("email_is_breached", true); + require("email_social_count", true); + require("email_social_providers", false); + require("phone_risk_score", true); + require("phone_line_type", true); + require("phone_carrier", true); + require("phone_social_count", true); + require("phone_social_providers", false); + require("source_provider", true); + require("last_enriched_at", true); + return new SubjectProfile(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventIngestResult.java b/src/main/java/me/didit/types/MonitoringEventIngestResult.java new file mode 100644 index 0000000..4e24bba --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventIngestResult.java @@ -0,0 +1,607 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Outcome of ingesting one event. The same shape is returned for a new event and for an idempotent replay. + */ +public final class MonitoringEventIngestResult extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_uuid", "event_id", "status", "score", "rules_evaluated_count", "rules_matched_count", "correlation_pending", "retention_class", "expires_at", "idempotent_replay", "rule_matches"); + + private MonitoringEventIngestResult(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventIngestResult} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventIngestResult fromJson(JsonNode json) { + return new MonitoringEventIngestResult(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Didit identifier of the stored event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventUuid() { + return Json.required(json, "event_uuid", Conv.STRING); + } + + /** + * Your event_id. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Status after the real-time rules ran. See MonitoringEvent.status. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventIngestResult.Status status() { + return Json.required(json, "status", Conv.enumeration(MonitoringEventIngestResult.Status::of)); + } + + /** + * Score after the real-time rules ran. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long score() { + return Json.required(json, "score", Conv.LONG); + } + + /** + * Number of event rules evaluated in the request. 0 in a sandbox application, where no rules run. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long rulesEvaluatedCount() { + return Json.required(json, "rules_evaluated_count", Conv.LONG); + } + + /** + * Number of rules that matched, including TEST mode rules. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long rulesMatchedCount() { + return Json.required(json, "rules_matched_count", Conv.LONG); + } + + /** + * true when an asynchronous rule still has to run. Read GET /v3/events/{event_uuid}/ a few minutes later for the final status and score. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean correlationPending() { + return Json.required(json, "correlation_pending", Conv.BOOLEAN); + } + + /** + * Retention class, after any promotion by a matched rule. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventIngestResult.RetentionClass retentionClass() { + return Json.required(json, "retention_class", Conv.enumeration(MonitoringEventIngestResult.RetentionClass::of)); + } + + /** + * When the event expires under its retention class and is deleted. Null for monetary_or_compliance (payment and dispute) events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expiresAt() { + return Json.optional(json, "expires_at", Conv.DATE_TIME); + } + + /** + * true when an event with this event_id and the same content was already stored; the values are those of the stored event and no rule ran again. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean idempotentReplay() { + return Json.required(json, "idempotent_replay", Conv.BOOLEAN); + } + + /** + * Rules that matched the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List ruleMatches() { + return Json.required(json, "rule_matches", Conv.list(Conv.model(MonitoringEventIngestResult.RuleMatches::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventIngestResult}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventIngestResult from) { + super(from); + } + + /** + * Didit identifier of the stored event. + * + * @param eventUuid the value + * @return this builder + */ + public Builder eventUuid(String eventUuid) { + return set("event_uuid", Conv.STRING, eventUuid); + } + + /** + * Your event_id. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Status after the real-time rules ran. See MonitoringEvent.status. + * + * @param status the value + * @return this builder + */ + public Builder status(MonitoringEventIngestResult.Status status) { + return set("status", Conv.enumeration(MonitoringEventIngestResult.Status::of), status); + } + + /** + * Score after the real-time rules ran. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * Number of event rules evaluated in the request. 0 in a sandbox application, where no rules run. + * + * @param rulesEvaluatedCount the value + * @return this builder + */ + public Builder rulesEvaluatedCount(long rulesEvaluatedCount) { + return set("rules_evaluated_count", Conv.LONG, rulesEvaluatedCount); + } + + /** + * Number of rules that matched, including TEST mode rules. + * + * @param rulesMatchedCount the value + * @return this builder + */ + public Builder rulesMatchedCount(long rulesMatchedCount) { + return set("rules_matched_count", Conv.LONG, rulesMatchedCount); + } + + /** + * true when an asynchronous rule still has to run. Read GET /v3/events/{event_uuid}/ a few minutes later for the final status and score. + * + * @param correlationPending the value + * @return this builder + */ + public Builder correlationPending(boolean correlationPending) { + return set("correlation_pending", Conv.BOOLEAN, correlationPending); + } + + /** + * Retention class, after any promotion by a matched rule. + * + * @param retentionClass the value + * @return this builder + */ + public Builder retentionClass(MonitoringEventIngestResult.RetentionClass retentionClass) { + return set("retention_class", Conv.enumeration(MonitoringEventIngestResult.RetentionClass::of), retentionClass); + } + + /** + * When the event expires under its retention class and is deleted. Null for monetary_or_compliance (payment and dispute) events. + * + * @param expiresAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expiresAt(@Nullable OffsetDateTime expiresAt) { + return setNullable("expires_at", Conv.DATE_TIME, expiresAt); + } + + /** + * true when an event with this event_id and the same content was already stored; the values are those of the stored event and no rule ran again. + * + * @param idempotentReplay the value + * @return this builder + */ + public Builder idempotentReplay(boolean idempotentReplay) { + return set("idempotent_replay", Conv.BOOLEAN, idempotentReplay); + } + + /** + * Rules that matched the event. + * + * @param ruleMatches the value + * @return this builder + */ + public Builder ruleMatches(List ruleMatches) { + return set("rule_matches", Conv.list(Conv.model(MonitoringEventIngestResult.RuleMatches::fromJson)), ruleMatches); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventIngestResult} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEventIngestResult build() { + require("event_uuid", false); + require("event_id", false); + require("status", false); + require("score", false); + require("rules_evaluated_count", false); + require("rules_matched_count", false); + require("correlation_pending", false); + require("retention_class", false); + require("expires_at", true); + require("idempotent_replay", false); + require("rule_matches", false); + return new MonitoringEventIngestResult(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Status after the real-time rules ran. See MonitoringEvent.status. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACCEPTED"}. */ + public static final Status ACCEPTED = new Status("ACCEPTED"); + + /** The value {@code "APPROVED"}. */ + public static final Status APPROVED = new Status("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Status DECLINED = new Status("DECLINED"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, APPROVED, IN_REVIEW, DECLINED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Retention class, after any promotion by a matched rule. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class RetentionClass extends StringEnum { + /** The value {@code "low_value"}. */ + public static final RetentionClass LOW_VALUE = new RetentionClass("low_value"); + + /** The value {@code "standard"}. */ + public static final RetentionClass STANDARD = new RetentionClass("standard"); + + /** The value {@code "risk_relevant"}. */ + public static final RetentionClass RISK_RELEVANT = new RetentionClass("risk_relevant"); + + /** The value {@code "monetary_or_compliance"}. */ + public static final RetentionClass MONETARY_OR_COMPLIANCE = new RetentionClass("monetary_or_compliance"); + + private static final List KNOWN_VALUES = List.of(LOW_VALUE, STANDARD, RISK_RELEVANT, MONETARY_OR_COMPLIANCE); + + private RetentionClass(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static RetentionClass of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RetentionClass::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class RuleMatches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("rule_id", "library_key", "mode", "is_async"); + + private RuleMatches(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code RuleMatches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static RuleMatches fromJson(JsonNode json) { + return new RuleMatches(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * UUID of the rule. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String ruleId() { + return Json.required(json, "rule_id", Conv.STRING); + } + + /** + * Library key when the rule was installed from the rules library, otherwise null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional libraryKey() { + return Json.optional(json, "library_key", Conv.STRING); + } + + /** + * TEST rules record the match but apply no actions. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventIngestResult.RuleMatches.Mode mode() { + return Json.required(json, "mode", Conv.enumeration(MonitoringEventIngestResult.RuleMatches.Mode::of)); + } + + /** + * true when the match came from the asynchronous pass. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isAsync() { + return Json.required(json, "is_async", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link RuleMatches}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(RuleMatches from) { + super(from); + } + + /** + * UUID of the rule. + * + * @param ruleId the value + * @return this builder + */ + public Builder ruleId(String ruleId) { + return set("rule_id", Conv.STRING, ruleId); + } + + /** + * Library key when the rule was installed from the rules library, otherwise null. + * + * @param libraryKey the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder libraryKey(@Nullable String libraryKey) { + return setNullable("library_key", Conv.STRING, libraryKey); + } + + /** + * TEST rules record the match but apply no actions. + * + * @param mode the value + * @return this builder + */ + public Builder mode(MonitoringEventIngestResult.RuleMatches.Mode mode) { + return set("mode", Conv.enumeration(MonitoringEventIngestResult.RuleMatches.Mode::of), mode); + } + + /** + * true when the match came from the asynchronous pass. + * + * @param isAsync the value + * @return this builder + */ + public Builder isAsync(boolean isAsync) { + return set("is_async", Conv.BOOLEAN, isAsync); + } + + /** + * Builds the value. + * + * @return a new immutable {@code RuleMatches} + * @throws IllegalStateException when a required field is missing + */ + public RuleMatches build() { + require("rule_id", false); + require("library_key", true); + require("mode", false); + require("is_async", false); + return new RuleMatches(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * TEST rules record the match but apply no actions. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Mode extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final Mode ACTIVE = new Mode("ACTIVE"); + + /** The value {@code "TEST"}. */ + public static final Mode TEST = new Mode("TEST"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, TEST); + + private Mode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Mode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Mode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventPage.java b/src/main/java/me/didit/types/MonitoringEventPage.java new file mode 100644 index 0000000..bca4c7c --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventPage.java @@ -0,0 +1,165 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** One page of monitoring events, newest occurred_at first. */ +public final class MonitoringEventPage extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private MonitoringEventPage(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventPage} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventPage fromJson(JsonNode json) { + return new MonitoringEventPage(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Number of matching events, exact up to 100 and capped at 100 above that. Do not compute page counts from it; follow next until it is null. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(MonitoringEvent::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventPage}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventPage from) { + super(from); + } + + /** + * Number of matching events, exact up to 100 and capped at 100 above that. Do not compute page counts from it; follow next until it is null. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(MonitoringEvent::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventPage} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEventPage build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new MonitoringEventPage(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventRuleRun.java b/src/main/java/me/didit/types/MonitoringEventRuleRun.java new file mode 100644 index 0000000..cf94590 --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventRuleRun.java @@ -0,0 +1,408 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class MonitoringEventRuleRun extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("rule_uuid", "rule_title", "library_key", "matched", "mode", "is_async", "severity", "score_delta", "action_summary", "matched_payload", "created_at"); + + private MonitoringEventRuleRun(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventRuleRun} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventRuleRun fromJson(JsonNode json) { + return new MonitoringEventRuleRun(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * UUID of the transaction monitoring rule. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String ruleUuid() { + return Json.required(json, "rule_uuid", Conv.STRING); + } + + /** + * Title of the rule. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String ruleTitle() { + return Json.required(json, "rule_title", Conv.STRING); + } + + /** + * Library key when the rule was installed from the rules library, otherwise null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional libraryKey() { + return Json.optional(json, "library_key", Conv.STRING); + } + + /** + * Always true: only matching runs are recorded. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean matched() { + return Json.required(json, "matched", Conv.BOOLEAN); + } + + /** + * Mode of the rule when it ran. A TEST run records evidence but applied no actions. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventRuleRun.Mode mode() { + return Json.required(json, "mode", Conv.enumeration(MonitoringEventRuleRun.Mode::of)); + } + + /** + * true when the run came from the asynchronous correlation pass rather than the ingest request. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isAsync() { + return Json.required(json, "is_async", Conv.BOOLEAN); + } + + /** + * Severity of the rule. Null for TEST runs and for rules without a severity. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional severity() { + return Json.optional(json, "severity", Conv.enumeration(MonitoringEventRuleRun.Severity::of)); + } + + /** + * How much this run changed the event's score (0 for TEST runs). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long scoreDelta() { + return Json.required(json, "score_delta", Conv.LONG); + } + + /** + * Short summary of the actions applied, for example score +40; add_tag:VPN_LOGIN. Null when nothing was applied. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actionSummary() { + return Json.optional(json, "action_summary", Conv.STRING); + } + + /** + * The conditions and aggregations that matched. Condition entries carry field, operator, expected_value, current_value, matched, group_index, group_logic and field_source (server, sdk or customer); aggregation entries carry metric, field, operator, expected_value, computed_value, window, filters, matched and sample_references. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List> matchedPayload() { + return Json.required(json, "matched_payload", Conv.list(Conv.OBJECT)); + } + + /** + * When the rule run was recorded. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventRuleRun}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventRuleRun from) { + super(from); + } + + /** + * UUID of the transaction monitoring rule. + * + * @param ruleUuid the value + * @return this builder + */ + public Builder ruleUuid(String ruleUuid) { + return set("rule_uuid", Conv.STRING, ruleUuid); + } + + /** + * Title of the rule. + * + * @param ruleTitle the value + * @return this builder + */ + public Builder ruleTitle(String ruleTitle) { + return set("rule_title", Conv.STRING, ruleTitle); + } + + /** + * Library key when the rule was installed from the rules library, otherwise null. + * + * @param libraryKey the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder libraryKey(@Nullable String libraryKey) { + return setNullable("library_key", Conv.STRING, libraryKey); + } + + /** + * Always true: only matching runs are recorded. + * + * @param matched the value + * @return this builder + */ + public Builder matched(boolean matched) { + return set("matched", Conv.BOOLEAN, matched); + } + + /** + * Mode of the rule when it ran. A TEST run records evidence but applied no actions. + * + * @param mode the value + * @return this builder + */ + public Builder mode(MonitoringEventRuleRun.Mode mode) { + return set("mode", Conv.enumeration(MonitoringEventRuleRun.Mode::of), mode); + } + + /** + * true when the run came from the asynchronous correlation pass rather than the ingest request. + * + * @param isAsync the value + * @return this builder + */ + public Builder isAsync(boolean isAsync) { + return set("is_async", Conv.BOOLEAN, isAsync); + } + + /** + * Severity of the rule. Null for TEST runs and for rules without a severity. + * + * @param severity the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder severity(MonitoringEventRuleRun.@Nullable Severity severity) { + return setNullable("severity", Conv.enumeration(MonitoringEventRuleRun.Severity::of), severity); + } + + /** + * How much this run changed the event's score (0 for TEST runs). + * + * @param scoreDelta the value + * @return this builder + */ + public Builder scoreDelta(long scoreDelta) { + return set("score_delta", Conv.LONG, scoreDelta); + } + + /** + * Short summary of the actions applied, for example score +40; add_tag:VPN_LOGIN. Null when nothing was applied. + * + * @param actionSummary the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actionSummary(@Nullable String actionSummary) { + return setNullable("action_summary", Conv.STRING, actionSummary); + } + + /** + * The conditions and aggregations that matched. Condition entries carry field, operator, expected_value, current_value, matched, group_index, group_logic and field_source (server, sdk or customer); aggregation entries carry metric, field, operator, expected_value, computed_value, window, filters, matched and sample_references. + * + * @param matchedPayload the value + * @return this builder + */ + public Builder matchedPayload(List> matchedPayload) { + return setAny("matched_payload", matchedPayload); + } + + /** + * When the rule run was recorded. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventRuleRun} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEventRuleRun build() { + require("rule_uuid", false); + require("rule_title", false); + require("library_key", true); + require("matched", false); + require("mode", false); + require("is_async", false); + require("severity", true); + require("score_delta", false); + require("action_summary", true); + require("matched_payload", false); + require("created_at", false); + return new MonitoringEventRuleRun(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Mode of the rule when it ran. A TEST run records evidence but applied no actions. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Mode extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final Mode ACTIVE = new Mode("ACTIVE"); + + /** The value {@code "TEST"}. */ + public static final Mode TEST = new Mode("TEST"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, TEST); + + private Mode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Mode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Mode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Severity of the rule. Null for TEST runs and for rules without a severity. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Severity extends StringEnum { + /** The value {@code "UNKNOWN"}. */ + public static final Severity UNKNOWN = new Severity("UNKNOWN"); + + /** The value {@code "LOW"}. */ + public static final Severity LOW = new Severity("LOW"); + + /** The value {@code "MEDIUM"}. */ + public static final Severity MEDIUM = new Severity("MEDIUM"); + + /** The value {@code "HIGH"}. */ + public static final Severity HIGH = new Severity("HIGH"); + + /** The value {@code "CRITICAL"}. */ + public static final Severity CRITICAL = new Severity("CRITICAL"); + + private static final List KNOWN_VALUES = List.of(UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL); + + private Severity(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Severity of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Severity::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventsCreateBatchParams.java b/src/main/java/me/didit/types/MonitoringEventsCreateBatchParams.java new file mode 100644 index 0000000..53d9eab --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventsCreateBatchParams.java @@ -0,0 +1,100 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code monitoring_events.create_batch}. */ +public final class MonitoringEventsCreateBatchParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("events"); + + private MonitoringEventsCreateBatchParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventsCreateBatchParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventsCreateBatchParams fromJson(JsonNode json) { + return new MonitoringEventsCreateBatchParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * 1 to 500 events. Each item follows MonitoringEventCreateRequest, including the 7-day past and 5-minute future window on occurred_at. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List events() { + return Json.required(json, "events", Conv.list(Conv.model(MonitoringEventCreateRequest::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventsCreateBatchParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventsCreateBatchParams from) { + super(from); + } + + /** + * 1 to 500 events. Each item follows MonitoringEventCreateRequest, including the 7-day past and 5-minute future window on occurred_at. + * + * @param events the value + * @return this builder + */ + public Builder events(List events) { + return set("events", Conv.list(Conv.model(MonitoringEventCreateRequest::fromJson)), events); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventsCreateBatchParams} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEventsCreateBatchParams build() { + require("events", false); + return new MonitoringEventsCreateBatchParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventsCreateBatchResponse.java b/src/main/java/me/didit/types/MonitoringEventsCreateBatchResponse.java new file mode 100644 index 0000000..0af5db7 --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventsCreateBatchResponse.java @@ -0,0 +1,1749 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.MultiStatusResponse; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Response of {@code monitoring_events.create_batch}; the shape depends on the HTTP status. + */ +public final class MonitoringEventsCreateBatchResponse extends MultiStatusResponse { + private MonitoringEventsCreateBatchResponse(int statusCode, JsonNode json) { + super(statusCode, json); + } + + /** + * Reads the response body received with {@code statusCode}. + * + * @param statusCode the HTTP status + * @param json the response body + * @return the response + */ + public static MonitoringEventsCreateBatchResponse fromResponse(int statusCode, JsonNode json) { + return new MonitoringEventsCreateBatchResponse(statusCode, json.deepCopy()); + } + + /** + * Whether the API answered 201. + * + * @return true for status 201 + */ + public boolean isCreated() { + return statusCode() == 201; + } + + /** + * The body of a 201 response. + * + * @return the body, or empty for another status + */ + public Optional asCreated() { + return isCreated() ? Optional.of(Conv.model(MonitoringEventsCreateBatchResponse.Created::fromJson).read(json)) : Optional.empty(); + } + + /** + * Whether the API answered 207. + * + * @return true for status 207 + */ + public boolean isStatus207() { + return statusCode() == 207; + } + + /** + * The body of a 207 response. + * + * @return the body, or empty for another status + */ + public Optional asStatus207() { + return isStatus207() ? Optional.of(Conv.model(MonitoringEventsCreateBatchResponse.Status207::fromJson).read(json)) : Optional.empty(); + } + + public static final class Created extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("accepted_count", "replayed_count", "rejected_count", "results"); + + private Created(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Created} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Created fromJson(JsonNode json) { + return new Created(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Items stored and evaluated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long acceptedCount() { + return Json.required(json, "accepted_count", Conv.LONG); + } + + /** + * Items already stored with the same content. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long replayedCount() { + return Json.required(json, "replayed_count", Conv.LONG); + } + + /** + * Items not stored. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long rejectedCount() { + return Json.required(json, "rejected_count", Conv.LONG); + } + + /** + * One entry per item, in request order. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(MonitoringEventsCreateBatchResponse.Created.Results::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Created}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Created from) { + super(from); + } + + /** + * Items stored and evaluated. + * + * @param acceptedCount the value + * @return this builder + */ + public Builder acceptedCount(long acceptedCount) { + return set("accepted_count", Conv.LONG, acceptedCount); + } + + /** + * Items already stored with the same content. + * + * @param replayedCount the value + * @return this builder + */ + public Builder replayedCount(long replayedCount) { + return set("replayed_count", Conv.LONG, replayedCount); + } + + /** + * Items not stored. + * + * @param rejectedCount the value + * @return this builder + */ + public Builder rejectedCount(long rejectedCount) { + return set("rejected_count", Conv.LONG, rejectedCount); + } + + /** + * One entry per item, in request order. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(MonitoringEventsCreateBatchResponse.Created.Results::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Created} + * @throws IllegalStateException when a required field is missing + */ + public Created build() { + require("accepted_count", false); + require("replayed_count", false); + require("rejected_count", false); + require("results", false); + return new Created(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Outcome of one item. Accepted and replayed items also carry every field of MonitoringEventIngestResult; rejected items carry event_id and errors instead. + */ + public static final class Results extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("index", "outcome", "event_id", "errors", "event_uuid", "status", "score", "rules_evaluated_count", "rules_matched_count", "correlation_pending", "retention_class", "expires_at", "idempotent_replay", "rule_matches"); + + private Results(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Results} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Results fromJson(JsonNode json) { + return new Results(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Position of the item in your events array. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long index() { + return Json.required(json, "index", Conv.LONG); + } + + /** + * accepted: stored and evaluated. replayed: already stored with the same content. rejected: not stored; see errors. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventsCreateBatchResponse.Created.Results.Outcome outcome() { + return Json.required(json, "outcome", Conv.enumeration(MonitoringEventsCreateBatchResponse.Created.Results.Outcome::of)); + } + + /** + * Your event_id. On items rejected by validation it is read from the item's snake_case event_id key, so it is null when the item omitted it or sent the eventId alias. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional eventId() { + return Json.optional(json, "event_id", Conv.STRING); + } + + /** + * Rejected items only. Validation errors keyed by field, in the same shape as the 400 of POST /v3/events/; a reused event_id with different content is reported as a string under errors.event_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> errors() { + return Json.optional(json, "errors", Conv.OBJECT); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional eventUuid() { + return Json.optional(json, "event_uuid", Conv.STRING); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(MonitoringEventsCreateBatchResponse.Created.Results.Status::of)); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.LONG); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rulesEvaluatedCount() { + return Json.optional(json, "rules_evaluated_count", Conv.LONG); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rulesMatchedCount() { + return Json.optional(json, "rules_matched_count", Conv.LONG); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional correlationPending() { + return Json.optional(json, "correlation_pending", Conv.BOOLEAN); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional retentionClass() { + return Json.optional(json, "retention_class", Conv.enumeration(MonitoringEventsCreateBatchResponse.Created.Results.RetentionClass::of)); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expiresAt() { + return Json.optional(json, "expires_at", Conv.DATE_TIME); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional idempotentReplay() { + return Json.optional(json, "idempotent_replay", Conv.BOOLEAN); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> ruleMatches() { + return Json.optional(json, "rule_matches", Conv.list(Conv.model(MonitoringEventsCreateBatchResponse.Created.Results.RuleMatches::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Results}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Results from) { + super(from); + } + + /** + * Position of the item in your events array. + * + * @param index the value + * @return this builder + */ + public Builder index(long index) { + return set("index", Conv.LONG, index); + } + + /** + * accepted: stored and evaluated. replayed: already stored with the same content. rejected: not stored; see errors. + * + * @param outcome the value + * @return this builder + */ + public Builder outcome(MonitoringEventsCreateBatchResponse.Created.Results.Outcome outcome) { + return set("outcome", Conv.enumeration(MonitoringEventsCreateBatchResponse.Created.Results.Outcome::of), outcome); + } + + /** + * Your event_id. On items rejected by validation it is read from the item's snake_case event_id key, so it is null when the item omitted it or sent the eventId alias. + * + * @param eventId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder eventId(@Nullable String eventId) { + return setNullable("event_id", Conv.STRING, eventId); + } + + /** + * Rejected items only. Validation errors keyed by field, in the same shape as the 400 of POST /v3/events/; a reused event_id with different content is reported as a string under errors.event_id. + * + * @param errors the value + * @return this builder + */ + public Builder errors(Map errors) { + return setAny("errors", errors); + } + + /** + * Accepted and replayed items only. + * + * @param eventUuid the value + * @return this builder + */ + public Builder eventUuid(String eventUuid) { + return set("event_uuid", Conv.STRING, eventUuid); + } + + /** + * Accepted and replayed items only. + * + * @param status the value + * @return this builder + */ + public Builder status(MonitoringEventsCreateBatchResponse.Created.Results.Status status) { + return set("status", Conv.enumeration(MonitoringEventsCreateBatchResponse.Created.Results.Status::of), status); + } + + /** + * Accepted and replayed items only. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * Accepted and replayed items only. + * + * @param rulesEvaluatedCount the value + * @return this builder + */ + public Builder rulesEvaluatedCount(long rulesEvaluatedCount) { + return set("rules_evaluated_count", Conv.LONG, rulesEvaluatedCount); + } + + /** + * Accepted and replayed items only. + * + * @param rulesMatchedCount the value + * @return this builder + */ + public Builder rulesMatchedCount(long rulesMatchedCount) { + return set("rules_matched_count", Conv.LONG, rulesMatchedCount); + } + + /** + * Accepted and replayed items only. + * + * @param correlationPending the value + * @return this builder + */ + public Builder correlationPending(boolean correlationPending) { + return set("correlation_pending", Conv.BOOLEAN, correlationPending); + } + + /** + * Accepted and replayed items only. + * + * @param retentionClass the value + * @return this builder + */ + public Builder retentionClass(MonitoringEventsCreateBatchResponse.Created.Results.RetentionClass retentionClass) { + return set("retention_class", Conv.enumeration(MonitoringEventsCreateBatchResponse.Created.Results.RetentionClass::of), retentionClass); + } + + /** + * Accepted and replayed items only. + * + * @param expiresAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expiresAt(@Nullable OffsetDateTime expiresAt) { + return setNullable("expires_at", Conv.DATE_TIME, expiresAt); + } + + /** + * Accepted and replayed items only. + * + * @param idempotentReplay the value + * @return this builder + */ + public Builder idempotentReplay(boolean idempotentReplay) { + return set("idempotent_replay", Conv.BOOLEAN, idempotentReplay); + } + + /** + * Accepted and replayed items only. + * + * @param ruleMatches the value + * @return this builder + */ + public Builder ruleMatches(List ruleMatches) { + return set("rule_matches", Conv.list(Conv.model(MonitoringEventsCreateBatchResponse.Created.Results.RuleMatches::fromJson)), ruleMatches); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Results} + * @throws IllegalStateException when a required field is missing + */ + public Results build() { + require("index", false); + require("outcome", false); + return new Results(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * accepted: stored and evaluated. replayed: already stored with the same content. rejected: not stored; see errors. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Outcome extends StringEnum { + /** The value {@code "accepted"}. */ + public static final Outcome ACCEPTED = new Outcome("accepted"); + + /** The value {@code "replayed"}. */ + public static final Outcome REPLAYED = new Outcome("replayed"); + + /** The value {@code "rejected"}. */ + public static final Outcome REJECTED = new Outcome("rejected"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, REPLAYED, REJECTED); + + private Outcome(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Outcome of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Accepted and replayed items only. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACCEPTED"}. */ + public static final Status ACCEPTED = new Status("ACCEPTED"); + + /** The value {@code "APPROVED"}. */ + public static final Status APPROVED = new Status("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Status DECLINED = new Status("DECLINED"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, APPROVED, IN_REVIEW, DECLINED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Accepted and replayed items only. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class RetentionClass extends StringEnum { + /** The value {@code "low_value"}. */ + public static final RetentionClass LOW_VALUE = new RetentionClass("low_value"); + + /** The value {@code "standard"}. */ + public static final RetentionClass STANDARD = new RetentionClass("standard"); + + /** The value {@code "risk_relevant"}. */ + public static final RetentionClass RISK_RELEVANT = new RetentionClass("risk_relevant"); + + /** The value {@code "monetary_or_compliance"}. */ + public static final RetentionClass MONETARY_OR_COMPLIANCE = new RetentionClass("monetary_or_compliance"); + + private static final List KNOWN_VALUES = List.of(LOW_VALUE, STANDARD, RISK_RELEVANT, MONETARY_OR_COMPLIANCE); + + private RetentionClass(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static RetentionClass of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RetentionClass::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class RuleMatches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("rule_id", "library_key", "mode", "is_async"); + + private RuleMatches(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code RuleMatches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static RuleMatches fromJson(JsonNode json) { + return new RuleMatches(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * UUID of the rule. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String ruleId() { + return Json.required(json, "rule_id", Conv.STRING); + } + + /** + * Library key when the rule was installed from the rules library, otherwise null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional libraryKey() { + return Json.optional(json, "library_key", Conv.STRING); + } + + /** + * TEST rules record the match but apply no actions. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventsCreateBatchResponse.Created.Results.RuleMatches.Mode mode() { + return Json.required(json, "mode", Conv.enumeration(MonitoringEventsCreateBatchResponse.Created.Results.RuleMatches.Mode::of)); + } + + /** + * true when the match came from the asynchronous pass. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isAsync() { + return Json.required(json, "is_async", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link RuleMatches}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(RuleMatches from) { + super(from); + } + + /** + * UUID of the rule. + * + * @param ruleId the value + * @return this builder + */ + public Builder ruleId(String ruleId) { + return set("rule_id", Conv.STRING, ruleId); + } + + /** + * Library key when the rule was installed from the rules library, otherwise null. + * + * @param libraryKey the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder libraryKey(@Nullable String libraryKey) { + return setNullable("library_key", Conv.STRING, libraryKey); + } + + /** + * TEST rules record the match but apply no actions. + * + * @param mode the value + * @return this builder + */ + public Builder mode(MonitoringEventsCreateBatchResponse.Created.Results.RuleMatches.Mode mode) { + return set("mode", Conv.enumeration(MonitoringEventsCreateBatchResponse.Created.Results.RuleMatches.Mode::of), mode); + } + + /** + * true when the match came from the asynchronous pass. + * + * @param isAsync the value + * @return this builder + */ + public Builder isAsync(boolean isAsync) { + return set("is_async", Conv.BOOLEAN, isAsync); + } + + /** + * Builds the value. + * + * @return a new immutable {@code RuleMatches} + * @throws IllegalStateException when a required field is missing + */ + public RuleMatches build() { + require("rule_id", false); + require("library_key", true); + require("mode", false); + require("is_async", false); + return new RuleMatches(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * TEST rules record the match but apply no actions. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Mode extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final Mode ACTIVE = new Mode("ACTIVE"); + + /** The value {@code "TEST"}. */ + public static final Mode TEST = new Mode("TEST"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, TEST); + + private Mode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Mode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Mode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + } + } + + public static final class Status207 extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("accepted_count", "replayed_count", "rejected_count", "results"); + + private Status207(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Status207} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Status207 fromJson(JsonNode json) { + return new Status207(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Items stored and evaluated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long acceptedCount() { + return Json.required(json, "accepted_count", Conv.LONG); + } + + /** + * Items already stored with the same content. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long replayedCount() { + return Json.required(json, "replayed_count", Conv.LONG); + } + + /** + * Items not stored. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long rejectedCount() { + return Json.required(json, "rejected_count", Conv.LONG); + } + + /** + * One entry per item, in request order. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(MonitoringEventsCreateBatchResponse.Status207.Results::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Status207}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Status207 from) { + super(from); + } + + /** + * Items stored and evaluated. + * + * @param acceptedCount the value + * @return this builder + */ + public Builder acceptedCount(long acceptedCount) { + return set("accepted_count", Conv.LONG, acceptedCount); + } + + /** + * Items already stored with the same content. + * + * @param replayedCount the value + * @return this builder + */ + public Builder replayedCount(long replayedCount) { + return set("replayed_count", Conv.LONG, replayedCount); + } + + /** + * Items not stored. + * + * @param rejectedCount the value + * @return this builder + */ + public Builder rejectedCount(long rejectedCount) { + return set("rejected_count", Conv.LONG, rejectedCount); + } + + /** + * One entry per item, in request order. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(MonitoringEventsCreateBatchResponse.Status207.Results::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Status207} + * @throws IllegalStateException when a required field is missing + */ + public Status207 build() { + require("accepted_count", false); + require("replayed_count", false); + require("rejected_count", false); + require("results", false); + return new Status207(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Outcome of one item. Accepted and replayed items also carry every field of MonitoringEventIngestResult; rejected items carry event_id and errors instead. + */ + public static final class Results extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("index", "outcome", "event_id", "errors", "event_uuid", "status", "score", "rules_evaluated_count", "rules_matched_count", "correlation_pending", "retention_class", "expires_at", "idempotent_replay", "rule_matches"); + + private Results(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Results} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Results fromJson(JsonNode json) { + return new Results(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Position of the item in your events array. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long index() { + return Json.required(json, "index", Conv.LONG); + } + + /** + * accepted: stored and evaluated. replayed: already stored with the same content. rejected: not stored; see errors. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventsCreateBatchResponse.Status207.Results.Outcome outcome() { + return Json.required(json, "outcome", Conv.enumeration(MonitoringEventsCreateBatchResponse.Status207.Results.Outcome::of)); + } + + /** + * Your event_id. On items rejected by validation it is read from the item's snake_case event_id key, so it is null when the item omitted it or sent the eventId alias. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional eventId() { + return Json.optional(json, "event_id", Conv.STRING); + } + + /** + * Rejected items only. Validation errors keyed by field, in the same shape as the 400 of POST /v3/events/; a reused event_id with different content is reported as a string under errors.event_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> errors() { + return Json.optional(json, "errors", Conv.OBJECT); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional eventUuid() { + return Json.optional(json, "event_uuid", Conv.STRING); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(MonitoringEventsCreateBatchResponse.Status207.Results.Status::of)); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.LONG); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rulesEvaluatedCount() { + return Json.optional(json, "rules_evaluated_count", Conv.LONG); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rulesMatchedCount() { + return Json.optional(json, "rules_matched_count", Conv.LONG); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional correlationPending() { + return Json.optional(json, "correlation_pending", Conv.BOOLEAN); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional retentionClass() { + return Json.optional(json, "retention_class", Conv.enumeration(MonitoringEventsCreateBatchResponse.Status207.Results.RetentionClass::of)); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expiresAt() { + return Json.optional(json, "expires_at", Conv.DATE_TIME); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional idempotentReplay() { + return Json.optional(json, "idempotent_replay", Conv.BOOLEAN); + } + + /** + * Accepted and replayed items only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> ruleMatches() { + return Json.optional(json, "rule_matches", Conv.list(Conv.model(MonitoringEventsCreateBatchResponse.Status207.Results.RuleMatches::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Results}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Results from) { + super(from); + } + + /** + * Position of the item in your events array. + * + * @param index the value + * @return this builder + */ + public Builder index(long index) { + return set("index", Conv.LONG, index); + } + + /** + * accepted: stored and evaluated. replayed: already stored with the same content. rejected: not stored; see errors. + * + * @param outcome the value + * @return this builder + */ + public Builder outcome(MonitoringEventsCreateBatchResponse.Status207.Results.Outcome outcome) { + return set("outcome", Conv.enumeration(MonitoringEventsCreateBatchResponse.Status207.Results.Outcome::of), outcome); + } + + /** + * Your event_id. On items rejected by validation it is read from the item's snake_case event_id key, so it is null when the item omitted it or sent the eventId alias. + * + * @param eventId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder eventId(@Nullable String eventId) { + return setNullable("event_id", Conv.STRING, eventId); + } + + /** + * Rejected items only. Validation errors keyed by field, in the same shape as the 400 of POST /v3/events/; a reused event_id with different content is reported as a string under errors.event_id. + * + * @param errors the value + * @return this builder + */ + public Builder errors(Map errors) { + return setAny("errors", errors); + } + + /** + * Accepted and replayed items only. + * + * @param eventUuid the value + * @return this builder + */ + public Builder eventUuid(String eventUuid) { + return set("event_uuid", Conv.STRING, eventUuid); + } + + /** + * Accepted and replayed items only. + * + * @param status the value + * @return this builder + */ + public Builder status(MonitoringEventsCreateBatchResponse.Status207.Results.Status status) { + return set("status", Conv.enumeration(MonitoringEventsCreateBatchResponse.Status207.Results.Status::of), status); + } + + /** + * Accepted and replayed items only. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * Accepted and replayed items only. + * + * @param rulesEvaluatedCount the value + * @return this builder + */ + public Builder rulesEvaluatedCount(long rulesEvaluatedCount) { + return set("rules_evaluated_count", Conv.LONG, rulesEvaluatedCount); + } + + /** + * Accepted and replayed items only. + * + * @param rulesMatchedCount the value + * @return this builder + */ + public Builder rulesMatchedCount(long rulesMatchedCount) { + return set("rules_matched_count", Conv.LONG, rulesMatchedCount); + } + + /** + * Accepted and replayed items only. + * + * @param correlationPending the value + * @return this builder + */ + public Builder correlationPending(boolean correlationPending) { + return set("correlation_pending", Conv.BOOLEAN, correlationPending); + } + + /** + * Accepted and replayed items only. + * + * @param retentionClass the value + * @return this builder + */ + public Builder retentionClass(MonitoringEventsCreateBatchResponse.Status207.Results.RetentionClass retentionClass) { + return set("retention_class", Conv.enumeration(MonitoringEventsCreateBatchResponse.Status207.Results.RetentionClass::of), retentionClass); + } + + /** + * Accepted and replayed items only. + * + * @param expiresAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expiresAt(@Nullable OffsetDateTime expiresAt) { + return setNullable("expires_at", Conv.DATE_TIME, expiresAt); + } + + /** + * Accepted and replayed items only. + * + * @param idempotentReplay the value + * @return this builder + */ + public Builder idempotentReplay(boolean idempotentReplay) { + return set("idempotent_replay", Conv.BOOLEAN, idempotentReplay); + } + + /** + * Accepted and replayed items only. + * + * @param ruleMatches the value + * @return this builder + */ + public Builder ruleMatches(List ruleMatches) { + return set("rule_matches", Conv.list(Conv.model(MonitoringEventsCreateBatchResponse.Status207.Results.RuleMatches::fromJson)), ruleMatches); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Results} + * @throws IllegalStateException when a required field is missing + */ + public Results build() { + require("index", false); + require("outcome", false); + return new Results(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * accepted: stored and evaluated. replayed: already stored with the same content. rejected: not stored; see errors. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Outcome extends StringEnum { + /** The value {@code "accepted"}. */ + public static final Outcome ACCEPTED = new Outcome("accepted"); + + /** The value {@code "replayed"}. */ + public static final Outcome REPLAYED = new Outcome("replayed"); + + /** The value {@code "rejected"}. */ + public static final Outcome REJECTED = new Outcome("rejected"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, REPLAYED, REJECTED); + + private Outcome(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Outcome of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Accepted and replayed items only. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACCEPTED"}. */ + public static final Status ACCEPTED = new Status("ACCEPTED"); + + /** The value {@code "APPROVED"}. */ + public static final Status APPROVED = new Status("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Status DECLINED = new Status("DECLINED"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, APPROVED, IN_REVIEW, DECLINED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Accepted and replayed items only. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class RetentionClass extends StringEnum { + /** The value {@code "low_value"}. */ + public static final RetentionClass LOW_VALUE = new RetentionClass("low_value"); + + /** The value {@code "standard"}. */ + public static final RetentionClass STANDARD = new RetentionClass("standard"); + + /** The value {@code "risk_relevant"}. */ + public static final RetentionClass RISK_RELEVANT = new RetentionClass("risk_relevant"); + + /** The value {@code "monetary_or_compliance"}. */ + public static final RetentionClass MONETARY_OR_COMPLIANCE = new RetentionClass("monetary_or_compliance"); + + private static final List KNOWN_VALUES = List.of(LOW_VALUE, STANDARD, RISK_RELEVANT, MONETARY_OR_COMPLIANCE); + + private RetentionClass(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static RetentionClass of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RetentionClass::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class RuleMatches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("rule_id", "library_key", "mode", "is_async"); + + private RuleMatches(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code RuleMatches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static RuleMatches fromJson(JsonNode json) { + return new RuleMatches(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * UUID of the rule. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String ruleId() { + return Json.required(json, "rule_id", Conv.STRING); + } + + /** + * Library key when the rule was installed from the rules library, otherwise null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional libraryKey() { + return Json.optional(json, "library_key", Conv.STRING); + } + + /** + * TEST rules record the match but apply no actions. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public MonitoringEventsCreateBatchResponse.Status207.Results.RuleMatches.Mode mode() { + return Json.required(json, "mode", Conv.enumeration(MonitoringEventsCreateBatchResponse.Status207.Results.RuleMatches.Mode::of)); + } + + /** + * true when the match came from the asynchronous pass. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isAsync() { + return Json.required(json, "is_async", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link RuleMatches}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(RuleMatches from) { + super(from); + } + + /** + * UUID of the rule. + * + * @param ruleId the value + * @return this builder + */ + public Builder ruleId(String ruleId) { + return set("rule_id", Conv.STRING, ruleId); + } + + /** + * Library key when the rule was installed from the rules library, otherwise null. + * + * @param libraryKey the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder libraryKey(@Nullable String libraryKey) { + return setNullable("library_key", Conv.STRING, libraryKey); + } + + /** + * TEST rules record the match but apply no actions. + * + * @param mode the value + * @return this builder + */ + public Builder mode(MonitoringEventsCreateBatchResponse.Status207.Results.RuleMatches.Mode mode) { + return set("mode", Conv.enumeration(MonitoringEventsCreateBatchResponse.Status207.Results.RuleMatches.Mode::of), mode); + } + + /** + * true when the match came from the asynchronous pass. + * + * @param isAsync the value + * @return this builder + */ + public Builder isAsync(boolean isAsync) { + return set("is_async", Conv.BOOLEAN, isAsync); + } + + /** + * Builds the value. + * + * @return a new immutable {@code RuleMatches} + * @throws IllegalStateException when a required field is missing + */ + public RuleMatches build() { + require("rule_id", false); + require("library_key", true); + require("mode", false); + require("is_async", false); + return new RuleMatches(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * TEST rules record the match but apply no actions. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Mode extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final Mode ACTIVE = new Mode("ACTIVE"); + + /** The value {@code "TEST"}. */ + public static final Mode TEST = new Mode("TEST"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, TEST); + + private Mode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Mode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Mode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventsCreateSdkTokenParams.java b/src/main/java/me/didit/types/MonitoringEventsCreateSdkTokenParams.java new file mode 100644 index 0000000..d69d51f --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventsCreateSdkTokenParams.java @@ -0,0 +1,269 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** Parameters for {@code monitoring_events.create_sdk_token}. */ +public final class MonitoringEventsCreateSdkTokenParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("vendor_data", "entity_type", "allowed_event_categories", "ttl_seconds", "max_uses", "bind_to_origin"); + + private MonitoringEventsCreateSdkTokenParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventsCreateSdkTokenParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventsCreateSdkTokenParams fromJson(JsonNode json) { + return new MonitoringEventsCreateSdkTokenParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Your id for the subject every event submitted with this credential belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String vendorData() { + return Json.required(json, "vendor_data", Conv.STRING); + } + + /** + * company binds a company subject; any other value (default individual) binds an individual. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.STRING); + } + + /** + * Categories the credential may submit. Omit it or send [] to allow every category. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> allowedEventCategories() { + return Json.optional(json, "allowed_event_categories", Conv.list(Conv.enumeration(MonitoringEventsCreateSdkTokenParams.AllowedEventCategories::of))); + } + + /** + * Lifetime of the credential in seconds. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ttlSeconds() { + return Json.optional(json, "ttl_seconds", Conv.LONG); + } + + /** + * Number of events the credential may submit (a batch spends one use per event). Send null for no cap within the lifetime. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional maxUses() { + return Json.optional(json, "max_uses", Conv.LONG); + } + + /** + * When set, the browser Origin header of every device request must equal this value exactly; a request without Origin is refused. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bindToOrigin() { + return Json.optional(json, "bind_to_origin", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventsCreateSdkTokenParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventsCreateSdkTokenParams from) { + super(from); + } + + /** + * Your id for the subject every event submitted with this credential belongs to. + * + * @param vendorData the value + * @return this builder + */ + public Builder vendorData(String vendorData) { + return set("vendor_data", Conv.STRING, vendorData); + } + + /** + * company binds a company subject; any other value (default individual) binds an individual. + * + * @param entityType the value + * @return this builder + */ + public Builder entityType(String entityType) { + return set("entity_type", Conv.STRING, entityType); + } + + /** + * Categories the credential may submit. Omit it or send [] to allow every category. + * + * @param allowedEventCategories the value + * @return this builder + */ + public Builder allowedEventCategories(List allowedEventCategories) { + return set("allowed_event_categories", Conv.list(Conv.enumeration(MonitoringEventsCreateSdkTokenParams.AllowedEventCategories::of)), allowedEventCategories); + } + + /** + * Lifetime of the credential in seconds. + * + * @param ttlSeconds the value + * @return this builder + */ + public Builder ttlSeconds(long ttlSeconds) { + return set("ttl_seconds", Conv.LONG, ttlSeconds); + } + + /** + * Number of events the credential may submit (a batch spends one use per event). Send null for no cap within the lifetime. + * + * @param maxUses the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder maxUses(@Nullable Long maxUses) { + return setNullable("max_uses", Conv.LONG, maxUses); + } + + /** + * When set, the browser Origin header of every device request must equal this value exactly; a request without Origin is refused. + * + * @param bindToOrigin the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder bindToOrigin(@Nullable String bindToOrigin) { + return setNullable("bind_to_origin", Conv.STRING, bindToOrigin); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventsCreateSdkTokenParams} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEventsCreateSdkTokenParams build() { + require("vendor_data", false); + return new MonitoringEventsCreateSdkTokenParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class AllowedEventCategories extends StringEnum { + /** The value {@code "auth"}. */ + public static final AllowedEventCategories AUTH = new AllowedEventCategories("auth"); + + /** The value {@code "session"}. */ + public static final AllowedEventCategories SESSION = new AllowedEventCategories("session"); + + /** The value {@code "gameplay"}. */ + public static final AllowedEventCategories GAMEPLAY = new AllowedEventCategories("gameplay"); + + /** The value {@code "bonus"}. */ + public static final AllowedEventCategories BONUS = new AllowedEventCategories("bonus"); + + /** The value {@code "affiliate"}. */ + public static final AllowedEventCategories AFFILIATE = new AllowedEventCategories("affiliate"); + + /** The value {@code "payment"}. */ + public static final AllowedEventCategories PAYMENT = new AllowedEventCategories("payment"); + + /** The value {@code "dispute"}. */ + public static final AllowedEventCategories DISPUTE = new AllowedEventCategories("dispute"); + + /** The value {@code "security"}. */ + public static final AllowedEventCategories SECURITY = new AllowedEventCategories("security"); + + /** The value {@code "responsible_gambling"}. */ + public static final AllowedEventCategories RESPONSIBLE_GAMBLING = new AllowedEventCategories("responsible_gambling"); + + /** The value {@code "custom"}. */ + public static final AllowedEventCategories CUSTOM = new AllowedEventCategories("custom"); + + private static final List KNOWN_VALUES = List.of(AUTH, SESSION, GAMEPLAY, BONUS, AFFILIATE, PAYMENT, DISPUTE, SECURITY, RESPONSIBLE_GAMBLING, CUSTOM); + + private AllowedEventCategories(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static AllowedEventCategories of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, AllowedEventCategories::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventsCreateSdkTokenResponse.java b/src/main/java/me/didit/types/MonitoringEventsCreateSdkTokenResponse.java new file mode 100644 index 0000000..11d278d --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventsCreateSdkTokenResponse.java @@ -0,0 +1,253 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class MonitoringEventsCreateSdkTokenResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("monitoring_token", "expires_at", "allowed_event_categories", "max_uses", "bind_to_origin"); + + private MonitoringEventsCreateSdkTokenResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventsCreateSdkTokenResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventsCreateSdkTokenResponse fromJson(JsonNode json) { + return new MonitoringEventsCreateSdkTokenResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The device credential (a 43-character URL-safe token). Send it in the X-Monitoring-Token header. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String monitoringToken() { + return Json.required(json, "monitoring_token", Conv.STRING); + } + + /** + * When the credential stops being accepted. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime expiresAt() { + return Json.required(json, "expires_at", Conv.DATE_TIME); + } + + /** + * Categories the credential may submit; [] means every category. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List allowedEventCategories() { + return Json.required(json, "allowed_event_categories", Conv.list(Conv.enumeration(MonitoringEventsCreateSdkTokenResponse.AllowedEventCategories::of))); + } + + /** + * Event budget of the credential; null means no cap. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional maxUses() { + return Json.optional(json, "max_uses", Conv.LONG); + } + + /** + * Origin the credential is bound to, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bindToOrigin() { + return Json.optional(json, "bind_to_origin", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventsCreateSdkTokenResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventsCreateSdkTokenResponse from) { + super(from); + } + + /** + * The device credential (a 43-character URL-safe token). Send it in the X-Monitoring-Token header. + * + * @param monitoringToken the value + * @return this builder + */ + public Builder monitoringToken(String monitoringToken) { + return set("monitoring_token", Conv.STRING, monitoringToken); + } + + /** + * When the credential stops being accepted. + * + * @param expiresAt the value + * @return this builder + */ + public Builder expiresAt(OffsetDateTime expiresAt) { + return set("expires_at", Conv.DATE_TIME, expiresAt); + } + + /** + * Categories the credential may submit; [] means every category. + * + * @param allowedEventCategories the value + * @return this builder + */ + public Builder allowedEventCategories(List allowedEventCategories) { + return set("allowed_event_categories", Conv.list(Conv.enumeration(MonitoringEventsCreateSdkTokenResponse.AllowedEventCategories::of)), allowedEventCategories); + } + + /** + * Event budget of the credential; null means no cap. + * + * @param maxUses the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder maxUses(@Nullable Long maxUses) { + return setNullable("max_uses", Conv.LONG, maxUses); + } + + /** + * Origin the credential is bound to, or null. + * + * @param bindToOrigin the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder bindToOrigin(@Nullable String bindToOrigin) { + return setNullable("bind_to_origin", Conv.STRING, bindToOrigin); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventsCreateSdkTokenResponse} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEventsCreateSdkTokenResponse build() { + require("monitoring_token", false); + require("expires_at", false); + require("allowed_event_categories", false); + require("max_uses", true); + require("bind_to_origin", true); + return new MonitoringEventsCreateSdkTokenResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class AllowedEventCategories extends StringEnum { + /** The value {@code "auth"}. */ + public static final AllowedEventCategories AUTH = new AllowedEventCategories("auth"); + + /** The value {@code "session"}. */ + public static final AllowedEventCategories SESSION = new AllowedEventCategories("session"); + + /** The value {@code "gameplay"}. */ + public static final AllowedEventCategories GAMEPLAY = new AllowedEventCategories("gameplay"); + + /** The value {@code "bonus"}. */ + public static final AllowedEventCategories BONUS = new AllowedEventCategories("bonus"); + + /** The value {@code "affiliate"}. */ + public static final AllowedEventCategories AFFILIATE = new AllowedEventCategories("affiliate"); + + /** The value {@code "payment"}. */ + public static final AllowedEventCategories PAYMENT = new AllowedEventCategories("payment"); + + /** The value {@code "dispute"}. */ + public static final AllowedEventCategories DISPUTE = new AllowedEventCategories("dispute"); + + /** The value {@code "security"}. */ + public static final AllowedEventCategories SECURITY = new AllowedEventCategories("security"); + + /** The value {@code "responsible_gambling"}. */ + public static final AllowedEventCategories RESPONSIBLE_GAMBLING = new AllowedEventCategories("responsible_gambling"); + + /** The value {@code "custom"}. */ + public static final AllowedEventCategories CUSTOM = new AllowedEventCategories("custom"); + + private static final List KNOWN_VALUES = List.of(AUTH, SESSION, GAMEPLAY, BONUS, AFFILIATE, PAYMENT, DISPUTE, SECURITY, RESPONSIBLE_GAMBLING, CUSTOM); + + private AllowedEventCategories(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static AllowedEventCategories of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, AllowedEventCategories::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventsListParams.java b/src/main/java/me/didit/types/MonitoringEventsListParams.java new file mode 100644 index 0000000..db2b978 --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventsListParams.java @@ -0,0 +1,705 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code monitoring_events.list}. */ +public final class MonitoringEventsListParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("limit", "offset", "event_category", "action_type", "status", "retention_class", "subject_vendor_data", "subject_entity_type", "business_vendor_data", "session_id", "device_fingerprint", "ip_address", "game_id", "round_id", "affiliate_code", "payment_reference_id", "counterparty_vendor_data", "matched_only", "occurred_from", "occurred_to", "search"); + + private MonitoringEventsListParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventsListParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventsListParams fromJson(JsonNode json) { + return new MonitoringEventsListParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Maximum number of events per page. Defaults to 50. Values above 1000000 return 400; a non-numeric, zero or negative value falls back to 50. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Number of events to skip. Defaults to 0. Prefer following the next URL from the response. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + /** + * Only events in these categories. Repeat the parameter for several values (?event_category=payment&event_category=dispute); an event matching any of them is returned. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> eventCategory() { + return Json.optional(json, "event_category", Conv.list(Conv.enumeration(MonitoringEventsListParams.EventCategory::of))); + } + + /** + * Only events with these action types (exact, case-sensitive). Repeat for several values. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> actionType() { + return Json.optional(json, "action_type", Conv.list(Conv.STRING)); + } + + /** + * Only events with these statuses. Repeat for several values. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> status() { + return Json.optional(json, "status", Conv.list(Conv.enumeration(MonitoringEventsListParams.Status::of))); + } + + /** + * Only events in these retention classes. Repeat for several values. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> retentionClass() { + return Json.optional(json, "retention_class", Conv.list(Conv.enumeration(MonitoringEventsListParams.RetentionClass::of))); + } + + /** + * Exact match on the subject's vendor_data. The same id can name an individual and a company; add subject_entity_type to pick one, or use GET /v3/events/subjects/{vendor_data}/. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectVendorData() { + return Json.optional(json, "subject_vendor_data", Conv.STRING); + } + + /** + * company returns only events of company subjects; any other non-empty value (for example individual) returns only events of individuals. Omit it to return both. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectEntityType() { + return Json.optional(json, "subject_entity_type", Conv.enumeration(MonitoringEventsListParams.SubjectEntityType::of)); + } + + /** + * Exact match on the business.vendor_data sent with the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional businessVendorData() { + return Json.optional(json, "business_vendor_data", Conv.STRING); + } + + /** + * Exact match on session.session_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * Exact match on device_context.fingerprint. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deviceFingerprint() { + return Json.optional(json, "device_fingerprint", Conv.STRING); + } + + /** + * Exact match on the event's IP address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipAddress() { + return Json.optional(json, "ip_address", Conv.STRING); + } + + /** + * Exact match on the promoted game_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional gameId() { + return Json.optional(json, "game_id", Conv.STRING); + } + + /** + * Exact match on the promoted round_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional roundId() { + return Json.optional(json, "round_id", Conv.STRING); + } + + /** + * Exact match on the promoted affiliate_code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional affiliateCode() { + return Json.optional(json, "affiliate_code", Conv.STRING); + } + + /** + * Exact match on the payment reference (from transaction_context.payment_reference_id or payload.payment_reference_id). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional paymentReferenceId() { + return Json.optional(json, "payment_reference_id", Conv.STRING); + } + + /** + * Exact match on the promoted counterparty_vendor_data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyVendorData() { + return Json.optional(json, "counterparty_vendor_data", Conv.STRING); + } + + /** + * true (also 1 or yes, case-insensitive) returns only events at least one rule matched (rules_matched_count above 0). Any other value is ignored. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchedOnly() { + return Json.optional(json, "matched_only", Conv.BOOLEAN); + } + + /** + * Only events with occurred_at at or after this instant (inclusive). Send an ISO 8601 value: 2026-10-01T00:00:00Z, an explicit offset with the + percent-encoded (2026-10-01T02:00:00%2B02:00; an unencoded + is decoded as a space and the value no longer parses), or a date alone (2026-10-01), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional occurredFrom() { + return Json.optional(json, "occurred_from", Conv.DATE_TIME); + } + + /** + * Only events with occurred_at at or before this instant (inclusive). A date alone means the start of that day, so occurred_to=2026-10-15 excludes the rest of 15 October; send 2026-10-15T23:59:59Z to include it. Send an ISO 8601 value: 2026-10-01T00:00:00Z, an explicit offset with the + percent-encoded (2026-10-01T02:00:00%2B02:00; an unencoded + is decoded as a space and the value no longer parses), or a date alone (2026-10-01), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional occurredTo() { + return Json.optional(json, "occurred_to", Conv.DATE_TIME); + } + + /** + * Case-insensitive substring match on event_id, action_type, subject_vendor_data or counterparty_vendor_data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional search() { + return Json.optional(json, "search", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventsListParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventsListParams from) { + super(from); + } + + /** + * Maximum number of events per page. Defaults to 50. Values above 1000000 return 400; a non-numeric, zero or negative value falls back to 50. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Number of events to skip. Defaults to 0. Prefer following the next URL from the response. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Only events in these categories. Repeat the parameter for several values (?event_category=payment&event_category=dispute); an event matching any of them is returned. + * + * @param eventCategory the value + * @return this builder + */ + public Builder eventCategory(List eventCategory) { + return set("event_category", Conv.list(Conv.enumeration(MonitoringEventsListParams.EventCategory::of)), eventCategory); + } + + /** + * Only events with these action types (exact, case-sensitive). Repeat for several values. + * + * @param actionType the value + * @return this builder + */ + public Builder actionType(List actionType) { + return set("action_type", Conv.list(Conv.STRING), actionType); + } + + /** + * Only events with these statuses. Repeat for several values. + * + * @param status the value + * @return this builder + */ + public Builder status(List status) { + return set("status", Conv.list(Conv.enumeration(MonitoringEventsListParams.Status::of)), status); + } + + /** + * Only events in these retention classes. Repeat for several values. + * + * @param retentionClass the value + * @return this builder + */ + public Builder retentionClass(List retentionClass) { + return set("retention_class", Conv.list(Conv.enumeration(MonitoringEventsListParams.RetentionClass::of)), retentionClass); + } + + /** + * Exact match on the subject's vendor_data. The same id can name an individual and a company; add subject_entity_type to pick one, or use GET /v3/events/subjects/{vendor_data}/. + * + * @param subjectVendorData the value + * @return this builder + */ + public Builder subjectVendorData(String subjectVendorData) { + return set("subject_vendor_data", Conv.STRING, subjectVendorData); + } + + /** + * company returns only events of company subjects; any other non-empty value (for example individual) returns only events of individuals. Omit it to return both. + * + * @param subjectEntityType the value + * @return this builder + */ + public Builder subjectEntityType(MonitoringEventsListParams.SubjectEntityType subjectEntityType) { + return set("subject_entity_type", Conv.enumeration(MonitoringEventsListParams.SubjectEntityType::of), subjectEntityType); + } + + /** + * Exact match on the business.vendor_data sent with the event. + * + * @param businessVendorData the value + * @return this builder + */ + public Builder businessVendorData(String businessVendorData) { + return set("business_vendor_data", Conv.STRING, businessVendorData); + } + + /** + * Exact match on session.session_id. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Exact match on device_context.fingerprint. + * + * @param deviceFingerprint the value + * @return this builder + */ + public Builder deviceFingerprint(String deviceFingerprint) { + return set("device_fingerprint", Conv.STRING, deviceFingerprint); + } + + /** + * Exact match on the event's IP address. + * + * @param ipAddress the value + * @return this builder + */ + public Builder ipAddress(String ipAddress) { + return set("ip_address", Conv.STRING, ipAddress); + } + + /** + * Exact match on the promoted game_id. + * + * @param gameId the value + * @return this builder + */ + public Builder gameId(String gameId) { + return set("game_id", Conv.STRING, gameId); + } + + /** + * Exact match on the promoted round_id. + * + * @param roundId the value + * @return this builder + */ + public Builder roundId(String roundId) { + return set("round_id", Conv.STRING, roundId); + } + + /** + * Exact match on the promoted affiliate_code. + * + * @param affiliateCode the value + * @return this builder + */ + public Builder affiliateCode(String affiliateCode) { + return set("affiliate_code", Conv.STRING, affiliateCode); + } + + /** + * Exact match on the payment reference (from transaction_context.payment_reference_id or payload.payment_reference_id). + * + * @param paymentReferenceId the value + * @return this builder + */ + public Builder paymentReferenceId(String paymentReferenceId) { + return set("payment_reference_id", Conv.STRING, paymentReferenceId); + } + + /** + * Exact match on the promoted counterparty_vendor_data. + * + * @param counterpartyVendorData the value + * @return this builder + */ + public Builder counterpartyVendorData(String counterpartyVendorData) { + return set("counterparty_vendor_data", Conv.STRING, counterpartyVendorData); + } + + /** + * true (also 1 or yes, case-insensitive) returns only events at least one rule matched (rules_matched_count above 0). Any other value is ignored. + * + * @param matchedOnly the value + * @return this builder + */ + public Builder matchedOnly(boolean matchedOnly) { + return set("matched_only", Conv.BOOLEAN, matchedOnly); + } + + /** + * Only events with occurred_at at or after this instant (inclusive). Send an ISO 8601 value: 2026-10-01T00:00:00Z, an explicit offset with the + percent-encoded (2026-10-01T02:00:00%2B02:00; an unencoded + is decoded as a space and the value no longer parses), or a date alone (2026-10-01), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted. + * + * @param occurredFrom the value + * @return this builder + */ + public Builder occurredFrom(OffsetDateTime occurredFrom) { + return set("occurred_from", Conv.DATE_TIME, occurredFrom); + } + + /** + * Only events with occurred_at at or before this instant (inclusive). A date alone means the start of that day, so occurred_to=2026-10-15 excludes the rest of 15 October; send 2026-10-15T23:59:59Z to include it. Send an ISO 8601 value: 2026-10-01T00:00:00Z, an explicit offset with the + percent-encoded (2026-10-01T02:00:00%2B02:00; an unencoded + is decoded as a space and the value no longer parses), or a date alone (2026-10-01), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted. + * + * @param occurredTo the value + * @return this builder + */ + public Builder occurredTo(OffsetDateTime occurredTo) { + return set("occurred_to", Conv.DATE_TIME, occurredTo); + } + + /** + * Case-insensitive substring match on event_id, action_type, subject_vendor_data or counterparty_vendor_data. + * + * @param search the value + * @return this builder + */ + public Builder search(String search) { + return set("search", Conv.STRING, search); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventsListParams} + */ + public MonitoringEventsListParams build() { + return new MonitoringEventsListParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EventCategory extends StringEnum { + /** The value {@code "auth"}. */ + public static final EventCategory AUTH = new EventCategory("auth"); + + /** The value {@code "session"}. */ + public static final EventCategory SESSION = new EventCategory("session"); + + /** The value {@code "gameplay"}. */ + public static final EventCategory GAMEPLAY = new EventCategory("gameplay"); + + /** The value {@code "bonus"}. */ + public static final EventCategory BONUS = new EventCategory("bonus"); + + /** The value {@code "affiliate"}. */ + public static final EventCategory AFFILIATE = new EventCategory("affiliate"); + + /** The value {@code "payment"}. */ + public static final EventCategory PAYMENT = new EventCategory("payment"); + + /** The value {@code "dispute"}. */ + public static final EventCategory DISPUTE = new EventCategory("dispute"); + + /** The value {@code "security"}. */ + public static final EventCategory SECURITY = new EventCategory("security"); + + /** The value {@code "responsible_gambling"}. */ + public static final EventCategory RESPONSIBLE_GAMBLING = new EventCategory("responsible_gambling"); + + /** The value {@code "custom"}. */ + public static final EventCategory CUSTOM = new EventCategory("custom"); + + private static final List KNOWN_VALUES = List.of(AUTH, SESSION, GAMEPLAY, BONUS, AFFILIATE, PAYMENT, DISPUTE, SECURITY, RESPONSIBLE_GAMBLING, CUSTOM); + + private EventCategory(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EventCategory of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EventCategory::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACCEPTED"}. */ + public static final Status ACCEPTED = new Status("ACCEPTED"); + + /** The value {@code "APPROVED"}. */ + public static final Status APPROVED = new Status("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Status DECLINED = new Status("DECLINED"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, APPROVED, IN_REVIEW, DECLINED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class RetentionClass extends StringEnum { + /** The value {@code "low_value"}. */ + public static final RetentionClass LOW_VALUE = new RetentionClass("low_value"); + + /** The value {@code "standard"}. */ + public static final RetentionClass STANDARD = new RetentionClass("standard"); + + /** The value {@code "risk_relevant"}. */ + public static final RetentionClass RISK_RELEVANT = new RetentionClass("risk_relevant"); + + /** The value {@code "monetary_or_compliance"}. */ + public static final RetentionClass MONETARY_OR_COMPLIANCE = new RetentionClass("monetary_or_compliance"); + + private static final List KNOWN_VALUES = List.of(LOW_VALUE, STANDARD, RISK_RELEVANT, MONETARY_OR_COMPLIANCE); + + private RetentionClass(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static RetentionClass of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RetentionClass::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SubjectEntityType extends StringEnum { + /** The value {@code "individual"}. */ + public static final SubjectEntityType INDIVIDUAL = new SubjectEntityType("individual"); + + /** The value {@code "company"}. */ + public static final SubjectEntityType COMPANY = new SubjectEntityType("company"); + + private static final List KNOWN_VALUES = List.of(INDIVIDUAL, COMPANY); + + private SubjectEntityType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SubjectEntityType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SubjectEntityType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventsRetrieveEnrichmentMetricsParams.java b/src/main/java/me/didit/types/MonitoringEventsRetrieveEnrichmentMetricsParams.java new file mode 100644 index 0000000..e247cff --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventsRetrieveEnrichmentMetricsParams.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code monitoring_events.retrieve_enrichment_metrics}. */ +public final class MonitoringEventsRetrieveEnrichmentMetricsParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("days"); + + private MonitoringEventsRetrieveEnrichmentMetricsParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventsRetrieveEnrichmentMetricsParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventsRetrieveEnrichmentMetricsParams fromJson(JsonNode json) { + return new MonitoringEventsRetrieveEnrichmentMetricsParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Length of the period in days. Defaults to 30; values are clamped to 1 to 365, and a non-integer value falls back to 30. The value applied is returned as period_days. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional days() { + return Json.optional(json, "days", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventsRetrieveEnrichmentMetricsParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventsRetrieveEnrichmentMetricsParams from) { + super(from); + } + + /** + * Length of the period in days. Defaults to 30; values are clamped to 1 to 365, and a non-integer value falls back to 30. The value applied is returned as period_days. + * + * @param days the value + * @return this builder + */ + public Builder days(long days) { + return set("days", Conv.LONG, days); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventsRetrieveEnrichmentMetricsParams} + */ + public MonitoringEventsRetrieveEnrichmentMetricsParams build() { + return new MonitoringEventsRetrieveEnrichmentMetricsParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventsRetrieveEnrichmentMetricsResponse.java b/src/main/java/me/didit/types/MonitoringEventsRetrieveEnrichmentMetricsResponse.java new file mode 100644 index 0000000..2addade --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventsRetrieveEnrichmentMetricsResponse.java @@ -0,0 +1,311 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class MonitoringEventsRetrieveEnrichmentMetricsResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("events_ingested", "events_rejected", "enrichment_lookups", "enrichment_cache_hits", "enrichment_provider_calls", "enrichment_skipped_low_value", "unique_ips_seen", "period_days", "cache_hit_rate", "unique_ip_ratio", "provider_calls_per_1k_events"); + + private MonitoringEventsRetrieveEnrichmentMetricsResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventsRetrieveEnrichmentMetricsResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventsRetrieveEnrichmentMetricsResponse fromJson(JsonNode json) { + return new MonitoringEventsRetrieveEnrichmentMetricsResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * New events stored. Idempotent replays are not counted. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long eventsIngested() { + return Json.required(json, "events_ingested", Conv.LONG); + } + + /** + * Items rejected inside batch requests (server and device lanes). A rejected single-event request is not counted. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long eventsRejected() { + return Json.required(json, "events_rejected", Conv.LONG); + } + + /** + * Events whose public IP address was resolved, from the cache or freshly. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long enrichmentLookups() { + return Json.required(json, "enrichment_lookups", Conv.LONG); + } + + /** + * Lookups answered from the cache. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long enrichmentCacheHits() { + return Json.required(json, "enrichment_cache_hits", Conv.LONG); + } + + /** + * Lookups that needed a fresh resolution of the address. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long enrichmentProviderCalls() { + return Json.required(json, "enrichment_provider_calls", Conv.LONG); + } + + /** + * Low-value telemetry events whose address was not already known. They never trigger a fresh resolution of their own. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long enrichmentSkippedLowValue() { + return Json.required(json, "enrichment_skipped_low_value", Conv.LONG); + } + + /** + * Addresses that needed a fresh resolution. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long uniqueIpsSeen() { + return Json.required(json, "unique_ips_seen", Conv.LONG); + } + + /** + * The days value applied after clamping. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long periodDays() { + return Json.required(json, "period_days", Conv.LONG); + } + + /** + * enrichment_cache_hits / enrichment_lookups, rounded to 4 decimals. Null when there were no lookups. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional cacheHitRate() { + return Json.optional(json, "cache_hit_rate", Conv.DOUBLE); + } + + /** + * unique_ips_seen / events_ingested, rounded to 4 decimals. Null when no events were ingested. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uniqueIpRatio() { + return Json.optional(json, "unique_ip_ratio", Conv.DOUBLE); + } + + /** + * Fresh resolutions per 1000 ingested events, rounded to 2 decimals. Null when no events were ingested. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional providerCallsPer1KEvents() { + return Json.optional(json, "provider_calls_per_1k_events", Conv.DOUBLE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventsRetrieveEnrichmentMetricsResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventsRetrieveEnrichmentMetricsResponse from) { + super(from); + } + + /** + * New events stored. Idempotent replays are not counted. + * + * @param eventsIngested the value + * @return this builder + */ + public Builder eventsIngested(long eventsIngested) { + return set("events_ingested", Conv.LONG, eventsIngested); + } + + /** + * Items rejected inside batch requests (server and device lanes). A rejected single-event request is not counted. + * + * @param eventsRejected the value + * @return this builder + */ + public Builder eventsRejected(long eventsRejected) { + return set("events_rejected", Conv.LONG, eventsRejected); + } + + /** + * Events whose public IP address was resolved, from the cache or freshly. + * + * @param enrichmentLookups the value + * @return this builder + */ + public Builder enrichmentLookups(long enrichmentLookups) { + return set("enrichment_lookups", Conv.LONG, enrichmentLookups); + } + + /** + * Lookups answered from the cache. + * + * @param enrichmentCacheHits the value + * @return this builder + */ + public Builder enrichmentCacheHits(long enrichmentCacheHits) { + return set("enrichment_cache_hits", Conv.LONG, enrichmentCacheHits); + } + + /** + * Lookups that needed a fresh resolution of the address. + * + * @param enrichmentProviderCalls the value + * @return this builder + */ + public Builder enrichmentProviderCalls(long enrichmentProviderCalls) { + return set("enrichment_provider_calls", Conv.LONG, enrichmentProviderCalls); + } + + /** + * Low-value telemetry events whose address was not already known. They never trigger a fresh resolution of their own. + * + * @param enrichmentSkippedLowValue the value + * @return this builder + */ + public Builder enrichmentSkippedLowValue(long enrichmentSkippedLowValue) { + return set("enrichment_skipped_low_value", Conv.LONG, enrichmentSkippedLowValue); + } + + /** + * Addresses that needed a fresh resolution. + * + * @param uniqueIpsSeen the value + * @return this builder + */ + public Builder uniqueIpsSeen(long uniqueIpsSeen) { + return set("unique_ips_seen", Conv.LONG, uniqueIpsSeen); + } + + /** + * The days value applied after clamping. + * + * @param periodDays the value + * @return this builder + */ + public Builder periodDays(long periodDays) { + return set("period_days", Conv.LONG, periodDays); + } + + /** + * enrichment_cache_hits / enrichment_lookups, rounded to 4 decimals. Null when there were no lookups. + * + * @param cacheHitRate the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder cacheHitRate(@Nullable Double cacheHitRate) { + return setNullable("cache_hit_rate", Conv.DOUBLE, cacheHitRate); + } + + /** + * unique_ips_seen / events_ingested, rounded to 4 decimals. Null when no events were ingested. + * + * @param uniqueIpRatio the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder uniqueIpRatio(@Nullable Double uniqueIpRatio) { + return setNullable("unique_ip_ratio", Conv.DOUBLE, uniqueIpRatio); + } + + /** + * Fresh resolutions per 1000 ingested events, rounded to 2 decimals. Null when no events were ingested. + * + * @param providerCallsPer1KEvents the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder providerCallsPer1KEvents(@Nullable Double providerCallsPer1KEvents) { + return setNullable("provider_calls_per_1k_events", Conv.DOUBLE, providerCallsPer1KEvents); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventsRetrieveEnrichmentMetricsResponse} + * @throws IllegalStateException when a required field is missing + */ + public MonitoringEventsRetrieveEnrichmentMetricsResponse build() { + require("events_ingested", false); + require("events_rejected", false); + require("enrichment_lookups", false); + require("enrichment_cache_hits", false); + require("enrichment_provider_calls", false); + require("enrichment_skipped_low_value", false); + require("unique_ips_seen", false); + require("period_days", false); + require("cache_hit_rate", true); + require("unique_ip_ratio", true); + require("provider_calls_per_1k_events", true); + return new MonitoringEventsRetrieveEnrichmentMetricsResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/MonitoringEventsRetrieveSubjectTimelineParams.java b/src/main/java/me/didit/types/MonitoringEventsRetrieveSubjectTimelineParams.java new file mode 100644 index 0000000..ec0f08d --- /dev/null +++ b/src/main/java/me/didit/types/MonitoringEventsRetrieveSubjectTimelineParams.java @@ -0,0 +1,685 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code monitoring_events.retrieve_subject_timeline}. */ +public final class MonitoringEventsRetrieveSubjectTimelineParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("entity_type", "limit", "offset", "event_category", "action_type", "status", "retention_class", "business_vendor_data", "session_id", "device_fingerprint", "ip_address", "game_id", "round_id", "affiliate_code", "payment_reference_id", "counterparty_vendor_data", "matched_only", "occurred_from", "occurred_to", "search"); + + private MonitoringEventsRetrieveSubjectTimelineParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MonitoringEventsRetrieveSubjectTimelineParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MonitoringEventsRetrieveSubjectTimelineParams fromJson(JsonNode json) { + return new MonitoringEventsRetrieveSubjectTimelineParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * company returns the company with this vendor_data; any other value, or none, returns the individual. subject_entity_type is accepted as an alias when entity_type is absent. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.enumeration(MonitoringEventsRetrieveSubjectTimelineParams.EntityType::of)); + } + + /** + * Maximum number of events per page. Defaults to 50. Values above 1000000 return 400; a non-numeric, zero or negative value falls back to 50. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Number of events to skip. Defaults to 0. Prefer following the next URL from the response. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + /** + * Only events in these categories. Repeat the parameter for several values (?event_category=payment&event_category=dispute); an event matching any of them is returned. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> eventCategory() { + return Json.optional(json, "event_category", Conv.list(Conv.enumeration(MonitoringEventsRetrieveSubjectTimelineParams.EventCategory::of))); + } + + /** + * Only events with these action types (exact, case-sensitive). Repeat for several values. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> actionType() { + return Json.optional(json, "action_type", Conv.list(Conv.STRING)); + } + + /** + * Only events with these statuses. Repeat for several values. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> status() { + return Json.optional(json, "status", Conv.list(Conv.enumeration(MonitoringEventsRetrieveSubjectTimelineParams.Status::of))); + } + + /** + * Only events in these retention classes. Repeat for several values. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> retentionClass() { + return Json.optional(json, "retention_class", Conv.list(Conv.enumeration(MonitoringEventsRetrieveSubjectTimelineParams.RetentionClass::of))); + } + + /** + * Exact match on the business.vendor_data sent with the event. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional businessVendorData() { + return Json.optional(json, "business_vendor_data", Conv.STRING); + } + + /** + * Exact match on session.session_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * Exact match on device_context.fingerprint. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deviceFingerprint() { + return Json.optional(json, "device_fingerprint", Conv.STRING); + } + + /** + * Exact match on the event's IP address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipAddress() { + return Json.optional(json, "ip_address", Conv.STRING); + } + + /** + * Exact match on the promoted game_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional gameId() { + return Json.optional(json, "game_id", Conv.STRING); + } + + /** + * Exact match on the promoted round_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional roundId() { + return Json.optional(json, "round_id", Conv.STRING); + } + + /** + * Exact match on the promoted affiliate_code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional affiliateCode() { + return Json.optional(json, "affiliate_code", Conv.STRING); + } + + /** + * Exact match on the payment reference (from transaction_context.payment_reference_id or payload.payment_reference_id). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional paymentReferenceId() { + return Json.optional(json, "payment_reference_id", Conv.STRING); + } + + /** + * Exact match on the promoted counterparty_vendor_data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyVendorData() { + return Json.optional(json, "counterparty_vendor_data", Conv.STRING); + } + + /** + * true (also 1 or yes, case-insensitive) returns only events at least one rule matched (rules_matched_count above 0). Any other value is ignored. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchedOnly() { + return Json.optional(json, "matched_only", Conv.BOOLEAN); + } + + /** + * Only events with occurred_at at or after this instant (inclusive). Send an ISO 8601 value: 2026-10-01T00:00:00Z, an explicit offset with the + percent-encoded (2026-10-01T02:00:00%2B02:00; an unencoded + is decoded as a space and the value no longer parses), or a date alone (2026-10-01), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional occurredFrom() { + return Json.optional(json, "occurred_from", Conv.DATE_TIME); + } + + /** + * Only events with occurred_at at or before this instant (inclusive). A date alone means the start of that day, so occurred_to=2026-10-15 excludes the rest of 15 October; send 2026-10-15T23:59:59Z to include it. Send an ISO 8601 value: 2026-10-01T00:00:00Z, an explicit offset with the + percent-encoded (2026-10-01T02:00:00%2B02:00; an unencoded + is decoded as a space and the value no longer parses), or a date alone (2026-10-01), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional occurredTo() { + return Json.optional(json, "occurred_to", Conv.DATE_TIME); + } + + /** + * Case-insensitive substring match on event_id, action_type, subject_vendor_data or counterparty_vendor_data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional search() { + return Json.optional(json, "search", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MonitoringEventsRetrieveSubjectTimelineParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MonitoringEventsRetrieveSubjectTimelineParams from) { + super(from); + } + + /** + * company returns the company with this vendor_data; any other value, or none, returns the individual. subject_entity_type is accepted as an alias when entity_type is absent. + * + * @param entityType the value + * @return this builder + */ + public Builder entityType(MonitoringEventsRetrieveSubjectTimelineParams.EntityType entityType) { + return set("entity_type", Conv.enumeration(MonitoringEventsRetrieveSubjectTimelineParams.EntityType::of), entityType); + } + + /** + * Maximum number of events per page. Defaults to 50. Values above 1000000 return 400; a non-numeric, zero or negative value falls back to 50. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Number of events to skip. Defaults to 0. Prefer following the next URL from the response. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Only events in these categories. Repeat the parameter for several values (?event_category=payment&event_category=dispute); an event matching any of them is returned. + * + * @param eventCategory the value + * @return this builder + */ + public Builder eventCategory(List eventCategory) { + return set("event_category", Conv.list(Conv.enumeration(MonitoringEventsRetrieveSubjectTimelineParams.EventCategory::of)), eventCategory); + } + + /** + * Only events with these action types (exact, case-sensitive). Repeat for several values. + * + * @param actionType the value + * @return this builder + */ + public Builder actionType(List actionType) { + return set("action_type", Conv.list(Conv.STRING), actionType); + } + + /** + * Only events with these statuses. Repeat for several values. + * + * @param status the value + * @return this builder + */ + public Builder status(List status) { + return set("status", Conv.list(Conv.enumeration(MonitoringEventsRetrieveSubjectTimelineParams.Status::of)), status); + } + + /** + * Only events in these retention classes. Repeat for several values. + * + * @param retentionClass the value + * @return this builder + */ + public Builder retentionClass(List retentionClass) { + return set("retention_class", Conv.list(Conv.enumeration(MonitoringEventsRetrieveSubjectTimelineParams.RetentionClass::of)), retentionClass); + } + + /** + * Exact match on the business.vendor_data sent with the event. + * + * @param businessVendorData the value + * @return this builder + */ + public Builder businessVendorData(String businessVendorData) { + return set("business_vendor_data", Conv.STRING, businessVendorData); + } + + /** + * Exact match on session.session_id. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Exact match on device_context.fingerprint. + * + * @param deviceFingerprint the value + * @return this builder + */ + public Builder deviceFingerprint(String deviceFingerprint) { + return set("device_fingerprint", Conv.STRING, deviceFingerprint); + } + + /** + * Exact match on the event's IP address. + * + * @param ipAddress the value + * @return this builder + */ + public Builder ipAddress(String ipAddress) { + return set("ip_address", Conv.STRING, ipAddress); + } + + /** + * Exact match on the promoted game_id. + * + * @param gameId the value + * @return this builder + */ + public Builder gameId(String gameId) { + return set("game_id", Conv.STRING, gameId); + } + + /** + * Exact match on the promoted round_id. + * + * @param roundId the value + * @return this builder + */ + public Builder roundId(String roundId) { + return set("round_id", Conv.STRING, roundId); + } + + /** + * Exact match on the promoted affiliate_code. + * + * @param affiliateCode the value + * @return this builder + */ + public Builder affiliateCode(String affiliateCode) { + return set("affiliate_code", Conv.STRING, affiliateCode); + } + + /** + * Exact match on the payment reference (from transaction_context.payment_reference_id or payload.payment_reference_id). + * + * @param paymentReferenceId the value + * @return this builder + */ + public Builder paymentReferenceId(String paymentReferenceId) { + return set("payment_reference_id", Conv.STRING, paymentReferenceId); + } + + /** + * Exact match on the promoted counterparty_vendor_data. + * + * @param counterpartyVendorData the value + * @return this builder + */ + public Builder counterpartyVendorData(String counterpartyVendorData) { + return set("counterparty_vendor_data", Conv.STRING, counterpartyVendorData); + } + + /** + * true (also 1 or yes, case-insensitive) returns only events at least one rule matched (rules_matched_count above 0). Any other value is ignored. + * + * @param matchedOnly the value + * @return this builder + */ + public Builder matchedOnly(boolean matchedOnly) { + return set("matched_only", Conv.BOOLEAN, matchedOnly); + } + + /** + * Only events with occurred_at at or after this instant (inclusive). Send an ISO 8601 value: 2026-10-01T00:00:00Z, an explicit offset with the + percent-encoded (2026-10-01T02:00:00%2B02:00; an unencoded + is decoded as a space and the value no longer parses), or a date alone (2026-10-01), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted. + * + * @param occurredFrom the value + * @return this builder + */ + public Builder occurredFrom(OffsetDateTime occurredFrom) { + return set("occurred_from", Conv.DATE_TIME, occurredFrom); + } + + /** + * Only events with occurred_at at or before this instant (inclusive). A date alone means the start of that day, so occurred_to=2026-10-15 excludes the rest of 15 October; send 2026-10-15T23:59:59Z to include it. Send an ISO 8601 value: 2026-10-01T00:00:00Z, an explicit offset with the + percent-encoded (2026-10-01T02:00:00%2B02:00; an unencoded + is decoded as a space and the value no longer parses), or a date alone (2026-10-01), which means 00:00:00 UTC of that day. A value without an offset is read as UTC. Values in any other format are not accepted. + * + * @param occurredTo the value + * @return this builder + */ + public Builder occurredTo(OffsetDateTime occurredTo) { + return set("occurred_to", Conv.DATE_TIME, occurredTo); + } + + /** + * Case-insensitive substring match on event_id, action_type, subject_vendor_data or counterparty_vendor_data. + * + * @param search the value + * @return this builder + */ + public Builder search(String search) { + return set("search", Conv.STRING, search); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MonitoringEventsRetrieveSubjectTimelineParams} + */ + public MonitoringEventsRetrieveSubjectTimelineParams build() { + return new MonitoringEventsRetrieveSubjectTimelineParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EntityType extends StringEnum { + /** The value {@code "individual"}. */ + public static final EntityType INDIVIDUAL = new EntityType("individual"); + + /** The value {@code "company"}. */ + public static final EntityType COMPANY = new EntityType("company"); + + private static final List KNOWN_VALUES = List.of(INDIVIDUAL, COMPANY); + + private EntityType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EntityType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EntityType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EventCategory extends StringEnum { + /** The value {@code "auth"}. */ + public static final EventCategory AUTH = new EventCategory("auth"); + + /** The value {@code "session"}. */ + public static final EventCategory SESSION = new EventCategory("session"); + + /** The value {@code "gameplay"}. */ + public static final EventCategory GAMEPLAY = new EventCategory("gameplay"); + + /** The value {@code "bonus"}. */ + public static final EventCategory BONUS = new EventCategory("bonus"); + + /** The value {@code "affiliate"}. */ + public static final EventCategory AFFILIATE = new EventCategory("affiliate"); + + /** The value {@code "payment"}. */ + public static final EventCategory PAYMENT = new EventCategory("payment"); + + /** The value {@code "dispute"}. */ + public static final EventCategory DISPUTE = new EventCategory("dispute"); + + /** The value {@code "security"}. */ + public static final EventCategory SECURITY = new EventCategory("security"); + + /** The value {@code "responsible_gambling"}. */ + public static final EventCategory RESPONSIBLE_GAMBLING = new EventCategory("responsible_gambling"); + + /** The value {@code "custom"}. */ + public static final EventCategory CUSTOM = new EventCategory("custom"); + + private static final List KNOWN_VALUES = List.of(AUTH, SESSION, GAMEPLAY, BONUS, AFFILIATE, PAYMENT, DISPUTE, SECURITY, RESPONSIBLE_GAMBLING, CUSTOM); + + private EventCategory(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EventCategory of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EventCategory::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACCEPTED"}. */ + public static final Status ACCEPTED = new Status("ACCEPTED"); + + /** The value {@code "APPROVED"}. */ + public static final Status APPROVED = new Status("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Status DECLINED = new Status("DECLINED"); + + private static final List KNOWN_VALUES = List.of(ACCEPTED, APPROVED, IN_REVIEW, DECLINED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class RetentionClass extends StringEnum { + /** The value {@code "low_value"}. */ + public static final RetentionClass LOW_VALUE = new RetentionClass("low_value"); + + /** The value {@code "standard"}. */ + public static final RetentionClass STANDARD = new RetentionClass("standard"); + + /** The value {@code "risk_relevant"}. */ + public static final RetentionClass RISK_RELEVANT = new RetentionClass("risk_relevant"); + + /** The value {@code "monetary_or_compliance"}. */ + public static final RetentionClass MONETARY_OR_COMPLIANCE = new RetentionClass("monetary_or_compliance"); + + private static final List KNOWN_VALUES = List.of(LOW_VALUE, STANDARD, RISK_RELEVANT, MONETARY_OR_COMPLIANCE); + + private RetentionClass(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static RetentionClass of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RetentionClass::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/NetworkDetailResponse.java b/src/main/java/me/didit/types/NetworkDetailResponse.java new file mode 100644 index 0000000..fac1562 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkDetailResponse.java @@ -0,0 +1,673 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * The network detail, plus the organization's sharing mode, whether cross-organization signals apply to it, your application's tags on the network, member outcomes and pattern scores. + */ +public final class NetworkDetailResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "network_id", "name", "status", "risk", "pattern_types", "signal_types", "member_counts", "member_status", "signal_coverage", "first_activity_at", "last_activity_at", "stats", "fraud_tags", "risk_signals", "reviewed_at", "reviewed_by", "review_reason", "notes_count", "sharing_mode", "cross_org_available", "cross_org_unavailable_reason", "tags", "outcomes", "patterns"); + + private NetworkDetailResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkDetailResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkDetailResponse fromJson(JsonNode json) { + return new NetworkDetailResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * Human-readable per-organization sequential id, e.g. N-0001. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional networkId() { + return Json.optional(json, "network_id", Conv.STRING); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * Network lifecycle status. Distinct from the per-applicant outcome_status inside a network (approved/in_review/declined/not_completed). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(NetworkListItem.Status::of)); + } + + /** + * The risk field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.model(NetworkRiskDisplay::fromJson)); + } + + /** + * The clustering patterns detected in this network. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> patternTypes() { + return Json.optional(json, "pattern_types", Conv.list(Conv.enumeration(NetworkListItem.PatternTypes::of))); + } + + /** + * The distinct signal types present in this network. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> signalTypes() { + return Json.optional(json, "signal_types", Conv.list(Conv.enumeration(NetworkListItem.SignalTypes::of))); + } + + /** + * Counts of members by category (e.g. total, per outcome_status). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> memberCounts() { + return Json.optional(json, "member_counts", Conv.OBJECT); + } + + /** + * Breakdown of members by outcome_status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> memberStatus() { + return Json.optional(json, "member_status", Conv.OBJECT); + } + + /** + * Per signal_type coverage of this network's members. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> signalCoverage() { + return Json.optional(json, "signal_coverage", Conv.OBJECT); + } + + /** + * The first_activity_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional firstActivityAt() { + return Json.optional(json, "first_activity_at", Conv.DATE_TIME); + } + + /** + * The last_activity_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastActivityAt() { + return Json.optional(json, "last_activity_at", Conv.DATE_TIME); + } + + /** + * Aggregate stats for the network (e.g. member totals). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> stats() { + return Json.optional(json, "stats", Conv.OBJECT); + } + + /** + * Fraud-pattern tags surfaced on the network. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional>> fraudTags() { + return Json.optional(json, "fraud_tags", Conv.list(Conv.OBJECT)); + } + + /** + * Risk-signal summaries: residential_proxy_reuse, email_intelligence, phone_intelligence. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> riskSignals() { + return Json.optional(json, "risk_signals", Conv.OBJECT); + } + + /** + * The reviewed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reviewedAt() { + return Json.optional(json, "reviewed_at", Conv.DATE_TIME); + } + + /** + * The reviewed_by field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reviewedBy() { + return Json.optional(json, "reviewed_by", Conv.STRING); + } + + /** + * The review_reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reviewReason() { + return Json.optional(json, "review_reason", Conv.STRING); + } + + /** + * Number of private analyst notes in this investigation. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional notesCount() { + return Json.optional(json, "notes_count", Conv.LONG); + } + + /** + * The organization's cross-organization sharing mode. Only global compares networks across organizations; private and off keep them to the organization's own history. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sharingMode() { + return Json.optional(json, "sharing_mode", Conv.enumeration(NetworkDetailResponse.SharingMode::of)); + } + + /** + * Whether cross-organization signals apply to this organization. They need both the Identity Enterprise plan and the global sharing mode. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional crossOrgAvailable() { + return Json.optional(json, "cross_org_available", Conv.BOOLEAN); + } + + /** + * Why cross-organization signals do not apply, checked in this order. plan: the organization's Identity plan does not include the cross-organization fraud network (it needs Identity Enterprise), whatever the sharing mode. sharing_mode: the plan includes it, but the sharing mode is private or off. Null when cross_org_available is true. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional crossOrgUnavailableReason() { + return Json.optional(json, "cross_org_unavailable_reason", Conv.enumeration(NetworkDetailResponse.CrossOrgUnavailableReason::of)); + } + + /** + * Your application's tags on the network, oldest first. Each application sees only its own tags. fraud_tags is separate and unchanged: the tags the network's members carry. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> tags() { + return Json.optional(json, "tags", Conv.list(Conv.model(NetworkTag::fromJson))); + } + + /** + * Where the network's people and businesses stand. Each member you may see counts once, by the status of their most recent verification that reached a decision. Transaction parties are not counted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional outcomes() { + return Json.optional(json, "outcomes", Conv.model(NetworkOutcomes::fromJson)); + } + + /** + * Pattern scores over the whole network. Today one item, background. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> patterns() { + return Json.optional(json, "patterns", Conv.list(Conv.model(NetworkPatternScore::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkDetailResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkDetailResponse from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Human-readable per-organization sequential id, e.g. N-0001. + * + * @param networkId the value + * @return this builder + */ + public Builder networkId(String networkId) { + return set("network_id", Conv.STRING, networkId); + } + + /** + * The name field. + * + * @param name the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder name(@Nullable String name) { + return setNullable("name", Conv.STRING, name); + } + + /** + * Network lifecycle status. Distinct from the per-applicant outcome_status inside a network (approved/in_review/declined/not_completed). + * + * @param status the value + * @return this builder + */ + public Builder status(NetworkListItem.Status status) { + return set("status", Conv.enumeration(NetworkListItem.Status::of), status); + } + + /** + * The risk field. + * + * @param risk the value + * @return this builder + */ + public Builder risk(NetworkRiskDisplay risk) { + return set("risk", Conv.model(NetworkRiskDisplay::fromJson), risk); + } + + /** + * The clustering patterns detected in this network. + * + * @param patternTypes the value + * @return this builder + */ + public Builder patternTypes(List patternTypes) { + return set("pattern_types", Conv.list(Conv.enumeration(NetworkListItem.PatternTypes::of)), patternTypes); + } + + /** + * The distinct signal types present in this network. + * + * @param signalTypes the value + * @return this builder + */ + public Builder signalTypes(List signalTypes) { + return set("signal_types", Conv.list(Conv.enumeration(NetworkListItem.SignalTypes::of)), signalTypes); + } + + /** + * Counts of members by category (e.g. total, per outcome_status). + * + * @param memberCounts the value + * @return this builder + */ + public Builder memberCounts(Map memberCounts) { + return setAny("member_counts", memberCounts); + } + + /** + * Breakdown of members by outcome_status. + * + * @param memberStatus the value + * @return this builder + */ + public Builder memberStatus(Map memberStatus) { + return setAny("member_status", memberStatus); + } + + /** + * Per signal_type coverage of this network's members. + * + * @param signalCoverage the value + * @return this builder + */ + public Builder signalCoverage(Map signalCoverage) { + return setAny("signal_coverage", signalCoverage); + } + + /** + * The first_activity_at field. + * + * @param firstActivityAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder firstActivityAt(@Nullable OffsetDateTime firstActivityAt) { + return setNullable("first_activity_at", Conv.DATE_TIME, firstActivityAt); + } + + /** + * The last_activity_at field. + * + * @param lastActivityAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastActivityAt(@Nullable OffsetDateTime lastActivityAt) { + return setNullable("last_activity_at", Conv.DATE_TIME, lastActivityAt); + } + + /** + * Aggregate stats for the network (e.g. member totals). + * + * @param stats the value + * @return this builder + */ + public Builder stats(Map stats) { + return setAny("stats", stats); + } + + /** + * Fraud-pattern tags surfaced on the network. + * + * @param fraudTags the value + * @return this builder + */ + public Builder fraudTags(List> fraudTags) { + return setAny("fraud_tags", fraudTags); + } + + /** + * Risk-signal summaries: residential_proxy_reuse, email_intelligence, phone_intelligence. + * + * @param riskSignals the value + * @return this builder + */ + public Builder riskSignals(Map riskSignals) { + return setAny("risk_signals", riskSignals); + } + + /** + * The reviewed_at field. + * + * @param reviewedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reviewedAt(@Nullable OffsetDateTime reviewedAt) { + return setNullable("reviewed_at", Conv.DATE_TIME, reviewedAt); + } + + /** + * The reviewed_by field. + * + * @param reviewedBy the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reviewedBy(@Nullable String reviewedBy) { + return setNullable("reviewed_by", Conv.STRING, reviewedBy); + } + + /** + * The review_reason field. + * + * @param reviewReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reviewReason(@Nullable String reviewReason) { + return setNullable("review_reason", Conv.STRING, reviewReason); + } + + /** + * Number of private analyst notes in this investigation. + * + * @param notesCount the value + * @return this builder + */ + public Builder notesCount(long notesCount) { + return set("notes_count", Conv.LONG, notesCount); + } + + /** + * The organization's cross-organization sharing mode. Only global compares networks across organizations; private and off keep them to the organization's own history. + * + * @param sharingMode the value + * @return this builder + */ + public Builder sharingMode(NetworkDetailResponse.SharingMode sharingMode) { + return set("sharing_mode", Conv.enumeration(NetworkDetailResponse.SharingMode::of), sharingMode); + } + + /** + * Whether cross-organization signals apply to this organization. They need both the Identity Enterprise plan and the global sharing mode. + * + * @param crossOrgAvailable the value + * @return this builder + */ + public Builder crossOrgAvailable(boolean crossOrgAvailable) { + return set("cross_org_available", Conv.BOOLEAN, crossOrgAvailable); + } + + /** + * Why cross-organization signals do not apply, checked in this order. plan: the organization's Identity plan does not include the cross-organization fraud network (it needs Identity Enterprise), whatever the sharing mode. sharing_mode: the plan includes it, but the sharing mode is private or off. Null when cross_org_available is true. + * + * @param crossOrgUnavailableReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder crossOrgUnavailableReason(NetworkDetailResponse.@Nullable CrossOrgUnavailableReason crossOrgUnavailableReason) { + return setNullable("cross_org_unavailable_reason", Conv.enumeration(NetworkDetailResponse.CrossOrgUnavailableReason::of), crossOrgUnavailableReason); + } + + /** + * Your application's tags on the network, oldest first. Each application sees only its own tags. fraud_tags is separate and unchanged: the tags the network's members carry. + * + * @param tags the value + * @return this builder + */ + public Builder tags(List tags) { + return set("tags", Conv.list(Conv.model(NetworkTag::fromJson)), tags); + } + + /** + * Where the network's people and businesses stand. Each member you may see counts once, by the status of their most recent verification that reached a decision. Transaction parties are not counted. + * + * @param outcomes the value + * @return this builder + */ + public Builder outcomes(NetworkOutcomes outcomes) { + return set("outcomes", Conv.model(NetworkOutcomes::fromJson), outcomes); + } + + /** + * Pattern scores over the whole network. Today one item, background. + * + * @param patterns the value + * @return this builder + */ + public Builder patterns(List patterns) { + return set("patterns", Conv.list(Conv.model(NetworkPatternScore::fromJson)), patterns); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkDetailResponse} + */ + public NetworkDetailResponse build() { + return new NetworkDetailResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * The organization's cross-organization sharing mode. Only global compares networks across organizations; private and off keep them to the organization's own history. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SharingMode extends StringEnum { + /** The value {@code "off"}. */ + public static final SharingMode OFF = new SharingMode("off"); + + /** The value {@code "private"}. */ + public static final SharingMode PRIVATE = new SharingMode("private"); + + /** The value {@code "global"}. */ + public static final SharingMode GLOBAL = new SharingMode("global"); + + private static final List KNOWN_VALUES = List.of(OFF, PRIVATE, GLOBAL); + + private SharingMode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SharingMode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SharingMode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Why cross-organization signals do not apply, checked in this order. plan: the organization's Identity plan does not include the cross-organization fraud network (it needs Identity Enterprise), whatever the sharing mode. sharing_mode: the plan includes it, but the sharing mode is private or off. Null when cross_org_available is true. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class CrossOrgUnavailableReason extends StringEnum { + /** The value {@code "plan"}. */ + public static final CrossOrgUnavailableReason PLAN = new CrossOrgUnavailableReason("plan"); + + /** The value {@code "sharing_mode"}. */ + public static final CrossOrgUnavailableReason SHARING_MODE = new CrossOrgUnavailableReason("sharing_mode"); + + private static final List KNOWN_VALUES = List.of(PLAN, SHARING_MODE); + + private CrossOrgUnavailableReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static CrossOrgUnavailableReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, CrossOrgUnavailableReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/NetworkListCounts.java b/src/main/java/me/didit/types/NetworkListCounts.java new file mode 100644 index 0000000..fcb87f5 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkListCounts.java @@ -0,0 +1,1640 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** + * Network counts for the list's views and filter options, each network counted once. Returned only when summary is not false. scopes count every network visible to the application. views count the visible networks inside the selected scope. facets count the networks in the selected scope and status view (dismissed networks are left out unless status includes them). The other list filters do not change these counts. + */ +public final class NetworkListCounts extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("scopes", "views", "facets"); + + private NetworkListCounts(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkListCounts} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkListCounts fromJson(JsonNode json) { + return new NetworkListCounts(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Visible networks by product. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scopes() { + return Json.optional(json, "scopes", Conv.model(NetworkListCounts.Scopes::fromJson)); + } + + /** + * Visible networks inside the selected scope, by status, plus two saved views. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional views() { + return Json.optional(json, "views", Conv.model(NetworkListCounts.Views::fromJson)); + } + + /** + * Counts for each filter option, inside the selected scope and status view. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional facets() { + return Json.optional(json, "facets", Conv.model(NetworkListCounts.Facets::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkListCounts}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkListCounts from) { + super(from); + } + + /** + * Visible networks by product. + * + * @param scopes the value + * @return this builder + */ + public Builder scopes(NetworkListCounts.Scopes scopes) { + return set("scopes", Conv.model(NetworkListCounts.Scopes::fromJson), scopes); + } + + /** + * Visible networks inside the selected scope, by status, plus two saved views. + * + * @param views the value + * @return this builder + */ + public Builder views(NetworkListCounts.Views views) { + return set("views", Conv.model(NetworkListCounts.Views::fromJson), views); + } + + /** + * Counts for each filter option, inside the selected scope and status view. + * + * @param facets the value + * @return this builder + */ + public Builder facets(NetworkListCounts.Facets facets) { + return set("facets", Conv.model(NetworkListCounts.Facets::fromJson), facets); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkListCounts} + */ + public NetworkListCounts build() { + return new NetworkListCounts(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** Visible networks by product. */ + public static final class Scopes extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("all", "identity", "transactions"); + + private Scopes(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Scopes} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Scopes fromJson(JsonNode json) { + return new Scopes(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The all field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional all() { + return Json.optional(json, "all", Conv.LONG); + } + + /** + * Every other network. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional identity() { + return Json.optional(json, "identity", Conv.LONG); + } + + /** + * Networks whose members are all transaction parties. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactions() { + return Json.optional(json, "transactions", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Scopes}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Scopes from) { + super(from); + } + + /** + * The all field. + * + * @param all the value + * @return this builder + */ + public Builder all(long all) { + return set("all", Conv.LONG, all); + } + + /** + * Every other network. + * + * @param identity the value + * @return this builder + */ + public Builder identity(long identity) { + return set("identity", Conv.LONG, identity); + } + + /** + * Networks whose members are all transaction parties. + * + * @param transactions the value + * @return this builder + */ + public Builder transactions(long transactions) { + return set("transactions", Conv.LONG, transactions); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Scopes} + */ + public Scopes build() { + return new Scopes(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Visible networks inside the selected scope, by status, plus two saved views. */ + public static final class Views extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("all", "active", "in_review", "resolved", "dismissed", "mine", "high_risk"); + + private Views(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Views} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Views fromJson(JsonNode json) { + return new Views(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The all field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional all() { + return Json.optional(json, "all", Conv.LONG); + } + + /** + * The active field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional active() { + return Json.optional(json, "active", Conv.LONG); + } + + /** + * The in_review field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional inReview() { + return Json.optional(json, "in_review", Conv.LONG); + } + + /** + * The resolved field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional resolved() { + return Json.optional(json, "resolved", Conv.LONG); + } + + /** + * The dismissed field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dismissed() { + return Json.optional(json, "dismissed", Conv.LONG); + } + + /** + * Networks assigned to the calling console member. Always 0 for an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional mine() { + return Json.optional(json, "mine", Conv.LONG); + } + + /** + * Networks with a risk score of 80 or more that are not dismissed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional highRisk() { + return Json.optional(json, "high_risk", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Views}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Views from) { + super(from); + } + + /** + * The all field. + * + * @param all the value + * @return this builder + */ + public Builder all(long all) { + return set("all", Conv.LONG, all); + } + + /** + * The active field. + * + * @param active the value + * @return this builder + */ + public Builder active(long active) { + return set("active", Conv.LONG, active); + } + + /** + * The in_review field. + * + * @param inReview the value + * @return this builder + */ + public Builder inReview(long inReview) { + return set("in_review", Conv.LONG, inReview); + } + + /** + * The resolved field. + * + * @param resolved the value + * @return this builder + */ + public Builder resolved(long resolved) { + return set("resolved", Conv.LONG, resolved); + } + + /** + * The dismissed field. + * + * @param dismissed the value + * @return this builder + */ + public Builder dismissed(long dismissed) { + return set("dismissed", Conv.LONG, dismissed); + } + + /** + * Networks assigned to the calling console member. Always 0 for an API key. + * + * @param mine the value + * @return this builder + */ + public Builder mine(long mine) { + return set("mine", Conv.LONG, mine); + } + + /** + * Networks with a risk score of 80 or more that are not dismissed. + * + * @param highRisk the value + * @return this builder + */ + public Builder highRisk(long highRisk) { + return set("high_risk", Conv.LONG, highRisk); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Views} + */ + public Views build() { + return new Views(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Counts for each filter option, inside the selected scope and status view. */ + public static final class Facets extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("total", "last_activity_days", "risk_min", "signal_types", "priority", "assignee", "has_case", "min_size"); + + private Facets(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Facets} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Facets fromJson(JsonNode json) { + return new Facets(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Networks in the selected scope and status view. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional total() { + return Json.optional(json, "total", Conv.LONG); + } + + /** + * Networks with activity in the last 7, 30 and 90 days, keyed by the number of days. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastActivityDays() { + return Json.optional(json, "last_activity_days", Conv.model(NetworkListCounts.Facets.LastActivityDays::fromJson)); + } + + /** + * Networks with a risk score of at least 50 and at least 80, keyed by the threshold. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional riskMin() { + return Json.optional(json, "risk_min", Conv.model(NetworkListCounts.Facets.RiskMin::fromJson)); + } + + /** + * Networks with at least one signal of each type. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional signalTypes() { + return Json.optional(json, "signal_types", Conv.model(NetworkListCounts.Facets.SignalTypes::fromJson)); + } + + /** + * Networks by priority. none counts networks without a priority. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional priority() { + return Json.optional(json, "priority", Conv.model(NetworkListCounts.Facets.Priority::fromJson)); + } + + /** + * Networks by assignee. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional assignee() { + return Json.optional(json, "assignee", Conv.model(NetworkListCounts.Facets.Assignee::fromJson)); + } + + /** + * Networks this application opened a case for. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional hasCase() { + return Json.optional(json, "has_case", Conv.LONG); + } + + /** + * Networks with at least 10 members, keyed by the minimum size. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional minSize() { + return Json.optional(json, "min_size", Conv.model(NetworkListCounts.Facets.MinSize::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Facets}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Facets from) { + super(from); + } + + /** + * Networks in the selected scope and status view. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * Networks with activity in the last 7, 30 and 90 days, keyed by the number of days. + * + * @param lastActivityDays the value + * @return this builder + */ + public Builder lastActivityDays(NetworkListCounts.Facets.LastActivityDays lastActivityDays) { + return set("last_activity_days", Conv.model(NetworkListCounts.Facets.LastActivityDays::fromJson), lastActivityDays); + } + + /** + * Networks with a risk score of at least 50 and at least 80, keyed by the threshold. + * + * @param riskMin the value + * @return this builder + */ + public Builder riskMin(NetworkListCounts.Facets.RiskMin riskMin) { + return set("risk_min", Conv.model(NetworkListCounts.Facets.RiskMin::fromJson), riskMin); + } + + /** + * Networks with at least one signal of each type. + * + * @param signalTypes the value + * @return this builder + */ + public Builder signalTypes(NetworkListCounts.Facets.SignalTypes signalTypes) { + return set("signal_types", Conv.model(NetworkListCounts.Facets.SignalTypes::fromJson), signalTypes); + } + + /** + * Networks by priority. none counts networks without a priority. + * + * @param priority the value + * @return this builder + */ + public Builder priority(NetworkListCounts.Facets.Priority priority) { + return set("priority", Conv.model(NetworkListCounts.Facets.Priority::fromJson), priority); + } + + /** + * Networks by assignee. + * + * @param assignee the value + * @return this builder + */ + public Builder assignee(NetworkListCounts.Facets.Assignee assignee) { + return set("assignee", Conv.model(NetworkListCounts.Facets.Assignee::fromJson), assignee); + } + + /** + * Networks this application opened a case for. + * + * @param hasCase the value + * @return this builder + */ + public Builder hasCase(long hasCase) { + return set("has_case", Conv.LONG, hasCase); + } + + /** + * Networks with at least 10 members, keyed by the minimum size. + * + * @param minSize the value + * @return this builder + */ + public Builder minSize(NetworkListCounts.Facets.MinSize minSize) { + return set("min_size", Conv.model(NetworkListCounts.Facets.MinSize::fromJson), minSize); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Facets} + */ + public Facets build() { + return new Facets(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** Networks with activity in the last 7, 30 and 90 days, keyed by the number of days. */ + public static final class LastActivityDays extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("7", "30", "90"); + + private LastActivityDays(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code LastActivityDays} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static LastActivityDays fromJson(JsonNode json) { + return new LastActivityDays(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The 7 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _7() { + return Json.optional(json, "7", Conv.LONG); + } + + /** + * The 30 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _30() { + return Json.optional(json, "30", Conv.LONG); + } + + /** + * The 90 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _90() { + return Json.optional(json, "90", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link LastActivityDays}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(LastActivityDays from) { + super(from); + } + + /** + * The 7 field. + * + * @param _7 the value + * @return this builder + */ + public Builder _7(long _7) { + return set("7", Conv.LONG, _7); + } + + /** + * The 30 field. + * + * @param _30 the value + * @return this builder + */ + public Builder _30(long _30) { + return set("30", Conv.LONG, _30); + } + + /** + * The 90 field. + * + * @param _90 the value + * @return this builder + */ + public Builder _90(long _90) { + return set("90", Conv.LONG, _90); + } + + /** + * Builds the value. + * + * @return a new immutable {@code LastActivityDays} + */ + public LastActivityDays build() { + return new LastActivityDays(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Networks with a risk score of at least 50 and at least 80, keyed by the threshold. */ + public static final class RiskMin extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("50", "80"); + + private RiskMin(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code RiskMin} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static RiskMin fromJson(JsonNode json) { + return new RiskMin(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The 50 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _50() { + return Json.optional(json, "50", Conv.LONG); + } + + /** + * The 80 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _80() { + return Json.optional(json, "80", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link RiskMin}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(RiskMin from) { + super(from); + } + + /** + * The 50 field. + * + * @param _50 the value + * @return this builder + */ + public Builder _50(long _50) { + return set("50", Conv.LONG, _50); + } + + /** + * The 80 field. + * + * @param _80 the value + * @return this builder + */ + public Builder _80(long _80) { + return set("80", Conv.LONG, _80); + } + + /** + * Builds the value. + * + * @return a new immutable {@code RiskMin} + */ + public RiskMin build() { + return new RiskMin(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Networks with at least one signal of each type. */ + public static final class SignalTypes extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("ip_address", "device", "face", "document_number", "phone", "email", "address"); + + private SignalTypes(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SignalTypes} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SignalTypes fromJson(JsonNode json) { + return new SignalTypes(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The ip_address field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipAddress() { + return Json.optional(json, "ip_address", Conv.LONG); + } + + /** + * The device field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional device() { + return Json.optional(json, "device", Conv.LONG); + } + + /** + * The face field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional face() { + return Json.optional(json, "face", Conv.LONG); + } + + /** + * The document_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentNumber() { + return Json.optional(json, "document_number", Conv.LONG); + } + + /** + * The phone field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phone() { + return Json.optional(json, "phone", Conv.LONG); + } + + /** + * The email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.LONG); + } + + /** + * The address field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional address() { + return Json.optional(json, "address", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SignalTypes}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SignalTypes from) { + super(from); + } + + /** + * The ip_address field. + * + * @param ipAddress the value + * @return this builder + */ + public Builder ipAddress(long ipAddress) { + return set("ip_address", Conv.LONG, ipAddress); + } + + /** + * The device field. + * + * @param device the value + * @return this builder + */ + public Builder device(long device) { + return set("device", Conv.LONG, device); + } + + /** + * The face field. + * + * @param face the value + * @return this builder + */ + public Builder face(long face) { + return set("face", Conv.LONG, face); + } + + /** + * The document_number field. + * + * @param documentNumber the value + * @return this builder + */ + public Builder documentNumber(long documentNumber) { + return set("document_number", Conv.LONG, documentNumber); + } + + /** + * The phone field. + * + * @param phone the value + * @return this builder + */ + public Builder phone(long phone) { + return set("phone", Conv.LONG, phone); + } + + /** + * The email field. + * + * @param email the value + * @return this builder + */ + public Builder email(long email) { + return set("email", Conv.LONG, email); + } + + /** + * The address field. + * + * @param address the value + * @return this builder + */ + public Builder address(long address) { + return set("address", Conv.LONG, address); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SignalTypes} + */ + public SignalTypes build() { + return new SignalTypes(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Networks by priority. none counts networks without a priority. */ + public static final class Priority extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("low", "medium", "high", "none"); + + private Priority(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Priority} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Priority fromJson(JsonNode json) { + return new Priority(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The low field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional low() { + return Json.optional(json, "low", Conv.LONG); + } + + /** + * The medium field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional medium() { + return Json.optional(json, "medium", Conv.LONG); + } + + /** + * The high field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional high() { + return Json.optional(json, "high", Conv.LONG); + } + + /** + * The none field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional none() { + return Json.optional(json, "none", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Priority}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Priority from) { + super(from); + } + + /** + * The low field. + * + * @param low the value + * @return this builder + */ + public Builder low(long low) { + return set("low", Conv.LONG, low); + } + + /** + * The medium field. + * + * @param medium the value + * @return this builder + */ + public Builder medium(long medium) { + return set("medium", Conv.LONG, medium); + } + + /** + * The high field. + * + * @param high the value + * @return this builder + */ + public Builder high(long high) { + return set("high", Conv.LONG, high); + } + + /** + * The none field. + * + * @param none the value + * @return this builder + */ + public Builder none(long none) { + return set("none", Conv.LONG, none); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Priority} + */ + public Priority build() { + return new Priority(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Networks by assignee. */ + public static final class Assignee extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("me", "unassigned", "users"); + + private Assignee(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Assignee} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Assignee fromJson(JsonNode json) { + return new Assignee(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Assigned to the calling console member. Always 0 for an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional me() { + return Json.optional(json, "me", Conv.LONG); + } + + /** + * Assigned to nobody. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional unassigned() { + return Json.optional(json, "unassigned", Conv.LONG); + } + + /** + * One entry per assignee, most networks first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> users() { + return Json.optional(json, "users", Conv.list(Conv.model(NetworkListCounts.Facets.Assignee.Users::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Assignee}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Assignee from) { + super(from); + } + + /** + * Assigned to the calling console member. Always 0 for an API key. + * + * @param me the value + * @return this builder + */ + public Builder me(long me) { + return set("me", Conv.LONG, me); + } + + /** + * Assigned to nobody. + * + * @param unassigned the value + * @return this builder + */ + public Builder unassigned(long unassigned) { + return set("unassigned", Conv.LONG, unassigned); + } + + /** + * One entry per assignee, most networks first. + * + * @param users the value + * @return this builder + */ + public Builder users(List users) { + return set("users", Conv.list(Conv.model(NetworkListCounts.Facets.Assignee.Users::fromJson)), users); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Assignee} + */ + public Assignee build() { + return new Assignee(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Users extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "full_name", "email", "count"); + + private Users(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Users} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Users fromJson(JsonNode json) { + return new Users(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * The assignee's name, or their email when no name is set. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fullName() { + return Json.optional(json, "full_name", Conv.STRING); + } + + /** + * The email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + /** + * The count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional count() { + return Json.optional(json, "count", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Users}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Users from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The assignee's name, or their email when no name is set. + * + * @param fullName the value + * @return this builder + */ + public Builder fullName(String fullName) { + return set("full_name", Conv.STRING, fullName); + } + + /** + * The email field. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * The count field. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Users} + */ + public Users build() { + return new Users(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } + + /** Networks with at least 10 members, keyed by the minimum size. */ + public static final class MinSize extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("10"); + + private MinSize(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code MinSize} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static MinSize fromJson(JsonNode json) { + return new MinSize(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The 10 field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional _10() { + return Json.optional(json, "10", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link MinSize}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(MinSize from) { + super(from); + } + + /** + * The 10 field. + * + * @param _10 the value + * @return this builder + */ + public Builder _10(long _10) { + return set("10", Conv.LONG, _10); + } + + /** + * Builds the value. + * + * @return a new immutable {@code MinSize} + */ + public MinSize build() { + return new MinSize(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } +} diff --git a/src/main/java/me/didit/types/NetworkOutcomes.java b/src/main/java/me/didit/types/NetworkOutcomes.java new file mode 100644 index 0000000..6a53d91 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkOutcomes.java @@ -0,0 +1,161 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** + * Where the network's people and businesses stand. Each member you may see counts once, by the status of their most recent verification that reached a decision. Transaction parties are not counted. + */ +public final class NetworkOutcomes extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("approved", "declined", "in_review", "total"); + + private NetworkOutcomes(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkOutcomes} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkOutcomes fromJson(JsonNode json) { + return new NetworkOutcomes(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Members whose most recent decided verification was approved. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional approved() { + return Json.optional(json, "approved", Conv.LONG); + } + + /** + * Members whose most recent decided verification was declined. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional declined() { + return Json.optional(json, "declined", Conv.LONG); + } + + /** + * Members whose most recent decided verification is in review. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional inReview() { + return Json.optional(json, "in_review", Conv.LONG); + } + + /** + * Members with at least one verification, decided or not. A member whose verifications are all undecided (not started, in progress, expired or abandoned) counts here and in no other key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional total() { + return Json.optional(json, "total", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkOutcomes}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkOutcomes from) { + super(from); + } + + /** + * Members whose most recent decided verification was approved. + * + * @param approved the value + * @return this builder + */ + public Builder approved(long approved) { + return set("approved", Conv.LONG, approved); + } + + /** + * Members whose most recent decided verification was declined. + * + * @param declined the value + * @return this builder + */ + public Builder declined(long declined) { + return set("declined", Conv.LONG, declined); + } + + /** + * Members whose most recent decided verification is in review. + * + * @param inReview the value + * @return this builder + */ + public Builder inReview(long inReview) { + return set("in_review", Conv.LONG, inReview); + } + + /** + * Members with at least one verification, decided or not. A member whose verifications are all undecided (not started, in progress, expired or abandoned) counts here and in no other key. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkOutcomes} + */ + public NetworkOutcomes build() { + return new NetworkOutcomes(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkPatternScore.java b/src/main/java/me/didit/types/NetworkPatternScore.java new file mode 100644 index 0000000..818356f --- /dev/null +++ b/src/main/java/me/didit/types/NetworkPatternScore.java @@ -0,0 +1,267 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * How alike one kind of the members' data is across the whole network. A pattern is not a signal: no shared value links the members. + */ +public final class NetworkPatternScore extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "score", "members", "reason", "computed_at"); + + private NetworkPatternScore(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkPatternScore} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkPatternScore fromJson(JsonNode json) { + return new NetworkPatternScore(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * background: the mean similarity of every pair of member selfie backgrounds, with the person masked out. It reads the newest selfie of each person, at most the 50 newest, and leaves out age assurance sessions. It covers the whole network, not only the members of your applications. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional type() { + return Json.optional(json, "type", Conv.enumeration(NetworkPatternScore.Type::of)); + } + + /** + * From 0 to 1, rounded to 4 decimals. Null when reason is set. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.DOUBLE); + } + + /** + * How many members' selfies the score compared. A selfie that cannot be read, or shows too little background, is not compared. Null while computing; 0 or 1 with not_enough_selfies. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional members() { + return Json.optional(json, "members", Conv.LONG); + } + + /** + * Why there is no score. computing: the score for the current members is being computed; read the network again later. not_enough_selfies: fewer than two members have a usable selfie. Null when there is a score. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.enumeration(NetworkPatternScore.Reason::of)); + } + + /** + * When the score was computed. Null while computing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional computedAt() { + return Json.optional(json, "computed_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkPatternScore}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkPatternScore from) { + super(from); + } + + /** + * background: the mean similarity of every pair of member selfie backgrounds, with the person masked out. It reads the newest selfie of each person, at most the 50 newest, and leaves out age assurance sessions. It covers the whole network, not only the members of your applications. + * + * @param type the value + * @return this builder + */ + public Builder type(NetworkPatternScore.Type type) { + return set("type", Conv.enumeration(NetworkPatternScore.Type::of), type); + } + + /** + * From 0 to 1, rounded to 4 decimals. Null when reason is set. + * + * @param score the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder score(@Nullable Double score) { + return setNullable("score", Conv.DOUBLE, score); + } + + /** + * How many members' selfies the score compared. A selfie that cannot be read, or shows too little background, is not compared. Null while computing; 0 or 1 with not_enough_selfies. + * + * @param members the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder members(@Nullable Long members) { + return setNullable("members", Conv.LONG, members); + } + + /** + * Why there is no score. computing: the score for the current members is being computed; read the network again later. not_enough_selfies: fewer than two members have a usable selfie. Null when there is a score. + * + * @param reason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reason(NetworkPatternScore.@Nullable Reason reason) { + return setNullable("reason", Conv.enumeration(NetworkPatternScore.Reason::of), reason); + } + + /** + * When the score was computed. Null while computing. + * + * @param computedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder computedAt(@Nullable OffsetDateTime computedAt) { + return setNullable("computed_at", Conv.DATE_TIME, computedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkPatternScore} + */ + public NetworkPatternScore build() { + return new NetworkPatternScore(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * background: the mean similarity of every pair of member selfie backgrounds, with the person masked out. It reads the newest selfie of each person, at most the 50 newest, and leaves out age assurance sessions. It covers the whole network, not only the members of your applications. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "background"}. */ + public static final Type BACKGROUND = new Type("background"); + + private static final List KNOWN_VALUES = List.of(BACKGROUND); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Why there is no score. computing: the score for the current members is being computed; read the network again later. not_enough_selfies: fewer than two members have a usable selfie. Null when there is a score. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Reason extends StringEnum { + /** The value {@code "computing"}. */ + public static final Reason COMPUTING = new Reason("computing"); + + /** The value {@code "not_enough_selfies"}. */ + public static final Reason NOT_ENOUGH_SELFIES = new Reason("not_enough_selfies"); + + private static final List KNOWN_VALUES = List.of(COMPUTING, NOT_ENOUGH_SELFIES); + + private Reason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Reason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Reason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTag.java b/src/main/java/me/didit/types/NetworkTag.java new file mode 100644 index 0000000..01d5ecc --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTag.java @@ -0,0 +1,245 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** One of your application's tags on the network. */ +public final class NetworkTag extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "name", "color", "source", "added_by", "added_at"); + + private NetworkTag(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTag} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTag fromJson(JsonNode json) { + return new NetworkTag(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The tag's UUID, the same value you pass as tag_uuid. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * Hex color, for example #6366F1. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional color() { + return Json.optional(json, "color", Conv.STRING); + } + + /** + * didit for a Didit preset tag, custom for a tag your team created. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(NetworkTag.Source::of)); + } + + /** + * Email of the console member who added the tag. Null when an API key added it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional addedBy() { + return Json.optional(json, "added_by", Conv.STRING); + } + + /** + * The added_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional addedAt() { + return Json.optional(json, "added_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTag}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTag from) { + super(from); + } + + /** + * The tag's UUID, the same value you pass as tag_uuid. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Hex color, for example #6366F1. + * + * @param color the value + * @return this builder + */ + public Builder color(String color) { + return set("color", Conv.STRING, color); + } + + /** + * didit for a Didit preset tag, custom for a tag your team created. + * + * @param source the value + * @return this builder + */ + public Builder source(NetworkTag.Source source) { + return set("source", Conv.enumeration(NetworkTag.Source::of), source); + } + + /** + * Email of the console member who added the tag. Null when an API key added it. + * + * @param addedBy the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder addedBy(@Nullable String addedBy) { + return setNullable("added_by", Conv.STRING, addedBy); + } + + /** + * The added_at field. + * + * @param addedAt the value + * @return this builder + */ + public Builder addedAt(OffsetDateTime addedAt) { + return set("added_at", Conv.DATE_TIME, addedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTag} + */ + public NetworkTag build() { + return new NetworkTag(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * didit for a Didit preset tag, custom for a tag your team created. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "didit"}. */ + public static final Source DIDIT = new Source("didit"); + + /** The value {@code "custom"}. */ + public static final Source CUSTOM = new Source("custom"); + + private static final List KNOWN_VALUES = List.of(DIDIT, CUSTOM); + + private Source(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTagRequest.java b/src/main/java/me/didit/types/NetworkTagRequest.java new file mode 100644 index 0000000..9c54122 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTagRequest.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class NetworkTagRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("tag_uuid"); + + private NetworkTagRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTagRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTagRequest fromJson(JsonNode json) { + return new NetworkTagRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * UUID of one of your application's tags. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String tagUuid() { + return Json.required(json, "tag_uuid", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTagRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTagRequest from) { + super(from); + } + + /** + * UUID of one of your application's tags. + * + * @param tagUuid the value + * @return this builder + */ + public Builder tagUuid(String tagUuid) { + return set("tag_uuid", Conv.STRING, tagUuid); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTagRequest} + * @throws IllegalStateException when a required field is missing + */ + public NetworkTagRequest build() { + require("tag_uuid", false); + return new NetworkTagRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsListNetworksResponse.java b/src/main/java/me/didit/types/NetworkTimeline.java similarity index 61% rename from src/main/java/me/didit/types/SessionsListNetworksResponse.java rename to src/main/java/me/didit/types/NetworkTimeline.java index 1fbdc46..b5a757d 100644 --- a/src/main/java/me/didit/types/SessionsListNetworksResponse.java +++ b/src/main/java/me/didit/types/NetworkTimeline.java @@ -12,22 +12,23 @@ import me.didit.core.Json; import me.didit.core.JsonModel; -public final class SessionsListNetworksResponse extends JsonModel { +/** Without contract: the network's own events, oldest first, not paginated. */ +public final class NetworkTimeline extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("results"); - private SessionsListNetworksResponse(ObjectNode json, Map> files) { + private NetworkTimeline(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code SessionsListNetworksResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code NetworkTimeline} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static SessionsListNetworksResponse fromJson(JsonNode json) { - return new SessionsListNetworksResponse(Json.copyObject(json), Map.of()); + public static NetworkTimeline fromJson(JsonNode json) { + return new NetworkTimeline(Json.copyObject(json), Map.of()); } /** @@ -54,8 +55,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(EntityNetworkMembership::fromJson))); + public Optional> results() { + return Json.optional(json, "results", Conv.list(Conv.model(NetworkTimelineEvent::fromJson))); } @Override @@ -63,11 +64,11 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link SessionsListNetworksResponse}. */ + /** Builds a {@link NetworkTimeline}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(SessionsListNetworksResponse from) { + private Builder(NetworkTimeline from) { super(from); } @@ -77,17 +78,17 @@ private Builder(SessionsListNetworksResponse from) { * @param results the value * @return this builder */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(EntityNetworkMembership::fromJson)), results); + public Builder results(List results) { + return set("results", Conv.list(Conv.model(NetworkTimelineEvent::fromJson)), results); } /** * Builds the value. * - * @return a new immutable {@code SessionsListNetworksResponse} + * @return a new immutable {@code NetworkTimeline} */ - public SessionsListNetworksResponse build() { - return new SessionsListNetworksResponse(buildJson(), buildFiles()); + public NetworkTimeline build() { + return new NetworkTimeline(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/NetworkTimelineAccount.java b/src/main/java/me/didit/types/NetworkTimelineAccount.java new file mode 100644 index 0000000..399a97c --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineAccount.java @@ -0,0 +1,309 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** The member account the event belongs to. */ +public final class NetworkTimelineAccount extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "node_type", "uuid", "vendor_data", "display_name", "ownership", "route"); + + private NetworkTimelineAccount(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineAccount} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineAccount fromJson(JsonNode json) { + return new NetworkTimelineAccount(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * <kind>:<uuid>, where kind is vendor_user, vendor_business or transaction_party. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + /** + * The node_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeType() { + return Json.optional(json, "node_type", Conv.enumeration(NetworkTimelineAccount.NodeType::of)); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * The vendor_data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * The display_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional displayName() { + return Json.optional(json, "display_name", Conv.STRING); + } + + /** + * owned when the record belongs to the application you called (for a console member, to an application they were granted). organization when it belongs to another application of your organization. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ownership() { + return Json.optional(json, "ownership", Conv.enumeration(NetworkTimelineAccount.Ownership::of)); + } + + /** + * Where the record opens: its kind, its UUID and the application it belongs to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional route() { + return Json.optional(json, "route", Conv.model(NetworkTimelineRoute::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineAccount}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineAccount from) { + super(from); + } + + /** + * <kind>:<uuid>, where kind is vendor_user, vendor_business or transaction_party. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * The node_type field. + * + * @param nodeType the value + * @return this builder + */ + public Builder nodeType(NetworkTimelineAccount.NodeType nodeType) { + return set("node_type", Conv.enumeration(NetworkTimelineAccount.NodeType::of), nodeType); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The vendor_data field. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * The display_name field. + * + * @param displayName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder displayName(@Nullable String displayName) { + return setNullable("display_name", Conv.STRING, displayName); + } + + /** + * owned when the record belongs to the application you called (for a console member, to an application they were granted). organization when it belongs to another application of your organization. + * + * @param ownership the value + * @return this builder + */ + public Builder ownership(NetworkTimelineAccount.Ownership ownership) { + return set("ownership", Conv.enumeration(NetworkTimelineAccount.Ownership::of), ownership); + } + + /** + * Where the record opens: its kind, its UUID and the application it belongs to. + * + * @param route the value + * @return this builder + */ + public Builder route(NetworkTimelineRoute route) { + return set("route", Conv.model(NetworkTimelineRoute::fromJson), route); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineAccount} + */ + public NetworkTimelineAccount build() { + return new NetworkTimelineAccount(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NodeType extends StringEnum { + /** The value {@code "account"}. */ + public static final NodeType ACCOUNT = new NodeType("account"); + + /** The value {@code "business_account"}. */ + public static final NodeType BUSINESS_ACCOUNT = new NodeType("business_account"); + + /** The value {@code "transaction_party"}. */ + public static final NodeType TRANSACTION_PARTY = new NodeType("transaction_party"); + + private static final List KNOWN_VALUES = List.of(ACCOUNT, BUSINESS_ACCOUNT, TRANSACTION_PARTY); + + private NodeType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NodeType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NodeType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * owned when the record belongs to the application you called (for a console member, to an application they were granted). organization when it belongs to another application of your organization. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Ownership extends StringEnum { + /** The value {@code "owned"}. */ + public static final Ownership OWNED = new Ownership("owned"); + + /** The value {@code "organization"}. */ + public static final Ownership ORGANIZATION = new Ownership("organization"); + + private static final List KNOWN_VALUES = List.of(OWNED, ORGANIZATION); + + private Ownership(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Ownership of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Ownership::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineAmount.java b/src/main/java/me/didit/types/NetworkTimelineAmount.java new file mode 100644 index 0000000..58122e8 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineAmount.java @@ -0,0 +1,139 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class NetworkTimelineAmount extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("value", "currency", "direction"); + + private NetworkTimelineAmount(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineAmount} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineAmount fromJson(JsonNode json) { + return new NetworkTimelineAmount(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Decimal amount as a string. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional value() { + return Json.optional(json, "value", Conv.STRING); + } + + /** + * The currency field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currency() { + return Json.optional(json, "currency", Conv.STRING); + } + + /** + * The direction field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional direction() { + return Json.optional(json, "direction", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineAmount}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineAmount from) { + super(from); + } + + /** + * Decimal amount as a string. + * + * @param value the value + * @return this builder + */ + public Builder value(String value) { + return set("value", Conv.STRING, value); + } + + /** + * The currency field. + * + * @param currency the value + * @return this builder + */ + public Builder currency(String currency) { + return set("currency", Conv.STRING, currency); + } + + /** + * The direction field. + * + * @param direction the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder direction(@Nullable String direction) { + return setNullable("direction", Conv.STRING, direction); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineAmount} + */ + public NetworkTimelineAmount build() { + return new NetworkTimelineAmount(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineAttempt.java b/src/main/java/me/didit/types/NetworkTimelineAttempt.java new file mode 100644 index 0000000..d60ad2a --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineAttempt.java @@ -0,0 +1,221 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** One verification session of the account. */ +public final class NetworkTimelineAttempt extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "session_number", "attempt", "attempt_count", "status", "status_changed_at", "decline_reasons"); + + private NetworkTimelineAttempt(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineAttempt} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineAttempt fromJson(JsonNode json) { + return new NetworkTimelineAttempt(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The session UUID, or the business session UUID for a company. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * The session_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionNumber() { + return Json.optional(json, "session_number", Conv.LONG); + } + + /** + * This session's position among the account's sessions, in creation order. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional attempt() { + return Json.optional(json, "attempt", Conv.LONG); + } + + /** + * How many sessions the account has. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional attemptCount() { + return Json.optional(json, "attempt_count", Conv.LONG); + } + + /** + * The session status, for example Approved or Declined. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * The status_changed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional statusChangedAt() { + return Json.optional(json, "status_changed_at", Conv.DATE_TIME); + } + + /** + * For a declined session only: its warnings, oldest first, once per warning. Empty otherwise. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> declineReasons() { + return Json.optional(json, "decline_reasons", Conv.list(Conv.model(NetworkTimelineDeclineReason::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineAttempt}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineAttempt from) { + super(from); + } + + /** + * The session UUID, or the business session UUID for a company. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * The session_number field. + * + * @param sessionNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionNumber(@Nullable Long sessionNumber) { + return setNullable("session_number", Conv.LONG, sessionNumber); + } + + /** + * This session's position among the account's sessions, in creation order. + * + * @param attempt the value + * @return this builder + */ + public Builder attempt(long attempt) { + return set("attempt", Conv.LONG, attempt); + } + + /** + * How many sessions the account has. + * + * @param attemptCount the value + * @return this builder + */ + public Builder attemptCount(long attemptCount) { + return set("attempt_count", Conv.LONG, attemptCount); + } + + /** + * The session status, for example Approved or Declined. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * The status_changed_at field. + * + * @param statusChangedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder statusChangedAt(@Nullable OffsetDateTime statusChangedAt) { + return setNullable("status_changed_at", Conv.DATE_TIME, statusChangedAt); + } + + /** + * For a declined session only: its warnings, oldest first, once per warning. Empty otherwise. + * + * @param declineReasons the value + * @return this builder + */ + public Builder declineReasons(List declineReasons) { + return set("decline_reasons", Conv.list(Conv.model(NetworkTimelineDeclineReason::fromJson)), declineReasons); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineAttempt} + */ + public NetworkTimelineAttempt build() { + return new NetworkTimelineAttempt(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineCaseLinked.java b/src/main/java/me/didit/types/NetworkTimelineCaseLinked.java new file mode 100644 index 0000000..2b60bae --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineCaseLinked.java @@ -0,0 +1,182 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** + * A case opened for the network in your organization. source_ref.ownership says whether it belongs to the application you called. + */ +public final class NetworkTimelineCaseLinked extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("case_id", "case_number", "status", "author_email", "author_name"); + + private NetworkTimelineCaseLinked(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineCaseLinked} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineCaseLinked fromJson(JsonNode json) { + return new NetworkTimelineCaseLinked(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The case_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional caseId() { + return Json.optional(json, "case_id", Conv.STRING); + } + + /** + * The case_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional caseNumber() { + return Json.optional(json, "case_number", Conv.STRING); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * The author_email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional authorEmail() { + return Json.optional(json, "author_email", Conv.STRING); + } + + /** + * The author_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional authorName() { + return Json.optional(json, "author_name", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineCaseLinked}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineCaseLinked from) { + super(from); + } + + /** + * The case_id field. + * + * @param caseId the value + * @return this builder + */ + public Builder caseId(String caseId) { + return set("case_id", Conv.STRING, caseId); + } + + /** + * The case_number field. + * + * @param caseNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder caseNumber(@Nullable String caseNumber) { + return setNullable("case_number", Conv.STRING, caseNumber); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * The author_email field. + * + * @param authorEmail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder authorEmail(@Nullable String authorEmail) { + return setNullable("author_email", Conv.STRING, authorEmail); + } + + /** + * The author_name field. + * + * @param authorName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder authorName(@Nullable String authorName) { + return setNullable("author_name", Conv.STRING, authorName); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineCaseLinked} + */ + public NetworkTimelineCaseLinked build() { + return new NetworkTimelineCaseLinked(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineDeclineReason.java b/src/main/java/me/didit/types/NetworkTimelineDeclineReason.java new file mode 100644 index 0000000..3a47a8f --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineDeclineReason.java @@ -0,0 +1,138 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class NetworkTimelineDeclineReason extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("risk", "feature", "short_description"); + + private NetworkTimelineDeclineReason(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineDeclineReason} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineDeclineReason fromJson(JsonNode json) { + return new NetworkTimelineDeclineReason(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The warning code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.STRING); + } + + /** + * The feature that raised the warning. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional feature() { + return Json.optional(json, "feature", Conv.STRING); + } + + /** + * The short_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional shortDescription() { + return Json.optional(json, "short_description", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineDeclineReason}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineDeclineReason from) { + super(from); + } + + /** + * The warning code. + * + * @param risk the value + * @return this builder + */ + public Builder risk(String risk) { + return set("risk", Conv.STRING, risk); + } + + /** + * The feature that raised the warning. + * + * @param feature the value + * @return this builder + */ + public Builder feature(String feature) { + return set("feature", Conv.STRING, feature); + } + + /** + * The short_description field. + * + * @param shortDescription the value + * @return this builder + */ + public Builder shortDescription(String shortDescription) { + return set("short_description", Conv.STRING, shortDescription); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineDeclineReason} + */ + public NetworkTimelineDeclineReason build() { + return new NetworkTimelineDeclineReason(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineDevice.java b/src/main/java/me/didit/types/NetworkTimelineDevice.java new file mode 100644 index 0000000..f698ee6 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineDevice.java @@ -0,0 +1,119 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class NetworkTimelineDevice extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("fingerprint", "label"); + + private NetworkTimelineDevice(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineDevice} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineDevice fromJson(JsonNode json) { + return new NetworkTimelineDevice(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The fingerprint field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fingerprint() { + return Json.optional(json, "fingerprint", Conv.STRING); + } + + /** + * A readable device label built from the event's device context. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional label() { + return Json.optional(json, "label", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineDevice}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineDevice from) { + super(from); + } + + /** + * The fingerprint field. + * + * @param fingerprint the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fingerprint(@Nullable String fingerprint) { + return setNullable("fingerprint", Conv.STRING, fingerprint); + } + + /** + * A readable device label built from the event's device context. + * + * @param label the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder label(@Nullable String label) { + return setNullable("label", Conv.STRING, label); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineDevice} + */ + public NetworkTimelineDevice build() { + return new NetworkTimelineDevice(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineIP.java b/src/main/java/me/didit/types/NetworkTimelineIP.java new file mode 100644 index 0000000..da85d30 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineIP.java @@ -0,0 +1,199 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class NetworkTimelineIP extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("address", "country_code", "city", "connection_type", "is_vpn_or_tor", "is_data_center"); + + private NetworkTimelineIP(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineIP} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineIP fromJson(JsonNode json) { + return new NetworkTimelineIP(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The address field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional address() { + return Json.optional(json, "address", Conv.STRING); + } + + /** + * The country_code field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countryCode() { + return Json.optional(json, "country_code", Conv.STRING); + } + + /** + * The city field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional city() { + return Json.optional(json, "city", Conv.STRING); + } + + /** + * The connection type label for the address, as free text. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional connectionType() { + return Json.optional(json, "connection_type", Conv.STRING); + } + + /** + * The is_vpn_or_tor field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isVpnOrTor() { + return Json.optional(json, "is_vpn_or_tor", Conv.BOOLEAN); + } + + /** + * The is_data_center field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isDataCenter() { + return Json.optional(json, "is_data_center", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineIP}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineIP from) { + super(from); + } + + /** + * The address field. + * + * @param address the value + * @return this builder + */ + public Builder address(String address) { + return set("address", Conv.STRING, address); + } + + /** + * The country_code field. + * + * @param countryCode the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder countryCode(@Nullable String countryCode) { + return setNullable("country_code", Conv.STRING, countryCode); + } + + /** + * The city field. + * + * @param city the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder city(@Nullable String city) { + return setNullable("city", Conv.STRING, city); + } + + /** + * The connection type label for the address, as free text. + * + * @param connectionType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder connectionType(@Nullable String connectionType) { + return setNullable("connection_type", Conv.STRING, connectionType); + } + + /** + * The is_vpn_or_tor field. + * + * @param isVpnOrTor the value + * @return this builder + */ + public Builder isVpnOrTor(boolean isVpnOrTor) { + return set("is_vpn_or_tor", Conv.BOOLEAN, isVpnOrTor); + } + + /** + * The is_data_center field. + * + * @param isDataCenter the value + * @return this builder + */ + public Builder isDataCenter(boolean isDataCenter) { + return set("is_data_center", Conv.BOOLEAN, isDataCenter); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineIP} + */ + public NetworkTimelineIP build() { + return new NetworkTimelineIP(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineLogin.java b/src/main/java/me/didit/types/NetworkTimelineLogin.java new file mode 100644 index 0000000..414ed89 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineLogin.java @@ -0,0 +1,220 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** A sign-in event you posted for a person through the events API. */ +public final class NetworkTimelineLogin extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("action", "succeeded", "status", "ip", "device", "is_first_login", "is_new_device"); + + private NetworkTimelineLogin(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineLogin} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineLogin fromJson(JsonNode json) { + return new NetworkTimelineLogin(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * <event_category>.<action_type>: auth.login or security.login_succeeded for a sign-in, auth.login_failed or security.login_failed for a failed one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional action() { + return Json.optional(json, "action", Conv.STRING); + } + + /** + * The succeeded field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional succeeded() { + return Json.optional(json, "succeeded", Conv.BOOLEAN); + } + + /** + * The event's status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * Null when the event has no IP address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ip() { + return Json.optional(json, "ip", Conv.model(NetworkTimelineIP::fromJson)); + } + + /** + * Null when the event has no device fingerprint or context. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional device() { + return Json.optional(json, "device", Conv.model(NetworkTimelineDevice::fromJson)); + } + + /** + * The account's first successful sign-in on record. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isFirstLogin() { + return Json.optional(json, "is_first_login", Conv.BOOLEAN); + } + + /** + * A successful sign-in from a device none of the account's earlier successful sign-ins came from. Never true on the first sign-in. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isNewDevice() { + return Json.optional(json, "is_new_device", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineLogin}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineLogin from) { + super(from); + } + + /** + * <event_category>.<action_type>: auth.login or security.login_succeeded for a sign-in, auth.login_failed or security.login_failed for a failed one. + * + * @param action the value + * @return this builder + */ + public Builder action(String action) { + return set("action", Conv.STRING, action); + } + + /** + * The succeeded field. + * + * @param succeeded the value + * @return this builder + */ + public Builder succeeded(boolean succeeded) { + return set("succeeded", Conv.BOOLEAN, succeeded); + } + + /** + * The event's status. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * Null when the event has no IP address. + * + * @param ip the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ip(@Nullable NetworkTimelineIP ip) { + return setNullable("ip", Conv.model(NetworkTimelineIP::fromJson), ip); + } + + /** + * Null when the event has no device fingerprint or context. + * + * @param device the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder device(@Nullable NetworkTimelineDevice device) { + return setNullable("device", Conv.model(NetworkTimelineDevice::fromJson), device); + } + + /** + * The account's first successful sign-in on record. + * + * @param isFirstLogin the value + * @return this builder + */ + public Builder isFirstLogin(boolean isFirstLogin) { + return set("is_first_login", Conv.BOOLEAN, isFirstLogin); + } + + /** + * A successful sign-in from a device none of the account's earlier successful sign-ins came from. Never true on the first sign-in. + * + * @param isNewDevice the value + * @return this builder + */ + public Builder isNewDevice(boolean isNewDevice) { + return set("is_new_device", Conv.BOOLEAN, isNewDevice); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineLogin} + */ + public NetworkTimelineLogin build() { + return new NetworkTimelineLogin(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineNoteAdded.java b/src/main/java/me/didit/types/NetworkTimelineNoteAdded.java new file mode 100644 index 0000000..60bcc52 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineNoteAdded.java @@ -0,0 +1,119 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** A note left on the network. The note text is not included. */ +public final class NetworkTimelineNoteAdded extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("note_id", "author_email"); + + private NetworkTimelineNoteAdded(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineNoteAdded} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineNoteAdded fromJson(JsonNode json) { + return new NetworkTimelineNoteAdded(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The note_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional noteId() { + return Json.optional(json, "note_id", Conv.STRING); + } + + /** + * The author_email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional authorEmail() { + return Json.optional(json, "author_email", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineNoteAdded}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineNoteAdded from) { + super(from); + } + + /** + * The note_id field. + * + * @param noteId the value + * @return this builder + */ + public Builder noteId(String noteId) { + return set("note_id", Conv.STRING, noteId); + } + + /** + * The author_email field. + * + * @param authorEmail the value + * @return this builder + */ + public Builder authorEmail(String authorEmail) { + return set("author_email", Conv.STRING, authorEmail); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineNoteAdded} + */ + public NetworkTimelineNoteAdded build() { + return new NetworkTimelineNoteAdded(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelinePage.java b/src/main/java/me/didit/types/NetworkTimelinePage.java new file mode 100644 index 0000000..2f7dec9 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelinePage.java @@ -0,0 +1,120 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** With contract=2: one page of typed events, newest first. */ +public final class NetworkTimelinePage extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("results", "next_cursor"); + + private NetworkTimelinePage(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelinePage} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelinePage fromJson(JsonNode json) { + return new NetworkTimelinePage(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The results field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> results() { + return Json.optional(json, "results", Conv.list(Conv.model(NetworkTypedTimelineEvent::fromJson))); + } + + /** + * Pass as cursor to read the next page. Null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nextCursor() { + return Json.optional(json, "next_cursor", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelinePage}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelinePage from) { + super(from); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(NetworkTypedTimelineEvent::fromJson)), results); + } + + /** + * Pass as cursor to read the next page. Null on the last page. + * + * @param nextCursor the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nextCursor(@Nullable String nextCursor) { + return setNullable("next_cursor", Conv.STRING, nextCursor); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelinePage} + */ + public NetworkTimelinePage build() { + return new NetworkTimelinePage(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineRoute.java b/src/main/java/me/didit/types/NetworkTimelineRoute.java new file mode 100644 index 0000000..bf66bef --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineRoute.java @@ -0,0 +1,139 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Where the record opens: its kind, its UUID and the application it belongs to. */ +public final class NetworkTimelineRoute extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("kind", "id", "application_id"); + + private NetworkTimelineRoute(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineRoute} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineRoute fromJson(JsonNode json) { + return new NetworkTimelineRoute(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * user, business or transaction_party for an account; session, business_session or transaction for a source record. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.STRING); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The application_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional applicationId() { + return Json.optional(json, "application_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineRoute}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineRoute from) { + super(from); + } + + /** + * user, business or transaction_party for an account; session, business_session or transaction for a source record. + * + * @param kind the value + * @return this builder + */ + public Builder kind(String kind) { + return set("kind", Conv.STRING, kind); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The application_id field. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineRoute} + */ + public NetworkTimelineRoute build() { + return new NetworkTimelineRoute(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineRuleHit.java b/src/main/java/me/didit/types/NetworkTimelineRuleHit.java new file mode 100644 index 0000000..dc9309a --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineRuleHit.java @@ -0,0 +1,242 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** + * A live rule that matched a member's transaction or event. Test-mode matches are not included. + */ +public final class NetworkTimelineRuleHit extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("rule_id", "rule_title", "rule_category", "rule_source", "severity", "score_delta", "status_target", "action_summary"); + + private NetworkTimelineRuleHit(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineRuleHit} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineRuleHit fromJson(JsonNode json) { + return new NetworkTimelineRuleHit(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The rule_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ruleId() { + return Json.optional(json, "rule_id", Conv.STRING); + } + + /** + * The rule_title field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ruleTitle() { + return Json.optional(json, "rule_title", Conv.STRING); + } + + /** + * The rule_category field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ruleCategory() { + return Json.optional(json, "rule_category", Conv.STRING); + } + + /** + * The rule_source field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ruleSource() { + return Json.optional(json, "rule_source", Conv.STRING); + } + + /** + * The severity field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional severity() { + return Json.optional(json, "severity", Conv.STRING); + } + + /** + * The score_delta field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scoreDelta() { + return Json.optional(json, "score_delta", Conv.LONG); + } + + /** + * The status_target field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional statusTarget() { + return Json.optional(json, "status_target", Conv.STRING); + } + + /** + * The action_summary field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actionSummary() { + return Json.optional(json, "action_summary", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineRuleHit}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineRuleHit from) { + super(from); + } + + /** + * The rule_id field. + * + * @param ruleId the value + * @return this builder + */ + public Builder ruleId(String ruleId) { + return set("rule_id", Conv.STRING, ruleId); + } + + /** + * The rule_title field. + * + * @param ruleTitle the value + * @return this builder + */ + public Builder ruleTitle(String ruleTitle) { + return set("rule_title", Conv.STRING, ruleTitle); + } + + /** + * The rule_category field. + * + * @param ruleCategory the value + * @return this builder + */ + public Builder ruleCategory(String ruleCategory) { + return set("rule_category", Conv.STRING, ruleCategory); + } + + /** + * The rule_source field. + * + * @param ruleSource the value + * @return this builder + */ + public Builder ruleSource(String ruleSource) { + return set("rule_source", Conv.STRING, ruleSource); + } + + /** + * The severity field. + * + * @param severity the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder severity(@Nullable String severity) { + return setNullable("severity", Conv.STRING, severity); + } + + /** + * The score_delta field. + * + * @param scoreDelta the value + * @return this builder + */ + public Builder scoreDelta(long scoreDelta) { + return set("score_delta", Conv.LONG, scoreDelta); + } + + /** + * The status_target field. + * + * @param statusTarget the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder statusTarget(@Nullable String statusTarget) { + return setNullable("status_target", Conv.STRING, statusTarget); + } + + /** + * The action_summary field. + * + * @param actionSummary the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actionSummary(@Nullable String actionSummary) { + return setNullable("action_summary", Conv.STRING, actionSummary); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineRuleHit} + */ + public NetworkTimelineRuleHit build() { + return new NetworkTimelineRuleHit(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineSourceRef.java b/src/main/java/me/didit/types/NetworkTimelineSourceRef.java new file mode 100644 index 0000000..9393de4 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineSourceRef.java @@ -0,0 +1,254 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** The record the event was read from. */ +public final class NetworkTimelineSourceRef extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("kind", "id", "ownership", "route"); + + private NetworkTimelineSourceRef(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineSourceRef} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineSourceRef fromJson(JsonNode json) { + return new NetworkTimelineSourceRef(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The kind field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(NetworkTimelineSourceRef.Kind::of)); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * owned when the record belongs to the application you called (for a console member, to an application they were granted). organization when it belongs to another application of your organization. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ownership() { + return Json.optional(json, "ownership", Conv.enumeration(NetworkTimelineSourceRef.Ownership::of)); + } + + /** + * Absent for monitoring_event and case. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional route() { + return Json.optional(json, "route", Conv.model(NetworkTimelineRoute::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineSourceRef}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineSourceRef from) { + super(from); + } + + /** + * The kind field. + * + * @param kind the value + * @return this builder + */ + public Builder kind(NetworkTimelineSourceRef.Kind kind) { + return set("kind", Conv.enumeration(NetworkTimelineSourceRef.Kind::of), kind); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * owned when the record belongs to the application you called (for a console member, to an application they were granted). organization when it belongs to another application of your organization. + * + * @param ownership the value + * @return this builder + */ + public Builder ownership(NetworkTimelineSourceRef.Ownership ownership) { + return set("ownership", Conv.enumeration(NetworkTimelineSourceRef.Ownership::of), ownership); + } + + /** + * Absent for monitoring_event and case. + * + * @param route the value + * @return this builder + */ + public Builder route(NetworkTimelineRoute route) { + return set("route", Conv.model(NetworkTimelineRoute::fromJson), route); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineSourceRef} + */ + public NetworkTimelineSourceRef build() { + return new NetworkTimelineSourceRef(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "session"}. */ + public static final Kind SESSION = new Kind("session"); + + /** The value {@code "business_session"}. */ + public static final Kind BUSINESS_SESSION = new Kind("business_session"); + + /** The value {@code "transaction"}. */ + public static final Kind TRANSACTION = new Kind("transaction"); + + /** The value {@code "monitoring_event"}. */ + public static final Kind MONITORING_EVENT = new Kind("monitoring_event"); + + /** The value {@code "case"}. */ + public static final Kind CASE = new Kind("case"); + + private static final List KNOWN_VALUES = List.of(SESSION, BUSINESS_SESSION, TRANSACTION, MONITORING_EVENT, CASE); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * owned when the record belongs to the application you called (for a console member, to an application they were granted). organization when it belongs to another application of your organization. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Ownership extends StringEnum { + /** The value {@code "owned"}. */ + public static final Ownership OWNED = new Ownership("owned"); + + /** The value {@code "organization"}. */ + public static final Ownership ORGANIZATION = new Ownership("organization"); + + private static final List KNOWN_VALUES = List.of(OWNED, ORGANIZATION); + + private Ownership(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Ownership of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Ownership::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTimelineTransaction.java b/src/main/java/me/didit/types/NetworkTimelineTransaction.java new file mode 100644 index 0000000..daf64bd --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTimelineTransaction.java @@ -0,0 +1,260 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** A transaction a member account took part in, dated by its transaction date. */ +public final class NetworkTimelineTransaction extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("transaction_id", "transaction_number", "transaction_type", "action_type", "amount", "status", "decision_reason_code", "decision_reason_label", "party_role"); + + private NetworkTimelineTransaction(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTimelineTransaction} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTimelineTransaction fromJson(JsonNode json) { + return new NetworkTimelineTransaction(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The transaction_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionId() { + return Json.optional(json, "transaction_id", Conv.STRING); + } + + /** + * The transaction_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionNumber() { + return Json.optional(json, "transaction_number", Conv.LONG); + } + + /** + * The transaction_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionType() { + return Json.optional(json, "transaction_type", Conv.STRING); + } + + /** + * The action_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actionType() { + return Json.optional(json, "action_type", Conv.STRING); + } + + /** + * Null when the transaction has no amount. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional amount() { + return Json.optional(json, "amount", Conv.model(NetworkTimelineAmount::fromJson)); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * The decision_reason_code field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decisionReasonCode() { + return Json.optional(json, "decision_reason_code", Conv.STRING); + } + + /** + * The decision_reason_label field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decisionReasonLabel() { + return Json.optional(json, "decision_reason_label", Conv.STRING); + } + + /** + * The account's role in the transaction: APPLICANT, REMITTER, BENEFICIARY or COUNTERPARTY. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional partyRole() { + return Json.optional(json, "party_role", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTimelineTransaction}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTimelineTransaction from) { + super(from); + } + + /** + * The transaction_id field. + * + * @param transactionId the value + * @return this builder + */ + public Builder transactionId(String transactionId) { + return set("transaction_id", Conv.STRING, transactionId); + } + + /** + * The transaction_number field. + * + * @param transactionNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder transactionNumber(@Nullable Long transactionNumber) { + return setNullable("transaction_number", Conv.LONG, transactionNumber); + } + + /** + * The transaction_type field. + * + * @param transactionType the value + * @return this builder + */ + public Builder transactionType(String transactionType) { + return set("transaction_type", Conv.STRING, transactionType); + } + + /** + * The action_type field. + * + * @param actionType the value + * @return this builder + */ + public Builder actionType(String actionType) { + return set("action_type", Conv.STRING, actionType); + } + + /** + * Null when the transaction has no amount. + * + * @param amount the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder amount(@Nullable NetworkTimelineAmount amount) { + return setNullable("amount", Conv.model(NetworkTimelineAmount::fromJson), amount); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * The decision_reason_code field. + * + * @param decisionReasonCode the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder decisionReasonCode(@Nullable String decisionReasonCode) { + return setNullable("decision_reason_code", Conv.STRING, decisionReasonCode); + } + + /** + * The decision_reason_label field. + * + * @param decisionReasonLabel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder decisionReasonLabel(@Nullable String decisionReasonLabel) { + return setNullable("decision_reason_label", Conv.STRING, decisionReasonLabel); + } + + /** + * The account's role in the transaction: APPLICANT, REMITTER, BENEFICIARY or COUNTERPARTY. + * + * @param partyRole the value + * @return this builder + */ + public Builder partyRole(String partyRole) { + return set("party_role", Conv.STRING, partyRole); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTimelineTransaction} + */ + public NetworkTimelineTransaction build() { + return new NetworkTimelineTransaction(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/NetworkTypedTimelineEvent.java b/src/main/java/me/didit/types/NetworkTypedTimelineEvent.java new file mode 100644 index 0000000..38f5613 --- /dev/null +++ b/src/main/java/me/didit/types/NetworkTypedTimelineEvent.java @@ -0,0 +1,436 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One typed timeline event: the four default fields, plus its id, when Didit stored it, the member account and record it belongs to, and the detail block of its event type. The other detail blocks are null. + */ +public final class NetworkTypedTimelineEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "event_type", "occurred_at", "ingested_at", "label", "metadata", "account", "source_ref", "verification_attempt", "transaction", "login", "rule_hit", "case_linked", "note_added"); + + private NetworkTypedTimelineEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworkTypedTimelineEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworkTypedTimelineEvent fromJson(JsonNode json) { + return new NetworkTypedTimelineEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * <source>:<uuid>, unique within the timeline. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The event_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional eventType() { + return Json.optional(json, "event_type", Conv.enumeration(NetworkTypedTimelineEvent.EventType::of)); + } + + /** + * When it happened. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional occurredAt() { + return Json.optional(json, "occurred_at", Conv.DATE_TIME); + } + + /** + * When Didit stored it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ingestedAt() { + return Json.optional(json, "ingested_at", Conv.DATE_TIME); + } + + /** + * A short English label, for example Verification attempt 2 of 3. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional label() { + return Json.optional(json, "label", Conv.STRING); + } + + /** + * For the network's own events, the same metadata as the default timeline. Empty otherwise. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> metadata() { + return Json.optional(json, "metadata", Conv.OBJECT); + } + + /** + * The member account. Null for the network's own events, case_linked and note_added. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional account() { + return Json.optional(json, "account", Conv.model(NetworkTimelineAccount::fromJson)); + } + + /** + * The source record. Null for the network's own events and note_added. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceRef() { + return Json.optional(json, "source_ref", Conv.model(NetworkTimelineSourceRef::fromJson)); + } + + /** + * Set on verification_attempt events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verificationAttempt() { + return Json.optional(json, "verification_attempt", Conv.model(NetworkTimelineAttempt::fromJson)); + } + + /** + * Set on transaction events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transaction() { + return Json.optional(json, "transaction", Conv.model(NetworkTimelineTransaction::fromJson)); + } + + /** + * Set on login events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional login() { + return Json.optional(json, "login", Conv.model(NetworkTimelineLogin::fromJson)); + } + + /** + * Set on rule_hit events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ruleHit() { + return Json.optional(json, "rule_hit", Conv.model(NetworkTimelineRuleHit::fromJson)); + } + + /** + * Set on case_linked events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional caseLinked() { + return Json.optional(json, "case_linked", Conv.model(NetworkTimelineCaseLinked::fromJson)); + } + + /** + * Set on note_added events. Never returned to an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional noteAdded() { + return Json.optional(json, "note_added", Conv.model(NetworkTimelineNoteAdded::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworkTypedTimelineEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworkTypedTimelineEvent from) { + super(from); + } + + /** + * <source>:<uuid>, unique within the timeline. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The event_type field. + * + * @param eventType the value + * @return this builder + */ + public Builder eventType(NetworkTypedTimelineEvent.EventType eventType) { + return set("event_type", Conv.enumeration(NetworkTypedTimelineEvent.EventType::of), eventType); + } + + /** + * When it happened. + * + * @param occurredAt the value + * @return this builder + */ + public Builder occurredAt(OffsetDateTime occurredAt) { + return set("occurred_at", Conv.DATE_TIME, occurredAt); + } + + /** + * When Didit stored it. + * + * @param ingestedAt the value + * @return this builder + */ + public Builder ingestedAt(OffsetDateTime ingestedAt) { + return set("ingested_at", Conv.DATE_TIME, ingestedAt); + } + + /** + * A short English label, for example Verification attempt 2 of 3. + * + * @param label the value + * @return this builder + */ + public Builder label(String label) { + return set("label", Conv.STRING, label); + } + + /** + * For the network's own events, the same metadata as the default timeline. Empty otherwise. + * + * @param metadata the value + * @return this builder + */ + public Builder metadata(Map metadata) { + return setAny("metadata", metadata); + } + + /** + * The member account. Null for the network's own events, case_linked and note_added. + * + * @param account the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder account(@Nullable NetworkTimelineAccount account) { + return setNullable("account", Conv.model(NetworkTimelineAccount::fromJson), account); + } + + /** + * The source record. Null for the network's own events and note_added. + * + * @param sourceRef the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sourceRef(@Nullable NetworkTimelineSourceRef sourceRef) { + return setNullable("source_ref", Conv.model(NetworkTimelineSourceRef::fromJson), sourceRef); + } + + /** + * Set on verification_attempt events. + * + * @param verificationAttempt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verificationAttempt(@Nullable NetworkTimelineAttempt verificationAttempt) { + return setNullable("verification_attempt", Conv.model(NetworkTimelineAttempt::fromJson), verificationAttempt); + } + + /** + * Set on transaction events. + * + * @param transaction the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder transaction(@Nullable NetworkTimelineTransaction transaction) { + return setNullable("transaction", Conv.model(NetworkTimelineTransaction::fromJson), transaction); + } + + /** + * Set on login events. + * + * @param login the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder login(@Nullable NetworkTimelineLogin login) { + return setNullable("login", Conv.model(NetworkTimelineLogin::fromJson), login); + } + + /** + * Set on rule_hit events. + * + * @param ruleHit the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ruleHit(@Nullable NetworkTimelineRuleHit ruleHit) { + return setNullable("rule_hit", Conv.model(NetworkTimelineRuleHit::fromJson), ruleHit); + } + + /** + * Set on case_linked events. + * + * @param caseLinked the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder caseLinked(@Nullable NetworkTimelineCaseLinked caseLinked) { + return setNullable("case_linked", Conv.model(NetworkTimelineCaseLinked::fromJson), caseLinked); + } + + /** + * Set on note_added events. Never returned to an API key. + * + * @param noteAdded the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder noteAdded(@Nullable NetworkTimelineNoteAdded noteAdded) { + return setNullable("note_added", Conv.model(NetworkTimelineNoteAdded::fromJson), noteAdded); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworkTypedTimelineEvent} + */ + public NetworkTypedTimelineEvent build() { + return new NetworkTypedTimelineEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EventType extends StringEnum { + /** The value {@code "network_detected"}. */ + public static final EventType NETWORK_DETECTED = new EventType("network_detected"); + + /** The value {@code "signal_observed"}. */ + public static final EventType SIGNAL_OBSERVED = new EventType("signal_observed"); + + /** The value {@code "network_active"}. */ + public static final EventType NETWORK_ACTIVE = new EventType("network_active"); + + /** The value {@code "network_in_review"}. */ + public static final EventType NETWORK_IN_REVIEW = new EventType("network_in_review"); + + /** The value {@code "network_resolved"}. */ + public static final EventType NETWORK_RESOLVED = new EventType("network_resolved"); + + /** The value {@code "network_dismissed"}. */ + public static final EventType NETWORK_DISMISSED = new EventType("network_dismissed"); + + /** The value {@code "verification_attempt"}. */ + public static final EventType VERIFICATION_ATTEMPT = new EventType("verification_attempt"); + + /** The value {@code "transaction"}. */ + public static final EventType TRANSACTION = new EventType("transaction"); + + /** The value {@code "login"}. */ + public static final EventType LOGIN = new EventType("login"); + + /** The value {@code "rule_hit"}. */ + public static final EventType RULE_HIT = new EventType("rule_hit"); + + /** The value {@code "case_linked"}. */ + public static final EventType CASE_LINKED = new EventType("case_linked"); + + /** The value {@code "note_added"}. */ + public static final EventType NOTE_ADDED = new EventType("note_added"); + + private static final List KNOWN_VALUES = List.of(NETWORK_DETECTED, SIGNAL_OBSERVED, NETWORK_ACTIVE, NETWORK_IN_REVIEW, NETWORK_RESOLVED, NETWORK_DISMISSED, VERIFICATION_ATTEMPT, TRANSACTION, LOGIN, RULE_HIT, CASE_LINKED, NOTE_ADDED); + + private EventType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EventType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EventType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/NetworksListParams.java b/src/main/java/me/didit/types/NetworksListParams.java index 50ae985..9c49feb 100644 --- a/src/main/java/me/didit/types/NetworksListParams.java +++ b/src/main/java/me/didit/types/NetworksListParams.java @@ -12,10 +12,11 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; +import me.didit.core.StringEnum; /** Parameters for {@code networks.list}. */ public final class NetworksListParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("status", "signal_type", "pattern_type", "risk_band", "min_size", "max_size", "date_from", "date_to", "q", "ordering", "limit", "offset"); + private static final Set KNOWN_FIELDS = Set.of("status", "scope", "signal_type", "pattern_type", "risk_band", "min_size", "max_size", "assignee", "priority", "risk_min", "has_case", "date_from", "date_to", "q", "ordering", "limit", "offset", "summary"); private NetworksListParams(ObjectNode json, Map> files) { super(json, files); @@ -60,6 +61,16 @@ public Optional status() { return Json.optional(json, "status", Conv.STRING); } + /** + * transactions keeps networks whose members are all transaction parties; identity keeps every other network. Omit for both. It also narrows counts.views and counts.facets. Any other value returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(NetworksListParams.Scope::of)); + } + /** * Comma-separated signal types: ip_address,device,face,document_number,phone,email,address. * @@ -110,6 +121,46 @@ public Optional maxSize() { return Json.optional(json, "max_size", Conv.LONG); } + /** + * Comma-separated assignees: unassigned, member emails (matched case-insensitively), or me for the calling console member. A network matches any value. me needs a console member token: an API key gets 400 {"assignee": ["\"me\" needs a user token; an API key has no assignee identity."]}. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional assignee() { + return Json.optional(json, "assignee", Conv.STRING); + } + + /** + * Comma-separated priorities: low, medium, high, or none for networks without a priority. Any other value returns 400 {"priority": "Unknown priority: <values>."}. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional priority() { + return Json.optional(json, "priority", Conv.STRING); + } + + /** + * Minimum risk score, a whole number from 0 to 100. A value that is not a whole number returns 400 {"risk_min": "Use a whole number from 0 to 100."}. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional riskMin() { + return Json.optional(json, "risk_min", Conv.LONG); + } + + /** + * true keeps the networks your application opened a case for, false the others. Any other value is ignored. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional hasCase() { + return Json.optional(json, "has_case", Conv.BOOLEAN); + } + /** * Lower bound on last_activity_at (ISO 8601 date or datetime). * @@ -170,6 +221,16 @@ public Optional offset() { return Json.optional(json, "offset", Conv.LONG); } + /** + * false returns the page and its count only, without kpis and counts. Defaults to true. Any value other than true or false returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional summary() { + return Json.optional(json, "summary", Conv.BOOLEAN); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -193,6 +254,16 @@ public Builder status(String status) { return set("status", Conv.STRING, status); } + /** + * transactions keeps networks whose members are all transaction parties; identity keeps every other network. Omit for both. It also narrows counts.views and counts.facets. Any other value returns 400. + * + * @param scope the value + * @return this builder + */ + public Builder scope(NetworksListParams.Scope scope) { + return set("scope", Conv.enumeration(NetworksListParams.Scope::of), scope); + } + /** * Comma-separated signal types: ip_address,device,face,document_number,phone,email,address. * @@ -243,6 +314,46 @@ public Builder maxSize(long maxSize) { return set("max_size", Conv.LONG, maxSize); } + /** + * Comma-separated assignees: unassigned, member emails (matched case-insensitively), or me for the calling console member. A network matches any value. me needs a console member token: an API key gets 400 {"assignee": ["\"me\" needs a user token; an API key has no assignee identity."]}. + * + * @param assignee the value + * @return this builder + */ + public Builder assignee(String assignee) { + return set("assignee", Conv.STRING, assignee); + } + + /** + * Comma-separated priorities: low, medium, high, or none for networks without a priority. Any other value returns 400 {"priority": "Unknown priority: <values>."}. + * + * @param priority the value + * @return this builder + */ + public Builder priority(String priority) { + return set("priority", Conv.STRING, priority); + } + + /** + * Minimum risk score, a whole number from 0 to 100. A value that is not a whole number returns 400 {"risk_min": "Use a whole number from 0 to 100."}. + * + * @param riskMin the value + * @return this builder + */ + public Builder riskMin(long riskMin) { + return set("risk_min", Conv.LONG, riskMin); + } + + /** + * true keeps the networks your application opened a case for, false the others. Any other value is ignored. + * + * @param hasCase the value + * @return this builder + */ + public Builder hasCase(boolean hasCase) { + return set("has_case", Conv.BOOLEAN, hasCase); + } + /** * Lower bound on last_activity_at (ISO 8601 date or datetime). * @@ -303,6 +414,16 @@ public Builder offset(long offset) { return set("offset", Conv.LONG, offset); } + /** + * false returns the page and its count only, without kpis and counts. Defaults to true. Any value other than true or false returns 400. + * + * @param summary the value + * @return this builder + */ + public Builder summary(boolean summary) { + return set("summary", Conv.BOOLEAN, summary); + } + /** * Builds the value. * @@ -317,4 +438,46 @@ protected Builder self() { return this; } } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "identity"}. */ + public static final Scope IDENTITY = new Scope("identity"); + + /** The value {@code "transactions"}. */ + public static final Scope TRANSACTIONS = new Scope("transactions"); + + private static final List KNOWN_VALUES = List.of(IDENTITY, TRANSACTIONS); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } } diff --git a/src/main/java/me/didit/types/NetworksListResponse.java b/src/main/java/me/didit/types/NetworksListResponse.java index 80a409d..f2ebb86 100644 --- a/src/main/java/me/didit/types/NetworksListResponse.java +++ b/src/main/java/me/didit/types/NetworksListResponse.java @@ -11,10 +11,11 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; +import me.didit.core.StringEnum; import org.jspecify.annotations.Nullable; public final class NetworksListResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results", "kpis"); + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results", "kpis", "counts", "sharing_mode", "cross_org_available", "cross_org_unavailable_reason"); private NetworksListResponse(ObjectNode json, Map> files) { super(json, files); @@ -52,11 +53,11 @@ public Builder toBuilder() { /** * The count field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public Optional count() { - return Json.optional(json, "count", Conv.LONG); + public long count() { + return Json.required(json, "count", Conv.LONG); } /** @@ -82,15 +83,15 @@ public Optional previous() { /** * The results field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(NetworkListItem::fromJson))); + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(NetworkListItem::fromJson))); } /** - * Each key is {value, delta} over the results matching the current filters. + * Each key is {value, delta} over the results matching the current filters. Omitted when summary=false. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -99,6 +100,46 @@ public Optional kpis() { return Json.optional(json, "kpis", Conv.model(NetworksListResponse.Kpis::fromJson)); } + /** + * Network counts for the list's views and filter options, each network counted once. Returned only when summary is not false. scopes count every network visible to the application. views count the visible networks inside the selected scope. facets count the networks in the selected scope and status view (dismissed networks are left out unless status includes them). The other list filters do not change these counts. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counts() { + return Json.optional(json, "counts", Conv.model(NetworkListCounts::fromJson)); + } + + /** + * The organization's cross-organization sharing mode. Only global compares networks across organizations; private and off keep them to the organization's own history. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public NetworksListResponse.SharingMode sharingMode() { + return Json.required(json, "sharing_mode", Conv.enumeration(NetworksListResponse.SharingMode::of)); + } + + /** + * Whether cross-organization signals apply to this organization. They need both the Identity Enterprise plan and the global sharing mode. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean crossOrgAvailable() { + return Json.required(json, "cross_org_available", Conv.BOOLEAN); + } + + /** + * Why cross-organization signals do not apply, checked in this order. plan: the organization's Identity plan does not include the cross-organization fraud network (it needs Identity Enterprise), whatever the sharing mode. sharing_mode: the plan includes it, but the sharing mode is private or off. Null when cross_org_available is true. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional crossOrgUnavailableReason() { + return Json.optional(json, "cross_org_unavailable_reason", Conv.enumeration(NetworksListResponse.CrossOrgUnavailableReason::of)); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -153,7 +194,7 @@ public Builder results(List results) { } /** - * Each key is {value, delta} over the results matching the current filters. + * Each key is {value, delta} over the results matching the current filters. Omitted when summary=false. * * @param kpis the value * @return this builder @@ -162,12 +203,60 @@ public Builder kpis(NetworksListResponse.Kpis kpis) { return set("kpis", Conv.model(NetworksListResponse.Kpis::fromJson), kpis); } + /** + * Network counts for the list's views and filter options, each network counted once. Returned only when summary is not false. scopes count every network visible to the application. views count the visible networks inside the selected scope. facets count the networks in the selected scope and status view (dismissed networks are left out unless status includes them). The other list filters do not change these counts. + * + * @param counts the value + * @return this builder + */ + public Builder counts(NetworkListCounts counts) { + return set("counts", Conv.model(NetworkListCounts::fromJson), counts); + } + + /** + * The organization's cross-organization sharing mode. Only global compares networks across organizations; private and off keep them to the organization's own history. + * + * @param sharingMode the value + * @return this builder + */ + public Builder sharingMode(NetworksListResponse.SharingMode sharingMode) { + return set("sharing_mode", Conv.enumeration(NetworksListResponse.SharingMode::of), sharingMode); + } + + /** + * Whether cross-organization signals apply to this organization. They need both the Identity Enterprise plan and the global sharing mode. + * + * @param crossOrgAvailable the value + * @return this builder + */ + public Builder crossOrgAvailable(boolean crossOrgAvailable) { + return set("cross_org_available", Conv.BOOLEAN, crossOrgAvailable); + } + + /** + * Why cross-organization signals do not apply, checked in this order. plan: the organization's Identity plan does not include the cross-organization fraud network (it needs Identity Enterprise), whatever the sharing mode. sharing_mode: the plan includes it, but the sharing mode is private or off. Null when cross_org_available is true. + * + * @param crossOrgUnavailableReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder crossOrgUnavailableReason(NetworksListResponse.@Nullable CrossOrgUnavailableReason crossOrgUnavailableReason) { + return setNullable("cross_org_unavailable_reason", Conv.enumeration(NetworksListResponse.CrossOrgUnavailableReason::of), crossOrgUnavailableReason); + } + /** * Builds the value. * * @return a new immutable {@code NetworksListResponse} + * @throws IllegalStateException when a required field is missing */ public NetworksListResponse build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + require("sharing_mode", false); + require("cross_org_available", false); + require("cross_org_unavailable_reason", true); return new NetworksListResponse(buildJson(), buildFiles()); } @@ -177,7 +266,9 @@ protected Builder self() { } } - /** Each key is {value, delta} over the results matching the current filters. */ + /** + * Each key is {value, delta} over the results matching the current filters. Omitted when summary=false. + */ public static final class Kpis extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("active", "in_review", "applicants_involved", "detected_this_period", "false_positive_rate"); @@ -342,4 +433,93 @@ protected Builder self() { } } } + + /** + * The organization's cross-organization sharing mode. Only global compares networks across organizations; private and off keep them to the organization's own history. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SharingMode extends StringEnum { + /** The value {@code "off"}. */ + public static final SharingMode OFF = new SharingMode("off"); + + /** The value {@code "private"}. */ + public static final SharingMode PRIVATE = new SharingMode("private"); + + /** The value {@code "global"}. */ + public static final SharingMode GLOBAL = new SharingMode("global"); + + private static final List KNOWN_VALUES = List.of(OFF, PRIVATE, GLOBAL); + + private SharingMode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SharingMode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SharingMode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Why cross-organization signals do not apply, checked in this order. plan: the organization's Identity plan does not include the cross-organization fraud network (it needs Identity Enterprise), whatever the sharing mode. sharing_mode: the plan includes it, but the sharing mode is private or off. Null when cross_org_available is true. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class CrossOrgUnavailableReason extends StringEnum { + /** The value {@code "plan"}. */ + public static final CrossOrgUnavailableReason PLAN = new CrossOrgUnavailableReason("plan"); + + /** The value {@code "sharing_mode"}. */ + public static final CrossOrgUnavailableReason SHARING_MODE = new CrossOrgUnavailableReason("sharing_mode"); + + private static final List KNOWN_VALUES = List.of(PLAN, SHARING_MODE); + + private CrossOrgUnavailableReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static CrossOrgUnavailableReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, CrossOrgUnavailableReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } } diff --git a/src/main/java/me/didit/types/NetworksRetrieveTimelineParams.java b/src/main/java/me/didit/types/NetworksRetrieveTimelineParams.java new file mode 100644 index 0000000..7880474 --- /dev/null +++ b/src/main/java/me/didit/types/NetworksRetrieveTimelineParams.java @@ -0,0 +1,271 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code networks.retrieve_timeline}. */ +public final class NetworksRetrieveTimelineParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("contract", "limit", "cursor", "event_type"); + + private NetworksRetrieveTimelineParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NetworksRetrieveTimelineParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NetworksRetrieveTimelineParams fromJson(JsonNode json) { + return new NetworksRetrieveTimelineParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * 2 returns the typed timeline. Omit it for the default timeline. Any other value returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional contract() { + return Json.optional(json, "contract", Conv.enumeration(NetworksRetrieveTimelineParams.Contract::of)); + } + + /** + * Typed timeline only. Events per page, 1 to 500. Defaults to 100. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Typed timeline only. The previous page's next_cursor. An invalid or empty cursor returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional cursor() { + return Json.optional(json, "cursor", Conv.STRING); + } + + /** + * Typed timeline only. Event types to keep, repeated or comma-separated, at most 20 values. Defaults to every type. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> eventType() { + return Json.optional(json, "event_type", Conv.list(Conv.enumeration(NetworksRetrieveTimelineParams.EventType::of))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NetworksRetrieveTimelineParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NetworksRetrieveTimelineParams from) { + super(from); + } + + /** + * 2 returns the typed timeline. Omit it for the default timeline. Any other value returns 400. + * + * @param contract the value + * @return this builder + */ + public Builder contract(NetworksRetrieveTimelineParams.Contract contract) { + return set("contract", Conv.enumeration(NetworksRetrieveTimelineParams.Contract::of), contract); + } + + /** + * Typed timeline only. Events per page, 1 to 500. Defaults to 100. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Typed timeline only. The previous page's next_cursor. An invalid or empty cursor returns 400. + * + * @param cursor the value + * @return this builder + */ + public Builder cursor(String cursor) { + return set("cursor", Conv.STRING, cursor); + } + + /** + * Typed timeline only. Event types to keep, repeated or comma-separated, at most 20 values. Defaults to every type. + * + * @param eventType the value + * @return this builder + */ + public Builder eventType(List eventType) { + return set("event_type", Conv.list(Conv.enumeration(NetworksRetrieveTimelineParams.EventType::of)), eventType); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NetworksRetrieveTimelineParams} + */ + public NetworksRetrieveTimelineParams build() { + return new NetworksRetrieveTimelineParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Contract extends StringEnum { + /** The value {@code "2"}. */ + public static final Contract _2 = new Contract("2"); + + private static final List KNOWN_VALUES = List.of(_2); + + private Contract(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Contract of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Contract::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EventType extends StringEnum { + /** The value {@code "network_detected"}. */ + public static final EventType NETWORK_DETECTED = new EventType("network_detected"); + + /** The value {@code "signal_observed"}. */ + public static final EventType SIGNAL_OBSERVED = new EventType("signal_observed"); + + /** The value {@code "network_active"}. */ + public static final EventType NETWORK_ACTIVE = new EventType("network_active"); + + /** The value {@code "network_in_review"}. */ + public static final EventType NETWORK_IN_REVIEW = new EventType("network_in_review"); + + /** The value {@code "network_resolved"}. */ + public static final EventType NETWORK_RESOLVED = new EventType("network_resolved"); + + /** The value {@code "network_dismissed"}. */ + public static final EventType NETWORK_DISMISSED = new EventType("network_dismissed"); + + /** The value {@code "verification_attempt"}. */ + public static final EventType VERIFICATION_ATTEMPT = new EventType("verification_attempt"); + + /** The value {@code "transaction"}. */ + public static final EventType TRANSACTION = new EventType("transaction"); + + /** The value {@code "login"}. */ + public static final EventType LOGIN = new EventType("login"); + + /** The value {@code "rule_hit"}. */ + public static final EventType RULE_HIT = new EventType("rule_hit"); + + /** The value {@code "case_linked"}. */ + public static final EventType CASE_LINKED = new EventType("case_linked"); + + /** The value {@code "note_added"}. */ + public static final EventType NOTE_ADDED = new EventType("note_added"); + + private static final List KNOWN_VALUES = List.of(NETWORK_DETECTED, SIGNAL_OBSERVED, NETWORK_ACTIVE, NETWORK_IN_REVIEW, NETWORK_RESOLVED, NETWORK_DISMISSED, VERIFICATION_ATTEMPT, TRANSACTION, LOGIN, RULE_HIT, CASE_LINKED, NOTE_ADDED); + + private EventType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EventType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EventType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/NetworksRetrieveTimelineResponse.java b/src/main/java/me/didit/types/NetworksRetrieveTimelineResponse.java index eeca76e..e26aa2a 100644 --- a/src/main/java/me/didit/types/NetworksRetrieveTimelineResponse.java +++ b/src/main/java/me/didit/types/NetworksRetrieveTimelineResponse.java @@ -2,97 +2,88 @@ package me.didit.types; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import me.didit.core.Conv; -import me.didit.core.FileUpload; import me.didit.core.Json; -import me.didit.core.JsonModel; +import me.didit.core.JsonUnion; -public final class NetworksRetrieveTimelineResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("results"); - - private NetworksRetrieveTimelineResponse(ObjectNode json, Map> files) { - super(json, files); +/** + * + *

One of: {@code NetworkTimeline}, {@code NetworkTimelinePage}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ +public final class NetworksRetrieveTimelineResponse extends JsonUnion { + private NetworksRetrieveTimelineResponse(JsonNode json) { + super(json); } /** - * Reads a {@code NetworksRetrieveTimelineResponse} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. + * Reads a {@code NetworksRetrieveTimelineResponse} from JSON. * - * @param json a JSON object - * @return the parsed value + * @param json any JSON value + * @return the union */ public static NetworksRetrieveTimelineResponse fromJson(JsonNode json) { - return new NetworksRetrieveTimelineResponse(Json.copyObject(json), Map.of()); + return new NetworksRetrieveTimelineResponse(json.deepCopy()); } /** - * Returns an empty builder. + * Wraps a {@code NetworkTimeline} value. * - * @return a new builder + * @param value the value + * @return the union */ - public static Builder builder() { - return new Builder(); + public static NetworksRetrieveTimelineResponse ofNetworkTimeline(NetworkTimeline value) { + return new NetworksRetrieveTimelineResponse(Conv.model(NetworkTimeline::fromJson).write(value)); } /** - * Returns a builder holding a copy of every field of this value. + * Whether this value is a {@code NetworkTimeline}. + * + * Without contract: the network's own events, oldest first, not paginated. * - * @return a new builder + * @return true when it matches */ - public Builder toBuilder() { - return new Builder(this); + public boolean isNetworkTimeline() { + return json.isObject(); } /** - * The results field. + * Reads this value as a {@code NetworkTimeline}. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return the value, or empty when {@link #isNetworkTimeline()} is false */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(NetworkTimelineEvent::fromJson))); + public Optional asNetworkTimeline() { + return isNetworkTimeline() ? Optional.of(Conv.model(NetworkTimeline::fromJson).read(json)) : Optional.empty(); } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; + /** + * Wraps a {@code NetworkTimelinePage} value. + * + * @param value the value + * @return the union + */ + public static NetworksRetrieveTimelineResponse ofNetworkTimelinePage(NetworkTimelinePage value) { + return new NetworksRetrieveTimelineResponse(Conv.model(NetworkTimelinePage::fromJson).write(value)); } - /** Builds a {@link NetworksRetrieveTimelineResponse}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(NetworksRetrieveTimelineResponse from) { - super(from); - } - - /** - * The results field. - * - * @param results the value - * @return this builder - */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(NetworkTimelineEvent::fromJson)), results); - } - - /** - * Builds the value. - * - * @return a new immutable {@code NetworksRetrieveTimelineResponse} - */ - public NetworksRetrieveTimelineResponse build() { - return new NetworksRetrieveTimelineResponse(buildJson(), buildFiles()); - } + /** + * Whether this value is a {@code NetworkTimelinePage}. + * + * With contract=2: one page of typed events, newest first. + * + * @return true when it matches + */ + public boolean isNetworkTimelinePage() { + return Json.hasAnyField(json, "results", "next_cursor"); + } - @Override - protected Builder self() { - return this; - } + /** + * Reads this value as a {@code NetworkTimelinePage}. + * + * @return the value, or empty when {@link #isNetworkTimelinePage()} is false + */ + public Optional asNetworkTimelinePage() { + return isNetworkTimelinePage() ? Optional.of(Conv.model(NetworkTimelinePage::fromJson).read(json)) : Optional.empty(); } } diff --git a/src/main/java/me/didit/types/UsersRetrieveOngoingMonitoringJobResponse.java b/src/main/java/me/didit/types/OngoingMonitoringJob.java similarity index 91% rename from src/main/java/me/didit/types/UsersRetrieveOngoingMonitoringJobResponse.java rename to src/main/java/me/didit/types/OngoingMonitoringJob.java index 13ef4db..4302f4f 100644 --- a/src/main/java/me/didit/types/UsersRetrieveOngoingMonitoringJobResponse.java +++ b/src/main/java/me/didit/types/OngoingMonitoringJob.java @@ -14,22 +14,22 @@ import me.didit.core.StringEnum; import org.jspecify.annotations.Nullable; -public final class UsersRetrieveOngoingMonitoringJobResponse extends JsonModel { +public final class OngoingMonitoringJob extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - private UsersRetrieveOngoingMonitoringJobResponse(ObjectNode json, Map> files) { + private OngoingMonitoringJob(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code UsersRetrieveOngoingMonitoringJobResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code OngoingMonitoringJob} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static UsersRetrieveOngoingMonitoringJobResponse fromJson(JsonNode json) { - return new UsersRetrieveOngoingMonitoringJobResponse(Json.copyObject(json), Map.of()); + public static OngoingMonitoringJob fromJson(JsonNode json) { + return new OngoingMonitoringJob(Json.copyObject(json), Map.of()); } /** @@ -66,8 +66,8 @@ public Optional jobId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(UsersRetrieveOngoingMonitoringJobResponse.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(OngoingMonitoringJob.Status::of)); } /** @@ -106,8 +106,8 @@ public Optional processed() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(UsersRetrieveOngoingMonitoringJobResponse.Outcomes::fromJson)); + public Optional outcomes() { + return Json.optional(json, "outcomes", Conv.model(OngoingMonitoringJob.Outcomes::fromJson)); } /** @@ -126,8 +126,8 @@ public Optional error() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(UsersRetrieveOngoingMonitoringJobResponse.Estimate::fromJson)); + public Optional estimate() { + return Json.optional(json, "estimate", Conv.model(OngoingMonitoringJob.Estimate::fromJson)); } @Override @@ -135,11 +135,11 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link UsersRetrieveOngoingMonitoringJobResponse}. */ + /** Builds a {@link OngoingMonitoringJob}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(UsersRetrieveOngoingMonitoringJobResponse from) { + private Builder(OngoingMonitoringJob from) { super(from); } @@ -159,8 +159,8 @@ public Builder jobId(String jobId) { * @param status the value * @return this builder */ - public Builder status(UsersRetrieveOngoingMonitoringJobResponse.Status status) { - return set("status", Conv.enumeration(UsersRetrieveOngoingMonitoringJobResponse.Status::of), status); + public Builder status(OngoingMonitoringJob.Status status) { + return set("status", Conv.enumeration(OngoingMonitoringJob.Status::of), status); } /** @@ -199,8 +199,8 @@ public Builder processed(long processed) { * @param outcomes the value * @return this builder */ - public Builder outcomes(UsersRetrieveOngoingMonitoringJobResponse.Outcomes outcomes) { - return set("outcomes", Conv.model(UsersRetrieveOngoingMonitoringJobResponse.Outcomes::fromJson), outcomes); + public Builder outcomes(OngoingMonitoringJob.Outcomes outcomes) { + return set("outcomes", Conv.model(OngoingMonitoringJob.Outcomes::fromJson), outcomes); } /** @@ -219,17 +219,17 @@ public Builder error(@Nullable String error) { * @param estimate the value * @return this builder */ - public Builder estimate(UsersRetrieveOngoingMonitoringJobResponse.Estimate estimate) { - return set("estimate", Conv.model(UsersRetrieveOngoingMonitoringJobResponse.Estimate::fromJson), estimate); + public Builder estimate(OngoingMonitoringJob.Estimate estimate) { + return set("estimate", Conv.model(OngoingMonitoringJob.Estimate::fromJson), estimate); } /** * Builds the value. * - * @return a new immutable {@code UsersRetrieveOngoingMonitoringJobResponse} + * @return a new immutable {@code OngoingMonitoringJob} */ - public UsersRetrieveOngoingMonitoringJobResponse build() { - return new UsersRetrieveOngoingMonitoringJobResponse(buildJson(), buildFiles()); + public OngoingMonitoringJob build() { + return new OngoingMonitoringJob(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/UsersSetOngoingMonitoringParams.java b/src/main/java/me/didit/types/OngoingMonitoringToggleRequest.java similarity index 89% rename from src/main/java/me/didit/types/UsersSetOngoingMonitoringParams.java rename to src/main/java/me/didit/types/OngoingMonitoringToggleRequest.java index 8f088f6..6265493 100644 --- a/src/main/java/me/didit/types/UsersSetOngoingMonitoringParams.java +++ b/src/main/java/me/didit/types/OngoingMonitoringToggleRequest.java @@ -13,23 +13,22 @@ import me.didit.core.Json; import me.didit.core.JsonModel; -/** Parameters for {@code users.set_ongoing_monitoring}. */ -public final class UsersSetOngoingMonitoringParams extends JsonModel { +public final class OngoingMonitoringToggleRequest extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("is_enabled", "estimate_only", "update_all", "filter_search", "filter_status", "filter_country", "filter_date_from", "filter_date_to", "didit_internal_id_list"); - private UsersSetOngoingMonitoringParams(ObjectNode json, Map> files) { + private OngoingMonitoringToggleRequest(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code UsersSetOngoingMonitoringParams} from JSON. Unknown fields are kept and readable + * Reads a {@code OngoingMonitoringToggleRequest} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static UsersSetOngoingMonitoringParams fromJson(JsonNode json) { - return new UsersSetOngoingMonitoringParams(Json.copyObject(json), Map.of()); + public static OngoingMonitoringToggleRequest fromJson(JsonNode json) { + return new OngoingMonitoringToggleRequest(Json.copyObject(json), Map.of()); } /** @@ -145,11 +144,11 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link UsersSetOngoingMonitoringParams}. */ + /** Builds a {@link OngoingMonitoringToggleRequest}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(UsersSetOngoingMonitoringParams from) { + private Builder(OngoingMonitoringToggleRequest from) { super(from); } @@ -246,12 +245,12 @@ public Builder diditInternalIdList(List diditInternalIdList) { /** * Builds the value. * - * @return a new immutable {@code UsersSetOngoingMonitoringParams} + * @return a new immutable {@code OngoingMonitoringToggleRequest} * @throws IllegalStateException when a required field is missing */ - public UsersSetOngoingMonitoringParams build() { + public OngoingMonitoringToggleRequest build() { require("is_enabled", false); - return new UsersSetOngoingMonitoringParams(buildJson(), buildFiles()); + return new OngoingMonitoringToggleRequest(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/OngoingMonitoringToggleResult.java b/src/main/java/me/didit/types/OngoingMonitoringToggleResult.java new file mode 100644 index 0000000..f336f80 --- /dev/null +++ b/src/main/java/me/didit/types/OngoingMonitoringToggleResult.java @@ -0,0 +1,339 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.JsonUnion; + +/** + * + *

One of: {@code CostPreview}, {@code OngoingMonitoringJob}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ +public final class OngoingMonitoringToggleResult extends JsonUnion { + private OngoingMonitoringToggleResult(JsonNode json) { + super(json); + } + + /** + * Reads a {@code OngoingMonitoringToggleResult} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static OngoingMonitoringToggleResult fromJson(JsonNode json) { + return new OngoingMonitoringToggleResult(json.deepCopy()); + } + + /** + * Wraps a {@code CostPreview} value. + * + * @param value the value + * @return the union + */ + public static OngoingMonitoringToggleResult ofCostPreview(OngoingMonitoringToggleResult.CostPreview value) { + return new OngoingMonitoringToggleResult(Conv.model(OngoingMonitoringToggleResult.CostPreview::fromJson).write(value)); + } + + /** + * Whether this value is a {@code CostPreview}. + * + * Cost preview. Returned directly when estimate_only is true (no writes are performed). + * + * @return true when it matches + */ + public boolean isCostPreview() { + return Json.hasAnyField(json, "users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); + } + + /** + * Reads this value as a {@code CostPreview}. + * + * @return the value, or empty when {@link #isCostPreview()} is false + */ + public Optional asCostPreview() { + return isCostPreview() ? Optional.of(Conv.model(OngoingMonitoringToggleResult.CostPreview::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code OngoingMonitoringJob} value. + * + * @param value the value + * @return the union + */ + public static OngoingMonitoringToggleResult ofOngoingMonitoringJob(OngoingMonitoringJob value) { + return new OngoingMonitoringToggleResult(Conv.model(OngoingMonitoringJob::fromJson).write(value)); + } + + /** + * Whether this value is a {@code OngoingMonitoringJob}. + * + * @return true when it matches + */ + public boolean isOngoingMonitoringJob() { + return Json.hasAnyField(json, "job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); + } + + /** + * Reads this value as a {@code OngoingMonitoringJob}. + * + * @return the value, or empty when {@link #isOngoingMonitoringJob()} is false + */ + public Optional asOngoingMonitoringJob() { + return isOngoingMonitoringJob() ? Optional.of(Conv.model(OngoingMonitoringJob::fromJson).read(json)) : Optional.empty(); + } + + /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ + public static final class CostPreview extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); + + private CostPreview(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code CostPreview} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static CostPreview fromJson(JsonNode json) { + return new CostPreview(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Entities in the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional usersTotal() { + return Json.optional(json, "users_total", Conv.LONG); + } + + /** + * The already_enabled field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional alreadyEnabled() { + return Json.optional(json, "already_enabled", Conv.LONG); + } + + /** + * Entities whose existing AML screening will be switched to monitored. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional withExistingAml() { + return Json.optional(json, "with_existing_aml", Conv.LONG); + } + + /** + * Entities without any AML screening that will be screened first (billed). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional needingScreening() { + return Json.optional(json, "needing_screening", Conv.LONG); + } + + /** + * The skipped_no_identity_data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional skippedNoIdentityData() { + return Json.optional(json, "skipped_no_identity_data", Conv.LONG); + } + + /** + * The skipped_blocked field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional skippedBlocked() { + return Json.optional(json, "skipped_blocked", Conv.LONG); + } + + /** + * The estimated_screening_cost_usd field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional estimatedScreeningCostUsd() { + return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); + } + + /** + * The estimated_annual_monitoring_cost_usd field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional estimatedAnnualMonitoringCostUsd() { + return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); + } + + /** + * The balance_sufficient field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional balanceSufficient() { + return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link CostPreview}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(CostPreview from) { + super(from); + } + + /** + * Entities in the selection. + * + * @param usersTotal the value + * @return this builder + */ + public Builder usersTotal(long usersTotal) { + return set("users_total", Conv.LONG, usersTotal); + } + + /** + * The already_enabled field. + * + * @param alreadyEnabled the value + * @return this builder + */ + public Builder alreadyEnabled(long alreadyEnabled) { + return set("already_enabled", Conv.LONG, alreadyEnabled); + } + + /** + * Entities whose existing AML screening will be switched to monitored. + * + * @param withExistingAml the value + * @return this builder + */ + public Builder withExistingAml(long withExistingAml) { + return set("with_existing_aml", Conv.LONG, withExistingAml); + } + + /** + * Entities without any AML screening that will be screened first (billed). + * + * @param needingScreening the value + * @return this builder + */ + public Builder needingScreening(long needingScreening) { + return set("needing_screening", Conv.LONG, needingScreening); + } + + /** + * The skipped_no_identity_data field. + * + * @param skippedNoIdentityData the value + * @return this builder + */ + public Builder skippedNoIdentityData(long skippedNoIdentityData) { + return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); + } + + /** + * The skipped_blocked field. + * + * @param skippedBlocked the value + * @return this builder + */ + public Builder skippedBlocked(long skippedBlocked) { + return set("skipped_blocked", Conv.LONG, skippedBlocked); + } + + /** + * The estimated_screening_cost_usd field. + * + * @param estimatedScreeningCostUsd the value + * @return this builder + */ + public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { + return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); + } + + /** + * The estimated_annual_monitoring_cost_usd field. + * + * @param estimatedAnnualMonitoringCostUsd the value + * @return this builder + */ + public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { + return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); + } + + /** + * The balance_sufficient field. + * + * @param balanceSufficient the value + * @return this builder + */ + public Builder balanceSufficient(boolean balanceSufficient) { + return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); + } + + /** + * Builds the value. + * + * @return a new immutable {@code CostPreview} + */ + public CostPreview build() { + return new CostPreview(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/PaginatedApplicationList.java b/src/main/java/me/didit/types/PaginatedApplicationList.java new file mode 100644 index 0000000..8d926e4 --- /dev/null +++ b/src/main/java/me/didit/types/PaginatedApplicationList.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class PaginatedApplicationList extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private PaginatedApplicationList(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code PaginatedApplicationList} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PaginatedApplicationList fromJson(JsonNode json) { + return new PaginatedApplicationList(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Total number of applications. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(Application::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link PaginatedApplicationList}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(PaginatedApplicationList from) { + super(from); + } + + /** + * Total number of applications. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(Application::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code PaginatedApplicationList} + * @throws IllegalStateException when a required field is missing + */ + public PaginatedApplicationList build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new PaginatedApplicationList(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/PaginatedOrganizationList.java b/src/main/java/me/didit/types/PaginatedOrganizationList.java new file mode 100644 index 0000000..5bfe682 --- /dev/null +++ b/src/main/java/me/didit/types/PaginatedOrganizationList.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class PaginatedOrganizationList extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private PaginatedOrganizationList(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code PaginatedOrganizationList} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PaginatedOrganizationList fromJson(JsonNode json) { + return new PaginatedOrganizationList(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Total number of organizations. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(Organization::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link PaginatedOrganizationList}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(PaginatedOrganizationList from) { + super(from); + } + + /** + * Total number of organizations. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(Organization::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code PaginatedOrganizationList} + * @throws IllegalStateException when a required field is missing + */ + public PaginatedOrganizationList build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new PaginatedOrganizationList(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/RegulatoryReportsListResponse.java b/src/main/java/me/didit/types/RegulatoryReportList.java similarity index 83% rename from src/main/java/me/didit/types/RegulatoryReportsListResponse.java rename to src/main/java/me/didit/types/RegulatoryReportList.java index 5322532..f4681df 100644 --- a/src/main/java/me/didit/types/RegulatoryReportsListResponse.java +++ b/src/main/java/me/didit/types/RegulatoryReportList.java @@ -13,22 +13,22 @@ import me.didit.core.JsonModel; import org.jspecify.annotations.Nullable; -public final class RegulatoryReportsListResponse extends JsonModel { +public final class RegulatoryReportList extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); - private RegulatoryReportsListResponse(ObjectNode json, Map> files) { + private RegulatoryReportList(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code RegulatoryReportsListResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code RegulatoryReportList} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static RegulatoryReportsListResponse fromJson(JsonNode json) { - return new RegulatoryReportsListResponse(Json.copyObject(json), Map.of()); + public static RegulatoryReportList fromJson(JsonNode json) { + return new RegulatoryReportList(Json.copyObject(json), Map.of()); } /** @@ -94,11 +94,11 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link RegulatoryReportsListResponse}. */ + /** Builds a {@link RegulatoryReportList}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(RegulatoryReportsListResponse from) { + private Builder(RegulatoryReportList from) { super(from); } @@ -145,10 +145,10 @@ public Builder results(List results) { /** * Builds the value. * - * @return a new immutable {@code RegulatoryReportsListResponse} + * @return a new immutable {@code RegulatoryReportList} */ - public RegulatoryReportsListResponse build() { - return new RegulatoryReportsListResponse(buildJson(), buildFiles()); + public RegulatoryReportList build() { + return new RegulatoryReportList(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/ReviewItem.java b/src/main/java/me/didit/types/ReviewItem.java index 5a71b2b..4693902 100644 --- a/src/main/java/me/didit/types/ReviewItem.java +++ b/src/main/java/me/didit/types/ReviewItem.java @@ -65,7 +65,7 @@ public Optional uuid() { } /** - * What kind of activity this entry records. STATUS_UPDATED rows come from manual decisions (PATCH /v3/session/{session_id}/update-status/) and from entries added via POST …/reviews/ (which default to this type even when no status is recorded). COMMENT rows come from Console reviewers; Console detail-page views are also stored as COMMENT rows flagged with metadata.system_event = "DETAIL_VIEWED". The remaining types track data edits, file changes, tags, and AML monitoring activity. STATUS_UPDATED rows are also written by system actors (e.g. AML ongoing monitoring with actor_type SYSTEM, and automatic feature-status updates), not only by the update-status endpoint and manual review posts. + * What kind of activity this entry records. STATUS_UPDATED rows come from manual decisions (PATCH /v3/session/{session_id}/update-status/) and from entries added via POST …/reviews/ (which default to this type even when no status is recorded). COMMENT rows come from Console reviewers; Console detail-page views are also stored as COMMENT rows flagged with metadata.system_event = "DETAIL_VIEWED". The remaining types track data edits, file changes, tags, and AML monitoring activity. STATUS_UPDATED rows are also written by system actors (e.g. AML ongoing monitoring with actor_type SYSTEM, and automatic feature-status updates), not only by the update-status endpoint and manual review posts. FEATURE_OVERRIDE_PENDING, FEATURE_OVERRIDE_APPLIED and FEATURE_OVERRIDE_CANCELLED record an override of one check's status (PATCH /v3/session/{session_id}/features/{node_id}/update-status/): requested to wait for the flow to finish, applied, or cancelled. They carry the check's status in previous_value / new_value ({"status": ...}), leave previous_status and new_status null, and name the override in metadata (feature_override_id, node_id, feature_model, forced, state, cancel_reason). The entry for the request carries the reviewer's comment; an override applied when the flow finishes is recorded with actor_type SYSTEM. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -218,7 +218,7 @@ public Builder uuid(String uuid) { } /** - * What kind of activity this entry records. STATUS_UPDATED rows come from manual decisions (PATCH /v3/session/{session_id}/update-status/) and from entries added via POST …/reviews/ (which default to this type even when no status is recorded). COMMENT rows come from Console reviewers; Console detail-page views are also stored as COMMENT rows flagged with metadata.system_event = "DETAIL_VIEWED". The remaining types track data edits, file changes, tags, and AML monitoring activity. STATUS_UPDATED rows are also written by system actors (e.g. AML ongoing monitoring with actor_type SYSTEM, and automatic feature-status updates), not only by the update-status endpoint and manual review posts. + * What kind of activity this entry records. STATUS_UPDATED rows come from manual decisions (PATCH /v3/session/{session_id}/update-status/) and from entries added via POST …/reviews/ (which default to this type even when no status is recorded). COMMENT rows come from Console reviewers; Console detail-page views are also stored as COMMENT rows flagged with metadata.system_event = "DETAIL_VIEWED". The remaining types track data edits, file changes, tags, and AML monitoring activity. STATUS_UPDATED rows are also written by system actors (e.g. AML ongoing monitoring with actor_type SYSTEM, and automatic feature-status updates), not only by the update-status endpoint and manual review posts. FEATURE_OVERRIDE_PENDING, FEATURE_OVERRIDE_APPLIED and FEATURE_OVERRIDE_CANCELLED record an override of one check's status (PATCH /v3/session/{session_id}/features/{node_id}/update-status/): requested to wait for the flow to finish, applied, or cancelled. They carry the check's status in previous_value / new_value ({"status": ...}), leave previous_status and new_status null, and name the override in metadata (feature_override_id, node_id, feature_model, forced, state, cancel_reason). The entry for the request carries the reviewer's comment; an override applied when the flow finishes is recorded with actor_type SYSTEM. * * @param activityType the value * @return this builder @@ -363,7 +363,7 @@ protected Builder self() { } /** - * What kind of activity this entry records. STATUS_UPDATED rows come from manual decisions (PATCH /v3/session/{session_id}/update-status/) and from entries added via POST …/reviews/ (which default to this type even when no status is recorded). COMMENT rows come from Console reviewers; Console detail-page views are also stored as COMMENT rows flagged with metadata.system_event = "DETAIL_VIEWED". The remaining types track data edits, file changes, tags, and AML monitoring activity. STATUS_UPDATED rows are also written by system actors (e.g. AML ongoing monitoring with actor_type SYSTEM, and automatic feature-status updates), not only by the update-status endpoint and manual review posts. + * What kind of activity this entry records. STATUS_UPDATED rows come from manual decisions (PATCH /v3/session/{session_id}/update-status/) and from entries added via POST …/reviews/ (which default to this type even when no status is recorded). COMMENT rows come from Console reviewers; Console detail-page views are also stored as COMMENT rows flagged with metadata.system_event = "DETAIL_VIEWED". The remaining types track data edits, file changes, tags, and AML monitoring activity. STATUS_UPDATED rows are also written by system actors (e.g. AML ongoing monitoring with actor_type SYSTEM, and automatic feature-status updates), not only by the update-status endpoint and manual review posts. FEATURE_OVERRIDE_PENDING, FEATURE_OVERRIDE_APPLIED and FEATURE_OVERRIDE_CANCELLED record an override of one check's status (PATCH /v3/session/{session_id}/features/{node_id}/update-status/): requested to wait for the flow to finish, applied, or cancelled. They carry the check's status in previous_value / new_value ({"status": ...}), leave previous_status and new_status null, and name the override in metadata (feature_override_id, node_id, feature_model, forced, state, cancel_reason). The entry for the request carries the reviewer's comment; an override applied when the flow finishes is recorded with actor_type SYSTEM. * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -407,7 +407,16 @@ public static final class ActivityType extends StringEnum { /** The value {@code "TAG_REMOVED"}. */ public static final ActivityType TAG_REMOVED = new ActivityType("TAG_REMOVED"); - private static final List KNOWN_VALUES = List.of(COMMENT, STATUS_UPDATED, KYC_DATA_UPDATED, POA_DATA_UPDATED, FILE_ADDED, FILE_REMOVED, AML_ONGOING_MATCH, AML_STATUS_UPDATED, AML_HIT_STATUS_UPDATED, KYB_DATA_UPDATED, KYB_DOCUMENT_UPLOADED, TAG_ADDED, TAG_REMOVED); + /** The value {@code "FEATURE_OVERRIDE_PENDING"}. */ + public static final ActivityType FEATURE_OVERRIDE_PENDING = new ActivityType("FEATURE_OVERRIDE_PENDING"); + + /** The value {@code "FEATURE_OVERRIDE_APPLIED"}. */ + public static final ActivityType FEATURE_OVERRIDE_APPLIED = new ActivityType("FEATURE_OVERRIDE_APPLIED"); + + /** The value {@code "FEATURE_OVERRIDE_CANCELLED"}. */ + public static final ActivityType FEATURE_OVERRIDE_CANCELLED = new ActivityType("FEATURE_OVERRIDE_CANCELLED"); + + private static final List KNOWN_VALUES = List.of(COMMENT, STATUS_UPDATED, KYC_DATA_UPDATED, POA_DATA_UPDATED, FILE_ADDED, FILE_REMOVED, AML_ONGOING_MATCH, AML_STATUS_UPDATED, AML_HIT_STATUS_UPDATED, KYB_DATA_UPDATED, KYB_DOCUMENT_UPLOADED, TAG_ADDED, TAG_REMOVED, FEATURE_OVERRIDE_PENDING, FEATURE_OVERRIDE_APPLIED, FEATURE_OVERRIDE_CANCELLED); private ActivityType(String value) { super(value); diff --git a/src/main/java/me/didit/types/SandboxSimulateRequest.java b/src/main/java/me/didit/types/SandboxSimulateRequest.java new file mode 100644 index 0000000..4fc546c --- /dev/null +++ b/src/main/java/me/didit/types/SandboxSimulateRequest.java @@ -0,0 +1,254 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * Force a sandbox session's status. Every field is optional, but at least one of new_status, scenario or risk is required. + */ +public final class SandboxSimulateRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("new_status", "comment", "scenario", "risk", "node_id", "send_webhooks"); + + private SandboxSimulateRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SandboxSimulateRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SandboxSimulateRequest fromJson(JsonNode json) { + return new SandboxSimulateRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The status to set. When you leave it out, it defaults to the default status of the risk case (risk, or the first risk the scenario's magic values carry), else to the scenario's default status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional newStatus() { + return Json.optional(json, "new_status", Conv.enumeration(SandboxSimulateRequest.NewStatus::of)); + } + + /** + * Note added to the activity entry the status change records. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional comment() { + return Json.optional(json, "comment", Conv.STRING); + } + + /** + * A scenario slug from the catalog (GET /v1/sandbox/scenarios/). It supplies the default status and, without risk, the warning its magic values carry. An unknown slug returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scenario() { + return Json.optional(json, "scenario", Conv.STRING); + } + + /** + * A risk case from the catalog. Its warning is added to the session, with a log type that follows new_status. An unknown value returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.STRING); + } + + /** + * Workflow node the risk warning is attached to. Defaults to the session's current node. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + /** + * true sends exactly the webhooks a real status change sends to the sandbox application's destinations: status.updated, plus the user.status.updated / user.data.updated (or business.status.updated / business.data.updated) events the change causes, for destinations subscribed to them. false changes the status without logging or sending any webhook: the missed-webhook recovery never re-sends that decision, the console still updates live, and the next status change of the session sends webhooks as usual. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sendWebhooks() { + return Json.optional(json, "send_webhooks", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SandboxSimulateRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SandboxSimulateRequest from) { + super(from); + } + + /** + * The status to set. When you leave it out, it defaults to the default status of the risk case (risk, or the first risk the scenario's magic values carry), else to the scenario's default status. + * + * @param newStatus the value + * @return this builder + */ + public Builder newStatus(SandboxSimulateRequest.NewStatus newStatus) { + return set("new_status", Conv.enumeration(SandboxSimulateRequest.NewStatus::of), newStatus); + } + + /** + * Note added to the activity entry the status change records. + * + * @param comment the value + * @return this builder + */ + public Builder comment(String comment) { + return set("comment", Conv.STRING, comment); + } + + /** + * A scenario slug from the catalog (GET /v1/sandbox/scenarios/). It supplies the default status and, without risk, the warning its magic values carry. An unknown slug returns 400. + * + * @param scenario the value + * @return this builder + */ + public Builder scenario(String scenario) { + return set("scenario", Conv.STRING, scenario); + } + + /** + * A risk case from the catalog. Its warning is added to the session, with a log type that follows new_status. An unknown value returns 400. + * + * @param risk the value + * @return this builder + */ + public Builder risk(String risk) { + return set("risk", Conv.STRING, risk); + } + + /** + * Workflow node the risk warning is attached to. Defaults to the session's current node. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * true sends exactly the webhooks a real status change sends to the sandbox application's destinations: status.updated, plus the user.status.updated / user.data.updated (or business.status.updated / business.data.updated) events the change causes, for destinations subscribed to them. false changes the status without logging or sending any webhook: the missed-webhook recovery never re-sends that decision, the console still updates live, and the next status change of the session sends webhooks as usual. + * + * @param sendWebhooks the value + * @return this builder + */ + public Builder sendWebhooks(boolean sendWebhooks) { + return set("send_webhooks", Conv.BOOLEAN, sendWebhooks); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SandboxSimulateRequest} + */ + public SandboxSimulateRequest build() { + return new SandboxSimulateRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * The status to set. When you leave it out, it defaults to the default status of the risk case (risk, or the first risk the scenario's magic values carry), else to the scenario's default status. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NewStatus extends StringEnum { + /** The value {@code "Approved"}. */ + public static final NewStatus APPROVED = new NewStatus("Approved"); + + /** The value {@code "Declined"}. */ + public static final NewStatus DECLINED = new NewStatus("Declined"); + + /** The value {@code "In Review"}. */ + public static final NewStatus IN_REVIEW = new NewStatus("In Review"); + + /** The value {@code "Expired"}. */ + public static final NewStatus EXPIRED = new NewStatus("Expired"); + + /** The value {@code "Abandoned"}. */ + public static final NewStatus ABANDONED = new NewStatus("Abandoned"); + + private static final List KNOWN_VALUES = List.of(APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED); + + private NewStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NewStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NewStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SandboxSimulateResponse.java b/src/main/java/me/didit/types/SandboxSimulateResponse.java new file mode 100644 index 0000000..14f2aad --- /dev/null +++ b/src/main/java/me/didit/types/SandboxSimulateResponse.java @@ -0,0 +1,326 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class SandboxSimulateResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "previous_status", "status", "scenario", "risk", "send_webhooks"); + + private SandboxSimulateResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SandboxSimulateResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SandboxSimulateResponse fromJson(JsonNode json) { + return new SandboxSimulateResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The session_id field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * The session's status before the simulation. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SandboxSimulateResponse.PreviousStatus previousStatus() { + return Json.required(json, "previous_status", Conv.enumeration(SandboxSimulateResponse.PreviousStatus::of)); + } + + /** + * The status the simulation set. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SandboxSimulateResponse.Status status() { + return Json.required(json, "status", Conv.enumeration(SandboxSimulateResponse.Status::of)); + } + + /** + * The scenario slug you passed, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scenario() { + return Json.optional(json, "scenario", Conv.STRING); + } + + /** + * The risk case applied (risk, or the one taken from the scenario), or null when none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.STRING); + } + + /** + * The send_webhooks value the simulation applied. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean sendWebhooks() { + return Json.required(json, "send_webhooks", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SandboxSimulateResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SandboxSimulateResponse from) { + super(from); + } + + /** + * The session_id field. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * The session's status before the simulation. + * + * @param previousStatus the value + * @return this builder + */ + public Builder previousStatus(SandboxSimulateResponse.PreviousStatus previousStatus) { + return set("previous_status", Conv.enumeration(SandboxSimulateResponse.PreviousStatus::of), previousStatus); + } + + /** + * The status the simulation set. + * + * @param status the value + * @return this builder + */ + public Builder status(SandboxSimulateResponse.Status status) { + return set("status", Conv.enumeration(SandboxSimulateResponse.Status::of), status); + } + + /** + * The scenario slug you passed, or null. + * + * @param scenario the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder scenario(@Nullable String scenario) { + return setNullable("scenario", Conv.STRING, scenario); + } + + /** + * The risk case applied (risk, or the one taken from the scenario), or null when none. + * + * @param risk the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder risk(@Nullable String risk) { + return setNullable("risk", Conv.STRING, risk); + } + + /** + * The send_webhooks value the simulation applied. + * + * @param sendWebhooks the value + * @return this builder + */ + public Builder sendWebhooks(boolean sendWebhooks) { + return set("send_webhooks", Conv.BOOLEAN, sendWebhooks); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SandboxSimulateResponse} + * @throws IllegalStateException when a required field is missing + */ + public SandboxSimulateResponse build() { + require("session_id", false); + require("previous_status", false); + require("status", false); + require("scenario", true); + require("risk", true); + require("send_webhooks", false); + return new SandboxSimulateResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * The session's status before the simulation. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class PreviousStatus extends StringEnum { + /** The value {@code "Not Started"}. */ + public static final PreviousStatus NOT_STARTED = new PreviousStatus("Not Started"); + + /** The value {@code "In Progress"}. */ + public static final PreviousStatus IN_PROGRESS = new PreviousStatus("In Progress"); + + /** The value {@code "Approved"}. */ + public static final PreviousStatus APPROVED = new PreviousStatus("Approved"); + + /** The value {@code "Declined"}. */ + public static final PreviousStatus DECLINED = new PreviousStatus("Declined"); + + /** The value {@code "In Review"}. */ + public static final PreviousStatus IN_REVIEW = new PreviousStatus("In Review"); + + /** The value {@code "Expired"}. */ + public static final PreviousStatus EXPIRED = new PreviousStatus("Expired"); + + /** The value {@code "Abandoned"}. */ + public static final PreviousStatus ABANDONED = new PreviousStatus("Abandoned"); + + /** The value {@code "Kyc Expired"}. */ + public static final PreviousStatus KYC_EXPIRED = new PreviousStatus("Kyc Expired"); + + /** The value {@code "Resubmitted"}. */ + public static final PreviousStatus RESUBMITTED = new PreviousStatus("Resubmitted"); + + /** The value {@code "Awaiting User"}. */ + public static final PreviousStatus AWAITING_USER = new PreviousStatus("Awaiting User"); + + private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED, KYC_EXPIRED, RESUBMITTED, AWAITING_USER); + + private PreviousStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static PreviousStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, PreviousStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The status the simulation set. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Expired"}. */ + public static final Status EXPIRED = new Status("Expired"); + + /** The value {@code "Abandoned"}. */ + public static final Status ABANDONED = new Status("Abandoned"); + + private static final List KNOWN_VALUES = List.of(APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/ServiceStatusFeed.java b/src/main/java/me/didit/types/ServiceStatusFeed.java new file mode 100644 index 0000000..eab8cc5 --- /dev/null +++ b/src/main/java/me/didit/types/ServiceStatusFeed.java @@ -0,0 +1,213 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * Public service status: the state of each Didit service, the overall state, and the incidents that are ongoing or were resolved in the last 7 days. + */ +public final class ServiceStatusFeed extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("status", "updated_at", "services", "incidents"); + + private ServiceStatusFeed(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ServiceStatusFeed} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ServiceStatusFeed fromJson(JsonNode json) { + return new ServiceStatusFeed(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The worst state of any service right now, decided by the ongoing incidents only. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ServiceStatusFeed.Status status() { + return Json.required(json, "status", Conv.enumeration(ServiceStatusFeed.Status::of)); + } + + /** + * When Didit built this response (ISO 8601, UTC). Responses are cached for 30 seconds, so it can be up to 30 seconds old. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime updatedAt() { + return Json.required(json, "updated_at", Conv.DATE_TIME); + } + + /** + * One entry per service, every service always listed. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List services() { + return Json.required(json, "services", Conv.list(Conv.model(ServiceStatusService::fromJson))); + } + + /** + * Newest started_at first. Every ongoing incident, plus the incidents resolved in the last 7 days (at most 50 resolved incidents). Empty when nothing happened in that time. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List incidents() { + return Json.required(json, "incidents", Conv.list(Conv.model(ServiceStatusIncident::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ServiceStatusFeed}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ServiceStatusFeed from) { + super(from); + } + + /** + * The worst state of any service right now, decided by the ongoing incidents only. + * + * @param status the value + * @return this builder + */ + public Builder status(ServiceStatusFeed.Status status) { + return set("status", Conv.enumeration(ServiceStatusFeed.Status::of), status); + } + + /** + * When Didit built this response (ISO 8601, UTC). Responses are cached for 30 seconds, so it can be up to 30 seconds old. + * + * @param updatedAt the value + * @return this builder + */ + public Builder updatedAt(OffsetDateTime updatedAt) { + return set("updated_at", Conv.DATE_TIME, updatedAt); + } + + /** + * One entry per service, every service always listed. + * + * @param services the value + * @return this builder + */ + public Builder services(List services) { + return set("services", Conv.list(Conv.model(ServiceStatusService::fromJson)), services); + } + + /** + * Newest started_at first. Every ongoing incident, plus the incidents resolved in the last 7 days (at most 50 resolved incidents). Empty when nothing happened in that time. + * + * @param incidents the value + * @return this builder + */ + public Builder incidents(List incidents) { + return set("incidents", Conv.list(Conv.model(ServiceStatusIncident::fromJson)), incidents); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ServiceStatusFeed} + * @throws IllegalStateException when a required field is missing + */ + public ServiceStatusFeed build() { + require("status", false); + require("updated_at", false); + require("services", false); + require("incidents", false); + return new ServiceStatusFeed(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * The worst state of any service right now, decided by the ongoing incidents only. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "operational"}. */ + public static final Status OPERATIONAL = new Status("operational"); + + /** The value {@code "degraded"}. */ + public static final Status DEGRADED = new Status("degraded"); + + /** The value {@code "down"}. */ + public static final Status DOWN = new Status("down"); + + private static final List KNOWN_VALUES = List.of(OPERATIONAL, DEGRADED, DOWN); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/ServiceStatusIncident.java b/src/main/java/me/didit/types/ServiceStatusIncident.java new file mode 100644 index 0000000..ba5dcdb --- /dev/null +++ b/src/main/java/me/didit/types/ServiceStatusIncident.java @@ -0,0 +1,481 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class ServiceStatusIncident extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "service", "service_name", "state", "status", "title", "body", "affected", "country_name", "started_at", "resolved_at", "duration_minutes"); + + private ServiceStatusIncident(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ServiceStatusIncident} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ServiceStatusIncident fromJson(JsonNode json) { + return new ServiceStatusIncident(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Incident ID. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String id() { + return Json.required(json, "id", Conv.STRING); + } + + /** + * Key of the affected service (see services[].key). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ServiceStatusIncident.Service service() { + return Json.required(json, "service", Conv.enumeration(ServiceStatusIncident.Service::of)); + } + + /** + * English display name of the affected service. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String serviceName() { + return Json.required(json, "service_name", Conv.STRING); + } + + /** + * How the service is affected. A resolved incident keeps the last state it had. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ServiceStatusIncident.State state() { + return Json.required(json, "state", Conv.enumeration(ServiceStatusIncident.State::of)); + } + + /** + * ongoing until the service recovers, then resolved. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ServiceStatusIncident.Status status() { + return Json.required(json, "status", Conv.enumeration(ServiceStatusIncident.Status::of)); + } + + /** + * English headline. To translate it, build your own text from service, state and country_name. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String title() { + return Json.required(json, "title", Conv.STRING); + } + + /** + * English detail line: since when checks may be slow or fail, or when it recovered. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String body() { + return Json.required(json, "body", Conv.STRING); + } + + /** + * Countries or registries affected, for example Brazil (CPF). Empty when the incident is not tied to a country or registry. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String affected() { + return Json.required(json, "affected", Conv.STRING); + } + + /** + * Country affected, in English. Empty when the incident is not tied to a country. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String countryName() { + return Json.required(json, "country_name", Conv.STRING); + } + + /** + * When the incident started (ISO 8601, UTC). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime startedAt() { + return Json.required(json, "started_at", Conv.DATE_TIME); + } + + /** + * When the service recovered (ISO 8601, UTC). null while the incident is ongoing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional resolvedAt() { + return Json.optional(json, "resolved_at", Conv.DATE_TIME); + } + + /** + * Whole minutes from started_at to resolved_at. null while the incident is ongoing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional durationMinutes() { + return Json.optional(json, "duration_minutes", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ServiceStatusIncident}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ServiceStatusIncident from) { + super(from); + } + + /** + * Incident ID. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * Key of the affected service (see services[].key). + * + * @param service the value + * @return this builder + */ + public Builder service(ServiceStatusIncident.Service service) { + return set("service", Conv.enumeration(ServiceStatusIncident.Service::of), service); + } + + /** + * English display name of the affected service. + * + * @param serviceName the value + * @return this builder + */ + public Builder serviceName(String serviceName) { + return set("service_name", Conv.STRING, serviceName); + } + + /** + * How the service is affected. A resolved incident keeps the last state it had. + * + * @param state the value + * @return this builder + */ + public Builder state(ServiceStatusIncident.State state) { + return set("state", Conv.enumeration(ServiceStatusIncident.State::of), state); + } + + /** + * ongoing until the service recovers, then resolved. + * + * @param status the value + * @return this builder + */ + public Builder status(ServiceStatusIncident.Status status) { + return set("status", Conv.enumeration(ServiceStatusIncident.Status::of), status); + } + + /** + * English headline. To translate it, build your own text from service, state and country_name. + * + * @param title the value + * @return this builder + */ + public Builder title(String title) { + return set("title", Conv.STRING, title); + } + + /** + * English detail line: since when checks may be slow or fail, or when it recovered. + * + * @param body the value + * @return this builder + */ + public Builder body(String body) { + return set("body", Conv.STRING, body); + } + + /** + * Countries or registries affected, for example Brazil (CPF). Empty when the incident is not tied to a country or registry. + * + * @param affected the value + * @return this builder + */ + public Builder affected(String affected) { + return set("affected", Conv.STRING, affected); + } + + /** + * Country affected, in English. Empty when the incident is not tied to a country. + * + * @param countryName the value + * @return this builder + */ + public Builder countryName(String countryName) { + return set("country_name", Conv.STRING, countryName); + } + + /** + * When the incident started (ISO 8601, UTC). + * + * @param startedAt the value + * @return this builder + */ + public Builder startedAt(OffsetDateTime startedAt) { + return set("started_at", Conv.DATE_TIME, startedAt); + } + + /** + * When the service recovered (ISO 8601, UTC). null while the incident is ongoing. + * + * @param resolvedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder resolvedAt(@Nullable OffsetDateTime resolvedAt) { + return setNullable("resolved_at", Conv.DATE_TIME, resolvedAt); + } + + /** + * Whole minutes from started_at to resolved_at. null while the incident is ongoing. + * + * @param durationMinutes the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder durationMinutes(@Nullable Long durationMinutes) { + return setNullable("duration_minutes", Conv.LONG, durationMinutes); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ServiceStatusIncident} + * @throws IllegalStateException when a required field is missing + */ + public ServiceStatusIncident build() { + require("id", false); + require("service", false); + require("service_name", false); + require("state", false); + require("status", false); + require("title", false); + require("body", false); + require("affected", false); + require("country_name", false); + require("started_at", false); + require("resolved_at", true); + require("duration_minutes", true); + return new ServiceStatusIncident(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Key of the affected service (see services[].key). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Service extends StringEnum { + /** The value {@code "service_id_verification"}. */ + public static final Service SERVICE_ID_VERIFICATION = new Service("service_id_verification"); + + /** The value {@code "service_liveness_face_match"}. */ + public static final Service SERVICE_LIVENESS_FACE_MATCH = new Service("service_liveness_face_match"); + + /** The value {@code "service_aml_screening"}. */ + public static final Service SERVICE_AML_SCREENING = new Service("service_aml_screening"); + + /** The value {@code "service_kyb_registries"}. */ + public static final Service SERVICE_KYB_REGISTRIES = new Service("service_kyb_registries"); + + /** The value {@code "service_database_validation"}. */ + public static final Service SERVICE_DATABASE_VALIDATION = new Service("service_database_validation"); + + /** The value {@code "service_phone_delivery"}. */ + public static final Service SERVICE_PHONE_DELIVERY = new Service("service_phone_delivery"); + + /** The value {@code "service_email_delivery"}. */ + public static final Service SERVICE_EMAIL_DELIVERY = new Service("service_email_delivery"); + + /** The value {@code "service_transaction_monitoring"}. */ + public static final Service SERVICE_TRANSACTION_MONITORING = new Service("service_transaction_monitoring"); + + private static final List KNOWN_VALUES = List.of(SERVICE_ID_VERIFICATION, SERVICE_LIVENESS_FACE_MATCH, SERVICE_AML_SCREENING, SERVICE_KYB_REGISTRIES, SERVICE_DATABASE_VALIDATION, SERVICE_PHONE_DELIVERY, SERVICE_EMAIL_DELIVERY, SERVICE_TRANSACTION_MONITORING); + + private Service(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Service of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Service::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * How the service is affected. A resolved incident keeps the last state it had. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class State extends StringEnum { + /** The value {@code "degraded"}. */ + public static final State DEGRADED = new State("degraded"); + + /** The value {@code "down"}. */ + public static final State DOWN = new State("down"); + + private static final List KNOWN_VALUES = List.of(DEGRADED, DOWN); + + private State(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static State of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, State::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * ongoing until the service recovers, then resolved. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ongoing"}. */ + public static final Status ONGOING = new Status("ongoing"); + + /** The value {@code "resolved"}. */ + public static final Status RESOLVED = new Status("resolved"); + + private static final List KNOWN_VALUES = List.of(ONGOING, RESOLVED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/ServiceStatusService.java b/src/main/java/me/didit/types/ServiceStatusService.java new file mode 100644 index 0000000..6e53fb5 --- /dev/null +++ b/src/main/java/me/didit/types/ServiceStatusService.java @@ -0,0 +1,249 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class ServiceStatusService extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("key", "name", "status"); + + private ServiceStatusService(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ServiceStatusService} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ServiceStatusService fromJson(JsonNode json) { + return new ServiceStatusService(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Stable service key. Match on this, never on the display name. service_id_verification: ID verification, service_liveness_face_match: Liveness and face match, service_aml_screening: AML screening, service_kyb_registries: KYB registries, service_database_validation: Database validation, service_phone_delivery: Phone verification delivery, service_email_delivery: Email verification delivery, service_transaction_monitoring: Transaction monitoring. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ServiceStatusService.Key key() { + return Json.required(json, "key", Conv.enumeration(ServiceStatusService.Key::of)); + } + + /** + * English display name of the service. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String name() { + return Json.required(json, "name", Conv.STRING); + } + + /** + * operational when no incident is ongoing for the service, otherwise the worst state of its ongoing incidents. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public ServiceStatusService.Status status() { + return Json.required(json, "status", Conv.enumeration(ServiceStatusService.Status::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ServiceStatusService}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ServiceStatusService from) { + super(from); + } + + /** + * Stable service key. Match on this, never on the display name. service_id_verification: ID verification, service_liveness_face_match: Liveness and face match, service_aml_screening: AML screening, service_kyb_registries: KYB registries, service_database_validation: Database validation, service_phone_delivery: Phone verification delivery, service_email_delivery: Email verification delivery, service_transaction_monitoring: Transaction monitoring. + * + * @param key the value + * @return this builder + */ + public Builder key(ServiceStatusService.Key key) { + return set("key", Conv.enumeration(ServiceStatusService.Key::of), key); + } + + /** + * English display name of the service. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * operational when no incident is ongoing for the service, otherwise the worst state of its ongoing incidents. + * + * @param status the value + * @return this builder + */ + public Builder status(ServiceStatusService.Status status) { + return set("status", Conv.enumeration(ServiceStatusService.Status::of), status); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ServiceStatusService} + * @throws IllegalStateException when a required field is missing + */ + public ServiceStatusService build() { + require("key", false); + require("name", false); + require("status", false); + return new ServiceStatusService(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Stable service key. Match on this, never on the display name. service_id_verification: ID verification, service_liveness_face_match: Liveness and face match, service_aml_screening: AML screening, service_kyb_registries: KYB registries, service_database_validation: Database validation, service_phone_delivery: Phone verification delivery, service_email_delivery: Email verification delivery, service_transaction_monitoring: Transaction monitoring. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Key extends StringEnum { + /** The value {@code "service_id_verification"}. */ + public static final Key SERVICE_ID_VERIFICATION = new Key("service_id_verification"); + + /** The value {@code "service_liveness_face_match"}. */ + public static final Key SERVICE_LIVENESS_FACE_MATCH = new Key("service_liveness_face_match"); + + /** The value {@code "service_aml_screening"}. */ + public static final Key SERVICE_AML_SCREENING = new Key("service_aml_screening"); + + /** The value {@code "service_kyb_registries"}. */ + public static final Key SERVICE_KYB_REGISTRIES = new Key("service_kyb_registries"); + + /** The value {@code "service_database_validation"}. */ + public static final Key SERVICE_DATABASE_VALIDATION = new Key("service_database_validation"); + + /** The value {@code "service_phone_delivery"}. */ + public static final Key SERVICE_PHONE_DELIVERY = new Key("service_phone_delivery"); + + /** The value {@code "service_email_delivery"}. */ + public static final Key SERVICE_EMAIL_DELIVERY = new Key("service_email_delivery"); + + /** The value {@code "service_transaction_monitoring"}. */ + public static final Key SERVICE_TRANSACTION_MONITORING = new Key("service_transaction_monitoring"); + + private static final List KNOWN_VALUES = List.of(SERVICE_ID_VERIFICATION, SERVICE_LIVENESS_FACE_MATCH, SERVICE_AML_SCREENING, SERVICE_KYB_REGISTRIES, SERVICE_DATABASE_VALIDATION, SERVICE_PHONE_DELIVERY, SERVICE_EMAIL_DELIVERY, SERVICE_TRANSACTION_MONITORING); + + private Key(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Key of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Key::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * operational when no incident is ongoing for the service, otherwise the worst state of its ongoing incidents. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "operational"}. */ + public static final Status OPERATIONAL = new Status("operational"); + + /** The value {@code "degraded"}. */ + public static final Status DEGRADED = new Status("degraded"); + + /** The value {@code "down"}. */ + public static final Status DOWN = new Status("down"); + + private static final List KNOWN_VALUES = List.of(OPERATIONAL, DEGRADED, DOWN); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionDataUpdatedEvent.java b/src/main/java/me/didit/types/SessionDataUpdatedEvent.java new file mode 100644 index 0000000..99d6aa4 --- /dev/null +++ b/src/main/java/me/didit/types/SessionDataUpdatedEvent.java @@ -0,0 +1,513 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Data of a verification session changed without a status change, for example after a manual review edit, a step override or an ongoing-monitoring rescreen. decision is sent only for Approved, Declined, In Review and Abandoned; it has the shape of GET /v3/session/{sessionId}/decision/ for destinations on webhook version 3 (the default), and the older reduced shape for version 1 or 2 destinations. Business sessions always receive the version 3 business decision. + */ +public final class SessionDataUpdatedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "session_id", "business_session_id", "session_kind", "status", "trigger", "workflow_id", "workflow_version", "vendor_data", "vendor_business_id", "metadata", "decision", "resubmit_info", "sandbox_scenario", "webhook_type"); + + private SessionDataUpdatedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionDataUpdatedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionDataUpdatedEvent fromJson(JsonNode json) { + return new SessionDataUpdatedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * The verification session. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Business (KYB) sessions only: the same value as session_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional businessSessionId() { + return Json.optional(json, "business_session_id", Conv.STRING); + } + + /** + * Business (KYB) sessions only. User (KYC) session events carry no top-level session_kind; read decision.session_kind instead. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionKind() { + return Json.optional(json, "session_kind", Conv.enumeration(SessionWebhookEventData.SessionKind::of)); + } + + /** + * Session status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionWebhookEventData.Status status() { + return Json.required(json, "status", Conv.enumeration(SessionWebhookEventData.Status::of)); + } + + /** + * Present when the event was not produced by the verification flow itself, for example manual_review, manual_step_update, ongoing_monitoring, webhook_reconciliation, manual_webhook_recovery, kyb_registry_change or a console_* edit. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional trigger() { + return Json.optional(json, "trigger", Conv.STRING); + } + + /** + * The session's workflow. Omitted when the session has none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowId() { + return Json.optional(json, "workflow_id", Conv.STRING); + } + + /** + * Version of the workflow the session ran. Sent together with workflow_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowVersion() { + return Json.optional(json, "workflow_version", Conv.LONG); + } + + /** + * Your vendor_data for the session. Omitted when empty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * Business (KYB) sessions only: the business the session belongs to. Omitted when none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorBusinessId() { + return Json.optional(json, "vendor_business_id", Conv.STRING); + } + + /** + * Your session metadata. Omitted when empty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> metadata() { + return Json.optional(json, "metadata", Conv.OBJECT); + } + + /** + * The decision field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decision() { + return Json.optional(json, "decision", Conv.model(SessionDecision::fromJson)); + } + + /** + * Only when status is Resubmitted: the steps the user must redo and why. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional resubmitInfo() { + return Json.optional(json, "resubmit_info", Conv.model(SessionWebhookEventData.ResubmitInfo::fromJson)); + } + + /** + * The simulated outcome armed on a sandbox session, or null. Always null for live sessions. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sandboxScenario() { + return Json.optional(json, "sandbox_scenario", Conv.STRING); + } + + /** + * Always data.updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionDataUpdatedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(SessionDataUpdatedEvent.WebhookType::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionDataUpdatedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionDataUpdatedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * The verification session. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Business (KYB) sessions only: the same value as session_id. + * + * @param businessSessionId the value + * @return this builder + */ + public Builder businessSessionId(String businessSessionId) { + return set("business_session_id", Conv.STRING, businessSessionId); + } + + /** + * Business (KYB) sessions only. User (KYC) session events carry no top-level session_kind; read decision.session_kind instead. + * + * @param sessionKind the value + * @return this builder + */ + public Builder sessionKind(SessionWebhookEventData.SessionKind sessionKind) { + return set("session_kind", Conv.enumeration(SessionWebhookEventData.SessionKind::of), sessionKind); + } + + /** + * Session status after the change. + * + * @param status the value + * @return this builder + */ + public Builder status(SessionWebhookEventData.Status status) { + return set("status", Conv.enumeration(SessionWebhookEventData.Status::of), status); + } + + /** + * Present when the event was not produced by the verification flow itself, for example manual_review, manual_step_update, ongoing_monitoring, webhook_reconciliation, manual_webhook_recovery, kyb_registry_change or a console_* edit. + * + * @param trigger the value + * @return this builder + */ + public Builder trigger(String trigger) { + return set("trigger", Conv.STRING, trigger); + } + + /** + * The session's workflow. Omitted when the session has none. + * + * @param workflowId the value + * @return this builder + */ + public Builder workflowId(String workflowId) { + return set("workflow_id", Conv.STRING, workflowId); + } + + /** + * Version of the workflow the session ran. Sent together with workflow_id. + * + * @param workflowVersion the value + * @return this builder + */ + public Builder workflowVersion(long workflowVersion) { + return set("workflow_version", Conv.LONG, workflowVersion); + } + + /** + * Your vendor_data for the session. Omitted when empty. + * + * @param vendorData the value + * @return this builder + */ + public Builder vendorData(String vendorData) { + return set("vendor_data", Conv.STRING, vendorData); + } + + /** + * Business (KYB) sessions only: the business the session belongs to. Omitted when none. + * + * @param vendorBusinessId the value + * @return this builder + */ + public Builder vendorBusinessId(String vendorBusinessId) { + return set("vendor_business_id", Conv.STRING, vendorBusinessId); + } + + /** + * Your session metadata. Omitted when empty. + * + * @param metadata the value + * @return this builder + */ + public Builder metadata(Map metadata) { + return setAny("metadata", metadata); + } + + /** + * The decision field. + * + * @param decision the value + * @return this builder + */ + public Builder decision(SessionDecision decision) { + return set("decision", Conv.model(SessionDecision::fromJson), decision); + } + + /** + * Only when status is Resubmitted: the steps the user must redo and why. + * + * @param resubmitInfo the value + * @return this builder + */ + public Builder resubmitInfo(SessionWebhookEventData.ResubmitInfo resubmitInfo) { + return set("resubmit_info", Conv.model(SessionWebhookEventData.ResubmitInfo::fromJson), resubmitInfo); + } + + /** + * The simulated outcome armed on a sandbox session, or null. Always null for live sessions. + * + * @param sandboxScenario the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sandboxScenario(@Nullable String sandboxScenario) { + return setNullable("sandbox_scenario", Conv.STRING, sandboxScenario); + } + + /** + * Always data.updated. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(SessionDataUpdatedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(SessionDataUpdatedEvent.WebhookType::of), webhookType); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionDataUpdatedEvent} + * @throws IllegalStateException when a required field is missing + */ + public SessionDataUpdatedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("session_id", false); + require("status", false); + require("sandbox_scenario", true); + require("webhook_type", false); + return new SessionDataUpdatedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always data.updated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "data.updated"}. */ + public static final WebhookType DATA_UPDATED = new WebhookType("data.updated"); + + private static final List KNOWN_VALUES = List.of(DATA_UPDATED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionsRetrieveDecisionResponse.java b/src/main/java/me/didit/types/SessionDecision.java similarity index 82% rename from src/main/java/me/didit/types/SessionsRetrieveDecisionResponse.java rename to src/main/java/me/didit/types/SessionDecision.java index 4f7451c..d19d2e0 100644 --- a/src/main/java/me/didit/types/SessionsRetrieveDecisionResponse.java +++ b/src/main/java/me/didit/types/SessionDecision.java @@ -16,22 +16,22 @@ import me.didit.core.StringEnum; import org.jspecify.annotations.Nullable; -public final class SessionsRetrieveDecisionResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("session_id", "session_kind", "session_number", "session_url", "status", "workflow_id", "features", "vendor_data", "metadata", "expected_details", "contact_details", "callback", "id_verifications", "nfc_skip_reason", "nfc_verifications", "liveness_checks", "face_matches", "phone_verifications", "email_verifications", "poa_verifications", "document_ai_documents", "questionnaire_responses", "aml_screenings", "ip_analyses", "database_validations", "reviews", "created_at", "expires_at", "registry_checks", "key_people_checks", "document_verifications", "environment", "resubmissions"); +public final class SessionDecision extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "session_kind", "shared_from_session", "session_number", "session_url", "status", "workflow_id", "features", "vendor_data", "metadata", "expected_details", "contact_details", "callback", "id_verifications", "nfc_skip_reason", "nfc_verifications", "liveness_checks", "face_matches", "phone_verifications", "email_verifications", "poa_verifications", "document_ai_documents", "questionnaire_responses", "signatures", "aml_screenings", "ip_analyses", "database_validations", "reviews", "created_at", "expires_at", "registry_checks", "key_people_checks", "document_verifications", "environment", "resubmissions", "investigator"); - private SessionsRetrieveDecisionResponse(ObjectNode json, Map> files) { + private SessionDecision(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code SessionsRetrieveDecisionResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code SessionDecision} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static SessionsRetrieveDecisionResponse fromJson(JsonNode json) { - return new SessionsRetrieveDecisionResponse(Json.copyObject(json), Map.of()); + public static SessionDecision fromJson(JsonNode json) { + return new SessionDecision(Json.copyObject(json), Map.of()); } /** @@ -53,7 +53,7 @@ public Builder toBuilder() { } /** - * The unique identifier of the session for which to retrieve verification results. + * The unique identifier of the session. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -68,8 +68,18 @@ public Optional sessionId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional sessionKind() { - return Json.optional(json, "session_kind", Conv.enumeration(SessionsRetrieveDecisionResponse.SessionKind::of)); + public Optional sessionKind() { + return Json.optional(json, "session_kind", Conv.enumeration(SessionDecision.SessionKind::of)); + } + + /** + * For a session cloned with POST /v3/session/import-shared/: the session_id of the original session in the source application. null for every other session. Omitted when include=events is passed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sharedFromSession() { + return Json.optional(json, "shared_from_session", Conv.STRING); } /** @@ -98,8 +108,8 @@ public Optional sessionUrl() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.Status::of)); } /** @@ -148,8 +158,8 @@ public Optional> metadata() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional expectedDetails() { - return Json.optional(json, "expected_details", Conv.model(SessionsRetrieveDecisionResponse.ExpectedDetails::fromJson)); + public Optional expectedDetails() { + return Json.optional(json, "expected_details", Conv.model(SessionDecision.ExpectedDetails::fromJson)); } /** @@ -158,8 +168,8 @@ public Optional expectedDetail * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional contactDetails() { - return Json.optional(json, "contact_details", Conv.model(SessionsRetrieveDecisionResponse.ContactDetails::fromJson)); + public Optional contactDetails() { + return Json.optional(json, "contact_details", Conv.model(SessionDecision.ContactDetails::fromJson)); } /** @@ -178,28 +188,28 @@ public Optional callback() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> idVerifications() { - return Json.optional(json, "id_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications::fromJson))); + public Optional> idVerifications() { + return Json.optional(json, "id_verifications", Conv.list(Conv.model(SessionDecision.IdVerifications::fromJson))); } /** - * Why NFC chip verification did not run, populated whenever the workflow includes an NFC step. null while NFC is pending or when the chip was actually read. Values: USER_SKIPPED - the user chose to skip on an NFC-capable device (allowed by allow_nfc_skip); DOCUMENT_WITHOUT_CHIP - the document has no readable NFC chip (not an ICAO 9303 electronic document); CHIP_CERTIFICATE_UNAVAILABLE - the issuing country's signing certificate (CSCA) covering the document's issue period is unavailable, so the chip signature cannot be verified; DEVICE_WITHOUT_NFC - the device reported no NFC reader; INTEGRATION_WITHOUT_NFC_ACCESS - the verification ran without a native SDK (e.g. in a web browser), which has no access to the phone's NFC reader; MRZ_KEY_UNAVAILABLE - the chip access key could not be derived from the document's MRZ. + * Why NFC chip verification did not run, populated whenever the workflow includes an NFC step. null while NFC is pending or when the chip was actually read. Values: USER_SKIPPED - the user chose to skip on an NFC-capable device (allowed by allow_nfc_skip); DOCUMENT_WITHOUT_CHIP - the document has no readable NFC chip (not an ICAO 9303 electronic document); CHIP_CERTIFICATE_UNAVAILABLE - the issuing country's signing certificate (CSCA) covering the document's issue period is unavailable, so the chip signature cannot be verified; DEVICE_WITHOUT_NFC - the device reported no NFC reader; INTEGRATION_WITHOUT_NFC_ACCESS - the verification ran without a native SDK (e.g. in a web browser), which has no access to the phone's NFC reader; MRZ_KEY_UNAVAILABLE - the chip access key could not be derived from the document's MRZ; JPKI_UNAVAILABLE - the document is a Japan My Number Card, whose chip is read through JPKI, and JPKI was not available for this verification: it is not enabled, or the app's native SDK cannot read My Number Cards. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional nfcSkipReason() { - return Json.optional(json, "nfc_skip_reason", Conv.enumeration(SessionsRetrieveDecisionResponse.NfcSkipReason::of)); + public Optional nfcSkipReason() { + return Json.optional(json, "nfc_skip_reason", Conv.enumeration(SessionDecision.NfcSkipReason::of)); } /** - * Array of NFC / ePassport reports produced by NFC steps in the workflow graph. **This is always a JSON array, never a singular nfc object** — read nfc_verifications[0] and iterate if your workflow can run NFC more than once. null until at least one NFC step has produced data. Each item is the full report, including node_id, feature-level status, chip_data, authenticity (SOD + DG integrity), certificate_summary, and warnings[]. When the NFC step was bypassed before it could ever be offered (chipless document, missing certificate, no NFC capability reported, unreadable MRZ key), this stays null and nfc_skip_reason explains why; a skip at the step itself (user tapped skip, or the reader turned out unavailable at scan time) still produces a report with is_nfc_skipped: true. + * Array of NFC / ePassport reports produced by NFC steps in the workflow graph. **This is always a JSON array, never a singular nfc object** — read nfc_verifications[0] and iterate if your workflow can run NFC more than once. null until at least one NFC step has produced data. Each item is the full report, including node_id, feature-level status, chip_protocol, chip_data, authenticity (SOD + DG integrity), certificate_summary, and warnings[]. A Japan My Number Card is read through JPKI instead of ICAO 9303: its item has chip_protocol: "jpki", carries its evidence under jpki, and leaves the ICAO fields null. When the NFC step was bypassed before it could ever be offered (chipless document, missing certificate, no NFC capability reported, unreadable MRZ key, JPKI unavailable for a My Number Card), this stays null and nfc_skip_reason explains why; a skip at the step itself (user tapped skip, or the reader turned out unavailable at scan time) still produces a report with is_nfc_skipped: true. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> nfcVerifications() { - return Json.optional(json, "nfc_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications::fromJson))); + public Optional> nfcVerifications() { + return Json.optional(json, "nfc_verifications", Conv.list(Conv.model(SessionDecision.NfcVerifications::fromJson))); } /** @@ -208,8 +218,8 @@ public Optional> nfcVeri * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> livenessChecks() { - return Json.optional(json, "liveness_checks", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.LivenessChecks::fromJson))); + public Optional> livenessChecks() { + return Json.optional(json, "liveness_checks", Conv.list(Conv.model(SessionDecision.LivenessChecks::fromJson))); } /** @@ -218,8 +228,8 @@ public Optional> livenessC * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> faceMatches() { - return Json.optional(json, "face_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.FaceMatches::fromJson))); + public Optional> faceMatches() { + return Json.optional(json, "face_matches", Conv.list(Conv.model(SessionDecision.FaceMatches::fromJson))); } /** @@ -228,8 +238,8 @@ public Optional> faceMatches( * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> phoneVerifications() { - return Json.optional(json, "phone_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications::fromJson))); + public Optional> phoneVerifications() { + return Json.optional(json, "phone_verifications", Conv.list(Conv.model(SessionDecision.PhoneVerifications::fromJson))); } /** @@ -238,8 +248,8 @@ public Optional> phone * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> emailVerifications() { - return Json.optional(json, "email_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications::fromJson))); + public Optional> emailVerifications() { + return Json.optional(json, "email_verifications", Conv.list(Conv.model(SessionDecision.EmailVerifications::fromJson))); } /** @@ -248,8 +258,8 @@ public Optional> email * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> poaVerifications() { - return Json.optional(json, "poa_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications::fromJson))); + public Optional> poaVerifications() { + return Json.optional(json, "poa_verifications", Conv.list(Conv.model(SessionDecision.PoaVerifications::fromJson))); } /** @@ -258,8 +268,8 @@ public Optional> poaVeri * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> documentAiDocuments() { - return Json.optional(json, "document_ai_documents", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentAiDocuments::fromJson))); + public Optional> documentAiDocuments() { + return Json.optional(json, "document_ai_documents", Conv.list(Conv.model(SessionDecision.DocumentAiDocuments::fromJson))); } /** @@ -268,8 +278,18 @@ public Optional> docu * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> questionnaireResponses() { - return Json.optional(json, "questionnaire_responses", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses::fromJson))); + public Optional> questionnaireResponses() { + return Json.optional(json, "questionnaire_responses", Conv.list(Conv.model(SessionDecision.QuestionnaireResponses::fromJson))); + } + + /** + * Electronic Signature results, one item per signer and signature step, for User and Business Verification. Present only when the workflow has an Electronic Signature step; an empty array until a signer reaches it. See the [Electronic Signature report](/core-technology/electronic-signature/report-electronic-signature). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> signatures() { + return Json.optional(json, "signatures", Conv.list(Conv.model(SignatureResult::fromJson))); } /** @@ -278,8 +298,8 @@ public Optional> q * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> amlScreenings() { - return Json.optional(json, "aml_screenings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings::fromJson))); + public Optional> amlScreenings() { + return Json.optional(json, "aml_screenings", Conv.list(Conv.model(AMLScreening::fromJson))); } /** @@ -288,8 +308,8 @@ public Optional> amlScreeni * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> ipAnalyses() { - return Json.optional(json, "ip_analyses", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses::fromJson))); + public Optional> ipAnalyses() { + return Json.optional(json, "ip_analyses", Conv.list(Conv.model(SessionDecision.IpAnalyses::fromJson))); } /** @@ -334,8 +354,8 @@ public Optional> ipAnalyses() * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> databaseValidations() { - return Json.optional(json, "database_validations", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations::fromJson))); + public Optional> databaseValidations() { + return Json.optional(json, "database_validations", Conv.list(Conv.model(SessionDecision.DatabaseValidations::fromJson))); } /** @@ -344,8 +364,8 @@ public Optional> data * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> reviews() { - return Json.optional(json, "reviews", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.Reviews::fromJson))); + public Optional> reviews() { + return Json.optional(json, "reviews", Conv.list(Conv.model(SessionDecision.Reviews::fromJson))); } /** @@ -374,8 +394,8 @@ public Optional expiresAt() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> registryChecks() { - return Json.optional(json, "registry_checks", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.RegistryChecks::fromJson))); + public Optional> registryChecks() { + return Json.optional(json, "registry_checks", Conv.list(Conv.model(SessionDecision.RegistryChecks::fromJson))); } /** @@ -384,8 +404,8 @@ public Optional> registryC * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> keyPeopleChecks() { - return Json.optional(json, "key_people_checks", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks::fromJson))); + public Optional> keyPeopleChecks() { + return Json.optional(json, "key_people_checks", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks::fromJson))); } /** @@ -394,8 +414,8 @@ public Optional> keyPeopl * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> documentVerifications() { - return Json.optional(json, "document_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications::fromJson))); + public Optional> documentVerifications() { + return Json.optional(json, "document_verifications", Conv.list(Conv.model(SessionDecision.DocumentVerifications::fromJson))); } /** @@ -404,8 +424,8 @@ public Optional> do * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional environment() { - return Json.optional(json, "environment", Conv.enumeration(SessionsRetrieveDecisionResponse.Environment::of)); + public Optional environment() { + return Json.optional(json, "environment", Conv.enumeration(SessionDecision.Environment::of)); } /** @@ -414,8 +434,18 @@ public Optional environment() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> resubmissions() { - return Json.optional(json, "resubmissions", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.Resubmissions::fromJson))); + public Optional> resubmissions() { + return Json.optional(json, "resubmissions", Conv.list(Conv.model(SessionDecision.Resubmissions::fromJson))); + } + + /** + * The item's latest Investigator review, or null when it never had one. Carried by the V3 session and business session decision (and so by status.updated webhooks). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional investigator() { + return Json.optional(json, "investigator", Conv.model(InvestigatorBlock::fromJson)); } @Override @@ -423,16 +453,16 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link SessionsRetrieveDecisionResponse}. */ + /** Builds a {@link SessionDecision}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(SessionsRetrieveDecisionResponse from) { + private Builder(SessionDecision from) { super(from); } /** - * The unique identifier of the session for which to retrieve verification results. + * The unique identifier of the session. * * @param sessionId the value * @return this builder @@ -447,8 +477,18 @@ public Builder sessionId(String sessionId) { * @param sessionKind the value * @return this builder */ - public Builder sessionKind(SessionsRetrieveDecisionResponse.SessionKind sessionKind) { - return set("session_kind", Conv.enumeration(SessionsRetrieveDecisionResponse.SessionKind::of), sessionKind); + public Builder sessionKind(SessionDecision.SessionKind sessionKind) { + return set("session_kind", Conv.enumeration(SessionDecision.SessionKind::of), sessionKind); + } + + /** + * For a session cloned with POST /v3/session/import-shared/: the session_id of the original session in the source application. null for every other session. Omitted when include=events is passed. + * + * @param sharedFromSession the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sharedFromSession(@Nullable String sharedFromSession) { + return setNullable("shared_from_session", Conv.STRING, sharedFromSession); } /** @@ -477,8 +517,8 @@ public Builder sessionUrl(@Nullable String sessionUrl) { * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDecisionResponse.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.Status::of), status); + public Builder status(SessionDecision.Status status) { + return set("status", Conv.enumeration(SessionDecision.Status::of), status); } /** @@ -527,8 +567,8 @@ public Builder metadata(@Nullable Map metadata) { * @param expectedDetails the value, or {@code null} to send JSON null * @return this builder */ - public Builder expectedDetails(SessionsRetrieveDecisionResponse.@Nullable ExpectedDetails expectedDetails) { - return setNullable("expected_details", Conv.model(SessionsRetrieveDecisionResponse.ExpectedDetails::fromJson), expectedDetails); + public Builder expectedDetails(SessionDecision.@Nullable ExpectedDetails expectedDetails) { + return setNullable("expected_details", Conv.model(SessionDecision.ExpectedDetails::fromJson), expectedDetails); } /** @@ -537,8 +577,8 @@ public Builder expectedDetails(SessionsRetrieveDecisionResponse.@Nullable Expect * @param contactDetails the value, or {@code null} to send JSON null * @return this builder */ - public Builder contactDetails(SessionsRetrieveDecisionResponse.@Nullable ContactDetails contactDetails) { - return setNullable("contact_details", Conv.model(SessionsRetrieveDecisionResponse.ContactDetails::fromJson), contactDetails); + public Builder contactDetails(SessionDecision.@Nullable ContactDetails contactDetails) { + return setNullable("contact_details", Conv.model(SessionDecision.ContactDetails::fromJson), contactDetails); } /** @@ -557,28 +597,28 @@ public Builder callback(@Nullable String callback) { * @param idVerifications the value, or {@code null} to send JSON null * @return this builder */ - public Builder idVerifications(@Nullable List idVerifications) { - return setNullable("id_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications::fromJson)), idVerifications); + public Builder idVerifications(@Nullable List idVerifications) { + return setNullable("id_verifications", Conv.list(Conv.model(SessionDecision.IdVerifications::fromJson)), idVerifications); } /** - * Why NFC chip verification did not run, populated whenever the workflow includes an NFC step. null while NFC is pending or when the chip was actually read. Values: USER_SKIPPED - the user chose to skip on an NFC-capable device (allowed by allow_nfc_skip); DOCUMENT_WITHOUT_CHIP - the document has no readable NFC chip (not an ICAO 9303 electronic document); CHIP_CERTIFICATE_UNAVAILABLE - the issuing country's signing certificate (CSCA) covering the document's issue period is unavailable, so the chip signature cannot be verified; DEVICE_WITHOUT_NFC - the device reported no NFC reader; INTEGRATION_WITHOUT_NFC_ACCESS - the verification ran without a native SDK (e.g. in a web browser), which has no access to the phone's NFC reader; MRZ_KEY_UNAVAILABLE - the chip access key could not be derived from the document's MRZ. + * Why NFC chip verification did not run, populated whenever the workflow includes an NFC step. null while NFC is pending or when the chip was actually read. Values: USER_SKIPPED - the user chose to skip on an NFC-capable device (allowed by allow_nfc_skip); DOCUMENT_WITHOUT_CHIP - the document has no readable NFC chip (not an ICAO 9303 electronic document); CHIP_CERTIFICATE_UNAVAILABLE - the issuing country's signing certificate (CSCA) covering the document's issue period is unavailable, so the chip signature cannot be verified; DEVICE_WITHOUT_NFC - the device reported no NFC reader; INTEGRATION_WITHOUT_NFC_ACCESS - the verification ran without a native SDK (e.g. in a web browser), which has no access to the phone's NFC reader; MRZ_KEY_UNAVAILABLE - the chip access key could not be derived from the document's MRZ; JPKI_UNAVAILABLE - the document is a Japan My Number Card, whose chip is read through JPKI, and JPKI was not available for this verification: it is not enabled, or the app's native SDK cannot read My Number Cards. * * @param nfcSkipReason the value, or {@code null} to send JSON null * @return this builder */ - public Builder nfcSkipReason(SessionsRetrieveDecisionResponse.@Nullable NfcSkipReason nfcSkipReason) { - return setNullable("nfc_skip_reason", Conv.enumeration(SessionsRetrieveDecisionResponse.NfcSkipReason::of), nfcSkipReason); + public Builder nfcSkipReason(SessionDecision.@Nullable NfcSkipReason nfcSkipReason) { + return setNullable("nfc_skip_reason", Conv.enumeration(SessionDecision.NfcSkipReason::of), nfcSkipReason); } /** - * Array of NFC / ePassport reports produced by NFC steps in the workflow graph. **This is always a JSON array, never a singular nfc object** — read nfc_verifications[0] and iterate if your workflow can run NFC more than once. null until at least one NFC step has produced data. Each item is the full report, including node_id, feature-level status, chip_data, authenticity (SOD + DG integrity), certificate_summary, and warnings[]. When the NFC step was bypassed before it could ever be offered (chipless document, missing certificate, no NFC capability reported, unreadable MRZ key), this stays null and nfc_skip_reason explains why; a skip at the step itself (user tapped skip, or the reader turned out unavailable at scan time) still produces a report with is_nfc_skipped: true. + * Array of NFC / ePassport reports produced by NFC steps in the workflow graph. **This is always a JSON array, never a singular nfc object** — read nfc_verifications[0] and iterate if your workflow can run NFC more than once. null until at least one NFC step has produced data. Each item is the full report, including node_id, feature-level status, chip_protocol, chip_data, authenticity (SOD + DG integrity), certificate_summary, and warnings[]. A Japan My Number Card is read through JPKI instead of ICAO 9303: its item has chip_protocol: "jpki", carries its evidence under jpki, and leaves the ICAO fields null. When the NFC step was bypassed before it could ever be offered (chipless document, missing certificate, no NFC capability reported, unreadable MRZ key, JPKI unavailable for a My Number Card), this stays null and nfc_skip_reason explains why; a skip at the step itself (user tapped skip, or the reader turned out unavailable at scan time) still produces a report with is_nfc_skipped: true. * * @param nfcVerifications the value, or {@code null} to send JSON null * @return this builder */ - public Builder nfcVerifications(@Nullable List nfcVerifications) { - return setNullable("nfc_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications::fromJson)), nfcVerifications); + public Builder nfcVerifications(@Nullable List nfcVerifications) { + return setNullable("nfc_verifications", Conv.list(Conv.model(SessionDecision.NfcVerifications::fromJson)), nfcVerifications); } /** @@ -587,8 +627,8 @@ public Builder nfcVerifications(@Nullable List livenessChecks) { - return setNullable("liveness_checks", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.LivenessChecks::fromJson)), livenessChecks); + public Builder livenessChecks(@Nullable List livenessChecks) { + return setNullable("liveness_checks", Conv.list(Conv.model(SessionDecision.LivenessChecks::fromJson)), livenessChecks); } /** @@ -597,8 +637,8 @@ public Builder livenessChecks(@Nullable List faceMatches) { - return setNullable("face_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.FaceMatches::fromJson)), faceMatches); + public Builder faceMatches(@Nullable List faceMatches) { + return setNullable("face_matches", Conv.list(Conv.model(SessionDecision.FaceMatches::fromJson)), faceMatches); } /** @@ -607,8 +647,8 @@ public Builder faceMatches(@Nullable List phoneVerifications) { - return setNullable("phone_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications::fromJson)), phoneVerifications); + public Builder phoneVerifications(@Nullable List phoneVerifications) { + return setNullable("phone_verifications", Conv.list(Conv.model(SessionDecision.PhoneVerifications::fromJson)), phoneVerifications); } /** @@ -617,8 +657,8 @@ public Builder phoneVerifications(@Nullable List emailVerifications) { - return setNullable("email_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications::fromJson)), emailVerifications); + public Builder emailVerifications(@Nullable List emailVerifications) { + return setNullable("email_verifications", Conv.list(Conv.model(SessionDecision.EmailVerifications::fromJson)), emailVerifications); } /** @@ -627,8 +667,8 @@ public Builder emailVerifications(@Nullable List poaVerifications) { - return setNullable("poa_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications::fromJson)), poaVerifications); + public Builder poaVerifications(@Nullable List poaVerifications) { + return setNullable("poa_verifications", Conv.list(Conv.model(SessionDecision.PoaVerifications::fromJson)), poaVerifications); } /** @@ -637,8 +677,8 @@ public Builder poaVerifications(@Nullable List documentAiDocuments) { - return setNullable("document_ai_documents", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentAiDocuments::fromJson)), documentAiDocuments); + public Builder documentAiDocuments(@Nullable List documentAiDocuments) { + return setNullable("document_ai_documents", Conv.list(Conv.model(SessionDecision.DocumentAiDocuments::fromJson)), documentAiDocuments); } /** @@ -647,8 +687,18 @@ public Builder documentAiDocuments(@Nullable List questionnaireResponses) { - return setNullable("questionnaire_responses", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses::fromJson)), questionnaireResponses); + public Builder questionnaireResponses(@Nullable List questionnaireResponses) { + return setNullable("questionnaire_responses", Conv.list(Conv.model(SessionDecision.QuestionnaireResponses::fromJson)), questionnaireResponses); + } + + /** + * Electronic Signature results, one item per signer and signature step, for User and Business Verification. Present only when the workflow has an Electronic Signature step; an empty array until a signer reaches it. See the [Electronic Signature report](/core-technology/electronic-signature/report-electronic-signature). + * + * @param signatures the value + * @return this builder + */ + public Builder signatures(List signatures) { + return set("signatures", Conv.list(Conv.model(SignatureResult::fromJson)), signatures); } /** @@ -657,8 +707,8 @@ public Builder questionnaireResponses(@Nullable List amlScreenings) { - return setNullable("aml_screenings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings::fromJson)), amlScreenings); + public Builder amlScreenings(@Nullable List amlScreenings) { + return setNullable("aml_screenings", Conv.list(Conv.model(AMLScreening::fromJson)), amlScreenings); } /** @@ -667,8 +717,8 @@ public Builder amlScreenings(@Nullable List ipAnalyses) { - return setNullable("ip_analyses", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses::fromJson)), ipAnalyses); + public Builder ipAnalyses(@Nullable List ipAnalyses) { + return setNullable("ip_analyses", Conv.list(Conv.model(SessionDecision.IpAnalyses::fromJson)), ipAnalyses); } /** @@ -713,8 +763,8 @@ public Builder ipAnalyses(@Nullable List databaseValidations) { - return setNullable("database_validations", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations::fromJson)), databaseValidations); + public Builder databaseValidations(@Nullable List databaseValidations) { + return setNullable("database_validations", Conv.list(Conv.model(SessionDecision.DatabaseValidations::fromJson)), databaseValidations); } /** @@ -723,8 +773,8 @@ public Builder databaseValidations(@Nullable List reviews) { - return set("reviews", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.Reviews::fromJson)), reviews); + public Builder reviews(List reviews) { + return set("reviews", Conv.list(Conv.model(SessionDecision.Reviews::fromJson)), reviews); } /** @@ -753,8 +803,8 @@ public Builder expiresAt(@Nullable OffsetDateTime expiresAt) { * @param registryChecks the value, or {@code null} to send JSON null * @return this builder */ - public Builder registryChecks(@Nullable List registryChecks) { - return setNullable("registry_checks", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.RegistryChecks::fromJson)), registryChecks); + public Builder registryChecks(@Nullable List registryChecks) { + return setNullable("registry_checks", Conv.list(Conv.model(SessionDecision.RegistryChecks::fromJson)), registryChecks); } /** @@ -763,8 +813,8 @@ public Builder registryChecks(@Nullable List keyPeopleChecks) { - return setNullable("key_people_checks", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks::fromJson)), keyPeopleChecks); + public Builder keyPeopleChecks(@Nullable List keyPeopleChecks) { + return setNullable("key_people_checks", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks::fromJson)), keyPeopleChecks); } /** @@ -773,8 +823,8 @@ public Builder keyPeopleChecks(@Nullable List documentVerifications) { - return setNullable("document_verifications", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications::fromJson)), documentVerifications); + public Builder documentVerifications(@Nullable List documentVerifications) { + return setNullable("document_verifications", Conv.list(Conv.model(SessionDecision.DocumentVerifications::fromJson)), documentVerifications); } /** @@ -783,8 +833,8 @@ public Builder documentVerifications(@Nullable List resubmissions) { - return set("resubmissions", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.Resubmissions::fromJson)), resubmissions); + public Builder resubmissions(List resubmissions) { + return set("resubmissions", Conv.list(Conv.model(SessionDecision.Resubmissions::fromJson)), resubmissions); + } + + /** + * The item's latest Investigator review, or null when it never had one. Carried by the V3 session and business session decision (and so by status.updated webhooks). + * + * @param investigator the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder investigator(@Nullable InvestigatorBlock investigator) { + return setNullable("investigator", Conv.model(InvestigatorBlock::fromJson), investigator); } /** * Builds the value. * - * @return a new immutable {@code SessionsRetrieveDecisionResponse} + * @return a new immutable {@code SessionDecision} */ - public SessionsRetrieveDecisionResponse build() { - return new SessionsRetrieveDecisionResponse(buildJson(), buildFiles()); + public SessionDecision build() { + return new SessionDecision(buildJson(), buildFiles()); } @Override @@ -997,8 +1057,8 @@ public Optional dateOfBirth() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional gender() { - return Json.optional(json, "gender", Conv.enumeration(SessionsRetrieveDecisionResponse.ExpectedDetails.Gender::of)); + public Optional gender() { + return Json.optional(json, "gender", Conv.enumeration(SessionDecision.ExpectedDetails.Gender::of)); } /** @@ -1057,8 +1117,8 @@ public Optional ipAddress() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> expectedDocumentTypes() { - return Json.optional(json, "expected_document_types", Conv.list(Conv.enumeration(SessionsRetrieveDecisionResponse.ExpectedDetails.ExpectedDocumentTypes::of))); + public Optional> expectedDocumentTypes() { + return Json.optional(json, "expected_document_types", Conv.list(Conv.enumeration(SessionDecision.ExpectedDetails.ExpectedDocumentTypes::of))); } /** @@ -1160,8 +1220,8 @@ public Builder dateOfBirth(LocalDate dateOfBirth) { * @param gender the value, or {@code null} to send JSON null * @return this builder */ - public Builder gender(SessionsRetrieveDecisionResponse.ExpectedDetails.@Nullable Gender gender) { - return setNullable("gender", Conv.enumeration(SessionsRetrieveDecisionResponse.ExpectedDetails.Gender::of), gender); + public Builder gender(SessionDecision.ExpectedDetails.@Nullable Gender gender) { + return setNullable("gender", Conv.enumeration(SessionDecision.ExpectedDetails.Gender::of), gender); } /** @@ -1220,8 +1280,8 @@ public Builder ipAddress(String ipAddress) { * @param expectedDocumentTypes the value * @return this builder */ - public Builder expectedDocumentTypes(List expectedDocumentTypes) { - return set("expected_document_types", Conv.list(Conv.enumeration(SessionsRetrieveDecisionResponse.ExpectedDetails.ExpectedDocumentTypes::of)), expectedDocumentTypes); + public Builder expectedDocumentTypes(List expectedDocumentTypes) { + return set("expected_document_types", Conv.list(Conv.enumeration(SessionDecision.ExpectedDetails.ExpectedDocumentTypes::of)), expectedDocumentTypes); } /** @@ -1593,8 +1653,8 @@ public Optional nodeId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional verificationMethod() { - return Json.optional(json, "verification_method", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.VerificationMethod::of)); + public Optional verificationMethod() { + return Json.optional(json, "verification_method", Conv.enumeration(SessionDecision.IdVerifications.VerificationMethod::of)); } /** @@ -1603,8 +1663,8 @@ public Optional assurance() { - return Json.optional(json, "assurance", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.Assurance::of)); + public Optional assurance() { + return Json.optional(json, "assurance", Conv.enumeration(SessionDecision.IdVerifications.Assurance::of)); } /** @@ -1623,8 +1683,8 @@ public Optional walletProvider() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional fallbackFrom() { - return Json.optional(json, "fallback_from", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.FallbackFrom::fromJson)); + public Optional fallbackFrom() { + return Json.optional(json, "fallback_from", Conv.model(SessionDecision.IdVerifications.FallbackFrom::fromJson)); } /** @@ -1633,8 +1693,8 @@ public Optional f * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional idLookup() { - return Json.optional(json, "id_lookup", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup::fromJson)); + public Optional idLookup() { + return Json.optional(json, "id_lookup", Conv.model(SessionDecision.IdVerifications.IdLookup::fromJson)); } /** @@ -1643,8 +1703,8 @@ public Optional idLoo * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional walletVerification() { - return Json.optional(json, "wallet_verification", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.WalletVerification::fromJson)); + public Optional walletVerification() { + return Json.optional(json, "wallet_verification", Conv.model(SessionDecision.IdVerifications.WalletVerification::fromJson)); } /** @@ -1653,8 +1713,8 @@ public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.IdVerifications.Status::of)); } /** @@ -1943,8 +2003,8 @@ public Optional nationality() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional extraFields() { - return Json.optional(json, "extra_fields", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ExtraFields::fromJson)); + public Optional extraFields() { + return Json.optional(json, "extra_fields", Conv.model(SessionDecision.IdVerifications.ExtraFields::fromJson)); } /** @@ -1953,8 +2013,8 @@ public Optional ex * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional mrz() { - return Json.optional(json, "mrz", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.Mrz::fromJson)); + public Optional mrz() { + return Json.optional(json, "mrz", Conv.model(SessionDecision.IdVerifications.Mrz::fromJson)); } /** @@ -1963,8 +2023,8 @@ public Optional mrz() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional parsedAddress() { - return Json.optional(json, "parsed_address", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress::fromJson)); + public Optional parsedAddress() { + return Json.optional(json, "parsed_address", Conv.model(SessionDecision.IdVerifications.ParsedAddress::fromJson)); } /** @@ -2003,8 +2063,8 @@ public Optional backImageCameraFrontFaceMatchScore() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> documentSelfieFaceMatches() { - return Json.optional(json, "document_selfie_face_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentSelfieFaceMatches::fromJson))); + public Optional> documentSelfieFaceMatches() { + return Json.optional(json, "document_selfie_face_matches", Conv.list(Conv.model(SessionDecision.IdVerifications.DocumentSelfieFaceMatches::fromJson))); } /** @@ -2013,8 +2073,8 @@ public Optional frontImageQualityScore() { - return Json.optional(json, "front_image_quality_score", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.FrontImageQualityScore::fromJson)); + public Optional frontImageQualityScore() { + return Json.optional(json, "front_image_quality_score", Conv.model(SessionDecision.IdVerifications.FrontImageQualityScore::fromJson)); } /** @@ -2023,8 +2083,8 @@ public Optional backImageQualityScore() { - return Json.optional(json, "back_image_quality_score", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.BackImageQualityScore::fromJson)); + public Optional backImageQualityScore() { + return Json.optional(json, "back_image_quality_score", Conv.model(SessionDecision.IdVerifications.BackImageQualityScore::fromJson)); } /** @@ -2033,8 +2093,8 @@ public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.Warnings::fromJson))); + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SessionDecision.IdVerifications.Warnings::fromJson))); } /** @@ -2043,8 +2103,8 @@ public Optional> * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> matches() { - return Json.optional(json, "matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.Matches::fromJson))); + public Optional> matches() { + return Json.optional(json, "matches", Conv.list(Conv.model(SessionDecision.IdVerifications.Matches::fromJson))); } /** @@ -2053,8 +2113,8 @@ public Optional> * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional documentLiveness() { - return Json.optional(json, "document_liveness", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness::fromJson)); + public Optional documentLiveness() { + return Json.optional(json, "document_liveness", Conv.model(SessionDecision.IdVerifications.DocumentLiveness::fromJson)); } @Override @@ -2086,8 +2146,8 @@ public Builder nodeId(String nodeId) { * @param verificationMethod the value * @return this builder */ - public Builder verificationMethod(SessionsRetrieveDecisionResponse.IdVerifications.VerificationMethod verificationMethod) { - return set("verification_method", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.VerificationMethod::of), verificationMethod); + public Builder verificationMethod(SessionDecision.IdVerifications.VerificationMethod verificationMethod) { + return set("verification_method", Conv.enumeration(SessionDecision.IdVerifications.VerificationMethod::of), verificationMethod); } /** @@ -2096,8 +2156,8 @@ public Builder verificationMethod(SessionsRetrieveDecisionResponse.IdVerificatio * @param assurance the value * @return this builder */ - public Builder assurance(SessionsRetrieveDecisionResponse.IdVerifications.Assurance assurance) { - return set("assurance", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.Assurance::of), assurance); + public Builder assurance(SessionDecision.IdVerifications.Assurance assurance) { + return set("assurance", Conv.enumeration(SessionDecision.IdVerifications.Assurance::of), assurance); } /** @@ -2116,8 +2176,8 @@ public Builder walletProvider(@Nullable String walletProvider) { * @param fallbackFrom the value, or {@code null} to send JSON null * @return this builder */ - public Builder fallbackFrom(SessionsRetrieveDecisionResponse.IdVerifications.@Nullable FallbackFrom fallbackFrom) { - return setNullable("fallback_from", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.FallbackFrom::fromJson), fallbackFrom); + public Builder fallbackFrom(SessionDecision.IdVerifications.@Nullable FallbackFrom fallbackFrom) { + return setNullable("fallback_from", Conv.model(SessionDecision.IdVerifications.FallbackFrom::fromJson), fallbackFrom); } /** @@ -2126,8 +2186,8 @@ public Builder fallbackFrom(SessionsRetrieveDecisionResponse.IdVerifications.@Nu * @param idLookup the value, or {@code null} to send JSON null * @return this builder */ - public Builder idLookup(SessionsRetrieveDecisionResponse.IdVerifications.@Nullable IdLookup idLookup) { - return setNullable("id_lookup", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup::fromJson), idLookup); + public Builder idLookup(SessionDecision.IdVerifications.@Nullable IdLookup idLookup) { + return setNullable("id_lookup", Conv.model(SessionDecision.IdVerifications.IdLookup::fromJson), idLookup); } /** @@ -2136,8 +2196,8 @@ public Builder idLookup(SessionsRetrieveDecisionResponse.IdVerifications.@Nullab * @param walletVerification the value, or {@code null} to send JSON null * @return this builder */ - public Builder walletVerification(SessionsRetrieveDecisionResponse.IdVerifications.@Nullable WalletVerification walletVerification) { - return setNullable("wallet_verification", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.WalletVerification::fromJson), walletVerification); + public Builder walletVerification(SessionDecision.IdVerifications.@Nullable WalletVerification walletVerification) { + return setNullable("wallet_verification", Conv.model(SessionDecision.IdVerifications.WalletVerification::fromJson), walletVerification); } /** @@ -2146,8 +2206,8 @@ public Builder walletVerification(SessionsRetrieveDecisionResponse.IdVerificatio * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDecisionResponse.IdVerifications.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.Status::of), status); + public Builder status(SessionDecision.IdVerifications.Status status) { + return set("status", Conv.enumeration(SessionDecision.IdVerifications.Status::of), status); } /** @@ -2436,8 +2496,8 @@ public Builder nationality(String nationality) { * @param extraFields the value * @return this builder */ - public Builder extraFields(SessionsRetrieveDecisionResponse.IdVerifications.ExtraFields extraFields) { - return set("extra_fields", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ExtraFields::fromJson), extraFields); + public Builder extraFields(SessionDecision.IdVerifications.ExtraFields extraFields) { + return set("extra_fields", Conv.model(SessionDecision.IdVerifications.ExtraFields::fromJson), extraFields); } /** @@ -2446,8 +2506,8 @@ public Builder extraFields(SessionsRetrieveDecisionResponse.IdVerifications.Extr * @param mrz the value, or {@code null} to send JSON null * @return this builder */ - public Builder mrz(SessionsRetrieveDecisionResponse.IdVerifications.@Nullable Mrz mrz) { - return setNullable("mrz", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.Mrz::fromJson), mrz); + public Builder mrz(SessionDecision.IdVerifications.@Nullable Mrz mrz) { + return setNullable("mrz", Conv.model(SessionDecision.IdVerifications.Mrz::fromJson), mrz); } /** @@ -2456,8 +2516,8 @@ public Builder mrz(SessionsRetrieveDecisionResponse.IdVerifications.@Nullable Mr * @param parsedAddress the value * @return this builder */ - public Builder parsedAddress(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress parsedAddress) { - return set("parsed_address", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress::fromJson), parsedAddress); + public Builder parsedAddress(SessionDecision.IdVerifications.ParsedAddress parsedAddress) { + return set("parsed_address", Conv.model(SessionDecision.IdVerifications.ParsedAddress::fromJson), parsedAddress); } /** @@ -2496,8 +2556,8 @@ public Builder backImageCameraFrontFaceMatchScore(@Nullable Double backImageCame * @param documentSelfieFaceMatches the value, or {@code null} to send JSON null * @return this builder */ - public Builder documentSelfieFaceMatches(@Nullable List documentSelfieFaceMatches) { - return setNullable("document_selfie_face_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentSelfieFaceMatches::fromJson)), documentSelfieFaceMatches); + public Builder documentSelfieFaceMatches(@Nullable List documentSelfieFaceMatches) { + return setNullable("document_selfie_face_matches", Conv.list(Conv.model(SessionDecision.IdVerifications.DocumentSelfieFaceMatches::fromJson)), documentSelfieFaceMatches); } /** @@ -2506,8 +2566,8 @@ public Builder documentSelfieFaceMatches(@Nullable List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.Warnings::fromJson)), warnings); + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SessionDecision.IdVerifications.Warnings::fromJson)), warnings); } /** @@ -2536,8 +2596,8 @@ public Builder warnings(List matches) { - return set("matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.Matches::fromJson)), matches); + public Builder matches(List matches) { + return set("matches", Conv.list(Conv.model(SessionDecision.IdVerifications.Matches::fromJson)), matches); } /** @@ -2546,8 +2606,8 @@ public Builder matches(List method() { - return Json.optional(json, "method", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.FallbackFrom.Method::of)); + public Optional method() { + return Json.optional(json, "method", Conv.enumeration(SessionDecision.IdVerifications.FallbackFrom.Method::of)); } /** @@ -2745,8 +2805,8 @@ private Builder(FallbackFrom from) { * @param method the value * @return this builder */ - public Builder method(SessionsRetrieveDecisionResponse.IdVerifications.FallbackFrom.Method method) { - return set("method", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.FallbackFrom.Method::of), method); + public Builder method(SessionDecision.IdVerifications.FallbackFrom.Method method) { + return set("method", Conv.enumeration(SessionDecision.IdVerifications.FallbackFrom.Method::of), method); } /** @@ -2913,8 +2973,8 @@ public Optional maxAttempts() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional outcome() { - return Json.optional(json, "outcome", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Outcome::of)); + public Optional outcome() { + return Json.optional(json, "outcome", Conv.enumeration(SessionDecision.IdVerifications.IdLookup.Outcome::of)); } /** @@ -2923,8 +2983,8 @@ public Optional> comparison() { - return Json.optional(json, "comparison", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison::fromJson))); + public Optional> comparison() { + return Json.optional(json, "comparison", Conv.list(Conv.model(SessionDecision.IdVerifications.IdLookup.Comparison::fromJson))); } /** @@ -3006,8 +3066,8 @@ public Builder maxAttempts(long maxAttempts) { * @param outcome the value, or {@code null} to send JSON null * @return this builder */ - public Builder outcome(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.@Nullable Outcome outcome) { - return setNullable("outcome", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Outcome::of), outcome); + public Builder outcome(SessionDecision.IdVerifications.IdLookup.@Nullable Outcome outcome) { + return setNullable("outcome", Conv.enumeration(SessionDecision.IdVerifications.IdLookup.Outcome::of), outcome); } /** @@ -3016,8 +3076,8 @@ public Builder outcome(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup * @param comparison the value * @return this builder */ - public Builder comparison(List comparison) { - return set("comparison", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison::fromJson)), comparison); + public Builder comparison(List comparison) { + return set("comparison", Conv.list(Conv.model(SessionDecision.IdVerifications.IdLookup.Comparison::fromJson)), comparison); } /** @@ -3149,8 +3209,8 @@ public Builder toBuilder() { * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison.Field field() { - return Json.required(json, "field", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison.Field::of)); + public SessionDecision.IdVerifications.IdLookup.Comparison.Field field() { + return Json.required(json, "field", Conv.enumeration(SessionDecision.IdVerifications.IdLookup.Comparison.Field::of)); } /** @@ -3189,8 +3249,8 @@ public Optional record_() { * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison.Result result() { - return Json.required(json, "result", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison.Result::of)); + public SessionDecision.IdVerifications.IdLookup.Comparison.Result result() { + return Json.required(json, "result", Conv.enumeration(SessionDecision.IdVerifications.IdLookup.Comparison.Result::of)); } @Override @@ -3212,8 +3272,8 @@ private Builder(Comparison from) { * @param field the value * @return this builder */ - public Builder field(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison.Field field) { - return set("field", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison.Field::of), field); + public Builder field(SessionDecision.IdVerifications.IdLookup.Comparison.Field field) { + return set("field", Conv.enumeration(SessionDecision.IdVerifications.IdLookup.Comparison.Field::of), field); } /** @@ -3252,8 +3312,8 @@ public Builder record_(@Nullable String record_) { * @param result the value * @return this builder */ - public Builder result(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison.Result result) { - return set("result", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.IdLookup.Comparison.Result::of), result); + public Builder result(SessionDecision.IdVerifications.IdLookup.Comparison.Result result) { + return set("result", Conv.enumeration(SessionDecision.IdVerifications.IdLookup.Comparison.Result::of), result); } /** @@ -3475,8 +3535,8 @@ public Optional credentialType() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional levelOfAssurance() { - return Json.optional(json, "level_of_assurance", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.WalletVerification.LevelOfAssurance::of)); + public Optional levelOfAssurance() { + return Json.optional(json, "level_of_assurance", Conv.enumeration(SessionDecision.IdVerifications.WalletVerification.LevelOfAssurance::of)); } /** @@ -3598,8 +3658,8 @@ public Builder credentialType(@Nullable String credentialType) { * @param levelOfAssurance the value, or {@code null} to send JSON null * @return this builder */ - public Builder levelOfAssurance(SessionsRetrieveDecisionResponse.IdVerifications.WalletVerification.@Nullable LevelOfAssurance levelOfAssurance) { - return setNullable("level_of_assurance", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.WalletVerification.LevelOfAssurance::of), levelOfAssurance); + public Builder levelOfAssurance(SessionDecision.IdVerifications.WalletVerification.@Nullable LevelOfAssurance levelOfAssurance) { + return setNullable("level_of_assurance", Conv.enumeration(SessionDecision.IdVerifications.WalletVerification.LevelOfAssurance::of), levelOfAssurance); } /** @@ -4735,8 +4795,8 @@ public Optional postalCode() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional rawResults() { - return Json.optional(json, "raw_results", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults::fromJson)); + public Optional rawResults() { + return Json.optional(json, "raw_results", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults::fromJson)); } /** @@ -4755,8 +4815,8 @@ public Optional addressType() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional documentLocation() { - return Json.optional(json, "document_location", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.DocumentLocation::fromJson)); + public Optional documentLocation() { + return Json.optional(json, "document_location", Conv.model(SessionDecision.IdVerifications.ParsedAddress.DocumentLocation::fromJson)); } /** @@ -4878,8 +4938,8 @@ public Builder postalCode(String postalCode) { * @param rawResults the value * @return this builder */ - public Builder rawResults(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults rawResults) { - return set("raw_results", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults::fromJson), rawResults); + public Builder rawResults(SessionDecision.IdVerifications.ParsedAddress.RawResults rawResults) { + return set("raw_results", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults::fromJson), rawResults); } /** @@ -4898,8 +4958,8 @@ public Builder addressType(String addressType) { * @param documentLocation the value * @return this builder */ - public Builder documentLocation(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.DocumentLocation documentLocation) { - return set("document_location", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.DocumentLocation::fromJson), documentLocation); + public Builder documentLocation(SessionDecision.IdVerifications.ParsedAddress.DocumentLocation documentLocation) { + return set("document_location", Conv.model(SessionDecision.IdVerifications.ParsedAddress.DocumentLocation::fromJson), documentLocation); } /** @@ -4979,8 +5039,8 @@ public Optional> types() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional geometry() { - return Json.optional(json, "geometry", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry::fromJson)); + public Optional geometry() { + return Json.optional(json, "geometry", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry::fromJson)); } /** @@ -5009,8 +5069,8 @@ public Optional formattedAddress() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> navigationPoints() { - return Json.optional(json, "navigation_points", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.NavigationPoints::fromJson))); + public Optional> navigationPoints() { + return Json.optional(json, "navigation_points", Conv.list(Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.NavigationPoints::fromJson))); } /** @@ -5019,8 +5079,8 @@ public Optional> addressComponents() { - return Json.optional(json, "address_components", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.AddressComponents::fromJson))); + public Optional> addressComponents() { + return Json.optional(json, "address_components", Conv.list(Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.AddressComponents::fromJson))); } @Override @@ -5052,8 +5112,8 @@ public Builder types(List types) { * @param geometry the value * @return this builder */ - public Builder geometry(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry geometry) { - return set("geometry", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry::fromJson), geometry); + public Builder geometry(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry geometry) { + return set("geometry", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry::fromJson), geometry); } /** @@ -5082,8 +5142,8 @@ public Builder formattedAddress(String formattedAddress) { * @param navigationPoints the value * @return this builder */ - public Builder navigationPoints(List navigationPoints) { - return set("navigation_points", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.NavigationPoints::fromJson)), navigationPoints); + public Builder navigationPoints(List navigationPoints) { + return set("navigation_points", Conv.list(Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.NavigationPoints::fromJson)), navigationPoints); } /** @@ -5092,8 +5152,8 @@ public Builder navigationPoints(List addressComponents) { - return set("address_components", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.AddressComponents::fromJson)), addressComponents); + public Builder addressComponents(List addressComponents) { + return set("address_components", Conv.list(Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.AddressComponents::fromJson)), addressComponents); } /** @@ -5153,8 +5213,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional location() { - return Json.optional(json, "location", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Location::fromJson)); + public Optional location() { + return Json.optional(json, "location", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Location::fromJson)); } /** @@ -5163,8 +5223,8 @@ public Optional viewport() { - return Json.optional(json, "viewport", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport::fromJson)); + public Optional viewport() { + return Json.optional(json, "viewport", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport::fromJson)); } /** @@ -5196,8 +5256,8 @@ private Builder(Geometry from) { * @param location the value * @return this builder */ - public Builder location(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Location location) { - return set("location", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Location::fromJson), location); + public Builder location(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Location location) { + return set("location", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Location::fromJson), location); } /** @@ -5206,8 +5266,8 @@ public Builder location(SessionsRetrieveDecisionResponse.IdVerifications.ParsedA * @param viewport the value * @return this builder */ - public Builder viewport(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport viewport) { - return set("viewport", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport::fromJson), viewport); + public Builder viewport(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport viewport) { + return set("viewport", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport::fromJson), viewport); } /** @@ -5382,8 +5442,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional northeast() { - return Json.optional(json, "northeast", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Northeast::fromJson)); + public Optional northeast() { + return Json.optional(json, "northeast", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Northeast::fromJson)); } /** @@ -5392,8 +5452,8 @@ public Optional southwest() { - return Json.optional(json, "southwest", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Southwest::fromJson)); + public Optional southwest() { + return Json.optional(json, "southwest", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Southwest::fromJson)); } @Override @@ -5415,8 +5475,8 @@ private Builder(Viewport from) { * @param northeast the value * @return this builder */ - public Builder northeast(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Northeast northeast) { - return set("northeast", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Northeast::fromJson), northeast); + public Builder northeast(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Northeast northeast) { + return set("northeast", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Northeast::fromJson), northeast); } /** @@ -5425,8 +5485,8 @@ public Builder northeast(SessionsRetrieveDecisionResponse.IdVerifications.Parsed * @param southwest the value * @return this builder */ - public Builder southwest(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Southwest southwest) { - return set("southwest", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Southwest::fromJson), southwest); + public Builder southwest(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Southwest southwest) { + return set("southwest", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.Geometry.Viewport.Southwest::fromJson), southwest); } /** @@ -5698,8 +5758,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional location() { - return Json.optional(json, "location", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.NavigationPoints.Location::fromJson)); + public Optional location() { + return Json.optional(json, "location", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.NavigationPoints.Location::fromJson)); } @Override @@ -5721,8 +5781,8 @@ private Builder(NavigationPoints from) { * @param location the value * @return this builder */ - public Builder location(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.NavigationPoints.Location location) { - return set("location", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.ParsedAddress.RawResults.NavigationPoints.Location::fromJson), location); + public Builder location(SessionDecision.IdVerifications.ParsedAddress.RawResults.NavigationPoints.Location location) { + return set("location", Conv.model(SessionDecision.IdVerifications.ParsedAddress.RawResults.NavigationPoints.Location::fromJson), location); } /** @@ -6190,8 +6250,8 @@ public Optional verificationDate() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional userDetails() { - return Json.optional(json, "user_details", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentSelfieFaceMatches.UserDetails::fromJson)); + public Optional userDetails() { + return Json.optional(json, "user_details", Conv.model(SessionDecision.IdVerifications.DocumentSelfieFaceMatches.UserDetails::fromJson)); } /** @@ -6250,8 +6310,8 @@ public Optional apiService() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional source() { - return Json.optional(json, "source", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.DocumentSelfieFaceMatches.Source::of)); + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(SessionDecision.IdVerifications.DocumentSelfieFaceMatches.Source::of)); } @Override @@ -6343,8 +6403,8 @@ public Builder verificationDate(@Nullable OffsetDateTime verificationDate) { * @param userDetails the value, or {@code null} to send JSON null * @return this builder */ - public Builder userDetails(SessionsRetrieveDecisionResponse.IdVerifications.DocumentSelfieFaceMatches.@Nullable UserDetails userDetails) { - return setNullable("user_details", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentSelfieFaceMatches.UserDetails::fromJson), userDetails); + public Builder userDetails(SessionDecision.IdVerifications.DocumentSelfieFaceMatches.@Nullable UserDetails userDetails) { + return setNullable("user_details", Conv.model(SessionDecision.IdVerifications.DocumentSelfieFaceMatches.UserDetails::fromJson), userDetails); } /** @@ -6403,8 +6463,8 @@ public Builder apiService(@Nullable String apiService) { * @param source the value * @return this builder */ - public Builder source(SessionsRetrieveDecisionResponse.IdVerifications.DocumentSelfieFaceMatches.Source source) { - return set("source", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.DocumentSelfieFaceMatches.Source::of), source); + public Builder source(SessionDecision.IdVerifications.DocumentSelfieFaceMatches.Source source) { + return set("source", Conv.enumeration(SessionDecision.IdVerifications.DocumentSelfieFaceMatches.Source::of), source); } /** @@ -6663,8 +6723,8 @@ public Optional brightnessScore() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional brightnessIssue() { - return Json.optional(json, "brightness_issue", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.FrontImageQualityScore.BrightnessIssue::of)); + public Optional brightnessIssue() { + return Json.optional(json, "brightness_issue", Conv.enumeration(SessionDecision.IdVerifications.FrontImageQualityScore.BrightnessIssue::of)); } /** @@ -6736,8 +6796,8 @@ public Builder brightnessScore(double brightnessScore) { * @param brightnessIssue the value * @return this builder */ - public Builder brightnessIssue(SessionsRetrieveDecisionResponse.IdVerifications.FrontImageQualityScore.BrightnessIssue brightnessIssue) { - return set("brightness_issue", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.FrontImageQualityScore.BrightnessIssue::of), brightnessIssue); + public Builder brightnessIssue(SessionDecision.IdVerifications.FrontImageQualityScore.BrightnessIssue brightnessIssue) { + return set("brightness_issue", Conv.enumeration(SessionDecision.IdVerifications.FrontImageQualityScore.BrightnessIssue::of), brightnessIssue); } /** @@ -6895,8 +6955,8 @@ public Optional brightnessScore() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional brightnessIssue() { - return Json.optional(json, "brightness_issue", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.BackImageQualityScore.BrightnessIssue::of)); + public Optional brightnessIssue() { + return Json.optional(json, "brightness_issue", Conv.enumeration(SessionDecision.IdVerifications.BackImageQualityScore.BrightnessIssue::of)); } /** @@ -6968,8 +7028,8 @@ public Builder brightnessScore(double brightnessScore) { * @param brightnessIssue the value * @return this builder */ - public Builder brightnessIssue(SessionsRetrieveDecisionResponse.IdVerifications.BackImageQualityScore.BrightnessIssue brightnessIssue) { - return set("brightness_issue", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.BackImageQualityScore.BrightnessIssue::of), brightnessIssue); + public Builder brightnessIssue(SessionDecision.IdVerifications.BackImageQualityScore.BrightnessIssue brightnessIssue) { + return set("brightness_issue", Conv.enumeration(SessionDecision.IdVerifications.BackImageQualityScore.BrightnessIssue::of), brightnessIssue); } /** @@ -7106,8 +7166,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.Warnings.Feature::of)); + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(SessionDecision.IdVerifications.Warnings.Feature::of)); } /** @@ -7189,8 +7249,8 @@ private Builder(Warnings from) { * @param feature the value * @return this builder */ - public Builder feature(SessionsRetrieveDecisionResponse.IdVerifications.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.Warnings.Feature::of), feature); + public Builder feature(SessionDecision.IdVerifications.Warnings.Feature feature) { + return set("feature", Conv.enumeration(SessionDecision.IdVerifications.Warnings.Feature::of), feature); } /** @@ -7433,8 +7493,8 @@ public Optional verificationDate() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional userDetails() { - return Json.optional(json, "user_details", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.Matches.UserDetails::fromJson)); + public Optional userDetails() { + return Json.optional(json, "user_details", Conv.model(SessionDecision.IdVerifications.Matches.UserDetails::fromJson)); } /** @@ -7536,8 +7596,8 @@ public Builder verificationDate(OffsetDateTime verificationDate) { * @param userDetails the value * @return this builder */ - public Builder userDetails(SessionsRetrieveDecisionResponse.IdVerifications.Matches.UserDetails userDetails) { - return set("user_details", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.Matches.UserDetails::fromJson), userDetails); + public Builder userDetails(SessionDecision.IdVerifications.Matches.UserDetails userDetails) { + return set("user_details", Conv.model(SessionDecision.IdVerifications.Matches.UserDetails::fromJson), userDetails); } /** @@ -7766,8 +7826,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional portraitReplace() { - return Json.optional(json, "portrait_replace", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PortraitReplace::fromJson)); + public Optional portraitReplace() { + return Json.optional(json, "portrait_replace", Conv.model(SessionDecision.IdVerifications.DocumentLiveness.PortraitReplace::fromJson)); } /** @@ -7776,8 +7836,8 @@ public Optional printedCopy() { - return Json.optional(json, "printed_copy", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PrintedCopy::fromJson)); + public Optional printedCopy() { + return Json.optional(json, "printed_copy", Conv.model(SessionDecision.IdVerifications.DocumentLiveness.PrintedCopy::fromJson)); } /** @@ -7786,8 +7846,8 @@ public Optional screenReplay() { - return Json.optional(json, "screen_replay", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.ScreenReplay::fromJson)); + public Optional screenReplay() { + return Json.optional(json, "screen_replay", Conv.model(SessionDecision.IdVerifications.DocumentLiveness.ScreenReplay::fromJson)); } @Override @@ -7809,8 +7869,8 @@ private Builder(DocumentLiveness from) { * @param portraitReplace the value, or {@code null} to send JSON null * @return this builder */ - public Builder portraitReplace(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.@Nullable PortraitReplace portraitReplace) { - return setNullable("portrait_replace", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PortraitReplace::fromJson), portraitReplace); + public Builder portraitReplace(SessionDecision.IdVerifications.DocumentLiveness.@Nullable PortraitReplace portraitReplace) { + return setNullable("portrait_replace", Conv.model(SessionDecision.IdVerifications.DocumentLiveness.PortraitReplace::fromJson), portraitReplace); } /** @@ -7819,8 +7879,8 @@ public Builder portraitReplace(SessionsRetrieveDecisionResponse.IdVerifications. * @param printedCopy the value, or {@code null} to send JSON null * @return this builder */ - public Builder printedCopy(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.@Nullable PrintedCopy printedCopy) { - return setNullable("printed_copy", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PrintedCopy::fromJson), printedCopy); + public Builder printedCopy(SessionDecision.IdVerifications.DocumentLiveness.@Nullable PrintedCopy printedCopy) { + return setNullable("printed_copy", Conv.model(SessionDecision.IdVerifications.DocumentLiveness.PrintedCopy::fromJson), printedCopy); } /** @@ -7829,8 +7889,8 @@ public Builder printedCopy(SessionsRetrieveDecisionResponse.IdVerifications.Docu * @param screenReplay the value, or {@code null} to send JSON null * @return this builder */ - public Builder screenReplay(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.@Nullable ScreenReplay screenReplay) { - return setNullable("screen_replay", Conv.model(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.ScreenReplay::fromJson), screenReplay); + public Builder screenReplay(SessionDecision.IdVerifications.DocumentLiveness.@Nullable ScreenReplay screenReplay) { + return setNullable("screen_replay", Conv.model(SessionDecision.IdVerifications.DocumentLiveness.ScreenReplay::fromJson), screenReplay); } /** @@ -7940,8 +8000,8 @@ public Optional reviewThreshold() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional bucket() { - return Json.optional(json, "bucket", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PortraitReplace.Bucket::of)); + public Optional bucket() { + return Json.optional(json, "bucket", Conv.enumeration(SessionDecision.IdVerifications.DocumentLiveness.PortraitReplace.Bucket::of)); } @Override @@ -8013,8 +8073,8 @@ public Builder reviewThreshold(@Nullable Double reviewThreshold) { * @param bucket the value, or {@code null} to send JSON null * @return this builder */ - public Builder bucket(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PortraitReplace.@Nullable Bucket bucket) { - return setNullable("bucket", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PortraitReplace.Bucket::of), bucket); + public Builder bucket(SessionDecision.IdVerifications.DocumentLiveness.PortraitReplace.@Nullable Bucket bucket) { + return setNullable("bucket", Conv.enumeration(SessionDecision.IdVerifications.DocumentLiveness.PortraitReplace.Bucket::of), bucket); } /** @@ -8171,8 +8231,8 @@ public Optional reviewThreshold() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional bucket() { - return Json.optional(json, "bucket", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PrintedCopy.Bucket::of)); + public Optional bucket() { + return Json.optional(json, "bucket", Conv.enumeration(SessionDecision.IdVerifications.DocumentLiveness.PrintedCopy.Bucket::of)); } @Override @@ -8244,8 +8304,8 @@ public Builder reviewThreshold(@Nullable Double reviewThreshold) { * @param bucket the value, or {@code null} to send JSON null * @return this builder */ - public Builder bucket(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PrintedCopy.@Nullable Bucket bucket) { - return setNullable("bucket", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.PrintedCopy.Bucket::of), bucket); + public Builder bucket(SessionDecision.IdVerifications.DocumentLiveness.PrintedCopy.@Nullable Bucket bucket) { + return setNullable("bucket", Conv.enumeration(SessionDecision.IdVerifications.DocumentLiveness.PrintedCopy.Bucket::of), bucket); } /** @@ -8402,8 +8462,8 @@ public Optional reviewThreshold() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional bucket() { - return Json.optional(json, "bucket", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.ScreenReplay.Bucket::of)); + public Optional bucket() { + return Json.optional(json, "bucket", Conv.enumeration(SessionDecision.IdVerifications.DocumentLiveness.ScreenReplay.Bucket::of)); } @Override @@ -8475,8 +8535,8 @@ public Builder reviewThreshold(@Nullable Double reviewThreshold) { * @param bucket the value, or {@code null} to send JSON null * @return this builder */ - public Builder bucket(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.ScreenReplay.@Nullable Bucket bucket) { - return setNullable("bucket", Conv.enumeration(SessionsRetrieveDecisionResponse.IdVerifications.DocumentLiveness.ScreenReplay.Bucket::of), bucket); + public Builder bucket(SessionDecision.IdVerifications.DocumentLiveness.ScreenReplay.@Nullable Bucket bucket) { + return setNullable("bucket", Conv.enumeration(SessionDecision.IdVerifications.DocumentLiveness.ScreenReplay.Bucket::of), bucket); } /** @@ -8544,7 +8604,7 @@ public boolean isKnown() { } /** - * Why NFC chip verification did not run, populated whenever the workflow includes an NFC step. null while NFC is pending or when the chip was actually read. Values: USER_SKIPPED - the user chose to skip on an NFC-capable device (allowed by allow_nfc_skip); DOCUMENT_WITHOUT_CHIP - the document has no readable NFC chip (not an ICAO 9303 electronic document); CHIP_CERTIFICATE_UNAVAILABLE - the issuing country's signing certificate (CSCA) covering the document's issue period is unavailable, so the chip signature cannot be verified; DEVICE_WITHOUT_NFC - the device reported no NFC reader; INTEGRATION_WITHOUT_NFC_ACCESS - the verification ran without a native SDK (e.g. in a web browser), which has no access to the phone's NFC reader; MRZ_KEY_UNAVAILABLE - the chip access key could not be derived from the document's MRZ. + * Why NFC chip verification did not run, populated whenever the workflow includes an NFC step. null while NFC is pending or when the chip was actually read. Values: USER_SKIPPED - the user chose to skip on an NFC-capable device (allowed by allow_nfc_skip); DOCUMENT_WITHOUT_CHIP - the document has no readable NFC chip (not an ICAO 9303 electronic document); CHIP_CERTIFICATE_UNAVAILABLE - the issuing country's signing certificate (CSCA) covering the document's issue period is unavailable, so the chip signature cannot be verified; DEVICE_WITHOUT_NFC - the device reported no NFC reader; INTEGRATION_WITHOUT_NFC_ACCESS - the verification ran without a native SDK (e.g. in a web browser), which has no access to the phone's NFC reader; MRZ_KEY_UNAVAILABLE - the chip access key could not be derived from the document's MRZ; JPKI_UNAVAILABLE - the document is a Japan My Number Card, whose chip is read through JPKI, and JPKI was not available for this verification: it is not enabled, or the app's native SDK cannot read My Number Cards. * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -8567,7 +8627,10 @@ public static final class NfcSkipReason extends StringEnum { /** The value {@code "MRZ_KEY_UNAVAILABLE"}. */ public static final NfcSkipReason MRZ_KEY_UNAVAILABLE = new NfcSkipReason("MRZ_KEY_UNAVAILABLE"); - private static final List KNOWN_VALUES = List.of(USER_SKIPPED, DOCUMENT_WITHOUT_CHIP, CHIP_CERTIFICATE_UNAVAILABLE, DEVICE_WITHOUT_NFC, INTEGRATION_WITHOUT_NFC_ACCESS, MRZ_KEY_UNAVAILABLE); + /** The value {@code "JPKI_UNAVAILABLE"}. */ + public static final NfcSkipReason JPKI_UNAVAILABLE = new NfcSkipReason("JPKI_UNAVAILABLE"); + + private static final List KNOWN_VALUES = List.of(USER_SKIPPED, DOCUMENT_WITHOUT_CHIP, CHIP_CERTIFICATE_UNAVAILABLE, DEVICE_WITHOUT_NFC, INTEGRATION_WITHOUT_NFC_ACCESS, MRZ_KEY_UNAVAILABLE, JPKI_UNAVAILABLE); private NfcSkipReason(String value) { super(value); @@ -8598,9 +8661,11 @@ public boolean isKnown() { } } - /** One NFC report — the full report for a single ePassport chip read. */ + /** + * One NFC report: the full report for a single chip read (an ICAO 9303 ePassport or eID chip, or a Japan My Number Card read through JPKI). + */ public static final class NfcVerifications extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "is_nfc_skipped", "skip_reason", "portrait_image", "signature_image", "chip_data", "authenticity", "certificate_summary", "warnings"); + private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "is_nfc_skipped", "skip_reason", "chip_protocol", "jpki", "portrait_image", "signature_image", "chip_data", "authenticity", "certificate_summary", "warnings"); private NfcVerifications(ObjectNode json, Map> files) { super(json, files); @@ -8651,8 +8716,8 @@ public Optional nodeId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.NfcVerifications.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.NfcVerifications.Status::of)); } /** @@ -8671,12 +8736,32 @@ public Optional isNfcSkipped() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional skipReason() { - return Json.optional(json, "skip_reason", Conv.enumeration(SessionsRetrieveDecisionResponse.NfcVerifications.SkipReason::of)); + public Optional skipReason() { + return Json.optional(json, "skip_reason", Conv.enumeration(SessionDecision.NfcVerifications.SkipReason::of)); + } + + /** + * How the NFC step reads the chip. icao: ICAO 9303 ePassport or eID chip, opened with the access key from the MRZ. jpki: Japan My Number Card, where the card signs a one-time challenge after the holder enters the card PIN in a native SDK; the result is under jpki. Also set on skipped steps, so you can tell a skipped My Number Card read apart. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional chipProtocol() { + return Json.optional(json, "chip_protocol", Conv.enumeration(SessionDecision.NfcVerifications.ChipProtocol::of)); + } + + /** + * What a Japan My Number Card read through JPKI proved. Present when chip_protocol is jpki and the card was read. null for ICAO chips, for skipped steps, and when the NFC step closed without any card read (that case raises JPKI_SIGNATURE_INVALID). Never carries the certificates, their serial numbers, the signatures or the 12-digit My Number. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional jpki() { + return Json.optional(json, "jpki", Conv.model(SessionDecision.NfcVerifications.Jpki::fromJson)); } /** - * The portrait_image field. + * Presigned URL to the portrait read from the chip (ICAO DG2). null for a JPKI read (chip_protocol: "jpki") and for skipped steps. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -8686,7 +8771,7 @@ public Optional portraitImage() { } /** - * The signature_image field. + * Presigned URL to the holder's signature image read from the chip (ICAO DG7), when the chip stores one. null for a JPKI read (chip_protocol: "jpki") and for skipped steps. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -8696,33 +8781,33 @@ public Optional signatureImage() { } /** - * The chip_data field. + * Personal data parsed from the ICAO chip's Data Groups. null for a JPKI read (chip_protocol: "jpki") and for skipped steps. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional chipData() { - return Json.optional(json, "chip_data", Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications.ChipData::fromJson)); + public Optional chipData() { + return Json.optional(json, "chip_data", Conv.model(SessionDecision.NfcVerifications.ChipData::fromJson)); } /** - * The authenticity field. + * ICAO passive authentication: SOD signature (sod_integrity) and Data Group hashes (dg_integrity). null for a JPKI read, which reports its proof under jpki. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional authenticity() { - return Json.optional(json, "authenticity", Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications.Authenticity::fromJson)); + public Optional authenticity() { + return Json.optional(json, "authenticity", Conv.model(SessionDecision.NfcVerifications.Authenticity::fromJson)); } /** - * The certificate_summary field. + * Summary of the ICAO Document Signer Certificate. null for a JPKI read (chip_protocol: "jpki") and for skipped steps. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional certificateSummary() { - return Json.optional(json, "certificate_summary", Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications.CertificateSummary::fromJson)); + public Optional certificateSummary() { + return Json.optional(json, "certificate_summary", Conv.model(SessionDecision.NfcVerifications.CertificateSummary::fromJson)); } /** @@ -8731,8 +8816,8 @@ public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications.Warnings::fromJson))); + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SessionDecision.NfcVerifications.Warnings::fromJson))); } @Override @@ -8764,8 +8849,8 @@ public Builder nodeId(String nodeId) { * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDecisionResponse.NfcVerifications.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.NfcVerifications.Status::of), status); + public Builder status(SessionDecision.NfcVerifications.Status status) { + return set("status", Conv.enumeration(SessionDecision.NfcVerifications.Status::of), status); } /** @@ -8784,58 +8869,78 @@ public Builder isNfcSkipped(boolean isNfcSkipped) { * @param skipReason the value, or {@code null} to send JSON null * @return this builder */ - public Builder skipReason(SessionsRetrieveDecisionResponse.NfcVerifications.@Nullable SkipReason skipReason) { - return setNullable("skip_reason", Conv.enumeration(SessionsRetrieveDecisionResponse.NfcVerifications.SkipReason::of), skipReason); + public Builder skipReason(SessionDecision.NfcVerifications.@Nullable SkipReason skipReason) { + return setNullable("skip_reason", Conv.enumeration(SessionDecision.NfcVerifications.SkipReason::of), skipReason); } /** - * The portrait_image field. + * How the NFC step reads the chip. icao: ICAO 9303 ePassport or eID chip, opened with the access key from the MRZ. jpki: Japan My Number Card, where the card signs a one-time challenge after the holder enters the card PIN in a native SDK; the result is under jpki. Also set on skipped steps, so you can tell a skipped My Number Card read apart. * - * @param portraitImage the value + * @param chipProtocol the value * @return this builder */ - public Builder portraitImage(String portraitImage) { - return set("portrait_image", Conv.STRING, portraitImage); + public Builder chipProtocol(SessionDecision.NfcVerifications.ChipProtocol chipProtocol) { + return set("chip_protocol", Conv.enumeration(SessionDecision.NfcVerifications.ChipProtocol::of), chipProtocol); + } + + /** + * What a Japan My Number Card read through JPKI proved. Present when chip_protocol is jpki and the card was read. null for ICAO chips, for skipped steps, and when the NFC step closed without any card read (that case raises JPKI_SIGNATURE_INVALID). Never carries the certificates, their serial numbers, the signatures or the 12-digit My Number. + * + * @param jpki the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder jpki(SessionDecision.NfcVerifications.@Nullable Jpki jpki) { + return setNullable("jpki", Conv.model(SessionDecision.NfcVerifications.Jpki::fromJson), jpki); + } + + /** + * Presigned URL to the portrait read from the chip (ICAO DG2). null for a JPKI read (chip_protocol: "jpki") and for skipped steps. + * + * @param portraitImage the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder portraitImage(@Nullable String portraitImage) { + return setNullable("portrait_image", Conv.STRING, portraitImage); } /** - * The signature_image field. + * Presigned URL to the holder's signature image read from the chip (ICAO DG7), when the chip stores one. null for a JPKI read (chip_protocol: "jpki") and for skipped steps. * - * @param signatureImage the value + * @param signatureImage the value, or {@code null} to send JSON null * @return this builder */ - public Builder signatureImage(String signatureImage) { - return set("signature_image", Conv.STRING, signatureImage); + public Builder signatureImage(@Nullable String signatureImage) { + return setNullable("signature_image", Conv.STRING, signatureImage); } /** - * The chip_data field. + * Personal data parsed from the ICAO chip's Data Groups. null for a JPKI read (chip_protocol: "jpki") and for skipped steps. * - * @param chipData the value + * @param chipData the value, or {@code null} to send JSON null * @return this builder */ - public Builder chipData(SessionsRetrieveDecisionResponse.NfcVerifications.ChipData chipData) { - return set("chip_data", Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications.ChipData::fromJson), chipData); + public Builder chipData(SessionDecision.NfcVerifications.@Nullable ChipData chipData) { + return setNullable("chip_data", Conv.model(SessionDecision.NfcVerifications.ChipData::fromJson), chipData); } /** - * The authenticity field. + * ICAO passive authentication: SOD signature (sod_integrity) and Data Group hashes (dg_integrity). null for a JPKI read, which reports its proof under jpki. * - * @param authenticity the value + * @param authenticity the value, or {@code null} to send JSON null * @return this builder */ - public Builder authenticity(SessionsRetrieveDecisionResponse.NfcVerifications.Authenticity authenticity) { - return set("authenticity", Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications.Authenticity::fromJson), authenticity); + public Builder authenticity(SessionDecision.NfcVerifications.@Nullable Authenticity authenticity) { + return setNullable("authenticity", Conv.model(SessionDecision.NfcVerifications.Authenticity::fromJson), authenticity); } /** - * The certificate_summary field. + * Summary of the ICAO Document Signer Certificate. null for a JPKI read (chip_protocol: "jpki") and for skipped steps. * - * @param certificateSummary the value + * @param certificateSummary the value, or {@code null} to send JSON null * @return this builder */ - public Builder certificateSummary(SessionsRetrieveDecisionResponse.NfcVerifications.CertificateSummary certificateSummary) { - return set("certificate_summary", Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications.CertificateSummary::fromJson), certificateSummary); + public Builder certificateSummary(SessionDecision.NfcVerifications.@Nullable CertificateSummary certificateSummary) { + return setNullable("certificate_summary", Conv.model(SessionDecision.NfcVerifications.CertificateSummary::fromJson), certificateSummary); } /** @@ -8844,8 +8949,8 @@ public Builder certificateSummary(SessionsRetrieveDecisionResponse.NfcVerificati * @param warnings the value * @return this builder */ - public Builder warnings(List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.NfcVerifications.Warnings::fromJson)), warnings); + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SessionDecision.NfcVerifications.Warnings::fromJson)), warnings); } /** @@ -8939,7 +9044,10 @@ public static final class SkipReason extends StringEnum { /** The value {@code "MRZ_KEY_UNAVAILABLE"}. */ public static final SkipReason MRZ_KEY_UNAVAILABLE = new SkipReason("MRZ_KEY_UNAVAILABLE"); - private static final List KNOWN_VALUES = List.of(USER_SKIPPED, DOCUMENT_WITHOUT_CHIP, CHIP_CERTIFICATE_UNAVAILABLE, DEVICE_WITHOUT_NFC, INTEGRATION_WITHOUT_NFC_ACCESS, MRZ_KEY_UNAVAILABLE); + /** The value {@code "JPKI_UNAVAILABLE"}. */ + public static final SkipReason JPKI_UNAVAILABLE = new SkipReason("JPKI_UNAVAILABLE"); + + private static final List KNOWN_VALUES = List.of(USER_SKIPPED, DOCUMENT_WITHOUT_CHIP, CHIP_CERTIFICATE_UNAVAILABLE, DEVICE_WITHOUT_NFC, INTEGRATION_WITHOUT_NFC_ACCESS, MRZ_KEY_UNAVAILABLE, JPKI_UNAVAILABLE); private SkipReason(String value) { super(value); @@ -8970,22 +9078,68 @@ public boolean isKnown() { } } - public static final class ChipData extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("document_type", "issuing_country", "document_number", "expiration_date", "first_name", "last_name", "birth_date", "gender", "nationality", "address", "place_of_birth"); + /** + * How the NFC step reads the chip. icao: ICAO 9303 ePassport or eID chip, opened with the access key from the MRZ. jpki: Japan My Number Card, where the card signs a one-time challenge after the holder enters the card PIN in a native SDK; the result is under jpki. Also set on skipped steps, so you can tell a skipped My Number Card read apart. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ChipProtocol extends StringEnum { + /** The value {@code "icao"}. */ + public static final ChipProtocol ICAO = new ChipProtocol("icao"); - private ChipData(ObjectNode json, Map> files) { + /** The value {@code "jpki"}. */ + public static final ChipProtocol JPKI = new ChipProtocol("jpki"); + + private static final List KNOWN_VALUES = List.of(ICAO, JPKI); + + private ChipProtocol(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ChipProtocol of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ChipProtocol::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * What a Japan My Number Card read through JPKI proved. Present when chip_protocol is jpki and the card was read. null for ICAO chips, for skipped steps, and when the NFC step closed without any card read (that case raises JPKI_SIGNATURE_INVALID). Never carries the certificates, their serial numbers, the signatures or the 12-digit My Number. + */ + public static final class Jpki extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("certificate_types", "chain_valid", "signature_valid", "revocation_status", "ca_generation", "certificate_expires_at", "certificates", "attributes", "attributes_match_ocr", "mismatched_attributes"); + + private Jpki(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code ChipData} from JSON. Unknown fields are kept and readable + * Reads a {@code Jpki} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static ChipData fromJson(JsonNode json) { - return new ChipData(Json.copyObject(json), Map.of()); + public static Jpki fromJson(JsonNode json) { + return new Jpki(Json.copyObject(json), Map.of()); } /** @@ -9007,113 +9161,103 @@ public Builder toBuilder() { } /** - * The document_type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentType() { - return Json.optional(json, "document_type", Conv.STRING); - } - - /** - * The issuing_country field. + * The card certificates read. user_authentication (4-digit PIN) is always read; signature (signature PIN) is read when the NFC node's jpki_request_attributes setting is on. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional issuingCountry() { - return Json.optional(json, "issuing_country", Conv.STRING); + public Optional> certificateTypes() { + return Json.optional(json, "certificate_types", Conv.list(Conv.enumeration(SessionDecision.NfcVerifications.Jpki.CertificateTypes::of))); } /** - * The document_number field. + * true when every certificate read chains to the JPKI certification authority, is inside its validity period and is a My Number Card certificate of its type. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional documentNumber() { - return Json.optional(json, "document_number", Conv.STRING); + public Optional chainValid() { + return Json.optional(json, "chain_valid", Conv.BOOLEAN); } /** - * The expiration_date field. + * true when every signature the card made over the one-time challenge verifies. The card only signs after its PIN is entered, so this proves possession of the card and knowledge of the PIN. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional expirationDate() { - return Json.optional(json, "expiration_date", Conv.DATE); + public Optional signatureValid() { + return Json.optional(json, "signature_valid", Conv.BOOLEAN); } /** - * The first_name field. + * good when the revocation check confirmed every certificate is valid, revoked when any certificate is revoked (for example a card reported lost), unchecked when no revocation answer was available. unchecked is never treated as good: the NFC node's jpki_revocation_unchecked_action decides the outcome (default REVIEW). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional firstName() { - return Json.optional(json, "first_name", Conv.STRING); + public Optional revocationStatus() { + return Json.optional(json, "revocation_status", Conv.enumeration(SessionDecision.NfcVerifications.Jpki.RevocationStatus::of)); } /** - * The last_name field. + * Generation of the JPKI certification authority that issued the certificates, for example 2023. null when no certificate chains to one. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional lastName() { - return Json.optional(json, "last_name", Conv.STRING); + public Optional caGeneration() { + return Json.optional(json, "ca_generation", Conv.STRING); } /** - * The birth_date field. + * The earliest expiry among the certificates read. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional birthDate() { - return Json.optional(json, "birth_date", Conv.DATE); + public Optional certificateExpiresAt() { + return Json.optional(json, "certificate_expires_at", Conv.DATE_TIME); } /** - * The gender field. + * Per-certificate results, one entry per certificate read. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional gender() { - return Json.optional(json, "gender", Conv.STRING); + public Optional> certificates() { + return Json.optional(json, "certificates", Conv.list(Conv.model(SessionDecision.NfcVerifications.Jpki.Certificates::fromJson))); } /** - * The nationality field. + * The holder's data from the signature certificate, once its chain is valid. null when the signature certificate was not requested or did not chain to the JPKI certification authority. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional nationality() { - return Json.optional(json, "nationality", Conv.STRING); + public Optional attributes() { + return Json.optional(json, "attributes", Conv.model(SessionDecision.NfcVerifications.Jpki.Attributes::fromJson)); } /** - * The address field. + * true when the attributes match the data read from the photo of the same card, false when any attribute differs (JPKI_AND_OCR_DATA_NOT_SAME), null when nothing could be compared, including whenever attributes is null. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional address() { - return Json.optional(json, "address", Conv.STRING); + public Optional attributesMatchOcr() { + return Json.optional(json, "attributes_match_ocr", Conv.BOOLEAN); } /** - * The place_of_birth field. + * Names of the attributes that differ from the photo data, never their values. Empty when everything compared matches. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional placeOfBirth() { - return Json.optional(json, "place_of_birth", Conv.STRING); + public Optional> mismatchedAttributes() { + return Json.optional(json, "mismatched_attributes", Conv.list(Conv.enumeration(SessionDecision.NfcVerifications.Jpki.MismatchedAttributes::of))); } @Override @@ -9121,131 +9265,121 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link ChipData}. */ + /** Builds a {@link Jpki}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(ChipData from) { + private Builder(Jpki from) { super(from); } /** - * The document_type field. - * - * @param documentType the value - * @return this builder - */ - public Builder documentType(String documentType) { - return set("document_type", Conv.STRING, documentType); - } - - /** - * The issuing_country field. + * The card certificates read. user_authentication (4-digit PIN) is always read; signature (signature PIN) is read when the NFC node's jpki_request_attributes setting is on. * - * @param issuingCountry the value + * @param certificateTypes the value * @return this builder */ - public Builder issuingCountry(String issuingCountry) { - return set("issuing_country", Conv.STRING, issuingCountry); + public Builder certificateTypes(List certificateTypes) { + return set("certificate_types", Conv.list(Conv.enumeration(SessionDecision.NfcVerifications.Jpki.CertificateTypes::of)), certificateTypes); } /** - * The document_number field. + * true when every certificate read chains to the JPKI certification authority, is inside its validity period and is a My Number Card certificate of its type. * - * @param documentNumber the value + * @param chainValid the value * @return this builder */ - public Builder documentNumber(String documentNumber) { - return set("document_number", Conv.STRING, documentNumber); + public Builder chainValid(boolean chainValid) { + return set("chain_valid", Conv.BOOLEAN, chainValid); } /** - * The expiration_date field. + * true when every signature the card made over the one-time challenge verifies. The card only signs after its PIN is entered, so this proves possession of the card and knowledge of the PIN. * - * @param expirationDate the value + * @param signatureValid the value * @return this builder */ - public Builder expirationDate(LocalDate expirationDate) { - return set("expiration_date", Conv.DATE, expirationDate); + public Builder signatureValid(boolean signatureValid) { + return set("signature_valid", Conv.BOOLEAN, signatureValid); } /** - * The first_name field. + * good when the revocation check confirmed every certificate is valid, revoked when any certificate is revoked (for example a card reported lost), unchecked when no revocation answer was available. unchecked is never treated as good: the NFC node's jpki_revocation_unchecked_action decides the outcome (default REVIEW). * - * @param firstName the value + * @param revocationStatus the value * @return this builder */ - public Builder firstName(String firstName) { - return set("first_name", Conv.STRING, firstName); + public Builder revocationStatus(SessionDecision.NfcVerifications.Jpki.RevocationStatus revocationStatus) { + return set("revocation_status", Conv.enumeration(SessionDecision.NfcVerifications.Jpki.RevocationStatus::of), revocationStatus); } /** - * The last_name field. + * Generation of the JPKI certification authority that issued the certificates, for example 2023. null when no certificate chains to one. * - * @param lastName the value + * @param caGeneration the value, or {@code null} to send JSON null * @return this builder */ - public Builder lastName(String lastName) { - return set("last_name", Conv.STRING, lastName); + public Builder caGeneration(@Nullable String caGeneration) { + return setNullable("ca_generation", Conv.STRING, caGeneration); } /** - * The birth_date field. + * The earliest expiry among the certificates read. * - * @param birthDate the value + * @param certificateExpiresAt the value, or {@code null} to send JSON null * @return this builder */ - public Builder birthDate(LocalDate birthDate) { - return set("birth_date", Conv.DATE, birthDate); + public Builder certificateExpiresAt(@Nullable OffsetDateTime certificateExpiresAt) { + return setNullable("certificate_expires_at", Conv.DATE_TIME, certificateExpiresAt); } /** - * The gender field. + * Per-certificate results, one entry per certificate read. * - * @param gender the value + * @param certificates the value * @return this builder */ - public Builder gender(String gender) { - return set("gender", Conv.STRING, gender); + public Builder certificates(List certificates) { + return set("certificates", Conv.list(Conv.model(SessionDecision.NfcVerifications.Jpki.Certificates::fromJson)), certificates); } /** - * The nationality field. + * The holder's data from the signature certificate, once its chain is valid. null when the signature certificate was not requested or did not chain to the JPKI certification authority. * - * @param nationality the value + * @param attributes the value, or {@code null} to send JSON null * @return this builder */ - public Builder nationality(String nationality) { - return set("nationality", Conv.STRING, nationality); + public Builder attributes(SessionDecision.NfcVerifications.Jpki.@Nullable Attributes attributes) { + return setNullable("attributes", Conv.model(SessionDecision.NfcVerifications.Jpki.Attributes::fromJson), attributes); } /** - * The address field. + * true when the attributes match the data read from the photo of the same card, false when any attribute differs (JPKI_AND_OCR_DATA_NOT_SAME), null when nothing could be compared, including whenever attributes is null. * - * @param address the value + * @param attributesMatchOcr the value, or {@code null} to send JSON null * @return this builder */ - public Builder address(String address) { - return set("address", Conv.STRING, address); + public Builder attributesMatchOcr(@Nullable Boolean attributesMatchOcr) { + return setNullable("attributes_match_ocr", Conv.BOOLEAN, attributesMatchOcr); } /** - * The place_of_birth field. + * Names of the attributes that differ from the photo data, never their values. Empty when everything compared matches. * - * @param placeOfBirth the value + * @param mismatchedAttributes the value * @return this builder */ - public Builder placeOfBirth(String placeOfBirth) { - return set("place_of_birth", Conv.STRING, placeOfBirth); + public Builder mismatchedAttributes(List mismatchedAttributes) { + return set("mismatched_attributes", Conv.list(Conv.enumeration(SessionDecision.NfcVerifications.Jpki.MismatchedAttributes::of)), mismatchedAttributes); } /** * Builds the value. * - * @return a new immutable {@code ChipData} + * @return a new immutable {@code Jpki} */ - public ChipData build() { - return new ChipData(buildJson(), buildFiles()); + public Jpki build() { + return new Jpki(buildJson(), buildFiles()); } @Override @@ -9253,536 +9387,652 @@ protected Builder self() { return this; } } - } - - public static final class Authenticity extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("sod_integrity", "dg_integrity"); - - private Authenticity(ObjectNode json, Map> files) { - super(json, files); - } /** - * Reads a {@code Authenticity} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. * - * @param json a JSON object - * @return the parsed value + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ - public static Authenticity fromJson(JsonNode json) { - return new Authenticity(Json.copyObject(json), Map.of()); + public static final class CertificateTypes extends StringEnum { + /** The value {@code "user_authentication"}. */ + public static final CertificateTypes USER_AUTHENTICATION = new CertificateTypes("user_authentication"); + + /** The value {@code "signature"}. */ + public static final CertificateTypes SIGNATURE = new CertificateTypes("signature"); + + private static final List KNOWN_VALUES = List.of(USER_AUTHENTICATION, SIGNATURE); + + private CertificateTypes(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static CertificateTypes of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, CertificateTypes::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } } /** - * Returns an empty builder. + * good when the revocation check confirmed every certificate is valid, revoked when any certificate is revoked (for example a card reported lost), unchecked when no revocation answer was available. unchecked is never treated as good: the NFC node's jpki_revocation_unchecked_action decides the outcome (default REVIEW). * - * @return a new builder + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ - public static Builder builder() { - return new Builder(); - } + public static final class RevocationStatus extends StringEnum { + /** The value {@code "good"}. */ + public static final RevocationStatus GOOD = new RevocationStatus("good"); - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The sod_integrity field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sodIntegrity() { - return Json.optional(json, "sod_integrity", Conv.BOOLEAN); - } - - /** - * The dg_integrity field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional dgIntegrity() { - return Json.optional(json, "dg_integrity", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** The value {@code "revoked"}. */ + public static final RevocationStatus REVOKED = new RevocationStatus("revoked"); - /** Builds a {@link Authenticity}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** The value {@code "unchecked"}. */ + public static final RevocationStatus UNCHECKED = new RevocationStatus("unchecked"); - private Builder(Authenticity from) { - super(from); - } + private static final List KNOWN_VALUES = List.of(GOOD, REVOKED, UNCHECKED); - /** - * The sod_integrity field. - * - * @param sodIntegrity the value - * @return this builder - */ - public Builder sodIntegrity(boolean sodIntegrity) { - return set("sod_integrity", Conv.BOOLEAN, sodIntegrity); + private RevocationStatus(String value) { + super(value); } /** - * The dg_integrity field. + * Returns the constant for a known value, or a new instance for any other value. * - * @param dgIntegrity the value - * @return this builder + * @param value the string value + * @return the enum value */ - public Builder dgIntegrity(boolean dgIntegrity) { - return set("dg_integrity", Conv.BOOLEAN, dgIntegrity); + public static RevocationStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RevocationStatus::new); } /** - * Builds the value. + * Returns the values known to this SDK version. * - * @return a new immutable {@code Authenticity} + * @return the known values */ - public Authenticity build() { - return new Authenticity(buildJson(), buildFiles()); + public static List knownValues() { + return KNOWN_VALUES; } @Override - protected Builder self() { - return this; + public boolean isKnown() { + return KNOWN_VALUES.contains(this); } } - } - - public static final class CertificateSummary extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("issuer", "subject", "serial_number", "not_valid_after", "not_valid_before"); - - private CertificateSummary(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code CertificateSummary} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static CertificateSummary fromJson(JsonNode json) { - return new CertificateSummary(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The issuer field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional issuer() { - return Json.optional(json, "issuer", Conv.STRING); - } - - /** - * The subject field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional subject() { - return Json.optional(json, "subject", Conv.STRING); - } /** - * The serial_number field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * What one certificate read from the card proved. Never the certificate itself or its serial number. */ - public Optional serialNumber() { - return Json.optional(json, "serial_number", Conv.STRING); - } + public static final class Certificates extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "chain_valid", "signature_valid", "ca_generation", "not_valid_before", "not_valid_after", "failure"); - /** - * The not_valid_after field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional notValidAfter() { - return Json.optional(json, "not_valid_after", Conv.DATE_TIME); - } + private Certificates(ObjectNode json, Map> files) { + super(json, files); + } - /** - * The not_valid_before field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional notValidBefore() { - return Json.optional(json, "not_valid_before", Conv.DATE_TIME); - } + /** + * Reads a {@code Certificates} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Certificates fromJson(JsonNode json) { + return new Certificates(Json.copyObject(json), Map.of()); + } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** Builds a {@link CertificateSummary}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - private Builder(CertificateSummary from) { - super(from); + /** + * Which card certificate this entry describes. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional type() { + return Json.optional(json, "type", Conv.enumeration(SessionDecision.NfcVerifications.Jpki.Certificates.Type::of)); } /** - * The issuer field. + * true when the certificate chains to the JPKI certification authority of its type, is inside its validity period and is a My Number Card certificate of that type. * - * @param issuer the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder issuer(String issuer) { - return set("issuer", Conv.STRING, issuer); + public Optional chainValid() { + return Json.optional(json, "chain_valid", Conv.BOOLEAN); } /** - * The subject field. + * true when the card's signature over the one-time challenge verifies against this certificate. * - * @param subject the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder subject(String subject) { - return set("subject", Conv.STRING, subject); + public Optional signatureValid() { + return Json.optional(json, "signature_valid", Conv.BOOLEAN); } /** - * The serial_number field. + * Generation of the JPKI certification authority that issued the certificate, for example 2023. null when no trusted issuer was found. * - * @param serialNumber the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder serialNumber(String serialNumber) { - return set("serial_number", Conv.STRING, serialNumber); + public Optional caGeneration() { + return Json.optional(json, "ca_generation", Conv.STRING); } /** - * The not_valid_after field. + * Start of the certificate's validity period. null when the certificate could not be read. * - * @param notValidAfter the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder notValidAfter(OffsetDateTime notValidAfter) { - return set("not_valid_after", Conv.DATE_TIME, notValidAfter); + public Optional notValidBefore() { + return Json.optional(json, "not_valid_before", Conv.DATE_TIME); } /** - * The not_valid_before field. + * End of the certificate's validity period. null when the certificate could not be read. * - * @param notValidBefore the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder notValidBefore(OffsetDateTime notValidBefore) { - return set("not_valid_before", Conv.DATE_TIME, notValidBefore); + public Optional notValidAfter() { + return Json.optional(json, "not_valid_after", Conv.DATE_TIME); } /** - * Builds the value. + * Why chain_valid is false: unreadable_certificate, untrusted_issuer (not issued by the JPKI certification authority of its type), expired, not_yet_valid, wrong_policy (not a My Number Card certificate of that type) or key_usage (the key may not sign). null when the chain is valid. * - * @return a new immutable {@code CertificateSummary} + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public CertificateSummary build() { - return new CertificateSummary(buildJson(), buildFiles()); + public Optional failure() { + return Json.optional(json, "failure", Conv.enumeration(SessionDecision.NfcVerifications.Jpki.Certificates.Failure::of)); } @Override - protected Builder self() { - return this; + protected Set knownFields() { + return KNOWN_FIELDS; } - } - } - - public static final class Warnings extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); - private Warnings(ObjectNode json, Map> files) { - super(json, files); - } + /** Builds a {@link Certificates}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** - * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Warnings fromJson(JsonNode json) { - return new Warnings(Json.copyObject(json), Map.of()); - } + private Builder(Certificates from) { + super(from); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * Which card certificate this entry describes. + * + * @param type the value + * @return this builder + */ + public Builder type(SessionDecision.NfcVerifications.Jpki.Certificates.Type type) { + return set("type", Conv.enumeration(SessionDecision.NfcVerifications.Jpki.Certificates.Type::of), type); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * true when the certificate chains to the JPKI certification authority of its type, is inside its validity period and is a My Number Card certificate of that type. + * + * @param chainValid the value + * @return this builder + */ + public Builder chainValid(boolean chainValid) { + return set("chain_valid", Conv.BOOLEAN, chainValid); + } - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.NfcVerifications.Warnings.Feature::of)); - } + /** + * true when the card's signature over the one-time challenge verifies against this certificate. + * + * @param signatureValid the value + * @return this builder + */ + public Builder signatureValid(boolean signatureValid) { + return set("signature_valid", Conv.BOOLEAN, signatureValid); + } - /** - * The risk field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional risk() { - return Json.optional(json, "risk", Conv.STRING); - } + /** + * Generation of the JPKI certification authority that issued the certificate, for example 2023. null when no trusted issuer was found. + * + * @param caGeneration the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder caGeneration(@Nullable String caGeneration) { + return setNullable("ca_generation", Conv.STRING, caGeneration); + } - /** - * The additional_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> additionalData() { - return Json.optional(json, "additional_data", Conv.OBJECT); - } + /** + * Start of the certificate's validity period. null when the certificate could not be read. + * + * @param notValidBefore the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder notValidBefore(@Nullable OffsetDateTime notValidBefore) { + return setNullable("not_valid_before", Conv.DATE_TIME, notValidBefore); + } - /** - * The log_type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional logType() { - return Json.optional(json, "log_type", Conv.STRING); - } + /** + * End of the certificate's validity period. null when the certificate could not be read. + * + * @param notValidAfter the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder notValidAfter(@Nullable OffsetDateTime notValidAfter) { + return setNullable("not_valid_after", Conv.DATE_TIME, notValidAfter); + } - /** - * The short_description field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional shortDescription() { - return Json.optional(json, "short_description", Conv.STRING); - } + /** + * Why chain_valid is false: unreadable_certificate, untrusted_issuer (not issued by the JPKI certification authority of its type), expired, not_yet_valid, wrong_policy (not a My Number Card certificate of that type) or key_usage (the key may not sign). null when the chain is valid. + * + * @param failure the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder failure(SessionDecision.NfcVerifications.Jpki.Certificates.@Nullable Failure failure) { + return setNullable("failure", Conv.enumeration(SessionDecision.NfcVerifications.Jpki.Certificates.Failure::of), failure); + } - /** - * The long_description field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional longDescription() { - return Json.optional(json, "long_description", Conv.STRING); - } + /** + * Builds the value. + * + * @return a new immutable {@code Certificates} + */ + public Certificates build() { + return new Certificates(buildJson(), buildFiles()); + } - /** - * The workflow graph node ID that generated this warning - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } + @Override + protected Builder self() { + return this; + } + } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** + * Which card certificate this entry describes. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "user_authentication"}. */ + public static final Type USER_AUTHENTICATION = new Type("user_authentication"); - /** Builds a {@link Warnings}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** The value {@code "signature"}. */ + public static final Type SIGNATURE = new Type("signature"); - private Builder(Warnings from) { - super(from); + private static final List KNOWN_VALUES = List.of(USER_AUTHENTICATION, SIGNATURE); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } } /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * Why chain_valid is false: unreadable_certificate, untrusted_issuer (not issued by the JPKI certification authority of its type), expired, not_yet_valid, wrong_policy (not a My Number Card certificate of that type) or key_usage (the key may not sign). null when the chain is valid. * - * @param feature the value - * @return this builder + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ - public Builder feature(SessionsRetrieveDecisionResponse.NfcVerifications.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.NfcVerifications.Warnings.Feature::of), feature); + public static final class Failure extends StringEnum { + /** The value {@code "unreadable_certificate"}. */ + public static final Failure UNREADABLE_CERTIFICATE = new Failure("unreadable_certificate"); + + /** The value {@code "untrusted_issuer"}. */ + public static final Failure UNTRUSTED_ISSUER = new Failure("untrusted_issuer"); + + /** The value {@code "expired"}. */ + public static final Failure EXPIRED = new Failure("expired"); + + /** The value {@code "not_yet_valid"}. */ + public static final Failure NOT_YET_VALID = new Failure("not_yet_valid"); + + /** The value {@code "wrong_policy"}. */ + public static final Failure WRONG_POLICY = new Failure("wrong_policy"); + + /** The value {@code "key_usage"}. */ + public static final Failure KEY_USAGE = new Failure("key_usage"); + + private static final List KNOWN_VALUES = List.of(UNREADABLE_CERTIFICATE, UNTRUSTED_ISSUER, EXPIRED, NOT_YET_VALID, WRONG_POLICY, KEY_USAGE); + + private Failure(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Failure of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Failure::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * The holder's data from the signature certificate, once its chain is valid. null when the signature certificate was not requested or did not chain to the JPKI certification authority. + */ + public static final class Attributes extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("full_name", "date_of_birth", "gender", "address", "name_substitute_characters", "address_substitute_characters"); + + private Attributes(ObjectNode json, Map> files) { + super(json, files); } /** - * The risk field. + * Reads a {@code Attributes} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. * - * @param risk the value - * @return this builder + * @param json a JSON object + * @return the parsed value */ - public Builder risk(String risk) { - return set("risk", Conv.STRING, risk); + public static Attributes fromJson(JsonNode json) { + return new Attributes(Json.copyObject(json), Map.of()); } /** - * The additional_data field. + * Returns an empty builder. * - * @param additionalData the value, or {@code null} to send JSON null - * @return this builder + * @return a new builder */ - public Builder additionalData(@Nullable Map additionalData) { - return setAny("additional_data", additionalData); + public static Builder builder() { + return new Builder(); } /** - * The log_type field. + * Returns a builder holding a copy of every field of this value. * - * @param logType the value - * @return this builder + * @return a new builder */ - public Builder logType(String logType) { - return set("log_type", Conv.STRING, logType); + public Builder toBuilder() { + return new Builder(this); } /** - * The short_description field. + * Name as registered, usually in Japanese script. * - * @param shortDescription the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder shortDescription(String shortDescription) { - return set("short_description", Conv.STRING, shortDescription); + public Optional fullName() { + return Json.optional(json, "full_name", Conv.STRING); } /** - * The long_description field. + * Date of birth, YYYY-MM-DD. null when the registered date is incomplete. * - * @param longDescription the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder longDescription(String longDescription) { - return set("long_description", Conv.STRING, longDescription); + public Optional dateOfBirth() { + return Json.optional(json, "date_of_birth", Conv.DATE); } /** - * The workflow graph node ID that generated this warning + * M, F, or null when not registered. * - * @param nodeId the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); + public Optional gender() { + return Json.optional(json, "gender", Conv.enumeration(SessionDecision.NfcVerifications.Jpki.Attributes.Gender::of)); } /** - * Builds the value. + * Registered address. * - * @return a new immutable {@code Warnings} + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Warnings build() { - return new Warnings(buildJson(), buildFiles()); + public Optional address() { + return Json.optional(json, "address", Conv.STRING); + } + + /** + * Position string marking the characters of full_name that were replaced by a substitute because the original character is outside the standard character set. null when none were replaced. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameSubstituteCharacters() { + return Json.optional(json, "name_substitute_characters", Conv.STRING); + } + + /** + * Position string marking substitute characters in address, as for name_substitute_characters. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional addressSubstituteCharacters() { + return Json.optional(json, "address_substitute_characters", Conv.STRING); } @Override - protected Builder self() { - return this; + protected Set knownFields() { + return KNOWN_FIELDS; } - } - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Feature extends StringEnum { - /** The value {@code "ID_VERIFICATION"}. */ - public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); + /** Builds a {@link Attributes}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** The value {@code "NFC"}. */ - public static final Feature NFC = new Feature("NFC"); + private Builder(Attributes from) { + super(from); + } - /** The value {@code "LIVENESS"}. */ - public static final Feature LIVENESS = new Feature("LIVENESS"); + /** + * Name as registered, usually in Japanese script. + * + * @param fullName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fullName(@Nullable String fullName) { + return setNullable("full_name", Conv.STRING, fullName); + } - /** The value {@code "FACEMATCH"}. */ - public static final Feature FACEMATCH = new Feature("FACEMATCH"); + /** + * Date of birth, YYYY-MM-DD. null when the registered date is incomplete. + * + * @param dateOfBirth the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dateOfBirth(@Nullable LocalDate dateOfBirth) { + return setNullable("date_of_birth", Conv.DATE, dateOfBirth); + } - /** The value {@code "PROOF_OF_ADDRESS"}. */ - public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); + /** + * M, F, or null when not registered. + * + * @param gender the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder gender(SessionDecision.NfcVerifications.Jpki.Attributes.@Nullable Gender gender) { + return setNullable("gender", Conv.enumeration(SessionDecision.NfcVerifications.Jpki.Attributes.Gender::of), gender); + } - /** The value {@code "PHONE"}. */ - public static final Feature PHONE = new Feature("PHONE"); + /** + * Registered address. + * + * @param address the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder address(@Nullable String address) { + return setNullable("address", Conv.STRING, address); + } - /** The value {@code "EMAIL"}. */ - public static final Feature EMAIL = new Feature("EMAIL"); + /** + * Position string marking the characters of full_name that were replaced by a substitute because the original character is outside the standard character set. null when none were replaced. + * + * @param nameSubstituteCharacters the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nameSubstituteCharacters(@Nullable String nameSubstituteCharacters) { + return setNullable("name_substitute_characters", Conv.STRING, nameSubstituteCharacters); + } - /** The value {@code "AML"}. */ - public static final Feature AML = new Feature("AML"); + /** + * Position string marking substitute characters in address, as for name_substitute_characters. + * + * @param addressSubstituteCharacters the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder addressSubstituteCharacters(@Nullable String addressSubstituteCharacters) { + return setNullable("address_substitute_characters", Conv.STRING, addressSubstituteCharacters); + } - /** The value {@code "LOCATION"}. */ - public static final Feature LOCATION = new Feature("LOCATION"); + /** + * Builds the value. + * + * @return a new immutable {@code Attributes} + */ + public Attributes build() { + return new Attributes(buildJson(), buildFiles()); + } - /** The value {@code "DATABASE_VALIDATION"}. */ - public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); + @Override + protected Builder self() { + return this; + } + } - /** The value {@code "QUESTIONNAIRE"}. */ - public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); + /** + * M, F, or null when not registered. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Gender extends StringEnum { + /** The value {@code "M"}. */ + public static final Gender M = new Gender("M"); - /** The value {@code "DOCUMENT_AI"}. */ - public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); + /** The value {@code "F"}. */ + public static final Gender F = new Gender("F"); - /** The value {@code "KYB_REGISTRY"}. */ - public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); + private static final List KNOWN_VALUES = List.of(M, F); - /** The value {@code "KYB_DOCUMENTS"}. */ - public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); + private Gender(String value) { + super(value); + } - /** The value {@code "KYB_KEY_PEOPLE"}. */ - public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Gender of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Gender::new); + } - private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } - private Feature(String value) { + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class MismatchedAttributes extends StringEnum { + /** The value {@code "full_name"}. */ + public static final MismatchedAttributes FULL_NAME = new MismatchedAttributes("full_name"); + + /** The value {@code "date_of_birth"}. */ + public static final MismatchedAttributes DATE_OF_BIRTH = new MismatchedAttributes("date_of_birth"); + + /** The value {@code "gender"}. */ + public static final MismatchedAttributes GENDER = new MismatchedAttributes("gender"); + + /** The value {@code "address"}. */ + public static final MismatchedAttributes ADDRESS = new MismatchedAttributes("address"); + + private static final List KNOWN_VALUES = List.of(FULL_NAME, DATE_OF_BIRTH, GENDER, ADDRESS); + + private MismatchedAttributes(String value) { super(value); } @@ -9792,8 +10042,8 @@ private Feature(String value) { * @param value the string value * @return the enum value */ - public static Feature of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); + public static MismatchedAttributes of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, MismatchedAttributes::new); } /** @@ -9801,7 +10051,7 @@ public static Feature of(String value) { * * @return the known values */ - public static List knownValues() { + public static List knownValues() { return KNOWN_VALUES; } @@ -9811,361 +10061,314 @@ public boolean isKnown() { } } } - } - - /** One liveness report — the full report for a single liveness pass. */ - public static final class LivenessChecks extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "method", "score", "reference_image", "video_url", "age_estimation", "matches", "warnings", "face_quality", "face_luminance"); - - private LivenessChecks(ObjectNode json, Map> files) { - super(json, files); - } /** - * Reads a {@code LivenessChecks} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value + * Personal data parsed from the ICAO chip's Data Groups. null for a JPKI read (chip_protocol: "jpki") and for skipped steps. */ - public static LivenessChecks fromJson(JsonNode json) { - return new LivenessChecks(Json.copyObject(json), Map.of()); - } + public static final class ChipData extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("document_type", "issuing_country", "document_number", "expiration_date", "first_name", "last_name", "birth_date", "gender", "nationality", "address", "place_of_birth"); - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + private ChipData(ObjectNode json, Map> files) { + super(json, files); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * Reads a {@code ChipData} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ChipData fromJson(JsonNode json) { + return new ChipData(Json.copyObject(json), Map.of()); + } - /** - * Workflow graph node id of the liveness step that produced this report. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * Feature-level status of this liveness step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.LivenessChecks.Status::of)); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * Method used for liveness detection - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional method() { - return Json.optional(json, "method", Conv.STRING); - } - - /** - * Confidence score of the liveness check - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional score() { - return Json.optional(json, "score", Conv.DOUBLE); - } - - /** - * URL of the reference image used - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional referenceImage() { - return Json.optional(json, "reference_image", Conv.STRING); - } - - /** - * URL of the liveness check video - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional videoUrl() { - return Json.optional(json, "video_url", Conv.STRING); - } - - /** - * Estimated age of the person - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional ageEstimation() { - return Json.optional(json, "age_estimation", Conv.DOUBLE); - } - - /** - * Similar faces found by the duplicate-face search (top matches, blocklisted/allowlisted entries first). Each entry's source tells you where the matching face came from: session (another verification session), imported (a face uploaded against a vendor user), list_entry (an org-managed allow/block list face), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). session_id, session_number, status and api_service are null for non-session sources. imported matches still populate vendor_data, verification_date and — when the vendor user has a name on file — user_details (with full_name only) from the vendor-user record; retained_template matches populate vendor_user_id, vendor_data, biometric_template_id and verification_date, while their image and identity fields are null; list_entry matches return all of these as null. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> matches() { - return Json.optional(json, "matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.LivenessChecks.Matches::fromJson))); - } - - /** - * The warnings field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.LivenessChecks.Warnings::fromJson))); - } - - /** - * Face image quality score (0-100%). Only available for passive liveness. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional faceQuality() { - return Json.optional(json, "face_quality", Conv.DOUBLE); - } - - /** - * Face luminance/brightness score (0-100%). Only available for passive liveness. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional faceLuminance() { - return Json.optional(json, "face_luminance", Conv.DOUBLE); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link LivenessChecks}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(LivenessChecks from) { - super(from); - } - - /** - * Workflow graph node id of the liveness step that produced this report. - * - * @param nodeId the value - * @return this builder - */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); - } - - /** - * Feature-level status of this liveness step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - * @param status the value - * @return this builder - */ - public Builder status(SessionsRetrieveDecisionResponse.LivenessChecks.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.LivenessChecks.Status::of), status); - } + /** + * The document_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentType() { + return Json.optional(json, "document_type", Conv.STRING); + } /** - * Method used for liveness detection + * The issuing_country field. * - * @param method the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder method(String method) { - return set("method", Conv.STRING, method); + public Optional issuingCountry() { + return Json.optional(json, "issuing_country", Conv.STRING); } /** - * Confidence score of the liveness check + * The document_number field. * - * @param score the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder score(double score) { - return set("score", Conv.DOUBLE, score); + public Optional documentNumber() { + return Json.optional(json, "document_number", Conv.STRING); } /** - * URL of the reference image used + * The expiration_date field. * - * @param referenceImage the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder referenceImage(String referenceImage) { - return set("reference_image", Conv.STRING, referenceImage); + public Optional expirationDate() { + return Json.optional(json, "expiration_date", Conv.DATE); } /** - * URL of the liveness check video + * The first_name field. * - * @param videoUrl the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder videoUrl(String videoUrl) { - return set("video_url", Conv.STRING, videoUrl); + public Optional firstName() { + return Json.optional(json, "first_name", Conv.STRING); } /** - * Estimated age of the person + * The last_name field. * - * @param ageEstimation the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder ageEstimation(double ageEstimation) { - return set("age_estimation", Conv.DOUBLE, ageEstimation); + public Optional lastName() { + return Json.optional(json, "last_name", Conv.STRING); } /** - * Similar faces found by the duplicate-face search (top matches, blocklisted/allowlisted entries first). Each entry's source tells you where the matching face came from: session (another verification session), imported (a face uploaded against a vendor user), list_entry (an org-managed allow/block list face), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). session_id, session_number, status and api_service are null for non-session sources. imported matches still populate vendor_data, verification_date and — when the vendor user has a name on file — user_details (with full_name only) from the vendor-user record; retained_template matches populate vendor_user_id, vendor_data, biometric_template_id and verification_date, while their image and identity fields are null; list_entry matches return all of these as null. + * The birth_date field. * - * @param matches the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder matches(List matches) { - return set("matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.LivenessChecks.Matches::fromJson)), matches); + public Optional birthDate() { + return Json.optional(json, "birth_date", Conv.DATE); } /** - * The warnings field. + * The gender field. * - * @param warnings the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder warnings(List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.LivenessChecks.Warnings::fromJson)), warnings); + public Optional gender() { + return Json.optional(json, "gender", Conv.STRING); } /** - * Face image quality score (0-100%). Only available for passive liveness. + * The nationality field. * - * @param faceQuality the value, or {@code null} to send JSON null - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder faceQuality(@Nullable Double faceQuality) { - return setNullable("face_quality", Conv.DOUBLE, faceQuality); + public Optional nationality() { + return Json.optional(json, "nationality", Conv.STRING); } /** - * Face luminance/brightness score (0-100%). Only available for passive liveness. + * The address field. * - * @param faceLuminance the value, or {@code null} to send JSON null - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder faceLuminance(@Nullable Double faceLuminance) { - return setNullable("face_luminance", Conv.DOUBLE, faceLuminance); + public Optional address() { + return Json.optional(json, "address", Conv.STRING); } /** - * Builds the value. + * The place_of_birth field. * - * @return a new immutable {@code LivenessChecks} + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public LivenessChecks build() { - return new LivenessChecks(buildJson(), buildFiles()); + public Optional placeOfBirth() { + return Json.optional(json, "place_of_birth", Conv.STRING); } @Override - protected Builder self() { - return this; + protected Set knownFields() { + return KNOWN_FIELDS; } - } - /** - * Feature-level status of this liveness step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Not Finished"}. */ - public static final Status NOT_FINISHED = new Status("Not Finished"); + /** Builds a {@link ChipData}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); + private Builder(ChipData from) { + super(from); + } - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); + /** + * The document_type field. + * + * @param documentType the value + * @return this builder + */ + public Builder documentType(String documentType) { + return set("document_type", Conv.STRING, documentType); + } - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); + /** + * The issuing_country field. + * + * @param issuingCountry the value + * @return this builder + */ + public Builder issuingCountry(String issuingCountry) { + return set("issuing_country", Conv.STRING, issuingCountry); + } - /** The value {@code "Resub Requested"}. */ - public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + /** + * The document_number field. + * + * @param documentNumber the value + * @return this builder + */ + public Builder documentNumber(String documentNumber) { + return set("document_number", Conv.STRING, documentNumber); + } - private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + /** + * The expiration_date field. + * + * @param expirationDate the value + * @return this builder + */ + public Builder expirationDate(LocalDate expirationDate) { + return set("expiration_date", Conv.DATE, expirationDate); + } - private Status(String value) { - super(value); - } + /** + * The first_name field. + * + * @param firstName the value + * @return this builder + */ + public Builder firstName(String firstName) { + return set("first_name", Conv.STRING, firstName); + } - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } + /** + * The last_name field. + * + * @param lastName the value + * @return this builder + */ + public Builder lastName(String lastName) { + return set("last_name", Conv.STRING, lastName); + } - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } + /** + * The birth_date field. + * + * @param birthDate the value + * @return this builder + */ + public Builder birthDate(LocalDate birthDate) { + return set("birth_date", Conv.DATE, birthDate); + } - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); + /** + * The gender field. + * + * @param gender the value + * @return this builder + */ + public Builder gender(String gender) { + return set("gender", Conv.STRING, gender); + } + + /** + * The nationality field. + * + * @param nationality the value + * @return this builder + */ + public Builder nationality(String nationality) { + return set("nationality", Conv.STRING, nationality); + } + + /** + * The address field. + * + * @param address the value + * @return this builder + */ + public Builder address(String address) { + return set("address", Conv.STRING, address); + } + + /** + * The place_of_birth field. + * + * @param placeOfBirth the value + * @return this builder + */ + public Builder placeOfBirth(String placeOfBirth) { + return set("place_of_birth", Conv.STRING, placeOfBirth); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ChipData} + */ + public ChipData build() { + return new ChipData(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } } } - public static final class Matches extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("session_id", "session_number", "similarity_percentage", "vendor_data", "vendor_user_id", "biometric_template_id", "verification_date", "user_details", "match_image_url", "status", "is_blocklisted", "is_allowlisted", "api_service", "source"); + /** + * ICAO passive authentication: SOD signature (sod_integrity) and Data Group hashes (dg_integrity). null for a JPKI read, which reports its proof under jpki. + */ + public static final class Authenticity extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("sod_integrity", "dg_integrity"); - private Matches(ObjectNode json, Map> files) { + private Authenticity(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code Matches} from JSON. Unknown fields are kept and readable + * Reads a {@code Authenticity} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static Matches fromJson(JsonNode json) { - return new Matches(Json.copyObject(json), Map.of()); + public static Authenticity fromJson(JsonNode json) { + return new Authenticity(Json.copyObject(json), Map.of()); } /** @@ -10187,143 +10390,161 @@ public Builder toBuilder() { } /** - * Session ID of the matching face. null when source is imported, list_entry, or retained_template. + * The sod_integrity field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional sessionId() { - return Json.optional(json, "session_id", Conv.STRING); + public Optional sodIntegrity() { + return Json.optional(json, "sod_integrity", Conv.BOOLEAN); } /** - * The session_number field. + * The dg_integrity field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional sessionNumber() { - return Json.optional(json, "session_number", Conv.LONG); + public Optional dgIntegrity() { + return Json.optional(json, "dg_integrity", Conv.BOOLEAN); } - /** - * Face similarity between the two images, clamped to 0–100. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional similarityPercentage() { - return Json.optional(json, "similarity_percentage", Conv.DOUBLE); + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Authenticity}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Authenticity from) { + super(from); + } + + /** + * The sod_integrity field. + * + * @param sodIntegrity the value + * @return this builder + */ + public Builder sodIntegrity(boolean sodIntegrity) { + return set("sod_integrity", Conv.BOOLEAN, sodIntegrity); + } + + /** + * The dg_integrity field. + * + * @param dgIntegrity the value + * @return this builder + */ + public Builder dgIntegrity(boolean dgIntegrity) { + return set("dg_integrity", Conv.BOOLEAN, dgIntegrity); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Authenticity} + */ + public Authenticity build() { + return new Authenticity(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * Summary of the ICAO Document Signer Certificate. null for a JPKI read (chip_protocol: "jpki") and for skipped steps. + */ + public static final class CertificateSummary extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("issuer", "subject", "serial_number", "not_valid_after", "not_valid_before"); + + private CertificateSummary(ObjectNode json, Map> files) { + super(json, files); } /** - * vendor_data of the matching session or linked User for imported and retained-template faces. + * Reads a {@code CertificateSummary} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param json a JSON object + * @return the parsed value */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); + public static CertificateSummary fromJson(JsonNode json) { + return new CertificateSummary(Json.copyObject(json), Map.of()); } /** - * Linked User id for imported and retained_template matches and for session matches linked to a User. null for list entries and sessions without a User. + * Returns an empty builder. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new builder */ - public Optional vendorUserId() { - return Json.optional(json, "vendor_user_id", Conv.STRING); + public static Builder builder() { + return new Builder(); } /** - * Id of the retained biometric template. Set only when source is retained_template; null otherwise. + * Returns a builder holding a copy of every field of this value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new builder */ - public Optional biometricTemplateId() { - return Json.optional(json, "biometric_template_id", Conv.STRING); + public Builder toBuilder() { + return new Builder(this); } /** - * When the matching face was captured, or when a retained template was created. null for list entries. + * The issuer field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional verificationDate() { - return Json.optional(json, "verification_date", Conv.DATE_TIME); + public Optional issuer() { + return Json.optional(json, "issuer", Conv.STRING); } /** - * Identity data from the matching session or imported User profile. Always null for retained_template and list_entry matches. + * The subject field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional userDetails() { - return Json.optional(json, "user_details", Conv.model(SessionsRetrieveDecisionResponse.LivenessChecks.Matches.UserDetails::fromJson)); + public Optional subject() { + return Json.optional(json, "subject", Conv.STRING); } /** - * Reference image for image-backed matches. Always null for retained_template matches because retained templates contain no image. + * The serial_number field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional matchImageUrl() { - return Json.optional(json, "match_image_url", Conv.STRING); + public Optional serialNumber() { + return Json.optional(json, "serial_number", Conv.STRING); } /** - * Lifecycle status of the matching session. null for non-session sources. + * The not_valid_after field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.STRING); + public Optional notValidAfter() { + return Json.optional(json, "not_valid_after", Conv.DATE_TIME); } /** - * Whether the matching face is on a blocklist. + * The not_valid_before field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional isBlocklisted() { - return Json.optional(json, "is_blocklisted", Conv.BOOLEAN); - } - - /** - * Whether the matching face is on an allowlist. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isAllowlisted() { - return Json.optional(json, "is_allowlisted", Conv.BOOLEAN); - } - - /** - * Standalone API service of the matching session (e.g. PASSIVE_LIVENESS, FACE_MATCH); null for workflow sessions and non-session sources. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional apiService() { - return Json.optional(json, "api_service", Conv.STRING); - } - - /** - * Origin of the matching face: session, imported (User face upload), list_entry (allow/block list), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional source() { - return Json.optional(json, "source", Conv.enumeration(SessionsRetrieveDecisionResponse.LivenessChecks.Matches.Source::of)); + public Optional notValidBefore() { + return Json.optional(json, "not_valid_before", Conv.DATE_TIME); } @Override @@ -10331,161 +10552,276 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link Matches}. */ + /** Builds a {@link CertificateSummary}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(Matches from) { + private Builder(CertificateSummary from) { super(from); } /** - * Session ID of the matching face. null when source is imported, list_entry, or retained_template. + * The issuer field. * - * @param sessionId the value, or {@code null} to send JSON null + * @param issuer the value * @return this builder */ - public Builder sessionId(@Nullable String sessionId) { - return setNullable("session_id", Conv.STRING, sessionId); + public Builder issuer(String issuer) { + return set("issuer", Conv.STRING, issuer); } /** - * The session_number field. + * The subject field. * - * @param sessionNumber the value, or {@code null} to send JSON null + * @param subject the value * @return this builder */ - public Builder sessionNumber(@Nullable Long sessionNumber) { - return setNullable("session_number", Conv.LONG, sessionNumber); + public Builder subject(String subject) { + return set("subject", Conv.STRING, subject); } /** - * Face similarity between the two images, clamped to 0–100. + * The serial_number field. * - * @param similarityPercentage the value + * @param serialNumber the value * @return this builder */ - public Builder similarityPercentage(double similarityPercentage) { - return set("similarity_percentage", Conv.DOUBLE, similarityPercentage); + public Builder serialNumber(String serialNumber) { + return set("serial_number", Conv.STRING, serialNumber); } /** - * vendor_data of the matching session or linked User for imported and retained-template faces. + * The not_valid_after field. * - * @param vendorData the value, or {@code null} to send JSON null + * @param notValidAfter the value * @return this builder */ - public Builder vendorData(@Nullable String vendorData) { - return setNullable("vendor_data", Conv.STRING, vendorData); + public Builder notValidAfter(OffsetDateTime notValidAfter) { + return set("not_valid_after", Conv.DATE_TIME, notValidAfter); } /** - * Linked User id for imported and retained_template matches and for session matches linked to a User. null for list entries and sessions without a User. + * The not_valid_before field. * - * @param vendorUserId the value, or {@code null} to send JSON null + * @param notValidBefore the value * @return this builder */ - public Builder vendorUserId(@Nullable String vendorUserId) { - return setNullable("vendor_user_id", Conv.STRING, vendorUserId); + public Builder notValidBefore(OffsetDateTime notValidBefore) { + return set("not_valid_before", Conv.DATE_TIME, notValidBefore); } /** - * Id of the retained biometric template. Set only when source is retained_template; null otherwise. + * Builds the value. * - * @param biometricTemplateId the value, or {@code null} to send JSON null - * @return this builder + * @return a new immutable {@code CertificateSummary} */ - public Builder biometricTemplateId(@Nullable String biometricTemplateId) { - return setNullable("biometric_template_id", Conv.STRING, biometricTemplateId); + public CertificateSummary build() { + return new CertificateSummary(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Warnings extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); + + private Warnings(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Warnings fromJson(JsonNode json) { + return new Warnings(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(SessionDecision.NfcVerifications.Warnings.Feature::of)); + } + + /** + * The risk field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.STRING); + } + + /** + * The additional_data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> additionalData() { + return Json.optional(json, "additional_data", Conv.OBJECT); + } + + /** + * The log_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional logType() { + return Json.optional(json, "log_type", Conv.STRING); + } + + /** + * The short_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional shortDescription() { + return Json.optional(json, "short_description", Conv.STRING); + } + + /** + * The long_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional longDescription() { + return Json.optional(json, "long_description", Conv.STRING); + } + + /** + * The workflow graph node ID that generated this warning + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Warnings}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Warnings from) { + super(from); } /** - * When the matching face was captured, or when a retained template was created. null for list entries. + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. * - * @param verificationDate the value, or {@code null} to send JSON null + * @param feature the value * @return this builder */ - public Builder verificationDate(@Nullable OffsetDateTime verificationDate) { - return setNullable("verification_date", Conv.DATE_TIME, verificationDate); + public Builder feature(SessionDecision.NfcVerifications.Warnings.Feature feature) { + return set("feature", Conv.enumeration(SessionDecision.NfcVerifications.Warnings.Feature::of), feature); } /** - * Identity data from the matching session or imported User profile. Always null for retained_template and list_entry matches. + * The risk field. * - * @param userDetails the value, or {@code null} to send JSON null + * @param risk the value * @return this builder */ - public Builder userDetails(SessionsRetrieveDecisionResponse.LivenessChecks.Matches.@Nullable UserDetails userDetails) { - return setNullable("user_details", Conv.model(SessionsRetrieveDecisionResponse.LivenessChecks.Matches.UserDetails::fromJson), userDetails); + public Builder risk(String risk) { + return set("risk", Conv.STRING, risk); } /** - * Reference image for image-backed matches. Always null for retained_template matches because retained templates contain no image. + * The additional_data field. * - * @param matchImageUrl the value, or {@code null} to send JSON null + * @param additionalData the value, or {@code null} to send JSON null * @return this builder */ - public Builder matchImageUrl(@Nullable String matchImageUrl) { - return setNullable("match_image_url", Conv.STRING, matchImageUrl); + public Builder additionalData(@Nullable Map additionalData) { + return setAny("additional_data", additionalData); } /** - * Lifecycle status of the matching session. null for non-session sources. + * The log_type field. * - * @param status the value, or {@code null} to send JSON null + * @param logType the value * @return this builder */ - public Builder status(@Nullable String status) { - return setNullable("status", Conv.STRING, status); + public Builder logType(String logType) { + return set("log_type", Conv.STRING, logType); } /** - * Whether the matching face is on a blocklist. + * The short_description field. * - * @param isBlocklisted the value + * @param shortDescription the value * @return this builder */ - public Builder isBlocklisted(boolean isBlocklisted) { - return set("is_blocklisted", Conv.BOOLEAN, isBlocklisted); + public Builder shortDescription(String shortDescription) { + return set("short_description", Conv.STRING, shortDescription); } /** - * Whether the matching face is on an allowlist. + * The long_description field. * - * @param isAllowlisted the value + * @param longDescription the value * @return this builder */ - public Builder isAllowlisted(boolean isAllowlisted) { - return set("is_allowlisted", Conv.BOOLEAN, isAllowlisted); + public Builder longDescription(String longDescription) { + return set("long_description", Conv.STRING, longDescription); } /** - * Standalone API service of the matching session (e.g. PASSIVE_LIVENESS, FACE_MATCH); null for workflow sessions and non-session sources. + * The workflow graph node ID that generated this warning * - * @param apiService the value, or {@code null} to send JSON null + * @param nodeId the value * @return this builder */ - public Builder apiService(@Nullable String apiService) { - return setNullable("api_service", Conv.STRING, apiService); - } - - /** - * Origin of the matching face: session, imported (User face upload), list_entry (allow/block list), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). - * - * @param source the value - * @return this builder - */ - public Builder source(SessionsRetrieveDecisionResponse.LivenessChecks.Matches.Source source) { - return set("source", Conv.enumeration(SessionsRetrieveDecisionResponse.LivenessChecks.Matches.Source::of), source); + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); } /** * Builds the value. * - * @return a new immutable {@code Matches} + * @return a new immutable {@code Warnings} */ - public Matches build() { - return new Matches(buildJson(), buildFiles()); + public Warnings build() { + return new Warnings(buildJson(), buildFiles()); } @Override @@ -10495,154 +10831,59 @@ protected Builder self() { } /** - * Identity data from the matching session or imported User profile. Always null for retained_template and list_entry matches. + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ - public static final class UserDetails extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("full_name", "document_type", "document_number"); - - private UserDetails(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code UserDetails} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static UserDetails fromJson(JsonNode json) { - return new UserDetails(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The full_name field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional fullName() { - return Json.optional(json, "full_name", Conv.STRING); - } + public static final class Feature extends StringEnum { + /** The value {@code "ID_VERIFICATION"}. */ + public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); - /** - * The document_type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentType() { - return Json.optional(json, "document_type", Conv.STRING); - } + /** The value {@code "NFC"}. */ + public static final Feature NFC = new Feature("NFC"); - /** - * The document_number field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentNumber() { - return Json.optional(json, "document_number", Conv.STRING); - } + /** The value {@code "LIVENESS"}. */ + public static final Feature LIVENESS = new Feature("LIVENESS"); - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** The value {@code "FACEMATCH"}. */ + public static final Feature FACEMATCH = new Feature("FACEMATCH"); - /** Builds a {@link UserDetails}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** The value {@code "PROOF_OF_ADDRESS"}. */ + public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); - private Builder(UserDetails from) { - super(from); - } + /** The value {@code "PHONE"}. */ + public static final Feature PHONE = new Feature("PHONE"); - /** - * The full_name field. - * - * @param fullName the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder fullName(@Nullable String fullName) { - return setNullable("full_name", Conv.STRING, fullName); - } + /** The value {@code "EMAIL"}. */ + public static final Feature EMAIL = new Feature("EMAIL"); - /** - * The document_type field. - * - * @param documentType the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder documentType(@Nullable String documentType) { - return setNullable("document_type", Conv.STRING, documentType); - } + /** The value {@code "AML"}. */ + public static final Feature AML = new Feature("AML"); - /** - * The document_number field. - * - * @param documentNumber the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder documentNumber(@Nullable String documentNumber) { - return setNullable("document_number", Conv.STRING, documentNumber); - } + /** The value {@code "LOCATION"}. */ + public static final Feature LOCATION = new Feature("LOCATION"); - /** - * Builds the value. - * - * @return a new immutable {@code UserDetails} - */ - public UserDetails build() { - return new UserDetails(buildJson(), buildFiles()); - } + /** The value {@code "DATABASE_VALIDATION"}. */ + public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); - @Override - protected Builder self() { - return this; - } - } - } + /** The value {@code "QUESTIONNAIRE"}. */ + public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); - /** - * Origin of the matching face: session, imported (User face upload), list_entry (allow/block list), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Source extends StringEnum { - /** The value {@code "session"}. */ - public static final Source SESSION = new Source("session"); + /** The value {@code "DOCUMENT_AI"}. */ + public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); - /** The value {@code "imported"}. */ - public static final Source IMPORTED = new Source("imported"); + /** The value {@code "KYB_REGISTRY"}. */ + public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); - /** The value {@code "list_entry"}. */ - public static final Source LIST_ENTRY = new Source("list_entry"); + /** The value {@code "KYB_DOCUMENTS"}. */ + public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); - /** The value {@code "retained_template"}. */ - public static final Source RETAINED_TEMPLATE = new Source("retained_template"); + /** The value {@code "KYB_KEY_PEOPLE"}. */ + public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); - private static final List KNOWN_VALUES = List.of(SESSION, IMPORTED, LIST_ENTRY, RETAINED_TEMPLATE); + private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); - private Source(String value) { + private Feature(String value) { super(value); } @@ -10652,8 +10893,8 @@ private Source(String value) { * @param value the string value * @return the enum value */ - public static Source of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + public static Feature of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); } /** @@ -10661,7 +10902,7 @@ public static Source of(String value) { * * @return the known values */ - public static List knownValues() { + public static List knownValues() { return KNOWN_VALUES; } @@ -10671,748 +10912,681 @@ public boolean isKnown() { } } } + } - public static final class Warnings extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); + /** One liveness report — the full report for a single liveness pass. */ + public static final class LivenessChecks extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "method", "score", "reference_image", "video_url", "age_estimation", "matches", "warnings", "face_quality", "face_luminance"); - private Warnings(ObjectNode json, Map> files) { - super(json, files); - } + private LivenessChecks(ObjectNode json, Map> files) { + super(json, files); + } - /** - * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Warnings fromJson(JsonNode json) { - return new Warnings(Json.copyObject(json), Map.of()); - } + /** + * Reads a {@code LivenessChecks} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static LivenessChecks fromJson(JsonNode json) { + return new LivenessChecks(Json.copyObject(json), Map.of()); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + /** + * Workflow graph node id of the liveness step that produced this report. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + /** + * Feature-level status of this liveness step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.LivenessChecks.Status::of)); + } + + /** + * Method used for liveness detection + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional method() { + return Json.optional(json, "method", Conv.STRING); + } + + /** + * Confidence score of the liveness check + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.DOUBLE); + } + + /** + * URL of the reference image used + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional referenceImage() { + return Json.optional(json, "reference_image", Conv.STRING); + } + + /** + * URL of the liveness check video + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional videoUrl() { + return Json.optional(json, "video_url", Conv.STRING); + } + + /** + * Estimated age of the person + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ageEstimation() { + return Json.optional(json, "age_estimation", Conv.DOUBLE); + } + + /** + * Similar faces found by the duplicate-face search (top matches, blocklisted/allowlisted entries first). Each entry's source tells you where the matching face came from: session (another verification session), imported (a face uploaded against a vendor user), list_entry (an org-managed allow/block list face), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). session_id, session_number, status and api_service are null for non-session sources. imported matches still populate vendor_data, verification_date and — when the vendor user has a name on file — user_details (with full_name only) from the vendor-user record; retained_template matches populate vendor_user_id, vendor_data, biometric_template_id and verification_date, while their image and identity fields are null; list_entry matches return all of these as null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> matches() { + return Json.optional(json, "matches", Conv.list(Conv.model(SessionDecision.LivenessChecks.Matches::fromJson))); + } + + /** + * The warnings field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SessionDecision.LivenessChecks.Warnings::fromJson))); + } + + /** + * Face image quality score (0-100%). Only available for passive liveness. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional faceQuality() { + return Json.optional(json, "face_quality", Conv.DOUBLE); + } + + /** + * Face luminance/brightness score (0-100%). Only available for passive liveness. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional faceLuminance() { + return Json.optional(json, "face_luminance", Conv.DOUBLE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link LivenessChecks}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(LivenessChecks from) { + super(from); + } + + /** + * Workflow graph node id of the liveness step that produced this report. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param nodeId the value + * @return this builder */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.LivenessChecks.Warnings.Feature::of)); + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); } /** - * The risk field. + * Feature-level status of this liveness step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param status the value + * @return this builder */ - public Optional risk() { - return Json.optional(json, "risk", Conv.STRING); + public Builder status(SessionDecision.LivenessChecks.Status status) { + return set("status", Conv.enumeration(SessionDecision.LivenessChecks.Status::of), status); } /** - * The additional_data field. + * Method used for liveness detection * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param method the value + * @return this builder */ - public Optional> additionalData() { - return Json.optional(json, "additional_data", Conv.OBJECT); + public Builder method(String method) { + return set("method", Conv.STRING, method); } /** - * The log_type field. + * Confidence score of the liveness check * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param score the value + * @return this builder */ - public Optional logType() { - return Json.optional(json, "log_type", Conv.STRING); + public Builder score(double score) { + return set("score", Conv.DOUBLE, score); } /** - * The short_description field. + * URL of the reference image used * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param referenceImage the value + * @return this builder */ - public Optional shortDescription() { - return Json.optional(json, "short_description", Conv.STRING); + public Builder referenceImage(String referenceImage) { + return set("reference_image", Conv.STRING, referenceImage); } /** - * The long_description field. + * URL of the liveness check video * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param videoUrl the value + * @return this builder */ - public Optional longDescription() { - return Json.optional(json, "long_description", Conv.STRING); + public Builder videoUrl(String videoUrl) { + return set("video_url", Conv.STRING, videoUrl); } /** - * The workflow graph node ID that generated this warning + * Estimated age of the person * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param ageEstimation the value + * @return this builder */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); + public Builder ageEstimation(double ageEstimation) { + return set("age_estimation", Conv.DOUBLE, ageEstimation); } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; + /** + * Similar faces found by the duplicate-face search (top matches, blocklisted/allowlisted entries first). Each entry's source tells you where the matching face came from: session (another verification session), imported (a face uploaded against a vendor user), list_entry (an org-managed allow/block list face), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). session_id, session_number, status and api_service are null for non-session sources. imported matches still populate vendor_data, verification_date and — when the vendor user has a name on file — user_details (with full_name only) from the vendor-user record; retained_template matches populate vendor_user_id, vendor_data, biometric_template_id and verification_date, while their image and identity fields are null; list_entry matches return all of these as null. + * + * @param matches the value + * @return this builder + */ + public Builder matches(List matches) { + return set("matches", Conv.list(Conv.model(SessionDecision.LivenessChecks.Matches::fromJson)), matches); } - /** Builds a {@link Warnings}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** + * The warnings field. + * + * @param warnings the value + * @return this builder + */ + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SessionDecision.LivenessChecks.Warnings::fromJson)), warnings); + } - private Builder(Warnings from) { - super(from); - } + /** + * Face image quality score (0-100%). Only available for passive liveness. + * + * @param faceQuality the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder faceQuality(@Nullable Double faceQuality) { + return setNullable("face_quality", Conv.DOUBLE, faceQuality); + } - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. - * - * @param feature the value - * @return this builder - */ - public Builder feature(SessionsRetrieveDecisionResponse.LivenessChecks.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.LivenessChecks.Warnings.Feature::of), feature); - } + /** + * Face luminance/brightness score (0-100%). Only available for passive liveness. + * + * @param faceLuminance the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder faceLuminance(@Nullable Double faceLuminance) { + return setNullable("face_luminance", Conv.DOUBLE, faceLuminance); + } - /** - * The risk field. - * - * @param risk the value - * @return this builder - */ - public Builder risk(String risk) { - return set("risk", Conv.STRING, risk); - } + /** + * Builds the value. + * + * @return a new immutable {@code LivenessChecks} + */ + public LivenessChecks build() { + return new LivenessChecks(buildJson(), buildFiles()); + } - /** - * The additional_data field. - * - * @param additionalData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder additionalData(@Nullable Map additionalData) { - return setAny("additional_data", additionalData); - } + @Override + protected Builder self() { + return this; + } + } - /** - * The log_type field. - * - * @param logType the value - * @return this builder - */ - public Builder logType(String logType) { - return set("log_type", Conv.STRING, logType); - } + /** + * Feature-level status of this liveness step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); - /** - * The short_description field. - * - * @param shortDescription the value - * @return this builder - */ - public Builder shortDescription(String shortDescription) { - return set("short_description", Conv.STRING, shortDescription); - } + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); - /** - * The long_description field. - * - * @param longDescription the value - * @return this builder - */ - public Builder longDescription(String longDescription) { - return set("long_description", Conv.STRING, longDescription); - } + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); - /** - * The workflow graph node ID that generated this warning - * - * @param nodeId the value - * @return this builder - */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); - } + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); - /** - * Builds the value. - * - * @return a new immutable {@code Warnings} - */ - public Warnings build() { - return new Warnings(buildJson(), buildFiles()); - } + /** The value {@code "Resub Requested"}. */ + public static final Status RESUB_REQUESTED = new Status("Resub Requested"); - @Override - protected Builder self() { - return this; - } + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private Status(String value) { + super(value); } /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * Returns the constant for a known value, or a new instance for any other value. * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + * @param value the string value + * @return the enum value */ - public static final class Feature extends StringEnum { - /** The value {@code "ID_VERIFICATION"}. */ - public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); - - /** The value {@code "NFC"}. */ - public static final Feature NFC = new Feature("NFC"); + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } - /** The value {@code "LIVENESS"}. */ - public static final Feature LIVENESS = new Feature("LIVENESS"); + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } - /** The value {@code "FACEMATCH"}. */ - public static final Feature FACEMATCH = new Feature("FACEMATCH"); + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } - /** The value {@code "PROOF_OF_ADDRESS"}. */ - public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); + public static final class Matches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "session_number", "similarity_percentage", "vendor_data", "vendor_user_id", "biometric_template_id", "verification_date", "user_details", "match_image_url", "status", "is_blocklisted", "is_allowlisted", "api_service", "source"); - /** The value {@code "PHONE"}. */ - public static final Feature PHONE = new Feature("PHONE"); + private Matches(ObjectNode json, Map> files) { + super(json, files); + } - /** The value {@code "EMAIL"}. */ - public static final Feature EMAIL = new Feature("EMAIL"); + /** + * Reads a {@code Matches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Matches fromJson(JsonNode json) { + return new Matches(Json.copyObject(json), Map.of()); + } - /** The value {@code "AML"}. */ - public static final Feature AML = new Feature("AML"); + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** The value {@code "LOCATION"}. */ - public static final Feature LOCATION = new Feature("LOCATION"); + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** The value {@code "DATABASE_VALIDATION"}. */ - public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); + /** + * Session ID of the matching face. null when source is imported, list_entry, or retained_template. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } - /** The value {@code "QUESTIONNAIRE"}. */ - public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); + /** + * The session_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionNumber() { + return Json.optional(json, "session_number", Conv.LONG); + } - /** The value {@code "DOCUMENT_AI"}. */ - public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); + /** + * Face similarity between the two images, clamped to 0–100. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional similarityPercentage() { + return Json.optional(json, "similarity_percentage", Conv.DOUBLE); + } - /** The value {@code "KYB_REGISTRY"}. */ - public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); + /** + * vendor_data of the matching session or linked User for imported and retained-template faces. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } - /** The value {@code "KYB_DOCUMENTS"}. */ - public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); + /** + * Linked User id for imported and retained_template matches and for session matches linked to a User. null for list entries and sessions without a User. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorUserId() { + return Json.optional(json, "vendor_user_id", Conv.STRING); + } - /** The value {@code "KYB_KEY_PEOPLE"}. */ - public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); + /** + * Id of the retained biometric template. Set only when source is retained_template; null otherwise. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional biometricTemplateId() { + return Json.optional(json, "biometric_template_id", Conv.STRING); + } - private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); + /** + * When the matching face was captured, or when a retained template was created. null for list entries. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verificationDate() { + return Json.optional(json, "verification_date", Conv.DATE_TIME); + } - private Feature(String value) { - super(value); - } + /** + * Identity data from the matching session or imported User profile. Always null for retained_template and list_entry matches. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional userDetails() { + return Json.optional(json, "user_details", Conv.model(SessionDecision.LivenessChecks.Matches.UserDetails::fromJson)); + } - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Feature of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - } - - /** One face-match report — the full report. */ - public static final class FaceMatches extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "score", "source_image_session_id", "source_image", "target_image", "warnings"); - - private FaceMatches(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code FaceMatches} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static FaceMatches fromJson(JsonNode json) { - return new FaceMatches(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Workflow graph node id of the face-match step that produced this report. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } - - /** - * Feature-level status of this face-match step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.FaceMatches.Status::of)); - } - - /** - * Confidence score of the face match - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional score() { - return Json.optional(json, "score", Conv.DOUBLE); - } - - /** - * Session id under which the source (reference) portrait is stored. In biometric-authentication flows this is the session's own id (the submitted portrait is stored under the new session). null when the source image is the same session's document portrait. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sourceImageSessionId() { - return Json.optional(json, "source_image_session_id", Conv.STRING); - } - - /** - * URL of the source image used for comparison - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sourceImage() { - return Json.optional(json, "source_image", Conv.STRING); - } - - /** - * URL of the target image used for comparison - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional targetImage() { - return Json.optional(json, "target_image", Conv.STRING); - } - - /** - * Face match verification warnings - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.FaceMatches.Warnings::fromJson))); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link FaceMatches}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(FaceMatches from) { - super(from); - } - - /** - * Workflow graph node id of the face-match step that produced this report. - * - * @param nodeId the value - * @return this builder - */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); - } - - /** - * Feature-level status of this face-match step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - * @param status the value - * @return this builder - */ - public Builder status(SessionsRetrieveDecisionResponse.FaceMatches.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.FaceMatches.Status::of), status); - } - - /** - * Confidence score of the face match - * - * @param score the value - * @return this builder - */ - public Builder score(double score) { - return set("score", Conv.DOUBLE, score); - } + /** + * Reference image for image-backed matches. Always null for retained_template matches because retained templates contain no image. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchImageUrl() { + return Json.optional(json, "match_image_url", Conv.STRING); + } /** - * Session id under which the source (reference) portrait is stored. In biometric-authentication flows this is the session's own id (the submitted portrait is stored under the new session). null when the source image is the same session's document portrait. + * Lifecycle status of the matching session. null for non-session sources. * - * @param sourceImageSessionId the value, or {@code null} to send JSON null - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder sourceImageSessionId(@Nullable String sourceImageSessionId) { - return setNullable("source_image_session_id", Conv.STRING, sourceImageSessionId); + public Optional status() { + return Json.optional(json, "status", Conv.STRING); } /** - * URL of the source image used for comparison + * Whether the matching face is on a blocklist. * - * @param sourceImage the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder sourceImage(String sourceImage) { - return set("source_image", Conv.STRING, sourceImage); + public Optional isBlocklisted() { + return Json.optional(json, "is_blocklisted", Conv.BOOLEAN); } /** - * URL of the target image used for comparison + * Whether the matching face is on an allowlist. * - * @param targetImage the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder targetImage(String targetImage) { - return set("target_image", Conv.STRING, targetImage); + public Optional isAllowlisted() { + return Json.optional(json, "is_allowlisted", Conv.BOOLEAN); } /** - * Face match verification warnings + * Standalone API service of the matching session (e.g. PASSIVE_LIVENESS, FACE_MATCH); null for workflow sessions and non-session sources. * - * @param warnings the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder warnings(List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.FaceMatches.Warnings::fromJson)), warnings); + public Optional apiService() { + return Json.optional(json, "api_service", Conv.STRING); } /** - * Builds the value. + * Origin of the matching face: session, imported (User face upload), list_entry (allow/block list), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). * - * @return a new immutable {@code FaceMatches} + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public FaceMatches build() { - return new FaceMatches(buildJson(), buildFiles()); + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(SessionDecision.LivenessChecks.Matches.Source::of)); } @Override - protected Builder self() { - return this; + protected Set knownFields() { + return KNOWN_FIELDS; } - } - - /** - * Feature-level status of this face-match step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Not Finished"}. */ - public static final Status NOT_FINISHED = new Status("Not Finished"); - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); + /** Builds a {@link Matches}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); + private Builder(Matches from) { + super(from); + } - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); + /** + * Session ID of the matching face. null when source is imported, list_entry, or retained_template. + * + * @param sessionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionId(@Nullable String sessionId) { + return setNullable("session_id", Conv.STRING, sessionId); + } - /** The value {@code "Resub Requested"}. */ - public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + /** + * The session_number field. + * + * @param sessionNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionNumber(@Nullable Long sessionNumber) { + return setNullable("session_number", Conv.LONG, sessionNumber); + } - private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + /** + * Face similarity between the two images, clamped to 0–100. + * + * @param similarityPercentage the value + * @return this builder + */ + public Builder similarityPercentage(double similarityPercentage) { + return set("similarity_percentage", Conv.DOUBLE, similarityPercentage); + } - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - public static final class Warnings extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); - - private Warnings(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Warnings fromJson(JsonNode json) { - return new Warnings(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.FaceMatches.Warnings.Feature::of)); - } - - /** - * The risk field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional risk() { - return Json.optional(json, "risk", Conv.STRING); - } - - /** - * The additional_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> additionalData() { - return Json.optional(json, "additional_data", Conv.OBJECT); - } - - /** - * The log_type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional logType() { - return Json.optional(json, "log_type", Conv.STRING); - } - - /** - * The short_description field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional shortDescription() { - return Json.optional(json, "short_description", Conv.STRING); - } - - /** - * The long_description field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional longDescription() { - return Json.optional(json, "long_description", Conv.STRING); - } - - /** - * The workflow graph node ID that generated this warning - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } + /** + * vendor_data of the matching session or linked User for imported and retained-template faces. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** + * Linked User id for imported and retained_template matches and for session matches linked to a User. null for list entries and sessions without a User. + * + * @param vendorUserId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorUserId(@Nullable String vendorUserId) { + return setNullable("vendor_user_id", Conv.STRING, vendorUserId); + } - /** Builds a {@link Warnings}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** + * Id of the retained biometric template. Set only when source is retained_template; null otherwise. + * + * @param biometricTemplateId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder biometricTemplateId(@Nullable String biometricTemplateId) { + return setNullable("biometric_template_id", Conv.STRING, biometricTemplateId); + } - private Builder(Warnings from) { - super(from); + /** + * When the matching face was captured, or when a retained template was created. null for list entries. + * + * @param verificationDate the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verificationDate(@Nullable OffsetDateTime verificationDate) { + return setNullable("verification_date", Conv.DATE_TIME, verificationDate); } /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * Identity data from the matching session or imported User profile. Always null for retained_template and list_entry matches. * - * @param feature the value + * @param userDetails the value, or {@code null} to send JSON null * @return this builder */ - public Builder feature(SessionsRetrieveDecisionResponse.FaceMatches.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.FaceMatches.Warnings.Feature::of), feature); + public Builder userDetails(SessionDecision.LivenessChecks.Matches.@Nullable UserDetails userDetails) { + return setNullable("user_details", Conv.model(SessionDecision.LivenessChecks.Matches.UserDetails::fromJson), userDetails); } /** - * The risk field. + * Reference image for image-backed matches. Always null for retained_template matches because retained templates contain no image. * - * @param risk the value + * @param matchImageUrl the value, or {@code null} to send JSON null * @return this builder */ - public Builder risk(String risk) { - return set("risk", Conv.STRING, risk); + public Builder matchImageUrl(@Nullable String matchImageUrl) { + return setNullable("match_image_url", Conv.STRING, matchImageUrl); } /** - * The additional_data field. + * Lifecycle status of the matching session. null for non-session sources. * - * @param additionalData the value, or {@code null} to send JSON null + * @param status the value, or {@code null} to send JSON null * @return this builder */ - public Builder additionalData(@Nullable Map additionalData) { - return setAny("additional_data", additionalData); + public Builder status(@Nullable String status) { + return setNullable("status", Conv.STRING, status); } /** - * The log_type field. + * Whether the matching face is on a blocklist. * - * @param logType the value + * @param isBlocklisted the value * @return this builder */ - public Builder logType(String logType) { - return set("log_type", Conv.STRING, logType); + public Builder isBlocklisted(boolean isBlocklisted) { + return set("is_blocklisted", Conv.BOOLEAN, isBlocklisted); } /** - * The short_description field. + * Whether the matching face is on an allowlist. * - * @param shortDescription the value + * @param isAllowlisted the value * @return this builder */ - public Builder shortDescription(String shortDescription) { - return set("short_description", Conv.STRING, shortDescription); + public Builder isAllowlisted(boolean isAllowlisted) { + return set("is_allowlisted", Conv.BOOLEAN, isAllowlisted); } /** - * The long_description field. + * Standalone API service of the matching session (e.g. PASSIVE_LIVENESS, FACE_MATCH); null for workflow sessions and non-session sources. * - * @param longDescription the value + * @param apiService the value, or {@code null} to send JSON null * @return this builder */ - public Builder longDescription(String longDescription) { - return set("long_description", Conv.STRING, longDescription); + public Builder apiService(@Nullable String apiService) { + return setNullable("api_service", Conv.STRING, apiService); } /** - * The workflow graph node ID that generated this warning + * Origin of the matching face: session, imported (User face upload), list_entry (allow/block list), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). * - * @param nodeId the value + * @param source the value * @return this builder */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); + public Builder source(SessionDecision.LivenessChecks.Matches.Source source) { + return set("source", Conv.enumeration(SessionDecision.LivenessChecks.Matches.Source::of), source); } /** * Builds the value. * - * @return a new immutable {@code Warnings} + * @return a new immutable {@code Matches} */ - public Warnings build() { - return new Warnings(buildJson(), buildFiles()); + public Matches build() { + return new Matches(buildJson(), buildFiles()); } @Override @@ -11422,59 +11596,154 @@ protected Builder self() { } /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + * Identity data from the matching session or imported User profile. Always null for retained_template and list_entry matches. */ - public static final class Feature extends StringEnum { - /** The value {@code "ID_VERIFICATION"}. */ - public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); - - /** The value {@code "NFC"}. */ - public static final Feature NFC = new Feature("NFC"); - - /** The value {@code "LIVENESS"}. */ - public static final Feature LIVENESS = new Feature("LIVENESS"); - - /** The value {@code "FACEMATCH"}. */ - public static final Feature FACEMATCH = new Feature("FACEMATCH"); + public static final class UserDetails extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("full_name", "document_type", "document_number"); - /** The value {@code "PROOF_OF_ADDRESS"}. */ - public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); + private UserDetails(ObjectNode json, Map> files) { + super(json, files); + } - /** The value {@code "PHONE"}. */ - public static final Feature PHONE = new Feature("PHONE"); + /** + * Reads a {@code UserDetails} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static UserDetails fromJson(JsonNode json) { + return new UserDetails(Json.copyObject(json), Map.of()); + } - /** The value {@code "EMAIL"}. */ - public static final Feature EMAIL = new Feature("EMAIL"); + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** The value {@code "AML"}. */ - public static final Feature AML = new Feature("AML"); + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** The value {@code "LOCATION"}. */ - public static final Feature LOCATION = new Feature("LOCATION"); + /** + * The full_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fullName() { + return Json.optional(json, "full_name", Conv.STRING); + } - /** The value {@code "DATABASE_VALIDATION"}. */ - public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); + /** + * The document_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentType() { + return Json.optional(json, "document_type", Conv.STRING); + } - /** The value {@code "QUESTIONNAIRE"}. */ - public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); + /** + * The document_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentNumber() { + return Json.optional(json, "document_number", Conv.STRING); + } - /** The value {@code "DOCUMENT_AI"}. */ - public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } - /** The value {@code "KYB_REGISTRY"}. */ - public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); + /** Builds a {@link UserDetails}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** The value {@code "KYB_DOCUMENTS"}. */ - public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); + private Builder(UserDetails from) { + super(from); + } - /** The value {@code "KYB_KEY_PEOPLE"}. */ - public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); + /** + * The full_name field. + * + * @param fullName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fullName(@Nullable String fullName) { + return setNullable("full_name", Conv.STRING, fullName); + } - private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); + /** + * The document_type field. + * + * @param documentType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder documentType(@Nullable String documentType) { + return setNullable("document_type", Conv.STRING, documentType); + } - private Feature(String value) { + /** + * The document_number field. + * + * @param documentNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder documentNumber(@Nullable String documentNumber) { + return setNullable("document_number", Conv.STRING, documentNumber); + } + + /** + * Builds the value. + * + * @return a new immutable {@code UserDetails} + */ + public UserDetails build() { + return new UserDetails(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * Origin of the matching face: session, imported (User face upload), list_entry (allow/block list), or retained_template (an image-free biometric template stored on a User, listed under /v3/biometric-templates/). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "session"}. */ + public static final Source SESSION = new Source("session"); + + /** The value {@code "imported"}. */ + public static final Source IMPORTED = new Source("imported"); + + /** The value {@code "list_entry"}. */ + public static final Source LIST_ENTRY = new Source("list_entry"); + + /** The value {@code "retained_template"}. */ + public static final Source RETAINED_TEMPLATE = new Source("retained_template"); + + private static final List KNOWN_VALUES = List.of(SESSION, IMPORTED, LIST_ENTRY, RETAINED_TEMPLATE); + + private Source(String value) { super(value); } @@ -11484,8 +11753,8 @@ private Feature(String value) { * @param value the string value * @return the enum value */ - public static Feature of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); } /** @@ -11493,7 +11762,7 @@ public static Feature of(String value) { * * @return the known values */ - public static List knownValues() { + public static List knownValues() { return KNOWN_VALUES; } @@ -11503,540 +11772,203 @@ public boolean isKnown() { } } } - } - public static final class PhoneVerifications extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "phone_number_prefix", "phone_number", "full_number", "country_code", "country_name", "carrier", "is_disposable", "is_virtual", "verification_method", "verification_attempts", "verified_at", "lifecycle", "warnings", "matches"); + public static final class Warnings extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); - private PhoneVerifications(ObjectNode json, Map> files) { - super(json, files); - } + private Warnings(ObjectNode json, Map> files) { + super(json, files); + } - /** - * Reads a {@code PhoneVerifications} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static PhoneVerifications fromJson(JsonNode json) { - return new PhoneVerifications(Json.copyObject(json), Map.of()); - } + /** + * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Warnings fromJson(JsonNode json) { + return new Warnings(Json.copyObject(json), Map.of()); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * Unique identifier for this phone step in the workflow graph. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(SessionDecision.LivenessChecks.Warnings.Feature::of)); + } - /** - * Feature-level status of this phone verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.PhoneVerifications.Status::of)); - } - - /** - * Country calling code prefix - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional phoneNumberPrefix() { - return Json.optional(json, "phone_number_prefix", Conv.STRING); - } - - /** - * Phone number without country prefix - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional phoneNumber() { - return Json.optional(json, "phone_number", Conv.STRING); - } - - /** - * Complete phone number with country prefix - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional fullNumber() { - return Json.optional(json, "full_number", Conv.STRING); - } - - /** - * Two-letter country code (ISO 3166-1 alpha-2) - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional countryCode() { - return Json.optional(json, "country_code", Conv.STRING); - } - - /** - * Full country name - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional countryName() { - return Json.optional(json, "country_name", Conv.STRING); - } - - /** - * The carrier field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional carrier() { - return Json.optional(json, "carrier", Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Carrier::fromJson)); - } - - /** - * Whether the phone number is from a disposable service - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isDisposable() { - return Json.optional(json, "is_disposable", Conv.BOOLEAN); - } - - /** - * Whether the phone number is virtual - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isVirtual() { - return Json.optional(json, "is_virtual", Conv.BOOLEAN); - } - - /** - * Method used to verify the phone number - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional verificationMethod() { - return Json.optional(json, "verification_method", Conv.STRING); - } - - /** - * Number of verification attempts made - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional verificationAttempts() { - return Json.optional(json, "verification_attempts", Conv.LONG); - } - - /** - * Timestamp when the phone was verified - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional verifiedAt() { - return Json.optional(json, "verified_at", Conv.DATE_TIME); - } - - /** - * Chronological list of events in the phone verification lifecycle. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> lifecycle() { - return Json.optional(json, "lifecycle", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Lifecycle::fromJson))); - } - - /** - * List of warnings related to phone verification - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Warnings::fromJson))); - } - - /** - * Matches found for this phone number. Each match has a source: session (another verification session in your application matched on the same number, returned with session_id/session_number/vendor_data/verification_date) or list_entry (the number is on an org-managed allow/block list — see /management-api/lists/overview). Cross-session matches use different vendor_data and are capped at 5. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> matches() { - return Json.optional(json, "matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Matches::fromJson))); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link PhoneVerifications}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(PhoneVerifications from) { - super(from); - } + /** + * The risk field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.STRING); + } /** - * Unique identifier for this phone step in the workflow graph. + * The additional_data field. * - * @param nodeId the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); + public Optional> additionalData() { + return Json.optional(json, "additional_data", Conv.OBJECT); } /** - * Feature-level status of this phone verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * The log_type field. * - * @param status the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder status(SessionsRetrieveDecisionResponse.PhoneVerifications.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.PhoneVerifications.Status::of), status); + public Optional logType() { + return Json.optional(json, "log_type", Conv.STRING); } /** - * Country calling code prefix + * The short_description field. * - * @param phoneNumberPrefix the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder phoneNumberPrefix(String phoneNumberPrefix) { - return set("phone_number_prefix", Conv.STRING, phoneNumberPrefix); + public Optional shortDescription() { + return Json.optional(json, "short_description", Conv.STRING); } /** - * Phone number without country prefix + * The long_description field. * - * @param phoneNumber the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder phoneNumber(String phoneNumber) { - return set("phone_number", Conv.STRING, phoneNumber); + public Optional longDescription() { + return Json.optional(json, "long_description", Conv.STRING); } /** - * Complete phone number with country prefix + * The workflow graph node ID that generated this warning * - * @param fullNumber the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder fullNumber(String fullNumber) { - return set("full_number", Conv.STRING, fullNumber); + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); } - /** - * Two-letter country code (ISO 3166-1 alpha-2) - * - * @param countryCode the value - * @return this builder - */ - public Builder countryCode(String countryCode) { - return set("country_code", Conv.STRING, countryCode); + @Override + protected Set knownFields() { + return KNOWN_FIELDS; } - /** - * Full country name - * - * @param countryName the value - * @return this builder - */ - public Builder countryName(String countryName) { - return set("country_name", Conv.STRING, countryName); - } + /** Builds a {@link Warnings}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** - * The carrier field. - * - * @param carrier the value - * @return this builder - */ - public Builder carrier(SessionsRetrieveDecisionResponse.PhoneVerifications.Carrier carrier) { - return set("carrier", Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Carrier::fromJson), carrier); - } + private Builder(Warnings from) { + super(from); + } - /** - * Whether the phone number is from a disposable service - * - * @param isDisposable the value - * @return this builder - */ - public Builder isDisposable(boolean isDisposable) { - return set("is_disposable", Conv.BOOLEAN, isDisposable); - } + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * + * @param feature the value + * @return this builder + */ + public Builder feature(SessionDecision.LivenessChecks.Warnings.Feature feature) { + return set("feature", Conv.enumeration(SessionDecision.LivenessChecks.Warnings.Feature::of), feature); + } - /** - * Whether the phone number is virtual - * - * @param isVirtual the value - * @return this builder - */ - public Builder isVirtual(boolean isVirtual) { - return set("is_virtual", Conv.BOOLEAN, isVirtual); - } + /** + * The risk field. + * + * @param risk the value + * @return this builder + */ + public Builder risk(String risk) { + return set("risk", Conv.STRING, risk); + } - /** - * Method used to verify the phone number - * - * @param verificationMethod the value - * @return this builder - */ - public Builder verificationMethod(String verificationMethod) { - return set("verification_method", Conv.STRING, verificationMethod); - } - - /** - * Number of verification attempts made - * - * @param verificationAttempts the value - * @return this builder - */ - public Builder verificationAttempts(long verificationAttempts) { - return set("verification_attempts", Conv.LONG, verificationAttempts); - } - - /** - * Timestamp when the phone was verified - * - * @param verifiedAt the value - * @return this builder - */ - public Builder verifiedAt(OffsetDateTime verifiedAt) { - return set("verified_at", Conv.DATE_TIME, verifiedAt); - } - - /** - * Chronological list of events in the phone verification lifecycle. - * - * @param lifecycle the value - * @return this builder - */ - public Builder lifecycle(List lifecycle) { - return set("lifecycle", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Lifecycle::fromJson)), lifecycle); - } - - /** - * List of warnings related to phone verification - * - * @param warnings the value - * @return this builder - */ - public Builder warnings(List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Warnings::fromJson)), warnings); - } - - /** - * Matches found for this phone number. Each match has a source: session (another verification session in your application matched on the same number, returned with session_id/session_number/vendor_data/verification_date) or list_entry (the number is on an org-managed allow/block list — see /management-api/lists/overview). Cross-session matches use different vendor_data and are capped at 5. - * - * @param matches the value - * @return this builder - */ - public Builder matches(List matches) { - return set("matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Matches::fromJson)), matches); - } - - /** - * Builds the value. - * - * @return a new immutable {@code PhoneVerifications} - */ - public PhoneVerifications build() { - return new PhoneVerifications(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Feature-level status of this phone verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Not Finished"}. */ - public static final Status NOT_FINISHED = new Status("Not Finished"); - - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); - - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); - - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); - - /** The value {@code "Expired"}. */ - public static final Status EXPIRED = new Status("Expired"); - - private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, EXPIRED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - public static final class Carrier extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("name", "type"); - - private Carrier(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Carrier} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Carrier fromJson(JsonNode json) { - return new Carrier(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Name of the phone carrier - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional name() { - return Json.optional(json, "name", Conv.STRING); - } - - /** - * Type of phone line (mobile, landline, etc) - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional type() { - return Json.optional(json, "type", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** + * The additional_data field. + * + * @param additionalData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder additionalData(@Nullable Map additionalData) { + return setAny("additional_data", additionalData); + } - /** Builds a {@link Carrier}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** + * The log_type field. + * + * @param logType the value + * @return this builder + */ + public Builder logType(String logType) { + return set("log_type", Conv.STRING, logType); + } - private Builder(Carrier from) { - super(from); + /** + * The short_description field. + * + * @param shortDescription the value + * @return this builder + */ + public Builder shortDescription(String shortDescription) { + return set("short_description", Conv.STRING, shortDescription); } /** - * Name of the phone carrier + * The long_description field. * - * @param name the value + * @param longDescription the value * @return this builder */ - public Builder name(String name) { - return set("name", Conv.STRING, name); + public Builder longDescription(String longDescription) { + return set("long_description", Conv.STRING, longDescription); } /** - * Type of phone line (mobile, landline, etc) + * The workflow graph node ID that generated this warning * - * @param type the value + * @param nodeId the value * @return this builder */ - public Builder type(String type) { - return set("type", Conv.STRING, type); + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); } /** * Builds the value. * - * @return a new immutable {@code Carrier} + * @return a new immutable {@code Warnings} */ - public Carrier build() { - return new Carrier(buildJson(), buildFiles()); + public Warnings build() { + return new Warnings(buildJson(), buildFiles()); } @Override @@ -12044,422 +11976,449 @@ protected Builder self() { return this; } } - } - - public static final class Lifecycle extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "timestamp", "details", "fee"); - - private Lifecycle(ObjectNode json, Map> files) { - super(json, files); - } /** - * Reads a {@code Lifecycle} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. * - * @param json a JSON object - * @return the parsed value + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ - public static Lifecycle fromJson(JsonNode json) { - return new Lifecycle(Json.copyObject(json), Map.of()); - } + public static final class Feature extends StringEnum { + /** The value {@code "ID_VERIFICATION"}. */ + public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional type() { - return Json.optional(json, "type", Conv.STRING); - } + /** The value {@code "NFC"}. */ + public static final Feature NFC = new Feature("NFC"); - /** - * The timestamp field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional timestamp() { - return Json.optional(json, "timestamp", Conv.DATE_TIME); - } + /** The value {@code "LIVENESS"}. */ + public static final Feature LIVENESS = new Feature("LIVENESS"); - /** - * Event-specific payload. Send events carry status, reason, channel, actual_channel; delivery events carry channel, status; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. PHONE_VERIFICATION_APPROVED). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional details() { - return Json.optional(json, "details", Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Lifecycle.Details::fromJson)); - } + /** The value {@code "FACEMATCH"}. */ + public static final Feature FACEMATCH = new Feature("FACEMATCH"); - /** - * The fee field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional fee() { - return Json.optional(json, "fee", Conv.DOUBLE); - } + /** The value {@code "PROOF_OF_ADDRESS"}. */ + public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** The value {@code "PHONE"}. */ + public static final Feature PHONE = new Feature("PHONE"); - /** Builds a {@link Lifecycle}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** The value {@code "EMAIL"}. */ + public static final Feature EMAIL = new Feature("EMAIL"); - private Builder(Lifecycle from) { - super(from); - } + /** The value {@code "AML"}. */ + public static final Feature AML = new Feature("AML"); - /** - * The type field. - * - * @param type the value - * @return this builder - */ - public Builder type(String type) { - return set("type", Conv.STRING, type); - } + /** The value {@code "LOCATION"}. */ + public static final Feature LOCATION = new Feature("LOCATION"); - /** - * The timestamp field. - * - * @param timestamp the value - * @return this builder - */ - public Builder timestamp(OffsetDateTime timestamp) { - return set("timestamp", Conv.DATE_TIME, timestamp); - } + /** The value {@code "DATABASE_VALIDATION"}. */ + public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); - /** - * Event-specific payload. Send events carry status, reason, channel, actual_channel; delivery events carry channel, status; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. PHONE_VERIFICATION_APPROVED). - * - * @param details the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder details(SessionsRetrieveDecisionResponse.PhoneVerifications.Lifecycle.@Nullable Details details) { - return setNullable("details", Conv.model(SessionsRetrieveDecisionResponse.PhoneVerifications.Lifecycle.Details::fromJson), details); - } + /** The value {@code "QUESTIONNAIRE"}. */ + public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); - /** - * The fee field. - * - * @param fee the value - * @return this builder - */ - public Builder fee(double fee) { - return set("fee", Conv.DOUBLE, fee); - } + /** The value {@code "DOCUMENT_AI"}. */ + public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); - /** - * Builds the value. - * - * @return a new immutable {@code Lifecycle} - */ - public Lifecycle build() { - return new Lifecycle(buildJson(), buildFiles()); - } + /** The value {@code "KYB_REGISTRY"}. */ + public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); - @Override - protected Builder self() { - return this; - } - } + /** The value {@code "KYB_DOCUMENTS"}. */ + public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); - /** - * Event-specific payload. Send events carry status, reason, channel, actual_channel; delivery events carry channel, status; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. PHONE_VERIFICATION_APPROVED). - */ - public static final class Details extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("status", "reason", "channel", "actual_channel", "code_tried"); + /** The value {@code "KYB_KEY_PEOPLE"}. */ + public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); - private Details(ObjectNode json, Map> files) { - super(json, files); - } + private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); - /** - * Reads a {@code Details} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Details fromJson(JsonNode json) { - return new Details(Json.copyObject(json), Map.of()); + private Feature(String value) { + super(value); } /** - * Returns an empty builder. + * Returns the constant for a known value, or a new instance for any other value. * - * @return a new builder + * @param value the string value + * @return the enum value */ - public static Builder builder() { - return new Builder(); + public static Feature of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); } /** - * Returns a builder holding a copy of every field of this value. + * Returns the values known to this SDK version. * - * @return a new builder + * @return the known values */ - public Builder toBuilder() { - return new Builder(this); + public static List knownValues() { + return KNOWN_VALUES; } - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.STRING); + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); } + } + } + } - /** - * The reason field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional reason() { - return Json.optional(json, "reason", Conv.STRING); - } + /** One face-match report — the full report. */ + public static final class FaceMatches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "score", "source_image_session_id", "source_image", "target_image", "warnings"); - /** - * The channel field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional channel() { - return Json.optional(json, "channel", Conv.STRING); - } + private FaceMatches(ObjectNode json, Map> files) { + super(json, files); + } - /** - * The actual_channel field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional actualChannel() { - return Json.optional(json, "actual_channel", Conv.STRING); - } + /** + * Reads a {@code FaceMatches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static FaceMatches fromJson(JsonNode json) { + return new FaceMatches(Json.copyObject(json), Map.of()); + } - /** - * The code_tried field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional codeTried() { - return Json.optional(json, "code_tried", Conv.STRING); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** Builds a {@link Details}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Details from) { - super(from); - } + /** + * Workflow graph node id of the face-match step that produced this report. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(String status) { - return set("status", Conv.STRING, status); - } + /** + * Feature-level status of this face-match step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.FaceMatches.Status::of)); + } - /** - * The reason field. - * - * @param reason the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder reason(@Nullable String reason) { - return setNullable("reason", Conv.STRING, reason); - } + /** + * Confidence score of the face match + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.DOUBLE); + } - /** - * The channel field. - * - * @param channel the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder channel(@Nullable String channel) { - return setNullable("channel", Conv.STRING, channel); - } + /** + * Session id under which the source (reference) portrait is stored. In biometric-authentication flows this is the session's own id (the submitted portrait is stored under the new session). null when the source image is the same session's document portrait. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceImageSessionId() { + return Json.optional(json, "source_image_session_id", Conv.STRING); + } - /** - * The actual_channel field. - * - * @param actualChannel the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder actualChannel(@Nullable String actualChannel) { - return setNullable("actual_channel", Conv.STRING, actualChannel); - } + /** + * URL of the source image used for comparison + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceImage() { + return Json.optional(json, "source_image", Conv.STRING); + } - /** - * The code_tried field. - * - * @param codeTried the value - * @return this builder - */ - public Builder codeTried(String codeTried) { - return set("code_tried", Conv.STRING, codeTried); - } + /** + * URL of the target image used for comparison + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional targetImage() { + return Json.optional(json, "target_image", Conv.STRING); + } - /** - * Builds the value. - * - * @return a new immutable {@code Details} - */ - public Details build() { - return new Details(buildJson(), buildFiles()); - } + /** + * Face match verification warnings + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SessionDecision.FaceMatches.Warnings::fromJson))); + } - @Override - protected Builder self() { - return this; - } - } - } + @Override + protected Set knownFields() { + return KNOWN_FIELDS; } - public static final class Warnings extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); + /** Builds a {@link FaceMatches}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - private Warnings(ObjectNode json, Map> files) { - super(json, files); + private Builder(FaceMatches from) { + super(from); } /** - * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. + * Workflow graph node id of the face-match step that produced this report. * - * @param json a JSON object - * @return the parsed value + * @param nodeId the value + * @return this builder */ - public static Warnings fromJson(JsonNode json) { - return new Warnings(Json.copyObject(json), Map.of()); + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); } /** - * Returns an empty builder. + * Feature-level status of this face-match step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. * - * @return a new builder + * @param status the value + * @return this builder */ - public static Builder builder() { - return new Builder(); + public Builder status(SessionDecision.FaceMatches.Status status) { + return set("status", Conv.enumeration(SessionDecision.FaceMatches.Status::of), status); } /** - * Returns a builder holding a copy of every field of this value. + * Confidence score of the face match * - * @return a new builder + * @param score the value + * @return this builder */ - public Builder toBuilder() { - return new Builder(this); + public Builder score(double score) { + return set("score", Conv.DOUBLE, score); } /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * Session id under which the source (reference) portrait is stored. In biometric-authentication flows this is the session's own id (the submitted portrait is stored under the new session). null when the source image is the same session's document portrait. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param sourceImageSessionId the value, or {@code null} to send JSON null + * @return this builder */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.PhoneVerifications.Warnings.Feature::of)); + public Builder sourceImageSessionId(@Nullable String sourceImageSessionId) { + return setNullable("source_image_session_id", Conv.STRING, sourceImageSessionId); } /** - * The risk field. + * URL of the source image used for comparison * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param sourceImage the value + * @return this builder */ - public Optional risk() { - return Json.optional(json, "risk", Conv.STRING); + public Builder sourceImage(String sourceImage) { + return set("source_image", Conv.STRING, sourceImage); } /** - * The additional_data field. + * URL of the target image used for comparison * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param targetImage the value + * @return this builder */ - public Optional> additionalData() { - return Json.optional(json, "additional_data", Conv.OBJECT); + public Builder targetImage(String targetImage) { + return set("target_image", Conv.STRING, targetImage); } /** - * The log_type field. + * Face match verification warnings * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param warnings the value + * @return this builder */ - public Optional logType() { - return Json.optional(json, "log_type", Conv.STRING); + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SessionDecision.FaceMatches.Warnings::fromJson)), warnings); } /** - * The short_description field. + * Builds the value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new immutable {@code FaceMatches} */ - public Optional shortDescription() { - return Json.optional(json, "short_description", Conv.STRING); + public FaceMatches build() { + return new FaceMatches(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Feature-level status of this face-match step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Resub Requested"}. */ + public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private Status(String value) { + super(value); } /** - * The long_description field. + * Returns the constant for a known value, or a new instance for any other value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param value the string value + * @return the enum value */ - public Optional longDescription() { - return Json.optional(json, "long_description", Conv.STRING); + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); } /** - * The workflow graph node ID that generated this warning + * Returns the values known to this SDK version. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class Warnings extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); + + private Warnings(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Warnings fromJson(JsonNode json) { + return new Warnings(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(SessionDecision.FaceMatches.Warnings.Feature::of)); + } + + /** + * The risk field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.STRING); + } + + /** + * The additional_data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> additionalData() { + return Json.optional(json, "additional_data", Conv.OBJECT); + } + + /** + * The log_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional logType() { + return Json.optional(json, "log_type", Conv.STRING); + } + + /** + * The short_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional shortDescription() { + return Json.optional(json, "short_description", Conv.STRING); + } + + /** + * The long_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional longDescription() { + return Json.optional(json, "long_description", Conv.STRING); + } + + /** + * The workflow graph node ID that generated this warning + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ public Optional nodeId() { return Json.optional(json, "node_id", Conv.STRING); @@ -12484,8 +12443,8 @@ private Builder(Warnings from) { * @param feature the value * @return this builder */ - public Builder feature(SessionsRetrieveDecisionResponse.PhoneVerifications.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.PhoneVerifications.Warnings.Feature::of), feature); + public Builder feature(SessionDecision.FaceMatches.Warnings.Feature feature) { + return set("feature", Conv.enumeration(SessionDecision.FaceMatches.Warnings.Feature::of), feature); } /** @@ -12645,404 +12604,156 @@ public boolean isKnown() { } } } + } - public static final class Matches extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("session_id", "session_number", "vendor_data", "verification_date", "phone_number", "status", "is_blocklisted", "api_service", "source"); - - private Matches(ObjectNode json, Map> files) { - super(json, files); - } + public static final class PhoneVerifications extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "phone_number_prefix", "phone_number", "full_number", "country_code", "country_name", "carrier", "is_disposable", "is_virtual", "verification_method", "verification_attempts", "verified_at", "lifecycle", "warnings", "matches"); - /** - * Reads a {@code Matches} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Matches fromJson(JsonNode json) { - return new Matches(Json.copyObject(json), Map.of()); - } + private PhoneVerifications(ObjectNode json, Map> files) { + super(json, files); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * Reads a {@code PhoneVerifications} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PhoneVerifications fromJson(JsonNode json) { + return new PhoneVerifications(Json.copyObject(json), Map.of()); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * The session_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sessionId() { - return Json.optional(json, "session_id", Conv.STRING); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * The session_number field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sessionNumber() { - return Json.optional(json, "session_number", Conv.LONG); - } + /** + * Unique identifier for this phone step in the workflow graph. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } - /** - * The vendor_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); - } + /** + * Feature-level status of this phone verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.PhoneVerifications.Status::of)); + } - /** - * The verification_date field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional verificationDate() { - return Json.optional(json, "verification_date", Conv.DATE_TIME); - } + /** + * Country calling code prefix + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneNumberPrefix() { + return Json.optional(json, "phone_number_prefix", Conv.STRING); + } - /** - * The phone_number field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional phoneNumber() { - return Json.optional(json, "phone_number", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.STRING); - } - - /** - * The is_blocklisted field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isBlocklisted() { - return Json.optional(json, "is_blocklisted", Conv.BOOLEAN); - } - - /** - * The api_service field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional apiService() { - return Json.optional(json, "api_service", Conv.STRING); - } - - /** - * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional source() { - return Json.optional(json, "source", Conv.enumeration(SessionsRetrieveDecisionResponse.PhoneVerifications.Matches.Source::of)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Matches}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Matches from) { - super(from); - } - - /** - * The session_id field. - * - * @param sessionId the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder sessionId(@Nullable String sessionId) { - return setNullable("session_id", Conv.STRING, sessionId); - } - - /** - * The session_number field. - * - * @param sessionNumber the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder sessionNumber(@Nullable Long sessionNumber) { - return setNullable("session_number", Conv.LONG, sessionNumber); - } - - /** - * The vendor_data field. - * - * @param vendorData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder vendorData(@Nullable String vendorData) { - return setNullable("vendor_data", Conv.STRING, vendorData); - } - - /** - * The verification_date field. - * - * @param verificationDate the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder verificationDate(@Nullable OffsetDateTime verificationDate) { - return setNullable("verification_date", Conv.DATE_TIME, verificationDate); - } - - /** - * The phone_number field. - * - * @param phoneNumber the value - * @return this builder - */ - public Builder phoneNumber(String phoneNumber) { - return set("phone_number", Conv.STRING, phoneNumber); - } - - /** - * The status field. - * - * @param status the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder status(@Nullable String status) { - return setNullable("status", Conv.STRING, status); - } - - /** - * The is_blocklisted field. - * - * @param isBlocklisted the value - * @return this builder - */ - public Builder isBlocklisted(boolean isBlocklisted) { - return set("is_blocklisted", Conv.BOOLEAN, isBlocklisted); - } - - /** - * The api_service field. - * - * @param apiService the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder apiService(@Nullable String apiService) { - return setNullable("api_service", Conv.STRING, apiService); - } - - /** - * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. - * - * @param source the value - * @return this builder - */ - public Builder source(SessionsRetrieveDecisionResponse.PhoneVerifications.Matches.Source source) { - return set("source", Conv.enumeration(SessionsRetrieveDecisionResponse.PhoneVerifications.Matches.Source::of), source); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Matches} - */ - public Matches build() { - return new Matches(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Source extends StringEnum { - /** The value {@code "session"}. */ - public static final Source SESSION = new Source("session"); - - /** The value {@code "list_entry"}. */ - public static final Source LIST_ENTRY = new Source("list_entry"); - - private static final List KNOWN_VALUES = List.of(SESSION, LIST_ENTRY); - - private Source(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Source of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Source::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - } - - public static final class EmailVerifications extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "email", "is_breached", "breaches", "is_disposable", "is_undeliverable", "verification_attempts", "verified_at", "warnings", "lifecycle", "matches"); - - private EmailVerifications(ObjectNode json, Map> files) { - super(json, files); - } + /** + * Phone number without country prefix + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneNumber() { + return Json.optional(json, "phone_number", Conv.STRING); + } /** - * Reads a {@code EmailVerifications} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. + * Complete phone number with country prefix * - * @param json a JSON object - * @return the parsed value + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public static EmailVerifications fromJson(JsonNode json) { - return new EmailVerifications(Json.copyObject(json), Map.of()); + public Optional fullNumber() { + return Json.optional(json, "full_number", Conv.STRING); } /** - * Returns an empty builder. + * Two-letter country code (ISO 3166-1 alpha-2) * - * @return a new builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public static Builder builder() { - return new Builder(); + public Optional countryCode() { + return Json.optional(json, "country_code", Conv.STRING); } /** - * Returns a builder holding a copy of every field of this value. + * Full country name * - * @return a new builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder toBuilder() { - return new Builder(this); + public Optional countryName() { + return Json.optional(json, "country_name", Conv.STRING); } /** - * Unique identifier for this email step in the workflow graph. + * The carrier field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); + public Optional carrier() { + return Json.optional(json, "carrier", Conv.model(SessionDecision.PhoneVerifications.Carrier::fromJson)); } /** - * Feature-level status of this email verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * Whether the phone number is from a disposable service * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.EmailVerifications.Status::of)); + public Optional isDisposable() { + return Json.optional(json, "is_disposable", Conv.BOOLEAN); } /** - * The email address being verified + * Whether the phone number is virtual * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional email() { - return Json.optional(json, "email", Conv.STRING); + public Optional isVirtual() { + return Json.optional(json, "is_virtual", Conv.BOOLEAN); } /** - * Indicates if the email was found in known data breaches + * Method used to verify the phone number * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional isBreached() { - return Json.optional(json, "is_breached", Conv.BOOLEAN); + public Optional verificationMethod() { + return Json.optional(json, "verification_method", Conv.STRING); } /** - * List of known breaches this email was found in - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> breaches() { - return Json.optional(json, "breaches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Breaches::fromJson))); - } - - /** - * Indicates if the email is from a disposable provider - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isDisposable() { - return Json.optional(json, "is_disposable", Conv.BOOLEAN); - } - - /** - * Indicates if the email address is undeliverable - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isUndeliverable() { - return Json.optional(json, "is_undeliverable", Conv.BOOLEAN); - } - - /** - * Number of verification attempts for this email + * Number of verification attempts made * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -13052,7 +12763,7 @@ public Optional verificationAttempts() { } /** - * Timestamp when the email was verified + * Timestamp when the phone was verified * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -13062,33 +12773,33 @@ public Optional verifiedAt() { } /** - * List of warnings related to email verification + * Chronological list of events in the phone verification lifecycle. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Warnings::fromJson))); + public Optional> lifecycle() { + return Json.optional(json, "lifecycle", Conv.list(Conv.model(SessionDecision.PhoneVerifications.Lifecycle::fromJson))); } /** - * Chronological list of events in the email verification lifecycle. + * List of warnings related to phone verification * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> lifecycle() { - return Json.optional(json, "lifecycle", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Lifecycle::fromJson))); + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SessionDecision.PhoneVerifications.Warnings::fromJson))); } /** - * Matches found for this email address. Each match has a source: session (another verification session in your application matched on the same email, returned with session_id/session_number/vendor_data/verification_date) or list_entry (the email is on an org-managed allow/block list — see /management-api/lists/overview). Cross-session matches use different vendor_data and are capped at 5. + * Matches found for this phone number. Each match has a source: session (another verification session in your application matched on the same number, returned with session_id/session_number/vendor_data/verification_date) or list_entry (the number is on an org-managed allow/block list — see /management-api/lists/overview). Cross-session matches use different vendor_data and are capped at 5. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> matches() { - return Json.optional(json, "matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Matches::fromJson))); + public Optional> matches() { + return Json.optional(json, "matches", Conv.list(Conv.model(SessionDecision.PhoneVerifications.Matches::fromJson))); } @Override @@ -13096,16 +12807,16 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link EmailVerifications}. */ + /** Builds a {@link PhoneVerifications}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(EmailVerifications from) { + private Builder(PhoneVerifications from) { super(from); } /** - * Unique identifier for this email step in the workflow graph. + * Unique identifier for this phone step in the workflow graph. * * @param nodeId the value * @return this builder @@ -13115,47 +12826,77 @@ public Builder nodeId(String nodeId) { } /** - * Feature-level status of this email verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * Feature-level status of this phone verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. * * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDecisionResponse.EmailVerifications.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.EmailVerifications.Status::of), status); + public Builder status(SessionDecision.PhoneVerifications.Status status) { + return set("status", Conv.enumeration(SessionDecision.PhoneVerifications.Status::of), status); } /** - * The email address being verified + * Country calling code prefix * - * @param email the value + * @param phoneNumberPrefix the value * @return this builder */ - public Builder email(String email) { - return set("email", Conv.STRING, email); + public Builder phoneNumberPrefix(String phoneNumberPrefix) { + return set("phone_number_prefix", Conv.STRING, phoneNumberPrefix); } /** - * Indicates if the email was found in known data breaches + * Phone number without country prefix * - * @param isBreached the value + * @param phoneNumber the value * @return this builder */ - public Builder isBreached(boolean isBreached) { - return set("is_breached", Conv.BOOLEAN, isBreached); + public Builder phoneNumber(String phoneNumber) { + return set("phone_number", Conv.STRING, phoneNumber); } /** - * List of known breaches this email was found in + * Complete phone number with country prefix * - * @param breaches the value + * @param fullNumber the value + * @return this builder + */ + public Builder fullNumber(String fullNumber) { + return set("full_number", Conv.STRING, fullNumber); + } + + /** + * Two-letter country code (ISO 3166-1 alpha-2) + * + * @param countryCode the value * @return this builder */ - public Builder breaches(List breaches) { - return set("breaches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Breaches::fromJson)), breaches); + public Builder countryCode(String countryCode) { + return set("country_code", Conv.STRING, countryCode); } /** - * Indicates if the email is from a disposable provider + * Full country name + * + * @param countryName the value + * @return this builder + */ + public Builder countryName(String countryName) { + return set("country_name", Conv.STRING, countryName); + } + + /** + * The carrier field. + * + * @param carrier the value + * @return this builder + */ + public Builder carrier(SessionDecision.PhoneVerifications.Carrier carrier) { + return set("carrier", Conv.model(SessionDecision.PhoneVerifications.Carrier::fromJson), carrier); + } + + /** + * Whether the phone number is from a disposable service * * @param isDisposable the value * @return this builder @@ -13165,17 +12906,27 @@ public Builder isDisposable(boolean isDisposable) { } /** - * Indicates if the email address is undeliverable + * Whether the phone number is virtual * - * @param isUndeliverable the value + * @param isVirtual the value * @return this builder */ - public Builder isUndeliverable(boolean isUndeliverable) { - return set("is_undeliverable", Conv.BOOLEAN, isUndeliverable); + public Builder isVirtual(boolean isVirtual) { + return set("is_virtual", Conv.BOOLEAN, isVirtual); } /** - * Number of verification attempts for this email + * Method used to verify the phone number + * + * @param verificationMethod the value + * @return this builder + */ + public Builder verificationMethod(String verificationMethod) { + return set("verification_method", Conv.STRING, verificationMethod); + } + + /** + * Number of verification attempts made * * @param verificationAttempts the value * @return this builder @@ -13185,7 +12936,7 @@ public Builder verificationAttempts(long verificationAttempts) { } /** - * Timestamp when the email was verified + * Timestamp when the phone was verified * * @param verifiedAt the value * @return this builder @@ -13195,42 +12946,42 @@ public Builder verifiedAt(OffsetDateTime verifiedAt) { } /** - * List of warnings related to email verification + * Chronological list of events in the phone verification lifecycle. * - * @param warnings the value + * @param lifecycle the value * @return this builder */ - public Builder warnings(List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Warnings::fromJson)), warnings); + public Builder lifecycle(List lifecycle) { + return set("lifecycle", Conv.list(Conv.model(SessionDecision.PhoneVerifications.Lifecycle::fromJson)), lifecycle); } /** - * Chronological list of events in the email verification lifecycle. + * List of warnings related to phone verification * - * @param lifecycle the value + * @param warnings the value * @return this builder */ - public Builder lifecycle(List lifecycle) { - return set("lifecycle", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Lifecycle::fromJson)), lifecycle); + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SessionDecision.PhoneVerifications.Warnings::fromJson)), warnings); } /** - * Matches found for this email address. Each match has a source: session (another verification session in your application matched on the same email, returned with session_id/session_number/vendor_data/verification_date) or list_entry (the email is on an org-managed allow/block list — see /management-api/lists/overview). Cross-session matches use different vendor_data and are capped at 5. + * Matches found for this phone number. Each match has a source: session (another verification session in your application matched on the same number, returned with session_id/session_number/vendor_data/verification_date) or list_entry (the number is on an org-managed allow/block list — see /management-api/lists/overview). Cross-session matches use different vendor_data and are capped at 5. * * @param matches the value * @return this builder */ - public Builder matches(List matches) { - return set("matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Matches::fromJson)), matches); + public Builder matches(List matches) { + return set("matches", Conv.list(Conv.model(SessionDecision.PhoneVerifications.Matches::fromJson)), matches); } /** * Builds the value. * - * @return a new immutable {@code EmailVerifications} + * @return a new immutable {@code PhoneVerifications} */ - public EmailVerifications build() { - return new EmailVerifications(buildJson(), buildFiles()); + public PhoneVerifications build() { + return new PhoneVerifications(buildJson(), buildFiles()); } @Override @@ -13240,7 +12991,7 @@ protected Builder self() { } /** - * Feature-level status of this email verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * Feature-level status of this phone verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -13291,22 +13042,22 @@ public boolean isKnown() { } } - public static final class Breaches extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("name", "domain", "logo_path", "breach_date", "description", "is_verified", "data_classes", "breach_emails_count"); + public static final class Carrier extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "type"); - private Breaches(ObjectNode json, Map> files) { + private Carrier(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code Breaches} from JSON. Unknown fields are kept and readable + * Reads a {@code Carrier} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static Breaches fromJson(JsonNode json) { - return new Breaches(Json.copyObject(json), Map.of()); + public static Carrier fromJson(JsonNode json) { + return new Carrier(Json.copyObject(json), Map.of()); } /** @@ -13328,7 +13079,7 @@ public Builder toBuilder() { } /** - * Name of the breach + * Name of the phone carrier * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -13338,73 +13089,138 @@ public Optional name() { } /** - * Domain affected by the breach + * Type of phone line (mobile, landline, etc) * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional domain() { - return Json.optional(json, "domain", Conv.STRING); + public Optional type() { + return Json.optional(json, "type", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Carrier}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Carrier from) { + super(from); + } + + /** + * Name of the phone carrier + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Type of phone line (mobile, landline, etc) + * + * @param type the value + * @return this builder + */ + public Builder type(String type) { + return set("type", Conv.STRING, type); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Carrier} + */ + public Carrier build() { + return new Carrier(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Lifecycle extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "timestamp", "details", "fee"); + + private Lifecycle(ObjectNode json, Map> files) { + super(json, files); } /** - * URL to the logo of the breached service + * Reads a {@code Lifecycle} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param json a JSON object + * @return the parsed value */ - public Optional logoPath() { - return Json.optional(json, "logo_path", Conv.STRING); + public static Lifecycle fromJson(JsonNode json) { + return new Lifecycle(Json.copyObject(json), Map.of()); } /** - * Date when the breach occurred + * Returns an empty builder. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new builder */ - public Optional breachDate() { - return Json.optional(json, "breach_date", Conv.DATE); + public static Builder builder() { + return new Builder(); } /** - * Description of the breach + * Returns a builder holding a copy of every field of this value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new builder */ - public Optional description() { - return Json.optional(json, "description", Conv.STRING); + public Builder toBuilder() { + return new Builder(this); } /** - * Whether the breach is verified + * The type field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional isVerified() { - return Json.optional(json, "is_verified", Conv.BOOLEAN); + public Optional type() { + return Json.optional(json, "type", Conv.STRING); } /** - * Types of data exposed in the breach + * The timestamp field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> dataClasses() { - return Json.optional(json, "data_classes", Conv.list(Conv.STRING)); + public Optional timestamp() { + return Json.optional(json, "timestamp", Conv.DATE_TIME); } /** - * Number of email addresses affected in the breach + * Event-specific payload. Send events carry status, reason, channel, actual_channel; delivery events carry channel, status; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. PHONE_VERIFICATION_APPROVED). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional breachEmailsCount() { - return Json.optional(json, "breach_emails_count", Conv.LONG); + public Optional details() { + return Json.optional(json, "details", Conv.model(SessionDecision.PhoneVerifications.Lifecycle.Details::fromJson)); + } + + /** + * The fee field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fee() { + return Json.optional(json, "fee", Conv.DOUBLE); } @Override @@ -13412,106 +13228,234 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link Breaches}. */ + /** Builds a {@link Lifecycle}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(Breaches from) { + private Builder(Lifecycle from) { super(from); } /** - * Name of the breach + * The type field. * - * @param name the value + * @param type the value * @return this builder */ - public Builder name(String name) { - return set("name", Conv.STRING, name); + public Builder type(String type) { + return set("type", Conv.STRING, type); } /** - * Domain affected by the breach + * The timestamp field. * - * @param domain the value + * @param timestamp the value * @return this builder */ - public Builder domain(String domain) { - return set("domain", Conv.STRING, domain); + public Builder timestamp(OffsetDateTime timestamp) { + return set("timestamp", Conv.DATE_TIME, timestamp); } /** - * URL to the logo of the breached service + * Event-specific payload. Send events carry status, reason, channel, actual_channel; delivery events carry channel, status; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. PHONE_VERIFICATION_APPROVED). * - * @param logoPath the value + * @param details the value, or {@code null} to send JSON null * @return this builder */ - public Builder logoPath(String logoPath) { - return set("logo_path", Conv.STRING, logoPath); + public Builder details(SessionDecision.PhoneVerifications.Lifecycle.@Nullable Details details) { + return setNullable("details", Conv.model(SessionDecision.PhoneVerifications.Lifecycle.Details::fromJson), details); } /** - * Date when the breach occurred + * The fee field. * - * @param breachDate the value + * @param fee the value * @return this builder */ - public Builder breachDate(LocalDate breachDate) { - return set("breach_date", Conv.DATE, breachDate); + public Builder fee(double fee) { + return set("fee", Conv.DOUBLE, fee); } /** - * Description of the breach + * Builds the value. * - * @param description the value - * @return this builder + * @return a new immutable {@code Lifecycle} */ - public Builder description(String description) { - return set("description", Conv.STRING, description); + public Lifecycle build() { + return new Lifecycle(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Event-specific payload. Send events carry status, reason, channel, actual_channel; delivery events carry channel, status; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. PHONE_VERIFICATION_APPROVED). + */ + public static final class Details extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("status", "reason", "channel", "actual_channel", "code_tried"); + + private Details(ObjectNode json, Map> files) { + super(json, files); } /** - * Whether the breach is verified + * Reads a {@code Details} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. * - * @param isVerified the value - * @return this builder + * @param json a JSON object + * @return the parsed value */ - public Builder isVerified(boolean isVerified) { - return set("is_verified", Conv.BOOLEAN, isVerified); + public static Details fromJson(JsonNode json) { + return new Details(Json.copyObject(json), Map.of()); } /** - * Types of data exposed in the breach + * Returns an empty builder. * - * @param dataClasses the value - * @return this builder + * @return a new builder */ - public Builder dataClasses(List dataClasses) { - return set("data_classes", Conv.list(Conv.STRING), dataClasses); + public static Builder builder() { + return new Builder(); } /** - * Number of email addresses affected in the breach + * Returns a builder holding a copy of every field of this value. * - * @param breachEmailsCount the value - * @return this builder + * @return a new builder */ - public Builder breachEmailsCount(long breachEmailsCount) { - return set("breach_emails_count", Conv.LONG, breachEmailsCount); + public Builder toBuilder() { + return new Builder(this); } /** - * Builds the value. + * The status field. * - * @return a new immutable {@code Breaches} + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Breaches build() { - return new Breaches(buildJson(), buildFiles()); + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * The reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * The channel field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional channel() { + return Json.optional(json, "channel", Conv.STRING); + } + + /** + * The actual_channel field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actualChannel() { + return Json.optional(json, "actual_channel", Conv.STRING); + } + + /** + * The code_tried field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional codeTried() { + return Json.optional(json, "code_tried", Conv.STRING); } @Override - protected Builder self() { - return this; + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Details}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Details from) { + super(from); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * The reason field. + * + * @param reason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reason(@Nullable String reason) { + return setNullable("reason", Conv.STRING, reason); + } + + /** + * The channel field. + * + * @param channel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder channel(@Nullable String channel) { + return setNullable("channel", Conv.STRING, channel); + } + + /** + * The actual_channel field. + * + * @param actualChannel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actualChannel(@Nullable String actualChannel) { + return setNullable("actual_channel", Conv.STRING, actualChannel); + } + + /** + * The code_tried field. + * + * @param codeTried the value + * @return this builder + */ + public Builder codeTried(String codeTried) { + return set("code_tried", Conv.STRING, codeTried); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Details} + */ + public Details build() { + return new Details(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } } } } @@ -13558,8 +13502,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.EmailVerifications.Warnings.Feature::of)); + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(SessionDecision.PhoneVerifications.Warnings.Feature::of)); } /** @@ -13641,8 +13585,8 @@ private Builder(Warnings from) { * @param feature the value * @return this builder */ - public Builder feature(SessionsRetrieveDecisionResponse.EmailVerifications.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.EmailVerifications.Warnings.Feature::of), feature); + public Builder feature(SessionDecision.PhoneVerifications.Warnings.Feature feature) { + return set("feature", Conv.enumeration(SessionDecision.PhoneVerifications.Warnings.Feature::of), feature); } /** @@ -13803,22 +13747,22 @@ public boolean isKnown() { } } - public static final class Lifecycle extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "timestamp", "details", "fee"); + public static final class Matches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "session_number", "vendor_data", "verification_date", "phone_number", "status", "is_blocklisted", "api_service", "source"); - private Lifecycle(ObjectNode json, Map> files) { + private Matches(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code Lifecycle} from JSON. Unknown fields are kept and readable + * Reads a {@code Matches} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static Lifecycle fromJson(JsonNode json) { - return new Lifecycle(Json.copyObject(json), Map.of()); + public static Matches fromJson(JsonNode json) { + return new Matches(Json.copyObject(json), Map.of()); } /** @@ -13840,43 +13784,93 @@ public Builder toBuilder() { } /** - * The type field. + * The session_id field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional type() { - return Json.optional(json, "type", Conv.STRING); + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); } /** - * The timestamp field. + * The session_number field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional timestamp() { - return Json.optional(json, "timestamp", Conv.DATE_TIME); + public Optional sessionNumber() { + return Json.optional(json, "session_number", Conv.LONG); } /** - * Event-specific payload. Send events carry status, reason; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. EMAIL_VERIFICATION_APPROVED). + * The vendor_data field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional details() { - return Json.optional(json, "details", Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Lifecycle.Details::fromJson)); + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); } /** - * The fee field. + * The verification_date field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional fee() { - return Json.optional(json, "fee", Conv.DOUBLE); + public Optional verificationDate() { + return Json.optional(json, "verification_date", Conv.DATE_TIME); + } + + /** + * The phone_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneNumber() { + return Json.optional(json, "phone_number", Conv.STRING); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * The is_blocklisted field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isBlocklisted() { + return Json.optional(json, "is_blocklisted", Conv.BOOLEAN); + } + + /** + * The api_service field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional apiService() { + return Json.optional(json, "api_service", Conv.STRING); + } + + /** + * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(SessionDecision.PhoneVerifications.Matches.Source::of)); } @Override @@ -13884,322 +13878,634 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link Lifecycle}. */ + /** Builds a {@link Matches}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(Lifecycle from) { + private Builder(Matches from) { super(from); } /** - * The type field. + * The session_id field. * - * @param type the value + * @param sessionId the value, or {@code null} to send JSON null * @return this builder */ - public Builder type(String type) { - return set("type", Conv.STRING, type); + public Builder sessionId(@Nullable String sessionId) { + return setNullable("session_id", Conv.STRING, sessionId); } /** - * The timestamp field. + * The session_number field. * - * @param timestamp the value + * @param sessionNumber the value, or {@code null} to send JSON null * @return this builder */ - public Builder timestamp(OffsetDateTime timestamp) { - return set("timestamp", Conv.DATE_TIME, timestamp); + public Builder sessionNumber(@Nullable Long sessionNumber) { + return setNullable("session_number", Conv.LONG, sessionNumber); } /** - * Event-specific payload. Send events carry status, reason; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. EMAIL_VERIFICATION_APPROVED). + * The vendor_data field. * - * @param details the value, or {@code null} to send JSON null + * @param vendorData the value, or {@code null} to send JSON null * @return this builder */ - public Builder details(SessionsRetrieveDecisionResponse.EmailVerifications.Lifecycle.@Nullable Details details) { - return setNullable("details", Conv.model(SessionsRetrieveDecisionResponse.EmailVerifications.Lifecycle.Details::fromJson), details); + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); } /** - * The fee field. + * The verification_date field. * - * @param fee the value + * @param verificationDate the value, or {@code null} to send JSON null * @return this builder */ - public Builder fee(double fee) { - return set("fee", Conv.DOUBLE, fee); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Lifecycle} - */ - public Lifecycle build() { - return new Lifecycle(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Event-specific payload. Send events carry status, reason; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. EMAIL_VERIFICATION_APPROVED). - */ - public static final class Details extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("status", "reason", "code_tried"); - - private Details(ObjectNode json, Map> files) { - super(json, files); + public Builder verificationDate(@Nullable OffsetDateTime verificationDate) { + return setNullable("verification_date", Conv.DATE_TIME, verificationDate); } /** - * Reads a {@code Details} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. + * The phone_number field. * - * @param json a JSON object - * @return the parsed value + * @param phoneNumber the value + * @return this builder */ - public static Details fromJson(JsonNode json) { - return new Details(Json.copyObject(json), Map.of()); + public Builder phoneNumber(String phoneNumber) { + return set("phone_number", Conv.STRING, phoneNumber); } /** - * Returns an empty builder. + * The status field. * - * @return a new builder + * @param status the value, or {@code null} to send JSON null + * @return this builder */ - public static Builder builder() { - return new Builder(); + public Builder status(@Nullable String status) { + return setNullable("status", Conv.STRING, status); } /** - * Returns a builder holding a copy of every field of this value. + * The is_blocklisted field. * - * @return a new builder + * @param isBlocklisted the value + * @return this builder */ - public Builder toBuilder() { - return new Builder(this); + public Builder isBlocklisted(boolean isBlocklisted) { + return set("is_blocklisted", Conv.BOOLEAN, isBlocklisted); } /** - * The status field. + * The api_service field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param apiService the value, or {@code null} to send JSON null + * @return this builder */ - public Optional status() { - return Json.optional(json, "status", Conv.STRING); + public Builder apiService(@Nullable String apiService) { + return setNullable("api_service", Conv.STRING, apiService); } /** - * The reason field. + * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param source the value + * @return this builder */ - public Optional reason() { - return Json.optional(json, "reason", Conv.STRING); + public Builder source(SessionDecision.PhoneVerifications.Matches.Source source) { + return set("source", Conv.enumeration(SessionDecision.PhoneVerifications.Matches.Source::of), source); } /** - * The code_tried field. + * Builds the value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new immutable {@code Matches} */ - public Optional codeTried() { - return Json.optional(json, "code_tried", Conv.STRING); + public Matches build() { + return new Matches(buildJson(), buildFiles()); } @Override - protected Set knownFields() { - return KNOWN_FIELDS; + protected Builder self() { + return this; } + } - /** Builds a {@link Details}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** + * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "session"}. */ + public static final Source SESSION = new Source("session"); - private Builder(Details from) { - super(from); - } + /** The value {@code "list_entry"}. */ + public static final Source LIST_ENTRY = new Source("list_entry"); - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(String status) { - return set("status", Conv.STRING, status); - } + private static final List KNOWN_VALUES = List.of(SESSION, LIST_ENTRY); - /** - * The reason field. - * - * @param reason the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder reason(@Nullable String reason) { - return setNullable("reason", Conv.STRING, reason); - } + private Source(String value) { + super(value); + } - /** - * The code_tried field. - * - * @param codeTried the value - * @return this builder - */ - public Builder codeTried(String codeTried) { - return set("code_tried", Conv.STRING, codeTried); - } + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } - /** - * Builds the value. - * - * @return a new immutable {@code Details} - */ - public Details build() { - return new Details(buildJson(), buildFiles()); - } + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } - @Override - protected Builder self() { - return this; - } + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); } } } + } - public static final class Matches extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("session_id", "session_number", "vendor_data", "verification_date", "email", "status", "is_blocklisted", "api_service", "source"); + public static final class EmailVerifications extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "email", "is_breached", "breaches", "is_disposable", "is_undeliverable", "verification_attempts", "verified_at", "warnings", "lifecycle", "matches"); - private Matches(ObjectNode json, Map> files) { - super(json, files); + private EmailVerifications(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code EmailVerifications} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static EmailVerifications fromJson(JsonNode json) { + return new EmailVerifications(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Unique identifier for this email step in the workflow graph. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + /** + * Feature-level status of this email verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.EmailVerifications.Status::of)); + } + + /** + * The email address being verified + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + /** + * Indicates if the email was found in known data breaches + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isBreached() { + return Json.optional(json, "is_breached", Conv.BOOLEAN); + } + + /** + * List of known breaches this email was found in + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> breaches() { + return Json.optional(json, "breaches", Conv.list(Conv.model(SessionDecision.EmailVerifications.Breaches::fromJson))); + } + + /** + * Indicates if the email is from a disposable provider + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isDisposable() { + return Json.optional(json, "is_disposable", Conv.BOOLEAN); + } + + /** + * Indicates if the email address is undeliverable + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isUndeliverable() { + return Json.optional(json, "is_undeliverable", Conv.BOOLEAN); + } + + /** + * Number of verification attempts for this email + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verificationAttempts() { + return Json.optional(json, "verification_attempts", Conv.LONG); + } + + /** + * Timestamp when the email was verified + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verifiedAt() { + return Json.optional(json, "verified_at", Conv.DATE_TIME); + } + + /** + * List of warnings related to email verification + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SessionDecision.EmailVerifications.Warnings::fromJson))); + } + + /** + * Chronological list of events in the email verification lifecycle. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> lifecycle() { + return Json.optional(json, "lifecycle", Conv.list(Conv.model(SessionDecision.EmailVerifications.Lifecycle::fromJson))); + } + + /** + * Matches found for this email address. Each match has a source: session (another verification session in your application matched on the same email, returned with session_id/session_number/vendor_data/verification_date) or list_entry (the email is on an org-managed allow/block list — see /management-api/lists/overview). Cross-session matches use different vendor_data and are capped at 5. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> matches() { + return Json.optional(json, "matches", Conv.list(Conv.model(SessionDecision.EmailVerifications.Matches::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link EmailVerifications}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(EmailVerifications from) { + super(from); } /** - * Reads a {@code Matches} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. + * Unique identifier for this email step in the workflow graph. * - * @param json a JSON object - * @return the parsed value + * @param nodeId the value + * @return this builder */ - public static Matches fromJson(JsonNode json) { - return new Matches(Json.copyObject(json), Map.of()); + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); } /** - * Returns an empty builder. + * Feature-level status of this email verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. * - * @return a new builder + * @param status the value + * @return this builder */ - public static Builder builder() { - return new Builder(); + public Builder status(SessionDecision.EmailVerifications.Status status) { + return set("status", Conv.enumeration(SessionDecision.EmailVerifications.Status::of), status); } /** - * Returns a builder holding a copy of every field of this value. + * The email address being verified * - * @return a new builder + * @param email the value + * @return this builder */ - public Builder toBuilder() { - return new Builder(this); + public Builder email(String email) { + return set("email", Conv.STRING, email); } /** - * The session_id field. + * Indicates if the email was found in known data breaches * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param isBreached the value + * @return this builder */ - public Optional sessionId() { - return Json.optional(json, "session_id", Conv.STRING); + public Builder isBreached(boolean isBreached) { + return set("is_breached", Conv.BOOLEAN, isBreached); } /** - * The session_number field. + * List of known breaches this email was found in * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param breaches the value + * @return this builder */ - public Optional sessionNumber() { - return Json.optional(json, "session_number", Conv.LONG); + public Builder breaches(List breaches) { + return set("breaches", Conv.list(Conv.model(SessionDecision.EmailVerifications.Breaches::fromJson)), breaches); } /** - * The vendor_data field. + * Indicates if the email is from a disposable provider * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param isDisposable the value + * @return this builder */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); + public Builder isDisposable(boolean isDisposable) { + return set("is_disposable", Conv.BOOLEAN, isDisposable); } /** - * The verification_date field. + * Indicates if the email address is undeliverable * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param isUndeliverable the value + * @return this builder */ - public Optional verificationDate() { - return Json.optional(json, "verification_date", Conv.DATE_TIME); + public Builder isUndeliverable(boolean isUndeliverable) { + return set("is_undeliverable", Conv.BOOLEAN, isUndeliverable); } /** - * The email field. + * Number of verification attempts for this email * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param verificationAttempts the value + * @return this builder */ - public Optional email() { - return Json.optional(json, "email", Conv.STRING); + public Builder verificationAttempts(long verificationAttempts) { + return set("verification_attempts", Conv.LONG, verificationAttempts); } /** - * The status field. + * Timestamp when the email was verified * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param verifiedAt the value + * @return this builder */ - public Optional status() { - return Json.optional(json, "status", Conv.STRING); + public Builder verifiedAt(OffsetDateTime verifiedAt) { + return set("verified_at", Conv.DATE_TIME, verifiedAt); } /** - * The is_blocklisted field. + * List of warnings related to email verification * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param warnings the value + * @return this builder */ - public Optional isBlocklisted() { - return Json.optional(json, "is_blocklisted", Conv.BOOLEAN); + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SessionDecision.EmailVerifications.Warnings::fromJson)), warnings); } /** - * The api_service field. + * Chronological list of events in the email verification lifecycle. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param lifecycle the value + * @return this builder */ - public Optional apiService() { - return Json.optional(json, "api_service", Conv.STRING); + public Builder lifecycle(List lifecycle) { + return set("lifecycle", Conv.list(Conv.model(SessionDecision.EmailVerifications.Lifecycle::fromJson)), lifecycle); } /** - * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. + * Matches found for this email address. Each match has a source: session (another verification session in your application matched on the same email, returned with session_id/session_number/vendor_data/verification_date) or list_entry (the email is on an org-managed allow/block list — see /management-api/lists/overview). Cross-session matches use different vendor_data and are capped at 5. + * + * @param matches the value + * @return this builder + */ + public Builder matches(List matches) { + return set("matches", Conv.list(Conv.model(SessionDecision.EmailVerifications.Matches::fromJson)), matches); + } + + /** + * Builds the value. + * + * @return a new immutable {@code EmailVerifications} + */ + public EmailVerifications build() { + return new EmailVerifications(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Feature-level status of this email verification step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Expired"}. */ + public static final Status EXPIRED = new Status("Expired"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, EXPIRED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class Breaches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "domain", "logo_path", "breach_date", "description", "is_verified", "data_classes", "breach_emails_count"); + + private Breaches(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Breaches} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Breaches fromJson(JsonNode json) { + return new Breaches(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Name of the breach + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * Domain affected by the breach + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional domain() { + return Json.optional(json, "domain", Conv.STRING); + } + + /** + * URL to the logo of the breached service + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional logoPath() { + return Json.optional(json, "logo_path", Conv.STRING); + } + + /** + * Date when the breach occurred + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional breachDate() { + return Json.optional(json, "breach_date", Conv.DATE); + } + + /** + * Description of the breach + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } + + /** + * Whether the breach is verified + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isVerified() { + return Json.optional(json, "is_verified", Conv.BOOLEAN); + } + + /** + * Types of data exposed in the breach + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> dataClasses() { + return Json.optional(json, "data_classes", Conv.list(Conv.STRING)); + } + + /** + * Number of email addresses affected in the breach * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional source() { - return Json.optional(json, "source", Conv.enumeration(SessionsRetrieveDecisionResponse.EmailVerifications.Matches.Source::of)); + public Optional breachEmailsCount() { + return Json.optional(json, "breach_emails_count", Conv.LONG); } @Override @@ -14207,111 +14513,101 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link Matches}. */ + /** Builds a {@link Breaches}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(Matches from) { + private Builder(Breaches from) { super(from); } /** - * The session_id field. + * Name of the breach * - * @param sessionId the value, or {@code null} to send JSON null + * @param name the value * @return this builder */ - public Builder sessionId(@Nullable String sessionId) { - return setNullable("session_id", Conv.STRING, sessionId); + public Builder name(String name) { + return set("name", Conv.STRING, name); } /** - * The session_number field. + * Domain affected by the breach * - * @param sessionNumber the value, or {@code null} to send JSON null + * @param domain the value * @return this builder */ - public Builder sessionNumber(@Nullable Long sessionNumber) { - return setNullable("session_number", Conv.LONG, sessionNumber); + public Builder domain(String domain) { + return set("domain", Conv.STRING, domain); } /** - * The vendor_data field. + * URL to the logo of the breached service * - * @param vendorData the value, or {@code null} to send JSON null + * @param logoPath the value * @return this builder */ - public Builder vendorData(@Nullable String vendorData) { - return setNullable("vendor_data", Conv.STRING, vendorData); + public Builder logoPath(String logoPath) { + return set("logo_path", Conv.STRING, logoPath); } /** - * The verification_date field. + * Date when the breach occurred * - * @param verificationDate the value, or {@code null} to send JSON null + * @param breachDate the value * @return this builder */ - public Builder verificationDate(@Nullable OffsetDateTime verificationDate) { - return setNullable("verification_date", Conv.DATE_TIME, verificationDate); + public Builder breachDate(LocalDate breachDate) { + return set("breach_date", Conv.DATE, breachDate); } /** - * The email field. + * Description of the breach * - * @param email the value + * @param description the value * @return this builder */ - public Builder email(String email) { - return set("email", Conv.STRING, email); + public Builder description(String description) { + return set("description", Conv.STRING, description); } /** - * The status field. + * Whether the breach is verified * - * @param status the value, or {@code null} to send JSON null + * @param isVerified the value * @return this builder */ - public Builder status(@Nullable String status) { - return setNullable("status", Conv.STRING, status); + public Builder isVerified(boolean isVerified) { + return set("is_verified", Conv.BOOLEAN, isVerified); } /** - * The is_blocklisted field. + * Types of data exposed in the breach * - * @param isBlocklisted the value + * @param dataClasses the value * @return this builder */ - public Builder isBlocklisted(boolean isBlocklisted) { - return set("is_blocklisted", Conv.BOOLEAN, isBlocklisted); + public Builder dataClasses(List dataClasses) { + return set("data_classes", Conv.list(Conv.STRING), dataClasses); } /** - * The api_service field. + * Number of email addresses affected in the breach * - * @param apiService the value, or {@code null} to send JSON null + * @param breachEmailsCount the value * @return this builder */ - public Builder apiService(@Nullable String apiService) { - return setNullable("api_service", Conv.STRING, apiService); + public Builder breachEmailsCount(long breachEmailsCount) { + return set("breach_emails_count", Conv.LONG, breachEmailsCount); } /** - * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. + * Builds the value. * - * @param source the value - * @return this builder - */ - public Builder source(SessionsRetrieveDecisionResponse.EmailVerifications.Matches.Source source) { - return set("source", Conv.enumeration(SessionsRetrieveDecisionResponse.EmailVerifications.Matches.Source::of), source); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Matches} + * @return a new immutable {@code Breaches} */ - public Matches build() { - return new Matches(buildJson(), buildFiles()); + public Breaches build() { + return new Breaches(buildJson(), buildFiles()); } @Override @@ -14319,667 +14615,584 @@ protected Builder self() { return this; } } + } + + public static final class Warnings extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); + + private Warnings(ObjectNode json, Map> files) { + super(json, files); + } /** - * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. + * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + * @param json a JSON object + * @return the parsed value */ - public static final class Source extends StringEnum { - /** The value {@code "session"}. */ - public static final Source SESSION = new Source("session"); + public static Warnings fromJson(JsonNode json) { + return new Warnings(Json.copyObject(json), Map.of()); + } - /** The value {@code "list_entry"}. */ - public static final Source LIST_ENTRY = new Source("list_entry"); + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - private static final List KNOWN_VALUES = List.of(SESSION, LIST_ENTRY); + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - private Source(String value) { - super(value); + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(SessionDecision.EmailVerifications.Warnings.Feature::of)); + } + + /** + * The risk field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.STRING); + } + + /** + * The additional_data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> additionalData() { + return Json.optional(json, "additional_data", Conv.OBJECT); + } + + /** + * The log_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional logType() { + return Json.optional(json, "log_type", Conv.STRING); + } + + /** + * The short_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional shortDescription() { + return Json.optional(json, "short_description", Conv.STRING); + } + + /** + * The long_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional longDescription() { + return Json.optional(json, "long_description", Conv.STRING); + } + + /** + * The workflow graph node ID that generated this warning + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Warnings}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Warnings from) { + super(from); } /** - * Returns the constant for a known value, or a new instance for any other value. + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. * - * @param value the string value - * @return the enum value + * @param feature the value + * @return this builder */ - public static Source of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + public Builder feature(SessionDecision.EmailVerifications.Warnings.Feature feature) { + return set("feature", Conv.enumeration(SessionDecision.EmailVerifications.Warnings.Feature::of), feature); } /** - * Returns the values known to this SDK version. + * The risk field. * - * @return the known values + * @param risk the value + * @return this builder */ - public static List knownValues() { - return KNOWN_VALUES; + public Builder risk(String risk) { + return set("risk", Conv.STRING, risk); } - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); + /** + * The additional_data field. + * + * @param additionalData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder additionalData(@Nullable Map additionalData) { + return setAny("additional_data", additionalData); } - } - } - } - public static final class PoaVerifications extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "document_file", "issuing_state", "document_type", "document_subtype", "document_language", "document_metadata", "issuer", "issue_date", "expiration_date", "poa_address", "poa_formatted_address", "poa_parsed_address", "name_on_document", "name_match_score_expected_details", "name_match_score_id_verification", "expected_details_address", "expected_details_formatted_address", "expected_details_parsed_address", "extra_fields", "extra_files", "warnings", "detected_codes"); + /** + * The log_type field. + * + * @param logType the value + * @return this builder + */ + public Builder logType(String logType) { + return set("log_type", Conv.STRING, logType); + } - private PoaVerifications(ObjectNode json, Map> files) { - super(json, files); - } + /** + * The short_description field. + * + * @param shortDescription the value + * @return this builder + */ + public Builder shortDescription(String shortDescription) { + return set("short_description", Conv.STRING, shortDescription); + } - /** - * Reads a {@code PoaVerifications} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static PoaVerifications fromJson(JsonNode json) { - return new PoaVerifications(Json.copyObject(json), Map.of()); - } + /** + * The long_description field. + * + * @param longDescription the value + * @return this builder + */ + public Builder longDescription(String longDescription) { + return set("long_description", Conv.STRING, longDescription); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * The workflow graph node ID that generated this warning + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * Builds the value. + * + * @return a new immutable {@code Warnings} + */ + public Warnings build() { + return new Warnings(buildJson(), buildFiles()); + } - /** - * Unique identifier for this POA step in the workflow graph. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } + @Override + protected Builder self() { + return this; + } + } - /** - * Feature-level status of this Proof of Address step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.PoaVerifications.Status::of)); - } + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Feature extends StringEnum { + /** The value {@code "ID_VERIFICATION"}. */ + public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); - /** - * URL of the proof of address document - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentFile() { - return Json.optional(json, "document_file", Conv.STRING); - } + /** The value {@code "NFC"}. */ + public static final Feature NFC = new Feature("NFC"); - /** - * Two-letter country code of the issuing state - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional issuingState() { - return Json.optional(json, "issuing_state", Conv.STRING); - } + /** The value {@code "LIVENESS"}. */ + public static final Feature LIVENESS = new Feature("LIVENESS"); - /** - * Type of proof of address document - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentType() { - return Json.optional(json, "document_type", Conv.STRING); - } + /** The value {@code "FACEMATCH"}. */ + public static final Feature FACEMATCH = new Feature("FACEMATCH"); - /** - * More specific classification of the POA document within its document_type, when one was detected. null otherwise. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentSubtype() { - return Json.optional(json, "document_subtype", Conv.STRING); - } + /** The value {@code "PROOF_OF_ADDRESS"}. */ + public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); - /** - * Language code of the document - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentLanguage() { - return Json.optional(json, "document_language", Conv.STRING); - } + /** The value {@code "PHONE"}. */ + public static final Feature PHONE = new Feature("PHONE"); - /** - * File-forensics metadata extracted from the uploaded POA document. Always contains the keys below (values are null when not applicable, e.g. EXIF dates for PDFs). null when no metadata could be extracted. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentMetadata() { - return Json.optional(json, "document_metadata", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.DocumentMetadata::fromJson)); - } + /** The value {@code "EMAIL"}. */ + public static final Feature EMAIL = new Feature("EMAIL"); - /** - * Name of the document issuer - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional issuer() { - return Json.optional(json, "issuer", Conv.STRING); - } + /** The value {@code "AML"}. */ + public static final Feature AML = new Feature("AML"); - /** - * Date when the document was issued - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional issueDate() { - return Json.optional(json, "issue_date", Conv.DATE); - } + /** The value {@code "LOCATION"}. */ + public static final Feature LOCATION = new Feature("LOCATION"); - /** - * Expiration date of the document, if available - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional expirationDate() { - return Json.optional(json, "expiration_date", Conv.DATE); - } + /** The value {@code "DATABASE_VALIDATION"}. */ + public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); - /** - * Raw address from the proof of address document - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional poaAddress() { - return Json.optional(json, "poa_address", Conv.STRING); - } + /** The value {@code "QUESTIONNAIRE"}. */ + public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); - /** - * Formatted address from the proof of address document - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional poaFormattedAddress() { - return Json.optional(json, "poa_formatted_address", Conv.STRING); - } + /** The value {@code "DOCUMENT_AI"}. */ + public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); - /** - * The poa_parsed_address field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional poaParsedAddress() { - return Json.optional(json, "poa_parsed_address", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.PoaParsedAddress::fromJson)); - } + /** The value {@code "KYB_REGISTRY"}. */ + public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); - /** - * Account-holder / addressee name extracted from the POA document, used for name matching. null when no name could be extracted. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nameOnDocument() { - return Json.optional(json, "name_on_document", Conv.STRING); - } + /** The value {@code "KYB_DOCUMENTS"}. */ + public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); - /** - * Similarity score (0-100) between name_on_document and the expected name supplied at session creation. null when either side is missing. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nameMatchScoreExpectedDetails() { - return Json.optional(json, "name_match_score_expected_details", Conv.DOUBLE); - } + /** The value {@code "KYB_KEY_PEOPLE"}. */ + public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); - /** - * Similarity score (0-100) between name_on_document and the name extracted by the ID verification step. null when either side is missing. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nameMatchScoreIdVerification() { - return Json.optional(json, "name_match_score_id_verification", Conv.DOUBLE); - } + private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); - /** - * Expected raw address for verification - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional expectedDetailsAddress() { - return Json.optional(json, "expected_details_address", Conv.STRING); - } + private Feature(String value) { + super(value); + } - /** - * Expected formatted address for verification - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional expectedDetailsFormattedAddress() { - return Json.optional(json, "expected_details_formatted_address", Conv.STRING); - } + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Feature of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); + } - /** - * Expected parsed address details for verification - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> expectedDetailsParsedAddress() { - return Json.optional(json, "expected_details_parsed_address", Conv.OBJECT); - } + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } - /** - * Additional banking and contact details extracted from the document. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional extraFields() { - return Json.optional(json, "extra_fields", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.ExtraFields::fromJson)); + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } } - /** - * Additional proof of address files - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> extraFiles() { - return Json.optional(json, "extra_files", Conv.list(Conv.STRING)); - } + public static final class Lifecycle extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "timestamp", "details", "fee"); - /** - * The warnings field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.Warnings::fromJson))); - } - - /** - * QR codes and barcodes detected across every processed page of the document. Empty when none are found. A located but undecodable QR code has parsed_payload: null. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> detectedCodes() { - return Json.optional(json, "detected_codes", Conv.list(Conv.model(DocumentAIDetectedCode::fromJson))); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link PoaVerifications}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(PoaVerifications from) { - super(from); - } + private Lifecycle(ObjectNode json, Map> files) { + super(json, files); + } /** - * Unique identifier for this POA step in the workflow graph. + * Reads a {@code Lifecycle} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. * - * @param nodeId the value - * @return this builder + * @param json a JSON object + * @return the parsed value */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); + public static Lifecycle fromJson(JsonNode json) { + return new Lifecycle(Json.copyObject(json), Map.of()); } /** - * Feature-level status of this Proof of Address step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * Returns an empty builder. * - * @param status the value - * @return this builder + * @return a new builder */ - public Builder status(SessionsRetrieveDecisionResponse.PoaVerifications.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.PoaVerifications.Status::of), status); + public static Builder builder() { + return new Builder(); } /** - * URL of the proof of address document + * Returns a builder holding a copy of every field of this value. * - * @param documentFile the value - * @return this builder + * @return a new builder */ - public Builder documentFile(String documentFile) { - return set("document_file", Conv.STRING, documentFile); + public Builder toBuilder() { + return new Builder(this); } /** - * Two-letter country code of the issuing state + * The type field. * - * @param issuingState the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder issuingState(String issuingState) { - return set("issuing_state", Conv.STRING, issuingState); + public Optional type() { + return Json.optional(json, "type", Conv.STRING); } /** - * Type of proof of address document + * The timestamp field. * - * @param documentType the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder documentType(String documentType) { - return set("document_type", Conv.STRING, documentType); + public Optional timestamp() { + return Json.optional(json, "timestamp", Conv.DATE_TIME); } /** - * More specific classification of the POA document within its document_type, when one was detected. null otherwise. + * Event-specific payload. Send events carry status, reason; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. EMAIL_VERIFICATION_APPROVED). * - * @param documentSubtype the value, or {@code null} to send JSON null - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder documentSubtype(@Nullable String documentSubtype) { - return setNullable("document_subtype", Conv.STRING, documentSubtype); + public Optional details() { + return Json.optional(json, "details", Conv.model(SessionDecision.EmailVerifications.Lifecycle.Details::fromJson)); } /** - * Language code of the document + * The fee field. * - * @param documentLanguage the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder documentLanguage(String documentLanguage) { - return set("document_language", Conv.STRING, documentLanguage); + public Optional fee() { + return Json.optional(json, "fee", Conv.DOUBLE); } - /** - * File-forensics metadata extracted from the uploaded POA document. Always contains the keys below (values are null when not applicable, e.g. EXIF dates for PDFs). null when no metadata could be extracted. - * - * @param documentMetadata the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder documentMetadata(SessionsRetrieveDecisionResponse.PoaVerifications.@Nullable DocumentMetadata documentMetadata) { - return setNullable("document_metadata", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.DocumentMetadata::fromJson), documentMetadata); + @Override + protected Set knownFields() { + return KNOWN_FIELDS; } - /** - * Name of the document issuer - * - * @param issuer the value - * @return this builder - */ - public Builder issuer(String issuer) { - return set("issuer", Conv.STRING, issuer); - } + /** Builds a {@link Lifecycle}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** - * Date when the document was issued - * - * @param issueDate the value - * @return this builder - */ - public Builder issueDate(LocalDate issueDate) { - return set("issue_date", Conv.DATE, issueDate); - } + private Builder(Lifecycle from) { + super(from); + } - /** - * Expiration date of the document, if available - * - * @param expirationDate the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder expirationDate(@Nullable LocalDate expirationDate) { - return setNullable("expiration_date", Conv.DATE, expirationDate); - } + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(String type) { + return set("type", Conv.STRING, type); + } - /** - * Raw address from the proof of address document - * - * @param poaAddress the value - * @return this builder - */ - public Builder poaAddress(String poaAddress) { - return set("poa_address", Conv.STRING, poaAddress); - } + /** + * The timestamp field. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(OffsetDateTime timestamp) { + return set("timestamp", Conv.DATE_TIME, timestamp); + } - /** - * Formatted address from the proof of address document - * - * @param poaFormattedAddress the value - * @return this builder - */ - public Builder poaFormattedAddress(String poaFormattedAddress) { - return set("poa_formatted_address", Conv.STRING, poaFormattedAddress); - } + /** + * Event-specific payload. Send events carry status, reason; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. EMAIL_VERIFICATION_APPROVED). + * + * @param details the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder details(SessionDecision.EmailVerifications.Lifecycle.@Nullable Details details) { + return setNullable("details", Conv.model(SessionDecision.EmailVerifications.Lifecycle.Details::fromJson), details); + } - /** - * The poa_parsed_address field. - * - * @param poaParsedAddress the value - * @return this builder - */ - public Builder poaParsedAddress(SessionsRetrieveDecisionResponse.PoaVerifications.PoaParsedAddress poaParsedAddress) { - return set("poa_parsed_address", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.PoaParsedAddress::fromJson), poaParsedAddress); - } + /** + * The fee field. + * + * @param fee the value + * @return this builder + */ + public Builder fee(double fee) { + return set("fee", Conv.DOUBLE, fee); + } - /** - * Account-holder / addressee name extracted from the POA document, used for name matching. null when no name could be extracted. - * - * @param nameOnDocument the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder nameOnDocument(@Nullable String nameOnDocument) { - return setNullable("name_on_document", Conv.STRING, nameOnDocument); - } + /** + * Builds the value. + * + * @return a new immutable {@code Lifecycle} + */ + public Lifecycle build() { + return new Lifecycle(buildJson(), buildFiles()); + } - /** - * Similarity score (0-100) between name_on_document and the expected name supplied at session creation. null when either side is missing. - * - * @param nameMatchScoreExpectedDetails the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder nameMatchScoreExpectedDetails(@Nullable Double nameMatchScoreExpectedDetails) { - return setNullable("name_match_score_expected_details", Conv.DOUBLE, nameMatchScoreExpectedDetails); + @Override + protected Builder self() { + return this; + } } /** - * Similarity score (0-100) between name_on_document and the name extracted by the ID verification step. null when either side is missing. - * - * @param nameMatchScoreIdVerification the value, or {@code null} to send JSON null - * @return this builder + * Event-specific payload. Send events carry status, reason; code-check events carry code_tried, status; final status events carry reason — or null when there is no reason to report (e.g. EMAIL_VERIFICATION_APPROVED). */ - public Builder nameMatchScoreIdVerification(@Nullable Double nameMatchScoreIdVerification) { - return setNullable("name_match_score_id_verification", Conv.DOUBLE, nameMatchScoreIdVerification); - } + public static final class Details extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("status", "reason", "code_tried"); - /** - * Expected raw address for verification - * - * @param expectedDetailsAddress the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder expectedDetailsAddress(@Nullable String expectedDetailsAddress) { - return setNullable("expected_details_address", Conv.STRING, expectedDetailsAddress); - } + private Details(ObjectNode json, Map> files) { + super(json, files); + } - /** - * Expected formatted address for verification - * - * @param expectedDetailsFormattedAddress the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder expectedDetailsFormattedAddress(@Nullable String expectedDetailsFormattedAddress) { - return setNullable("expected_details_formatted_address", Conv.STRING, expectedDetailsFormattedAddress); - } + /** + * Reads a {@code Details} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Details fromJson(JsonNode json) { + return new Details(Json.copyObject(json), Map.of()); + } - /** - * Expected parsed address details for verification - * - * @param expectedDetailsParsedAddress the value - * @return this builder - */ - public Builder expectedDetailsParsedAddress(Map expectedDetailsParsedAddress) { - return setAny("expected_details_parsed_address", expectedDetailsParsedAddress); - } - - /** - * Additional banking and contact details extracted from the document. - * - * @param extraFields the value - * @return this builder - */ - public Builder extraFields(SessionsRetrieveDecisionResponse.PoaVerifications.ExtraFields extraFields) { - return set("extra_fields", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.ExtraFields::fromJson), extraFields); - } - - /** - * Additional proof of address files - * - * @param extraFiles the value - * @return this builder - */ - public Builder extraFiles(List extraFiles) { - return set("extra_files", Conv.list(Conv.STRING), extraFiles); - } - - /** - * The warnings field. - * - * @param warnings the value - * @return this builder - */ - public Builder warnings(List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.Warnings::fromJson)), warnings); - } - - /** - * QR codes and barcodes detected across every processed page of the document. Empty when none are found. A located but undecodable QR code has parsed_payload: null. - * - * @param detectedCodes the value - * @return this builder - */ - public Builder detectedCodes(List detectedCodes) { - return set("detected_codes", Conv.list(Conv.model(DocumentAIDetectedCode::fromJson)), detectedCodes); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * Builds the value. - * - * @return a new immutable {@code PoaVerifications} - */ - public PoaVerifications build() { - return new PoaVerifications(buildJson(), buildFiles()); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - @Override - protected Builder self() { - return this; - } - } + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } - /** - * Feature-level status of this Proof of Address step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Not Finished"}. */ - public static final Status NOT_FINISHED = new Status("Not Finished"); + /** + * The reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); + /** + * The code_tried field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional codeTried() { + return Json.optional(json, "code_tried", Conv.STRING); + } - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); + /** Builds a {@link Details}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** The value {@code "Resub Requested"}. */ - public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + private Builder(Details from) { + super(from); + } - private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } - private Status(String value) { - super(value); - } + /** + * The reason field. + * + * @param reason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reason(@Nullable String reason) { + return setNullable("reason", Conv.STRING, reason); + } - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } + /** + * The code_tried field. + * + * @param codeTried the value + * @return this builder + */ + public Builder codeTried(String codeTried) { + return set("code_tried", Conv.STRING, codeTried); + } - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } + /** + * Builds the value. + * + * @return a new immutable {@code Details} + */ + public Details build() { + return new Details(buildJson(), buildFiles()); + } - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); + @Override + protected Builder self() { + return this; + } + } } } - /** - * File-forensics metadata extracted from the uploaded POA document. Always contains the keys below (values are null when not applicable, e.g. EXIF dates for PDFs). null when no metadata could be extracted. - */ - public static final class DocumentMetadata extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("file_size", "content_type", "creation_date", "modified_date", "overlay_manipulation", "creator", "producer", "software", "encryption", "is_signed", "is_tampered", "signature_info", "exif_original_date", "exif_digitized_date", "processed_by_known_editor", "has_different_creation_mod_date"); + public static final class Matches extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "session_number", "vendor_data", "verification_date", "email", "status", "is_blocklisted", "api_service", "source"); - private DocumentMetadata(ObjectNode json, Map> files) { + private Matches(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code DocumentMetadata} from JSON. Unknown fields are kept and readable + * Reads a {@code Matches} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static DocumentMetadata fromJson(JsonNode json) { - return new DocumentMetadata(Json.copyObject(json), Map.of()); + public static Matches fromJson(JsonNode json) { + return new Matches(Json.copyObject(json), Map.of()); } /** @@ -15001,163 +15214,93 @@ public Builder toBuilder() { } /** - * Size of the document + * The session_id field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional fileSize() { - return Json.optional(json, "file_size", Conv.LONG); + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); } /** - * Content type of the document + * The session_number field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional contentType() { - return Json.optional(json, "content_type", Conv.STRING); + public Optional sessionNumber() { + return Json.optional(json, "session_number", Conv.LONG); } /** - * Date when the document was created + * The vendor_data field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional creationDate() { - return Json.optional(json, "creation_date", Conv.DATE); + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); } /** - * Date when the document was modified + * The verification_date field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional modifiedDate() { - return Json.optional(json, "modified_date", Conv.DATE); + public Optional verificationDate() { + return Json.optional(json, "verification_date", Conv.DATE_TIME); } /** - * PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF. + * The email field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional overlayManipulation() { - return Json.optional(json, "overlay_manipulation", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.DocumentMetadata.OverlayManipulation::fromJson)); + public Optional email() { + return Json.optional(json, "email", Conv.STRING); } /** - * Creator application recorded in the document metadata. + * The status field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional creator() { - return Json.optional(json, "creator", Conv.STRING); + public Optional status() { + return Json.optional(json, "status", Conv.STRING); } /** - * Producer software recorded in the PDF metadata. + * The is_blocklisted field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional producer() { - return Json.optional(json, "producer", Conv.STRING); + public Optional isBlocklisted() { + return Json.optional(json, "is_blocklisted", Conv.BOOLEAN); } /** - * Editing/processing software recorded in the file metadata. + * The api_service field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional software() { - return Json.optional(json, "software", Conv.STRING); + public Optional apiService() { + return Json.optional(json, "api_service", Conv.STRING); } /** - * Encryption scheme of the document, when encrypted. + * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional encryption() { - return Json.optional(json, "encryption", Conv.STRING); - } - - /** - * Whether the PDF carries a digital signature. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isSigned() { - return Json.optional(json, "is_signed", Conv.BOOLEAN); - } - - /** - * Whether signature validation indicates the file was modified after signing. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isTampered() { - return Json.optional(json, "is_tampered", Conv.BOOLEAN); - } - - /** - * Details of the digital signature(s), when present. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> signatureInfo() { - return Json.optional(json, "signature_info", Conv.OBJECT); - } - - /** - * EXIF original capture date, for image uploads. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional exifOriginalDate() { - return Json.optional(json, "exif_original_date", Conv.STRING); - } - - /** - * EXIF digitization date, for image uploads. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional exifDigitizedDate() { - return Json.optional(json, "exif_digitized_date", Conv.STRING); - } - - /** - * Whether the file metadata indicates processing by a known document editor. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processedByKnownEditor() { - return Json.optional(json, "processed_by_known_editor", Conv.BOOLEAN); - } - - /** - * Whether the creation and modification dates differ — a common manipulation signal. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional hasDifferentCreationModDate() { - return Json.optional(json, "has_different_creation_mod_date", Conv.BOOLEAN); + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(SessionDecision.EmailVerifications.Matches.Source::of)); } @Override @@ -15165,5599 +15308,1224 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link DocumentMetadata}. */ + /** Builds a {@link Matches}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(DocumentMetadata from) { + private Builder(Matches from) { super(from); } /** - * Size of the document + * The session_id field. * - * @param fileSize the value + * @param sessionId the value, or {@code null} to send JSON null * @return this builder */ - public Builder fileSize(long fileSize) { - return set("file_size", Conv.LONG, fileSize); + public Builder sessionId(@Nullable String sessionId) { + return setNullable("session_id", Conv.STRING, sessionId); } /** - * Content type of the document + * The session_number field. * - * @param contentType the value + * @param sessionNumber the value, or {@code null} to send JSON null * @return this builder */ - public Builder contentType(String contentType) { - return set("content_type", Conv.STRING, contentType); + public Builder sessionNumber(@Nullable Long sessionNumber) { + return setNullable("session_number", Conv.LONG, sessionNumber); } /** - * Date when the document was created + * The vendor_data field. * - * @param creationDate the value + * @param vendorData the value, or {@code null} to send JSON null * @return this builder */ - public Builder creationDate(LocalDate creationDate) { - return set("creation_date", Conv.DATE, creationDate); + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); } /** - * Date when the document was modified + * The verification_date field. * - * @param modifiedDate the value + * @param verificationDate the value, or {@code null} to send JSON null * @return this builder */ - public Builder modifiedDate(LocalDate modifiedDate) { - return set("modified_date", Conv.DATE, modifiedDate); + public Builder verificationDate(@Nullable OffsetDateTime verificationDate) { + return setNullable("verification_date", Conv.DATE_TIME, verificationDate); } /** - * PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF. + * The email field. * - * @param overlayManipulation the value, or {@code null} to send JSON null + * @param email the value * @return this builder */ - public Builder overlayManipulation(SessionsRetrieveDecisionResponse.PoaVerifications.DocumentMetadata.@Nullable OverlayManipulation overlayManipulation) { - return setNullable("overlay_manipulation", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.DocumentMetadata.OverlayManipulation::fromJson), overlayManipulation); + public Builder email(String email) { + return set("email", Conv.STRING, email); } /** - * Creator application recorded in the document metadata. + * The status field. * - * @param creator the value, or {@code null} to send JSON null + * @param status the value, or {@code null} to send JSON null * @return this builder */ - public Builder creator(@Nullable String creator) { - return setNullable("creator", Conv.STRING, creator); + public Builder status(@Nullable String status) { + return setNullable("status", Conv.STRING, status); } /** - * Producer software recorded in the PDF metadata. + * The is_blocklisted field. * - * @param producer the value, or {@code null} to send JSON null + * @param isBlocklisted the value * @return this builder */ - public Builder producer(@Nullable String producer) { - return setNullable("producer", Conv.STRING, producer); + public Builder isBlocklisted(boolean isBlocklisted) { + return set("is_blocklisted", Conv.BOOLEAN, isBlocklisted); } /** - * Editing/processing software recorded in the file metadata. + * The api_service field. * - * @param software the value, or {@code null} to send JSON null + * @param apiService the value, or {@code null} to send JSON null * @return this builder */ - public Builder software(@Nullable String software) { - return setNullable("software", Conv.STRING, software); + public Builder apiService(@Nullable String apiService) { + return setNullable("api_service", Conv.STRING, apiService); } /** - * Encryption scheme of the document, when encrypted. + * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. * - * @param encryption the value, or {@code null} to send JSON null + * @param source the value * @return this builder */ - public Builder encryption(@Nullable String encryption) { - return setNullable("encryption", Conv.STRING, encryption); + public Builder source(SessionDecision.EmailVerifications.Matches.Source source) { + return set("source", Conv.enumeration(SessionDecision.EmailVerifications.Matches.Source::of), source); } /** - * Whether the PDF carries a digital signature. + * Builds the value. * - * @param isSigned the value, or {@code null} to send JSON null - * @return this builder + * @return a new immutable {@code Matches} */ - public Builder isSigned(@Nullable Boolean isSigned) { - return setNullable("is_signed", Conv.BOOLEAN, isSigned); + public Matches build() { + return new Matches(buildJson(), buildFiles()); } - /** - * Whether signature validation indicates the file was modified after signing. - * - * @param isTampered the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder isTampered(@Nullable Boolean isTampered) { - return setNullable("is_tampered", Conv.BOOLEAN, isTampered); + @Override + protected Builder self() { + return this; } + } - /** - * Details of the digital signature(s), when present. - * - * @param signatureInfo the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder signatureInfo(@Nullable Map signatureInfo) { - return setAny("signature_info", signatureInfo); - } + /** + * Where this match came from. session means another verification session in your application matched on this value. list_entry means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at /management-api/lists/overview for how to manage those lists. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "session"}. */ + public static final Source SESSION = new Source("session"); - /** - * EXIF original capture date, for image uploads. - * - * @param exifOriginalDate the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder exifOriginalDate(@Nullable String exifOriginalDate) { - return setNullable("exif_original_date", Conv.STRING, exifOriginalDate); - } + /** The value {@code "list_entry"}. */ + public static final Source LIST_ENTRY = new Source("list_entry"); - /** - * EXIF digitization date, for image uploads. - * - * @param exifDigitizedDate the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder exifDigitizedDate(@Nullable String exifDigitizedDate) { - return setNullable("exif_digitized_date", Conv.STRING, exifDigitizedDate); - } + private static final List KNOWN_VALUES = List.of(SESSION, LIST_ENTRY); - /** - * Whether the file metadata indicates processing by a known document editor. - * - * @param processedByKnownEditor the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder processedByKnownEditor(@Nullable Boolean processedByKnownEditor) { - return setNullable("processed_by_known_editor", Conv.BOOLEAN, processedByKnownEditor); + private Source(String value) { + super(value); } /** - * Whether the creation and modification dates differ — a common manipulation signal. + * Returns the constant for a known value, or a new instance for any other value. * - * @param hasDifferentCreationModDate the value, or {@code null} to send JSON null - * @return this builder + * @param value the string value + * @return the enum value */ - public Builder hasDifferentCreationModDate(@Nullable Boolean hasDifferentCreationModDate) { - return setNullable("has_different_creation_mod_date", Conv.BOOLEAN, hasDifferentCreationModDate); + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); } /** - * Builds the value. + * Returns the values known to this SDK version. * - * @return a new immutable {@code DocumentMetadata} + * @return the known values */ - public DocumentMetadata build() { - return new DocumentMetadata(buildJson(), buildFiles()); + public static List knownValues() { + return KNOWN_VALUES; } @Override - protected Builder self() { - return this; + public boolean isKnown() { + return KNOWN_VALUES.contains(this); } } + } + } - /** - * PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF. - */ - public static final class OverlayManipulation extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("detected", "analyzed", "signals", "manipulated_regions"); - - private OverlayManipulation(ObjectNode json, Map> files) { - super(json, files); - } + public static final class PoaVerifications extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "document_file", "issuing_state", "document_type", "document_subtype", "document_language", "document_metadata", "issuer", "issue_date", "expiration_date", "poa_address", "poa_formatted_address", "poa_parsed_address", "name_on_document", "name_match_score_expected_details", "name_match_score_id_verification", "expected_details_address", "expected_details_formatted_address", "expected_details_parsed_address", "extra_fields", "extra_files", "warnings", "detected_codes"); - /** - * Reads a {@code OverlayManipulation} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static OverlayManipulation fromJson(JsonNode json) { - return new OverlayManipulation(Json.copyObject(json), Map.of()); - } + private PoaVerifications(ObjectNode json, Map> files) { + super(json, files); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Whether overlay-text manipulation evidence was detected. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional detected() { - return Json.optional(json, "detected", Conv.BOOLEAN); - } - - /** - * Whether the PDF could be analyzed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional analyzed() { - return Json.optional(json, "analyzed", Conv.BOOLEAN); - } - - /** - * Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> signals() { - return Json.optional(json, "signals", Conv.list(Conv.STRING)); - } - - /** - * Suspected manipulated areas in PDF page coordinates. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> manipulatedRegions() { - return Json.optional(json, "manipulated_regions", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.DocumentMetadata.OverlayManipulation.ManipulatedRegions::fromJson))); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link OverlayManipulation}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(OverlayManipulation from) { - super(from); - } - - /** - * Whether overlay-text manipulation evidence was detected. - * - * @param detected the value - * @return this builder - */ - public Builder detected(boolean detected) { - return set("detected", Conv.BOOLEAN, detected); - } - - /** - * Whether the PDF could be analyzed. - * - * @param analyzed the value - * @return this builder - */ - public Builder analyzed(boolean analyzed) { - return set("analyzed", Conv.BOOLEAN, analyzed); - } - - /** - * Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation. - * - * @param signals the value - * @return this builder - */ - public Builder signals(List signals) { - return set("signals", Conv.list(Conv.STRING), signals); - } - - /** - * Suspected manipulated areas in PDF page coordinates. - * - * @param manipulatedRegions the value - * @return this builder - */ - public Builder manipulatedRegions(List manipulatedRegions) { - return set("manipulated_regions", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.DocumentMetadata.OverlayManipulation.ManipulatedRegions::fromJson)), manipulatedRegions); - } - - /** - * Builds the value. - * - * @return a new immutable {@code OverlayManipulation} - */ - public OverlayManipulation build() { - return new OverlayManipulation(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - public static final class ManipulatedRegions extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("page", "x", "y", "width", "height", "page_width", "page_height"); - - private ManipulatedRegions(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code ManipulatedRegions} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static ManipulatedRegions fromJson(JsonNode json) { - return new ManipulatedRegions(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The page field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional page() { - return Json.optional(json, "page", Conv.LONG); - } - - /** - * The x field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional x() { - return Json.optional(json, "x", Conv.DOUBLE); - } - - /** - * The y field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional y() { - return Json.optional(json, "y", Conv.DOUBLE); - } - - /** - * The width field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional width() { - return Json.optional(json, "width", Conv.DOUBLE); - } - - /** - * The height field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional height() { - return Json.optional(json, "height", Conv.DOUBLE); - } - - /** - * The page_width field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional pageWidth() { - return Json.optional(json, "page_width", Conv.DOUBLE); - } - - /** - * The page_height field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional pageHeight() { - return Json.optional(json, "page_height", Conv.DOUBLE); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link ManipulatedRegions}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(ManipulatedRegions from) { - super(from); - } - - /** - * The page field. - * - * @param page the value - * @return this builder - */ - public Builder page(long page) { - return set("page", Conv.LONG, page); - } - - /** - * The x field. - * - * @param x the value - * @return this builder - */ - public Builder x(double x) { - return set("x", Conv.DOUBLE, x); - } - - /** - * The y field. - * - * @param y the value - * @return this builder - */ - public Builder y(double y) { - return set("y", Conv.DOUBLE, y); - } - - /** - * The width field. - * - * @param width the value - * @return this builder - */ - public Builder width(double width) { - return set("width", Conv.DOUBLE, width); - } - - /** - * The height field. - * - * @param height the value - * @return this builder - */ - public Builder height(double height) { - return set("height", Conv.DOUBLE, height); - } - - /** - * The page_width field. - * - * @param pageWidth the value - * @return this builder - */ - public Builder pageWidth(double pageWidth) { - return set("page_width", Conv.DOUBLE, pageWidth); - } - - /** - * The page_height field. - * - * @param pageHeight the value - * @return this builder - */ - public Builder pageHeight(double pageHeight) { - return set("page_height", Conv.DOUBLE, pageHeight); - } - - /** - * Builds the value. - * - * @return a new immutable {@code ManipulatedRegions} - */ - public ManipulatedRegions build() { - return new ManipulatedRegions(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - } - - public static final class PoaParsedAddress extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("address_type", "street_1", "street_2", "city", "region", "country", "postal_code", "document_location"); - - private PoaParsedAddress(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code PoaParsedAddress} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static PoaParsedAddress fromJson(JsonNode json) { - return new PoaParsedAddress(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Type of address (Street, Avenue, etc) - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional addressType() { - return Json.optional(json, "address_type", Conv.STRING); - } - - /** - * Primary street address - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional street1() { - return Json.optional(json, "street_1", Conv.STRING); - } - - /** - * Secondary street address - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional street2() { - return Json.optional(json, "street_2", Conv.STRING); - } - - /** - * City name - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional city() { - return Json.optional(json, "city", Conv.STRING); - } - - /** - * Region or state name - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional region() { - return Json.optional(json, "region", Conv.STRING); - } - - /** - * Two-letter country code - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional country() { - return Json.optional(json, "country", Conv.STRING); - } - - /** - * Postal code - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional postalCode() { - return Json.optional(json, "postal_code", Conv.STRING); - } - - /** - * The document_location field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentLocation() { - return Json.optional(json, "document_location", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.PoaParsedAddress.DocumentLocation::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link PoaParsedAddress}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(PoaParsedAddress from) { - super(from); - } - - /** - * Type of address (Street, Avenue, etc) - * - * @param addressType the value - * @return this builder - */ - public Builder addressType(String addressType) { - return set("address_type", Conv.STRING, addressType); - } - - /** - * Primary street address - * - * @param street1 the value - * @return this builder - */ - public Builder street1(String street1) { - return set("street_1", Conv.STRING, street1); - } - - /** - * Secondary street address - * - * @param street2 the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder street2(@Nullable String street2) { - return setNullable("street_2", Conv.STRING, street2); - } - - /** - * City name - * - * @param city the value - * @return this builder - */ - public Builder city(String city) { - return set("city", Conv.STRING, city); - } - - /** - * Region or state name - * - * @param region the value - * @return this builder - */ - public Builder region(String region) { - return set("region", Conv.STRING, region); - } - - /** - * Two-letter country code - * - * @param country the value - * @return this builder - */ - public Builder country(String country) { - return set("country", Conv.STRING, country); - } - - /** - * Postal code - * - * @param postalCode the value - * @return this builder - */ - public Builder postalCode(String postalCode) { - return set("postal_code", Conv.STRING, postalCode); - } - - /** - * The document_location field. - * - * @param documentLocation the value - * @return this builder - */ - public Builder documentLocation(SessionsRetrieveDecisionResponse.PoaVerifications.PoaParsedAddress.DocumentLocation documentLocation) { - return set("document_location", Conv.model(SessionsRetrieveDecisionResponse.PoaVerifications.PoaParsedAddress.DocumentLocation::fromJson), documentLocation); - } - - /** - * Builds the value. - * - * @return a new immutable {@code PoaParsedAddress} - */ - public PoaParsedAddress build() { - return new PoaParsedAddress(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - public static final class DocumentLocation extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("latitude", "longitude"); - - private DocumentLocation(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code DocumentLocation} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static DocumentLocation fromJson(JsonNode json) { - return new DocumentLocation(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Latitude coordinate - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional latitude() { - return Json.optional(json, "latitude", Conv.DOUBLE); - } - - /** - * Longitude coordinate - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional longitude() { - return Json.optional(json, "longitude", Conv.DOUBLE); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link DocumentLocation}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(DocumentLocation from) { - super(from); - } - - /** - * Latitude coordinate - * - * @param latitude the value - * @return this builder - */ - public Builder latitude(double latitude) { - return set("latitude", Conv.DOUBLE, latitude); - } - - /** - * Longitude coordinate - * - * @param longitude the value - * @return this builder - */ - public Builder longitude(double longitude) { - return set("longitude", Conv.DOUBLE, longitude); - } - - /** - * Builds the value. - * - * @return a new immutable {@code DocumentLocation} - */ - public DocumentLocation build() { - return new DocumentLocation(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - - /** Additional banking and contact details extracted from the document. */ - public static final class ExtraFields extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("bank_account_number", "bank_iban", "bank_sort_code", "bank_routing_number", "bank_swift_bic", "bank_branch_name", "bank_branch_address", "document_phone_number", "additional_names"); - - private ExtraFields(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code ExtraFields} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static ExtraFields fromJson(JsonNode json) { - return new ExtraFields(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The bank_account_number field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional bankAccountNumber() { - return Json.optional(json, "bank_account_number", Conv.STRING); - } - - /** - * The bank_iban field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional bankIban() { - return Json.optional(json, "bank_iban", Conv.STRING); - } - - /** - * The bank_sort_code field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional bankSortCode() { - return Json.optional(json, "bank_sort_code", Conv.STRING); - } - - /** - * The bank_routing_number field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional bankRoutingNumber() { - return Json.optional(json, "bank_routing_number", Conv.STRING); - } - - /** - * The bank_swift_bic field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional bankSwiftBic() { - return Json.optional(json, "bank_swift_bic", Conv.STRING); - } - - /** - * The bank_branch_name field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional bankBranchName() { - return Json.optional(json, "bank_branch_name", Conv.STRING); - } - - /** - * The bank_branch_address field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional bankBranchAddress() { - return Json.optional(json, "bank_branch_address", Conv.STRING); - } - - /** - * The document_phone_number field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentPhoneNumber() { - return Json.optional(json, "document_phone_number", Conv.STRING); - } - - /** - * The additional_names field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> additionalNames() { - return Json.optional(json, "additional_names", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link ExtraFields}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(ExtraFields from) { - super(from); - } - - /** - * The bank_account_number field. - * - * @param bankAccountNumber the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder bankAccountNumber(@Nullable String bankAccountNumber) { - return setNullable("bank_account_number", Conv.STRING, bankAccountNumber); - } - - /** - * The bank_iban field. - * - * @param bankIban the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder bankIban(@Nullable String bankIban) { - return setNullable("bank_iban", Conv.STRING, bankIban); - } - - /** - * The bank_sort_code field. - * - * @param bankSortCode the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder bankSortCode(@Nullable String bankSortCode) { - return setNullable("bank_sort_code", Conv.STRING, bankSortCode); - } - - /** - * The bank_routing_number field. - * - * @param bankRoutingNumber the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder bankRoutingNumber(@Nullable String bankRoutingNumber) { - return setNullable("bank_routing_number", Conv.STRING, bankRoutingNumber); - } - - /** - * The bank_swift_bic field. - * - * @param bankSwiftBic the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder bankSwiftBic(@Nullable String bankSwiftBic) { - return setNullable("bank_swift_bic", Conv.STRING, bankSwiftBic); - } - - /** - * The bank_branch_name field. - * - * @param bankBranchName the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder bankBranchName(@Nullable String bankBranchName) { - return setNullable("bank_branch_name", Conv.STRING, bankBranchName); - } - - /** - * The bank_branch_address field. - * - * @param bankBranchAddress the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder bankBranchAddress(@Nullable String bankBranchAddress) { - return setNullable("bank_branch_address", Conv.STRING, bankBranchAddress); - } - - /** - * The document_phone_number field. - * - * @param documentPhoneNumber the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder documentPhoneNumber(@Nullable String documentPhoneNumber) { - return setNullable("document_phone_number", Conv.STRING, documentPhoneNumber); - } - - /** - * The additional_names field. - * - * @param additionalNames the value - * @return this builder - */ - public Builder additionalNames(List additionalNames) { - return set("additional_names", Conv.list(Conv.STRING), additionalNames); - } - - /** - * Builds the value. - * - * @return a new immutable {@code ExtraFields} - */ - public ExtraFields build() { - return new ExtraFields(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Warnings extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); - - private Warnings(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Warnings fromJson(JsonNode json) { - return new Warnings(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.PoaVerifications.Warnings.Feature::of)); - } - - /** - * The risk field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional risk() { - return Json.optional(json, "risk", Conv.STRING); - } - - /** - * The additional_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> additionalData() { - return Json.optional(json, "additional_data", Conv.OBJECT); - } - - /** - * The log_type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional logType() { - return Json.optional(json, "log_type", Conv.STRING); - } - - /** - * The short_description field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional shortDescription() { - return Json.optional(json, "short_description", Conv.STRING); - } - - /** - * The long_description field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional longDescription() { - return Json.optional(json, "long_description", Conv.STRING); - } - - /** - * The workflow graph node ID that generated this warning - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Warnings}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Warnings from) { - super(from); - } - - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. - * - * @param feature the value - * @return this builder - */ - public Builder feature(SessionsRetrieveDecisionResponse.PoaVerifications.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.PoaVerifications.Warnings.Feature::of), feature); - } - - /** - * The risk field. - * - * @param risk the value - * @return this builder - */ - public Builder risk(String risk) { - return set("risk", Conv.STRING, risk); - } - - /** - * The additional_data field. - * - * @param additionalData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder additionalData(@Nullable Map additionalData) { - return setAny("additional_data", additionalData); - } - - /** - * The log_type field. - * - * @param logType the value - * @return this builder - */ - public Builder logType(String logType) { - return set("log_type", Conv.STRING, logType); - } - - /** - * The short_description field. - * - * @param shortDescription the value - * @return this builder - */ - public Builder shortDescription(String shortDescription) { - return set("short_description", Conv.STRING, shortDescription); - } - - /** - * The long_description field. - * - * @param longDescription the value - * @return this builder - */ - public Builder longDescription(String longDescription) { - return set("long_description", Conv.STRING, longDescription); - } - - /** - * The workflow graph node ID that generated this warning - * - * @param nodeId the value - * @return this builder - */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Warnings} - */ - public Warnings build() { - return new Warnings(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Feature extends StringEnum { - /** The value {@code "ID_VERIFICATION"}. */ - public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); - - /** The value {@code "NFC"}. */ - public static final Feature NFC = new Feature("NFC"); - - /** The value {@code "LIVENESS"}. */ - public static final Feature LIVENESS = new Feature("LIVENESS"); - - /** The value {@code "FACEMATCH"}. */ - public static final Feature FACEMATCH = new Feature("FACEMATCH"); - - /** The value {@code "PROOF_OF_ADDRESS"}. */ - public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); - - /** The value {@code "PHONE"}. */ - public static final Feature PHONE = new Feature("PHONE"); - - /** The value {@code "EMAIL"}. */ - public static final Feature EMAIL = new Feature("EMAIL"); - - /** The value {@code "AML"}. */ - public static final Feature AML = new Feature("AML"); - - /** The value {@code "LOCATION"}. */ - public static final Feature LOCATION = new Feature("LOCATION"); - - /** The value {@code "DATABASE_VALIDATION"}. */ - public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); - - /** The value {@code "QUESTIONNAIRE"}. */ - public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); - - /** The value {@code "DOCUMENT_AI"}. */ - public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); - - /** The value {@code "KYB_REGISTRY"}. */ - public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); - - /** The value {@code "KYB_DOCUMENTS"}. */ - public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); - - /** The value {@code "KYB_KEY_PEOPLE"}. */ - public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); - - private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); - - private Feature(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Feature of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - } - - /** Document AI results grouped by workflow node. */ - public static final class DocumentAiDocuments extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("status", "node_id", "items", "warnings"); - - private DocumentAiDocuments(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code DocumentAiDocuments} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static DocumentAiDocuments fromJson(JsonNode json) { - return new DocumentAiDocuments(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Aggregate Document AI status for this node, rolled up from its items[]. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Status::of)); - } - - /** - * Unique identifier for this Document AI step in the workflow graph. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } - - /** - * The items field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> items() { - return Json.optional(json, "items", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items::fromJson))); - } - - /** - * Warning logs raised for this Document AI node. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional>> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.OBJECT)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link DocumentAiDocuments}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(DocumentAiDocuments from) { - super(from); - } - - /** - * Aggregate Document AI status for this node, rolled up from its items[]. - * - * @param status the value - * @return this builder - */ - public Builder status(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Status::of), status); - } - - /** - * Unique identifier for this Document AI step in the workflow graph. - * - * @param nodeId the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder nodeId(@Nullable String nodeId) { - return setNullable("node_id", Conv.STRING, nodeId); - } - - /** - * The items field. - * - * @param items the value - * @return this builder - */ - public Builder items(List items) { - return set("items", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items::fromJson)), items); - } - - /** - * Warning logs raised for this Document AI node. - * - * @param warnings the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder warnings(@Nullable List> warnings) { - return setAny("warnings", warnings); - } - - /** - * Builds the value. - * - * @return a new immutable {@code DocumentAiDocuments} - */ - public DocumentAiDocuments build() { - return new DocumentAiDocuments(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Aggregate Document AI status for this node, rolled up from its items[]. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); - - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); - - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); - - /** The value {@code "Not Finished"}. */ - public static final Status NOT_FINISHED = new Status("Not Finished"); - - private static final List KNOWN_VALUES = List.of(APPROVED, DECLINED, IN_REVIEW, NOT_FINISHED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - public static final class Items extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "node_id", "document_key", "original_filename", "file_size", "file_url", "status", "extracted_data", "detected_codes", "fields", "document_metadata", "cross_check_result", "created_at"); - - private Items(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Items} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Items fromJson(JsonNode json) { - return new Items(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The uuid field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional uuid() { - return Json.optional(json, "uuid", Conv.STRING); - } - - /** - * Workflow graph node that produced this document. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } - - /** - * Key of the configured Document AI document this file corresponds to. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentKey() { - return Json.optional(json, "document_key", Conv.STRING); - } - - /** - * Filename as uploaded by the user. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional originalFilename() { - return Json.optional(json, "original_filename", Conv.STRING); - } - - /** - * File size in bytes. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional fileSize() { - return Json.optional(json, "file_size", Conv.LONG); - } - - /** - * Short-lived presigned link to the uploaded file. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional fileUrl() { - return Json.optional(json, "file_url", Conv.STRING); - } - - /** - * Per-document Document AI status. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items.Status::of)); - } - - /** - * Values extracted by Document AI, keyed by the configured field key. Each value is a string, number, or null. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> extractedData() { - return Json.optional(json, "extracted_data", Conv.OBJECT); - } - - /** - * QR codes and barcodes detected across every processed page. Empty when none are found. A located but undecodable QR code has parsed_payload: null. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> detectedCodes() { - return Json.optional(json, "detected_codes", Conv.list(Conv.model(DocumentAIDetectedCode::fromJson))); - } - - /** - * The configured extraction fields for this document. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> fields() { - return Json.optional(json, "fields", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items.Fields::fromJson))); - } - - /** - * File-forensics metadata extracted from the uploaded document (e.g. is_tampered). null when nothing could be extracted. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> documentMetadata() { - return Json.optional(json, "document_metadata", Conv.OBJECT); - } - - /** - * Cross-reference of the extracted document fields against other session data. null when no cross-check was performed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> crossCheckResult() { - return Json.optional(json, "cross_check_result", Conv.OBJECT); - } - - /** - * The created_at field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional createdAt() { - return Json.optional(json, "created_at", Conv.DATE_TIME); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Items}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Items from) { - super(from); - } - - /** - * The uuid field. - * - * @param uuid the value - * @return this builder - */ - public Builder uuid(String uuid) { - return set("uuid", Conv.STRING, uuid); - } - - /** - * Workflow graph node that produced this document. - * - * @param nodeId the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder nodeId(@Nullable String nodeId) { - return setNullable("node_id", Conv.STRING, nodeId); - } - - /** - * Key of the configured Document AI document this file corresponds to. - * - * @param documentKey the value - * @return this builder - */ - public Builder documentKey(String documentKey) { - return set("document_key", Conv.STRING, documentKey); - } - - /** - * Filename as uploaded by the user. - * - * @param originalFilename the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder originalFilename(@Nullable String originalFilename) { - return setNullable("original_filename", Conv.STRING, originalFilename); - } - - /** - * File size in bytes. - * - * @param fileSize the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder fileSize(@Nullable Long fileSize) { - return setNullable("file_size", Conv.LONG, fileSize); - } - - /** - * Short-lived presigned link to the uploaded file. - * - * @param fileUrl the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder fileUrl(@Nullable String fileUrl) { - return setNullable("file_url", Conv.STRING, fileUrl); - } - - /** - * Per-document Document AI status. - * - * @param status the value - * @return this builder - */ - public Builder status(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items.Status::of), status); - } - - /** - * Values extracted by Document AI, keyed by the configured field key. Each value is a string, number, or null. - * - * @param extractedData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder extractedData(@Nullable Map extractedData) { - return setAny("extracted_data", extractedData); - } - - /** - * QR codes and barcodes detected across every processed page. Empty when none are found. A located but undecodable QR code has parsed_payload: null. - * - * @param detectedCodes the value - * @return this builder - */ - public Builder detectedCodes(List detectedCodes) { - return set("detected_codes", Conv.list(Conv.model(DocumentAIDetectedCode::fromJson)), detectedCodes); - } - - /** - * The configured extraction fields for this document. - * - * @param fields the value - * @return this builder - */ - public Builder fields(List fields) { - return set("fields", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items.Fields::fromJson)), fields); - } - - /** - * File-forensics metadata extracted from the uploaded document (e.g. is_tampered). null when nothing could be extracted. - * - * @param documentMetadata the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder documentMetadata(@Nullable Map documentMetadata) { - return setAny("document_metadata", documentMetadata); - } - - /** - * Cross-reference of the extracted document fields against other session data. null when no cross-check was performed. - * - * @param crossCheckResult the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder crossCheckResult(@Nullable Map crossCheckResult) { - return setAny("cross_check_result", crossCheckResult); - } - - /** - * The created_at field. - * - * @param createdAt the value - * @return this builder - */ - public Builder createdAt(OffsetDateTime createdAt) { - return set("created_at", Conv.DATE_TIME, createdAt); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Items} - */ - public Items build() { - return new Items(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Per-document Document AI status. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); - - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); - - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); - - /** The value {@code "Not Finished"}. */ - public static final Status NOT_FINISHED = new Status("Not Finished"); - - private static final List KNOWN_VALUES = List.of(APPROVED, DECLINED, IN_REVIEW, NOT_FINISHED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - public static final class Fields extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("key", "name", "instruction", "type", "required"); - - private Fields(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Fields} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Fields fromJson(JsonNode json) { - return new Fields(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Stable field key used as the key in extracted_data. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional key() { - return Json.optional(json, "key", Conv.STRING); - } - - /** - * Human-readable field name. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional name() { - return Json.optional(json, "name", Conv.STRING); - } - - /** - * Extraction instruction shown to the model. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional instruction() { - return Json.optional(json, "instruction", Conv.STRING); - } - - /** - * Expected value type of the extracted field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional type() { - return Json.optional(json, "type", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items.Fields.Type::of)); - } - - /** - * Whether this field is required. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional required() { - return Json.optional(json, "required", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Fields}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Fields from) { - super(from); - } - - /** - * Stable field key used as the key in extracted_data. - * - * @param key the value - * @return this builder - */ - public Builder key(String key) { - return set("key", Conv.STRING, key); - } - - /** - * Human-readable field name. - * - * @param name the value - * @return this builder - */ - public Builder name(String name) { - return set("name", Conv.STRING, name); - } - - /** - * Extraction instruction shown to the model. - * - * @param instruction the value - * @return this builder - */ - public Builder instruction(String instruction) { - return set("instruction", Conv.STRING, instruction); - } - - /** - * Expected value type of the extracted field. - * - * @param type the value - * @return this builder - */ - public Builder type(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items.Fields.Type type) { - return set("type", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentAiDocuments.Items.Fields.Type::of), type); - } - - /** - * Whether this field is required. - * - * @param required the value - * @return this builder - */ - public Builder required(boolean required) { - return set("required", Conv.BOOLEAN, required); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Fields} - */ - public Fields build() { - return new Fields(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Expected value type of the extracted field. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Type extends StringEnum { - /** The value {@code "text"}. */ - public static final Type TEXT = new Type("text"); - - /** The value {@code "number"}. */ - public static final Type NUMBER = new Type("number"); - - /** The value {@code "date"}. */ - public static final Type DATE = new Type("date"); - - private static final List KNOWN_VALUES = List.of(TEXT, NUMBER, DATE); - - private Type(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Type of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Type::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - } - } - - public static final class QuestionnaireResponses extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("node_id", "questionnaire_id", "title", "description", "languages", "default_language", "is_active", "is_simple_questionnaire", "questionnaire_group_id", "version", "published_at", "sections", "status"); - - private QuestionnaireResponses(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code QuestionnaireResponses} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static QuestionnaireResponses fromJson(JsonNode json) { - return new QuestionnaireResponses(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Unique identifier for this questionnaire step in the workflow graph. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } - - /** - * Unique identifier for the questionnaire - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional questionnaireId() { - return Json.optional(json, "questionnaire_id", Conv.STRING); - } - - /** - * Title of the questionnaire - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional title() { - return Json.optional(json, "title", Conv.STRING); - } - - /** - * Description of the questionnaire - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional description() { - return Json.optional(json, "description", Conv.STRING); - } - - /** - * Supported languages - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> languages() { - return Json.optional(json, "languages", Conv.list(Conv.STRING)); - } - - /** - * Default language - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional defaultLanguage() { - return Json.optional(json, "default_language", Conv.STRING); - } - - /** - * Whether the questionnaire is active - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isActive() { - return Json.optional(json, "is_active", Conv.BOOLEAN); - } - - /** - * Whether the questionnaire is a simple linear form (no branching graph). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isSimpleQuestionnaire() { - return Json.optional(json, "is_simple_questionnaire", Conv.BOOLEAN); - } - - /** - * Stable identifier of the questionnaire group across versions. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional questionnaireGroupId() { - return Json.optional(json, "questionnaire_group_id", Conv.STRING); - } - - /** - * Version number of the questionnaire that was answered. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional version() { - return Json.optional(json, "version", Conv.LONG); - } - - /** - * Timestamp at which this questionnaire version was published. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional publishedAt() { - return Json.optional(json, "published_at", Conv.DATE_TIME); - } - - /** - * The sections field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> sections() { - return Json.optional(json, "sections", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses.Sections::fromJson))); - } - - /** - * Status of the questionnaire - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link QuestionnaireResponses}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(QuestionnaireResponses from) { - super(from); - } - - /** - * Unique identifier for this questionnaire step in the workflow graph. - * - * @param nodeId the value - * @return this builder - */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); - } - - /** - * Unique identifier for the questionnaire - * - * @param questionnaireId the value - * @return this builder - */ - public Builder questionnaireId(String questionnaireId) { - return set("questionnaire_id", Conv.STRING, questionnaireId); - } - - /** - * Title of the questionnaire - * - * @param title the value - * @return this builder - */ - public Builder title(String title) { - return set("title", Conv.STRING, title); - } - - /** - * Description of the questionnaire - * - * @param description the value - * @return this builder - */ - public Builder description(String description) { - return set("description", Conv.STRING, description); - } - - /** - * Supported languages - * - * @param languages the value - * @return this builder - */ - public Builder languages(List languages) { - return set("languages", Conv.list(Conv.STRING), languages); - } - - /** - * Default language - * - * @param defaultLanguage the value - * @return this builder - */ - public Builder defaultLanguage(String defaultLanguage) { - return set("default_language", Conv.STRING, defaultLanguage); - } - - /** - * Whether the questionnaire is active - * - * @param isActive the value - * @return this builder - */ - public Builder isActive(boolean isActive) { - return set("is_active", Conv.BOOLEAN, isActive); - } - - /** - * Whether the questionnaire is a simple linear form (no branching graph). - * - * @param isSimpleQuestionnaire the value - * @return this builder - */ - public Builder isSimpleQuestionnaire(boolean isSimpleQuestionnaire) { - return set("is_simple_questionnaire", Conv.BOOLEAN, isSimpleQuestionnaire); - } - - /** - * Stable identifier of the questionnaire group across versions. - * - * @param questionnaireGroupId the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder questionnaireGroupId(@Nullable String questionnaireGroupId) { - return setNullable("questionnaire_group_id", Conv.STRING, questionnaireGroupId); - } - - /** - * Version number of the questionnaire that was answered. - * - * @param version the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder version(@Nullable Long version) { - return setNullable("version", Conv.LONG, version); - } - - /** - * Timestamp at which this questionnaire version was published. - * - * @param publishedAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder publishedAt(@Nullable OffsetDateTime publishedAt) { - return setNullable("published_at", Conv.DATE_TIME, publishedAt); - } - - /** - * The sections field. - * - * @param sections the value - * @return this builder - */ - public Builder sections(List sections) { - return set("sections", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses.Sections::fromJson)), sections); - } - - /** - * Status of the questionnaire - * - * @param status the value - * @return this builder - */ - public Builder status(String status) { - return set("status", Conv.STRING, status); - } - - /** - * Builds the value. - * - * @return a new immutable {@code QuestionnaireResponses} - */ - public QuestionnaireResponses build() { - return new QuestionnaireResponses(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - public static final class Sections extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("title", "description", "items"); - - private Sections(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Sections} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Sections fromJson(JsonNode json) { - return new Sections(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Section title - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional title() { - return Json.optional(json, "title", Conv.STRING); - } - - /** - * Section description - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional description() { - return Json.optional(json, "description", Conv.STRING); - } - - /** - * The items field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> items() { - return Json.optional(json, "items", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses.Sections.Items::fromJson))); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Sections}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Sections from) { - super(from); - } - - /** - * Section title - * - * @param title the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder title(@Nullable String title) { - return setNullable("title", Conv.STRING, title); - } - - /** - * Section description - * - * @param description the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder description(@Nullable String description) { - return setNullable("description", Conv.STRING, description); - } - - /** - * The items field. - * - * @param items the value - * @return this builder - */ - public Builder items(List items) { - return set("items", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses.Sections.Items::fromJson)), items); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Sections} - */ - public Sections build() { - return new Sections(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - public static final class Items extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "value", "element_type", "is_required", "title", "description", "placeholder", "choices", "max_files", "answer", "required_if", "repeatable_config"); - - private Items(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Items} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Items fromJson(JsonNode json) { - return new Items(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Unique identifier for the item - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String uuid() { - return Json.required(json, "uuid", Conv.STRING); - } - - /** - * Stable node identifier of this form element within the questionnaire graph. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional value() { - return Json.optional(json, "value", Conv.STRING); - } - - /** - * Type of the questionnaire element - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String elementType() { - return Json.required(json, "element_type", Conv.STRING); - } - - /** - * Whether the item is required - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public boolean isRequired() { - return Json.required(json, "is_required", Conv.BOOLEAN); - } - - /** - * Title of the item - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String title() { - return Json.required(json, "title", Conv.STRING); - } - - /** - * Description of the item - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional description() { - return Json.optional(json, "description", Conv.STRING); - } - - /** - * Placeholder text - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional placeholder() { - return Json.optional(json, "placeholder", Conv.STRING); - } - - /** - * Choices for dropdown or single choice elements - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> choices() { - return Json.optional(json, "choices", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses.Sections.Items.Choices::fromJson))); - } - - /** - * Maximum number of files allowed - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional maxFiles() { - return Json.optional(json, "max_files", Conv.LONG); - } - - /** - * Answer provided for the item - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional answer() { - return Json.optional(json, "answer", Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses.Sections.Items.Answer::fromJson)); - } - - /** - * Conditional-requirement rule, when the element is only required for certain answers. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> requiredIf() { - return Json.optional(json, "required_if", Conv.OBJECT); - } - - /** - * Configuration for repeatable element groups, when applicable. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> repeatableConfig() { - return Json.optional(json, "repeatable_config", Conv.OBJECT); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Items}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Items from) { - super(from); - } - - /** - * Unique identifier for the item - * - * @param uuid the value - * @return this builder - */ - public Builder uuid(String uuid) { - return set("uuid", Conv.STRING, uuid); - } - - /** - * Stable node identifier of this form element within the questionnaire graph. - * - * @param value the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder value(@Nullable String value) { - return setNullable("value", Conv.STRING, value); - } - - /** - * Type of the questionnaire element - * - * @param elementType the value - * @return this builder - */ - public Builder elementType(String elementType) { - return set("element_type", Conv.STRING, elementType); - } - - /** - * Whether the item is required - * - * @param isRequired the value - * @return this builder - */ - public Builder isRequired(boolean isRequired) { - return set("is_required", Conv.BOOLEAN, isRequired); - } - - /** - * Title of the item - * - * @param title the value - * @return this builder - */ - public Builder title(String title) { - return set("title", Conv.STRING, title); - } - - /** - * Description of the item - * - * @param description the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder description(@Nullable String description) { - return setNullable("description", Conv.STRING, description); - } - - /** - * Placeholder text - * - * @param placeholder the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder placeholder(@Nullable String placeholder) { - return setNullable("placeholder", Conv.STRING, placeholder); - } - - /** - * Choices for dropdown or single choice elements - * - * @param choices the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder choices(@Nullable List choices) { - return setNullable("choices", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses.Sections.Items.Choices::fromJson)), choices); - } - - /** - * Maximum number of files allowed - * - * @param maxFiles the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder maxFiles(@Nullable Long maxFiles) { - return setNullable("max_files", Conv.LONG, maxFiles); - } - - /** - * Answer provided for the item - * - * @param answer the value - * @return this builder - */ - public Builder answer(SessionsRetrieveDecisionResponse.QuestionnaireResponses.Sections.Items.Answer answer) { - return set("answer", Conv.model(SessionsRetrieveDecisionResponse.QuestionnaireResponses.Sections.Items.Answer::fromJson), answer); - } - - /** - * Conditional-requirement rule, when the element is only required for certain answers. - * - * @param requiredIf the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder requiredIf(@Nullable Map requiredIf) { - return setAny("required_if", requiredIf); - } - - /** - * Configuration for repeatable element groups, when applicable. - * - * @param repeatableConfig the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder repeatableConfig(@Nullable Map repeatableConfig) { - return setAny("repeatable_config", repeatableConfig); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Items} - * @throws IllegalStateException when a required field is missing - */ - public Items build() { - require("uuid", false); - require("value", true); - require("element_type", false); - require("is_required", false); - require("title", false); - return new Items(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - public static final class Choices extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("label", "value", "requires_text_input"); - - private Choices(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Choices} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Choices fromJson(JsonNode json) { - return new Choices(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The label field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String label() { - return Json.required(json, "label", Conv.STRING); - } - - /** - * The value field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String value() { - return Json.required(json, "value", Conv.STRING); - } - - /** - * Whether this choice requires additional text input - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional requiresTextInput() { - return Json.optional(json, "requires_text_input", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Choices}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Choices from) { - super(from); - } - - /** - * The label field. - * - * @param label the value - * @return this builder - */ - public Builder label(String label) { - return set("label", Conv.STRING, label); - } - - /** - * The value field. - * - * @param value the value - * @return this builder - */ - public Builder value(String value) { - return set("value", Conv.STRING, value); - } - - /** - * Whether this choice requires additional text input - * - * @param requiresTextInput the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder requiresTextInput(@Nullable Boolean requiresTextInput) { - return setNullable("requires_text_input", Conv.BOOLEAN, requiresTextInput); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Choices} - * @throws IllegalStateException when a required field is missing - */ - public Choices build() { - require("label", false); - require("value", false); - return new Choices(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Answer provided for the item */ - public static final class Answer extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("value", "text", "files"); - - private Answer(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Answer} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Answer fromJson(JsonNode json) { - return new Answer(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The value field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional value() { - return Json.optional(json, "value", Conv.STRING); - } - - /** - * The text field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional text() { - return Json.optional(json, "text", Conv.STRING); - } - - /** - * The files field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> files() { - return Json.optional(json, "files", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Answer}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Answer from) { - super(from); - } - - /** - * The value field. - * - * @param value the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder value(@Nullable String value) { - return setNullable("value", Conv.STRING, value); - } - - /** - * The text field. - * - * @param text the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder text(@Nullable String text) { - return setNullable("text", Conv.STRING, text); - } - - /** - * The files field. - * - * @param files the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder files(@Nullable List files) { - return setNullable("files", Conv.list(Conv.STRING), files); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Answer} - */ - public Answer build() { - return new Answer(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - } - } - - public static final class AmlScreenings extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("node_id", "status", "total_hits", "entity_type", "hits", "score", "screened_data", "is_ongoing_monitoring_enabled", "next_ongoing_monitoring_bill_date", "last_ongoing_screening_at", "last_ongoing_screening_datasets_updated_at", "warnings"); - - private AmlScreenings(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code AmlScreenings} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static AmlScreenings fromJson(JsonNode json) { - return new AmlScreenings(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Unique identifier for this AML step in the workflow graph. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } - - /** - * Feature-level status of this AML screening. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Status::of)); - } - - /** - * Total number of matches found - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional totalHits() { - return Json.optional(json, "total_hits", Conv.LONG); - } - - /** - * Type of entity screened: person or company - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional entityType() { - return Json.optional(json, "entity_type", Conv.STRING); - } - - /** - * The hits field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> hits() { - return Json.optional(json, "hits", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits::fromJson))); - } - - /** - * Overall AML screening score — the highest risk score across all hits (float, 0-100). null until computed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional score() { - return Json.optional(json, "score", Conv.DOUBLE); - } - - /** - * The screened_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedData() { - return Json.optional(json, "screened_data", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.ScreenedData::fromJson)); - } - - /** - * Whether AML ongoing monitoring is enabled for this screening. When true, the entity is re-screened periodically and any new hits update the report. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isOngoingMonitoringEnabled() { - return Json.optional(json, "is_ongoing_monitoring_enabled", Conv.BOOLEAN); - } - - /** - * Date (YYYY-MM-DD) when this ongoing-monitoring subscription will next be billed. Computed as the date of the last ongoing-monitoring billing plus 365 days when is_ongoing_monitoring_enabled = true; otherwise null. This is a billing date, not a screening date - use last_ongoing_screening_at for that. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nextOngoingMonitoringBillDate() { - return Json.optional(json, "next_ongoing_monitoring_bill_date", Conv.DATE); - } - - /** - * When ongoing monitoring last completed a re-screening of this record, whether or not the result changed. null when the record has never been re-screened; it is never back-filled from the enablement or billing date. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional lastOngoingScreeningAt() { - return Json.optional(json, "last_ongoing_screening_at", Conv.DATE_TIME); - } - - /** - * The most recent AML dataset update date the provider reported at the time of that screening. null when the provider reported no dataset dates; never estimated. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional lastOngoingScreeningDatasetsUpdatedAt() { - return Json.optional(json, "last_ongoing_screening_datasets_updated_at", Conv.DATE_TIME); - } - - /** - * The warnings field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Warnings::fromJson))); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link AmlScreenings}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(AmlScreenings from) { - super(from); - } - - /** - * Unique identifier for this AML step in the workflow graph. - * - * @param nodeId the value - * @return this builder - */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); - } - - /** - * Feature-level status of this AML screening. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - * @param status the value - * @return this builder - */ - public Builder status(SessionsRetrieveDecisionResponse.AmlScreenings.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Status::of), status); - } - - /** - * Total number of matches found - * - * @param totalHits the value - * @return this builder - */ - public Builder totalHits(long totalHits) { - return set("total_hits", Conv.LONG, totalHits); - } - - /** - * Type of entity screened: person or company - * - * @param entityType the value - * @return this builder - */ - public Builder entityType(String entityType) { - return set("entity_type", Conv.STRING, entityType); - } - - /** - * The hits field. - * - * @param hits the value - * @return this builder - */ - public Builder hits(List hits) { - return set("hits", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits::fromJson)), hits); - } - - /** - * Overall AML screening score — the highest risk score across all hits (float, 0-100). null until computed. - * - * @param score the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder score(@Nullable Double score) { - return setNullable("score", Conv.DOUBLE, score); - } - - /** - * The screened_data field. - * - * @param screenedData the value - * @return this builder - */ - public Builder screenedData(SessionsRetrieveDecisionResponse.AmlScreenings.ScreenedData screenedData) { - return set("screened_data", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.ScreenedData::fromJson), screenedData); - } - - /** - * Whether AML ongoing monitoring is enabled for this screening. When true, the entity is re-screened periodically and any new hits update the report. - * - * @param isOngoingMonitoringEnabled the value - * @return this builder - */ - public Builder isOngoingMonitoringEnabled(boolean isOngoingMonitoringEnabled) { - return set("is_ongoing_monitoring_enabled", Conv.BOOLEAN, isOngoingMonitoringEnabled); - } - - /** - * Date (YYYY-MM-DD) when this ongoing-monitoring subscription will next be billed. Computed as the date of the last ongoing-monitoring billing plus 365 days when is_ongoing_monitoring_enabled = true; otherwise null. This is a billing date, not a screening date - use last_ongoing_screening_at for that. - * - * @param nextOngoingMonitoringBillDate the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder nextOngoingMonitoringBillDate(@Nullable LocalDate nextOngoingMonitoringBillDate) { - return setNullable("next_ongoing_monitoring_bill_date", Conv.DATE, nextOngoingMonitoringBillDate); - } - - /** - * When ongoing monitoring last completed a re-screening of this record, whether or not the result changed. null when the record has never been re-screened; it is never back-filled from the enablement or billing date. - * - * @param lastOngoingScreeningAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder lastOngoingScreeningAt(@Nullable OffsetDateTime lastOngoingScreeningAt) { - return setNullable("last_ongoing_screening_at", Conv.DATE_TIME, lastOngoingScreeningAt); - } - - /** - * The most recent AML dataset update date the provider reported at the time of that screening. null when the provider reported no dataset dates; never estimated. - * - * @param lastOngoingScreeningDatasetsUpdatedAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder lastOngoingScreeningDatasetsUpdatedAt(@Nullable OffsetDateTime lastOngoingScreeningDatasetsUpdatedAt) { - return setNullable("last_ongoing_screening_datasets_updated_at", Conv.DATE_TIME, lastOngoingScreeningDatasetsUpdatedAt); - } - - /** - * The warnings field. - * - * @param warnings the value - * @return this builder - */ - public Builder warnings(List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Warnings::fromJson)), warnings); - } - - /** - * Builds the value. - * - * @return a new immutable {@code AmlScreenings} - */ - public AmlScreenings build() { - return new AmlScreenings(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Feature-level status of this AML screening. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Not Finished"}. */ - public static final Status NOT_FINISHED = new Status("Not Finished"); - - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); - - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); - - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); - - /** The value {@code "Resub Requested"}. */ - public static final Status RESUB_REQUESTED = new Status("Resub Requested"); - - private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - public static final class Hits extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("id", "url", "match", "score", "target", "caption", "datasets", "features", "rca_name", "last_seen", "risk_view", "first_seen", "properties", "match_score", "risk_score", "review_status", "score_breakdown", "pep_matches", "linked_entities", "warning_matches", "sanction_matches", "adverse_media_details", "adverse_media_matches", "additional_information"); - - private Hits(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Hits} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Hits fromJson(JsonNode json) { - return new Hits(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Unique identifier of the match - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional id() { - return Json.optional(json, "id", Conv.STRING); - } - - /** - * Source URL for the match - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional url() { - return Json.optional(json, "url", Conv.STRING); - } - - /** - * Whether this is a confirmed match - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional match() { - return Json.optional(json, "match", Conv.BOOLEAN); - } - - /** - * Deprecated weighted match score in the 0-1 range (e.g. 0.73). Use match_score (0-100) instead. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - * - * @deprecated Deprecated by the API. - */ - @Deprecated - public Optional score() { - return Json.optional(json, "score", Conv.DOUBLE); - } - - /** - * Whether the matched entity is the direct target of the listing (as opposed to a related party). null when the source does not specify it. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional target() { - return Json.optional(json, "target", Conv.BOOLEAN); - } - - /** - * Caption or title of the match - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional caption() { - return Json.optional(json, "caption", Conv.STRING); - } - - /** - * List of dataset categories where the match was found. Indicates whether the entity appeared in PEP lists (with level), sanctions databases, adverse media, warnings/regulatory enforcement, or other compliance datasets. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> datasets() { - return Json.optional(json, "datasets", Conv.list(Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.Datasets::of))); - } - - /** - * Additional features of the match - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> features() { - return Json.optional(json, "features", Conv.OBJECT); - } - - /** - * RCA name if applicable - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional rcaName() { - return Json.optional(json, "rca_name", Conv.STRING); - } - - /** - * Date when match was last seen - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional lastSeen() { - return Json.optional(json, "last_seen", Conv.DATE_TIME); - } - - /** - * The risk_view field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional riskView() { - return Json.optional(json, "risk_view", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView::fromJson)); - } - - /** - * Date when match was first seen - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional firstSeen() { - return Json.optional(json, "first_seen", Conv.DATE_TIME); - } - - /** - * The properties field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional properties() { - return Json.optional(json, "properties", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.Properties::fromJson)); - } - - /** - * Weighted identity confidence score (0-100) calculated from name similarity, date of birth, and country matching. Used to determine if hit is a False Positive or Possible Match. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional matchScore() { - return Json.optional(json, "match_score", Conv.DOUBLE); - } - - /** - * Entity risk score (0-100). Used to determine final AML status (Approved/In Review/Declined) based on the highest risk_score among non-False-Positive hits. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional riskScore() { - return Json.optional(json, "risk_score", Conv.DOUBLE); - } - - /** - * Review status for this hit. Can be 'Unreviewed', 'Confirmed Match', 'False Positive', or 'Inconclusive'. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional reviewStatus() { - return Json.optional(json, "review_status", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.ReviewStatus::of)); - } - - /** - * Detailed breakdown of how the match_score was calculated. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional scoreBreakdown() { - return Json.optional(json, "score_breakdown", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.ScoreBreakdown::fromJson)); - } - - /** - * The pep_matches field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> pepMatches() { - return Json.optional(json, "pep_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.PepMatches::fromJson))); - } - - /** - * The linked_entities field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> linkedEntities() { - return Json.optional(json, "linked_entities", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.LinkedEntities::fromJson))); - } - - /** - * List of warning and regulatory enforcement matches. Includes entries from categories: Warning, SIP, Warnings and Regulatory Enforcement, Fitness and Probity, SIE, and Insolvency. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> warningMatches() { - return Json.optional(json, "warning_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches::fromJson))); - } - - /** - * List of sanction list matches found for this entity. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> sanctionMatches() { - return Json.optional(json, "sanction_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.SanctionMatches::fromJson))); - } - - /** - * The adverse_media_details field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional adverseMediaDetails() { - return Json.optional(json, "adverse_media_details", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.AdverseMediaDetails::fromJson)); - } - - /** - * The adverse_media_matches field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> adverseMediaMatches() { - return Json.optional(json, "adverse_media_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.AdverseMediaMatches::fromJson))); - } - - /** - * The additional_information field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> additionalInformation() { - return Json.optional(json, "additional_information", Conv.OBJECT); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Hits}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Hits from) { - super(from); - } - - /** - * Unique identifier of the match - * - * @param id the value - * @return this builder - */ - public Builder id(String id) { - return set("id", Conv.STRING, id); - } - - /** - * Source URL for the match - * - * @param url the value - * @return this builder - */ - public Builder url(String url) { - return set("url", Conv.STRING, url); - } - - /** - * Whether this is a confirmed match - * - * @param match the value - * @return this builder - */ - public Builder match(boolean match) { - return set("match", Conv.BOOLEAN, match); - } - - /** - * Deprecated weighted match score in the 0-1 range (e.g. 0.73). Use match_score (0-100) instead. - * - * @param score the value, or {@code null} to send JSON null - * @return this builder - * - * @deprecated Deprecated by the API. - */ - @Deprecated - public Builder score(@Nullable Double score) { - return setNullable("score", Conv.DOUBLE, score); - } - - /** - * Whether the matched entity is the direct target of the listing (as opposed to a related party). null when the source does not specify it. - * - * @param target the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder target(@Nullable Boolean target) { - return setNullable("target", Conv.BOOLEAN, target); - } - - /** - * Caption or title of the match - * - * @param caption the value - * @return this builder - */ - public Builder caption(String caption) { - return set("caption", Conv.STRING, caption); - } - - /** - * List of dataset categories where the match was found. Indicates whether the entity appeared in PEP lists (with level), sanctions databases, adverse media, warnings/regulatory enforcement, or other compliance datasets. - * - * @param datasets the value - * @return this builder - */ - public Builder datasets(List datasets) { - return set("datasets", Conv.list(Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.Datasets::of)), datasets); - } - - /** - * Additional features of the match - * - * @param features the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder features(@Nullable Map features) { - return setAny("features", features); - } - - /** - * RCA name if applicable - * - * @param rcaName the value - * @return this builder - */ - public Builder rcaName(String rcaName) { - return set("rca_name", Conv.STRING, rcaName); - } - - /** - * Date when match was last seen - * - * @param lastSeen the value - * @return this builder - */ - public Builder lastSeen(OffsetDateTime lastSeen) { - return set("last_seen", Conv.DATE_TIME, lastSeen); - } - - /** - * The risk_view field. - * - * @param riskView the value - * @return this builder - */ - public Builder riskView(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView riskView) { - return set("risk_view", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView::fromJson), riskView); - } - - /** - * Date when match was first seen - * - * @param firstSeen the value - * @return this builder - */ - public Builder firstSeen(OffsetDateTime firstSeen) { - return set("first_seen", Conv.DATE_TIME, firstSeen); - } - - /** - * The properties field. - * - * @param properties the value - * @return this builder - */ - public Builder properties(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.Properties properties) { - return set("properties", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.Properties::fromJson), properties); - } - - /** - * Weighted identity confidence score (0-100) calculated from name similarity, date of birth, and country matching. Used to determine if hit is a False Positive or Possible Match. - * - * @param matchScore the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder matchScore(@Nullable Double matchScore) { - return setNullable("match_score", Conv.DOUBLE, matchScore); - } - - /** - * Entity risk score (0-100). Used to determine final AML status (Approved/In Review/Declined) based on the highest risk_score among non-False-Positive hits. - * - * @param riskScore the value - * @return this builder - */ - public Builder riskScore(double riskScore) { - return set("risk_score", Conv.DOUBLE, riskScore); - } - - /** - * Review status for this hit. Can be 'Unreviewed', 'Confirmed Match', 'False Positive', or 'Inconclusive'. - * - * @param reviewStatus the value - * @return this builder - */ - public Builder reviewStatus(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.ReviewStatus reviewStatus) { - return set("review_status", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.ReviewStatus::of), reviewStatus); - } - - /** - * Detailed breakdown of how the match_score was calculated. - * - * @param scoreBreakdown the value - * @return this builder - */ - public Builder scoreBreakdown(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.ScoreBreakdown scoreBreakdown) { - return set("score_breakdown", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.ScoreBreakdown::fromJson), scoreBreakdown); - } - - /** - * The pep_matches field. - * - * @param pepMatches the value - * @return this builder - */ - public Builder pepMatches(List pepMatches) { - return set("pep_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.PepMatches::fromJson)), pepMatches); - } - - /** - * The linked_entities field. - * - * @param linkedEntities the value - * @return this builder - */ - public Builder linkedEntities(List linkedEntities) { - return set("linked_entities", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.LinkedEntities::fromJson)), linkedEntities); - } - - /** - * List of warning and regulatory enforcement matches. Includes entries from categories: Warning, SIP, Warnings and Regulatory Enforcement, Fitness and Probity, SIE, and Insolvency. - * - * @param warningMatches the value - * @return this builder - */ - public Builder warningMatches(List warningMatches) { - return set("warning_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches::fromJson)), warningMatches); - } - - /** - * List of sanction list matches found for this entity. - * - * @param sanctionMatches the value - * @return this builder - */ - public Builder sanctionMatches(List sanctionMatches) { - return set("sanction_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.SanctionMatches::fromJson)), sanctionMatches); - } - - /** - * The adverse_media_details field. - * - * @param adverseMediaDetails the value - * @return this builder - */ - public Builder adverseMediaDetails(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.AdverseMediaDetails adverseMediaDetails) { - return set("adverse_media_details", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.AdverseMediaDetails::fromJson), adverseMediaDetails); - } - - /** - * The adverse_media_matches field. - * - * @param adverseMediaMatches the value - * @return this builder - */ - public Builder adverseMediaMatches(List adverseMediaMatches) { - return set("adverse_media_matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.AdverseMediaMatches::fromJson)), adverseMediaMatches); - } - - /** - * The additional_information field. - * - * @param additionalInformation the value - * @return this builder - */ - public Builder additionalInformation(Map additionalInformation) { - return setAny("additional_information", additionalInformation); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Hits} - */ - public Hits build() { - return new Hits(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Datasets extends StringEnum { - /** The value {@code "PEP"}. */ - public static final Datasets PEP = new Datasets("PEP"); - - /** The value {@code "PEP Level 1"}. */ - public static final Datasets PEP_LEVEL_1 = new Datasets("PEP Level 1"); - - /** The value {@code "PEP Level 2"}. */ - public static final Datasets PEP_LEVEL_2 = new Datasets("PEP Level 2"); - - /** The value {@code "PEP Level 3"}. */ - public static final Datasets PEP_LEVEL_3 = new Datasets("PEP Level 3"); - - /** The value {@code "PEP Level 4"}. */ - public static final Datasets PEP_LEVEL_4 = new Datasets("PEP Level 4"); - - /** The value {@code "Sanctions"}. */ - public static final Datasets SANCTIONS = new Datasets("Sanctions"); - - /** The value {@code "Adverse Media"}. */ - public static final Datasets ADVERSE_MEDIA = new Datasets("Adverse Media"); - - /** The value {@code "Warnings and Regulatory Enforcement"}. */ - public static final Datasets WARNINGS_AND_REGULATORY_ENFORCEMENT = new Datasets("Warnings and Regulatory Enforcement"); - - /** The value {@code "Fitness and Probity"}. */ - public static final Datasets FITNESS_AND_PROBITY = new Datasets("Fitness and Probity"); - - /** The value {@code "SIP"}. */ - public static final Datasets SIP = new Datasets("SIP"); - - /** The value {@code "SIE"}. */ - public static final Datasets SIE = new Datasets("SIE"); - - /** The value {@code "Insolvency"}. */ - public static final Datasets INSOLVENCY = new Datasets("Insolvency"); - - private static final List KNOWN_VALUES = List.of(PEP, PEP_LEVEL_1, PEP_LEVEL_2, PEP_LEVEL_3, PEP_LEVEL_4, SANCTIONS, ADVERSE_MEDIA, WARNINGS_AND_REGULATORY_ENFORCEMENT, FITNESS_AND_PROBITY, SIP, SIE, INSOLVENCY); - - private Datasets(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Datasets of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Datasets::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - public static final class RiskView extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("crimes", "countries", "categories", "custom_list"); - - private RiskView(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code RiskView} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static RiskView fromJson(JsonNode json) { - return new RiskView(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The crimes field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional crimes() { - return Json.optional(json, "crimes", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView.Crimes::fromJson)); - } - - /** - * The countries field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional countries() { - return Json.optional(json, "countries", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView.Countries::fromJson)); - } - - /** - * The categories field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional categories() { - return Json.optional(json, "categories", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView.Categories::fromJson)); - } - - /** - * The custom_list field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> customList() { - return Json.optional(json, "custom_list", Conv.OBJECT); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link RiskView}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(RiskView from) { - super(from); - } - - /** - * The crimes field. - * - * @param crimes the value - * @return this builder - */ - public Builder crimes(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView.Crimes crimes) { - return set("crimes", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView.Crimes::fromJson), crimes); - } - - /** - * The countries field. - * - * @param countries the value - * @return this builder - */ - public Builder countries(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView.Countries countries) { - return set("countries", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView.Countries::fromJson), countries); - } - - /** - * The categories field. - * - * @param categories the value - * @return this builder - */ - public Builder categories(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView.Categories categories) { - return set("categories", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.RiskView.Categories::fromJson), categories); - } - - /** - * The custom_list field. - * - * @param customList the value - * @return this builder - */ - public Builder customList(Map customList) { - return setAny("custom_list", customList); - } - - /** - * Builds the value. - * - * @return a new immutable {@code RiskView} - */ - public RiskView build() { - return new RiskView(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - public static final class Crimes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("score", "weightage", "risk_level", "risk_scores"); - - private Crimes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Crimes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Crimes fromJson(JsonNode json) { - return new Crimes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The score field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional score() { - return Json.optional(json, "score", Conv.LONG); - } - - /** - * The weightage field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional weightage() { - return Json.optional(json, "weightage", Conv.LONG); - } - - /** - * The risk_level field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional riskLevel() { - return Json.optional(json, "risk_level", Conv.STRING); - } - - /** - * The risk_scores field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> riskScores() { - return Json.optional(json, "risk_scores", Conv.OBJECT); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Crimes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Crimes from) { - super(from); - } - - /** - * The score field. - * - * @param score the value - * @return this builder - */ - public Builder score(long score) { - return set("score", Conv.LONG, score); - } - - /** - * The weightage field. - * - * @param weightage the value - * @return this builder - */ - public Builder weightage(long weightage) { - return set("weightage", Conv.LONG, weightage); - } - - /** - * The risk_level field. - * - * @param riskLevel the value - * @return this builder - */ - public Builder riskLevel(String riskLevel) { - return set("risk_level", Conv.STRING, riskLevel); - } - - /** - * The risk_scores field. - * - * @param riskScores the value - * @return this builder - */ - public Builder riskScores(Map riskScores) { - return setAny("risk_scores", riskScores); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Crimes} - */ - public Crimes build() { - return new Crimes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Countries extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("score", "weightage", "risk_level", "risk_scores"); - - private Countries(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Countries} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Countries fromJson(JsonNode json) { - return new Countries(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The score field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional score() { - return Json.optional(json, "score", Conv.LONG); - } - - /** - * The weightage field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional weightage() { - return Json.optional(json, "weightage", Conv.LONG); - } - - /** - * The risk_level field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional riskLevel() { - return Json.optional(json, "risk_level", Conv.STRING); - } - - /** - * The risk_scores field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> riskScores() { - return Json.optional(json, "risk_scores", Conv.OBJECT); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Countries}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Countries from) { - super(from); - } - - /** - * The score field. - * - * @param score the value - * @return this builder - */ - public Builder score(long score) { - return set("score", Conv.LONG, score); - } - - /** - * The weightage field. - * - * @param weightage the value - * @return this builder - */ - public Builder weightage(long weightage) { - return set("weightage", Conv.LONG, weightage); - } - - /** - * The risk_level field. - * - * @param riskLevel the value - * @return this builder - */ - public Builder riskLevel(String riskLevel) { - return set("risk_level", Conv.STRING, riskLevel); - } - - /** - * The risk_scores field. - * - * @param riskScores the value - * @return this builder - */ - public Builder riskScores(Map riskScores) { - return setAny("risk_scores", riskScores); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Countries} - */ - public Countries build() { - return new Countries(buildJson(), buildFiles()); - } + /** + * Reads a {@code PoaVerifications} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PoaVerifications fromJson(JsonNode json) { + return new PoaVerifications(Json.copyObject(json), Map.of()); + } - @Override - protected Builder self() { - return this; - } - } - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - public static final class Categories extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("score", "weightage", "risk_level", "risk_scores"); + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - private Categories(ObjectNode json, Map> files) { - super(json, files); - } + /** + * Unique identifier for this POA step in the workflow graph. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } - /** - * Reads a {@code Categories} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Categories fromJson(JsonNode json) { - return new Categories(Json.copyObject(json), Map.of()); - } + /** + * Feature-level status of this Proof of Address step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.PoaVerifications.Status::of)); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * URL of the proof of address document + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentFile() { + return Json.optional(json, "document_file", Conv.STRING); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * Two-letter country code of the issuing state + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional issuingState() { + return Json.optional(json, "issuing_state", Conv.STRING); + } - /** - * The score field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional score() { - return Json.optional(json, "score", Conv.LONG); - } + /** + * Type of proof of address document + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentType() { + return Json.optional(json, "document_type", Conv.STRING); + } - /** - * The weightage field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional weightage() { - return Json.optional(json, "weightage", Conv.LONG); - } + /** + * More specific classification of the POA document within its document_type, when one was detected. null otherwise. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentSubtype() { + return Json.optional(json, "document_subtype", Conv.STRING); + } - /** - * The risk_level field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional riskLevel() { - return Json.optional(json, "risk_level", Conv.STRING); - } + /** + * Language code of the document + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentLanguage() { + return Json.optional(json, "document_language", Conv.STRING); + } - /** - * The risk_scores field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> riskScores() { - return Json.optional(json, "risk_scores", Conv.OBJECT); - } + /** + * File-forensics metadata extracted from the uploaded POA document. Always contains the keys below (values are null when not applicable, e.g. EXIF dates for PDFs). null when no metadata could be extracted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentMetadata() { + return Json.optional(json, "document_metadata", Conv.model(SessionDecision.PoaVerifications.DocumentMetadata::fromJson)); + } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** + * Name of the document issuer + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional issuer() { + return Json.optional(json, "issuer", Conv.STRING); + } - /** Builds a {@link Categories}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** + * Date when the document was issued + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional issueDate() { + return Json.optional(json, "issue_date", Conv.DATE); + } - private Builder(Categories from) { - super(from); - } + /** + * Expiration date of the document, if available + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expirationDate() { + return Json.optional(json, "expiration_date", Conv.DATE); + } - /** - * The score field. - * - * @param score the value - * @return this builder - */ - public Builder score(long score) { - return set("score", Conv.LONG, score); - } + /** + * Raw address from the proof of address document + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional poaAddress() { + return Json.optional(json, "poa_address", Conv.STRING); + } - /** - * The weightage field. - * - * @param weightage the value - * @return this builder - */ - public Builder weightage(long weightage) { - return set("weightage", Conv.LONG, weightage); - } + /** + * Formatted address from the proof of address document + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional poaFormattedAddress() { + return Json.optional(json, "poa_formatted_address", Conv.STRING); + } - /** - * The risk_level field. - * - * @param riskLevel the value - * @return this builder - */ - public Builder riskLevel(String riskLevel) { - return set("risk_level", Conv.STRING, riskLevel); - } + /** + * The poa_parsed_address field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional poaParsedAddress() { + return Json.optional(json, "poa_parsed_address", Conv.model(SessionDecision.PoaVerifications.PoaParsedAddress::fromJson)); + } - /** - * The risk_scores field. - * - * @param riskScores the value - * @return this builder - */ - public Builder riskScores(Map riskScores) { - return setAny("risk_scores", riskScores); - } + /** + * Account-holder / addressee name extracted from the POA document, used for name matching. null when no name could be extracted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameOnDocument() { + return Json.optional(json, "name_on_document", Conv.STRING); + } - /** - * Builds the value. - * - * @return a new immutable {@code Categories} - */ - public Categories build() { - return new Categories(buildJson(), buildFiles()); - } + /** + * Similarity score (0-100) between name_on_document and the expected name supplied at session creation. null when either side is missing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameMatchScoreExpectedDetails() { + return Json.optional(json, "name_match_score_expected_details", Conv.DOUBLE); + } - @Override - protected Builder self() { - return this; - } - } - } - } + /** + * Similarity score (0-100) between name_on_document and the name extracted by the ID verification step. null when either side is missing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameMatchScoreIdVerification() { + return Json.optional(json, "name_match_score_id_verification", Conv.DOUBLE); + } - public static final class Properties extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("name", "alias", "notes", "title", "gender", "height", "topics", "weight", "address", "country", "website", "eyeColor", "keywords", "lastName", "position", "religion", "birthDate", "education", "ethnicity", "firstName", "hairColor", "weakAlias", "birthPlace", "modifiedAt", "wikidataId", "citizenship", "nationality"); + /** + * Expected raw address for verification + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expectedDetailsAddress() { + return Json.optional(json, "expected_details_address", Conv.STRING); + } - private Properties(ObjectNode json, Map> files) { - super(json, files); - } + /** + * Expected formatted address for verification + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expectedDetailsFormattedAddress() { + return Json.optional(json, "expected_details_formatted_address", Conv.STRING); + } - /** - * Reads a {@code Properties} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Properties fromJson(JsonNode json) { - return new Properties(Json.copyObject(json), Map.of()); - } + /** + * Expected parsed address details for verification + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> expectedDetailsParsedAddress() { + return Json.optional(json, "expected_details_parsed_address", Conv.OBJECT); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * Additional banking and contact details extracted from the document. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional extraFields() { + return Json.optional(json, "extra_fields", Conv.model(SessionDecision.PoaVerifications.ExtraFields::fromJson)); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * Additional proof of address files + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> extraFiles() { + return Json.optional(json, "extra_files", Conv.list(Conv.STRING)); + } - /** - * The name field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> name() { - return Json.optional(json, "name", Conv.list(Conv.STRING)); - } + /** + * The warnings field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SessionDecision.PoaVerifications.Warnings::fromJson))); + } - /** - * The alias field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> alias() { - return Json.optional(json, "alias", Conv.list(Conv.STRING)); - } + /** + * QR codes and barcodes detected across every processed page of the document. Empty when none are found. A located but undecodable QR code has parsed_payload: null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> detectedCodes() { + return Json.optional(json, "detected_codes", Conv.list(Conv.model(DocumentAIDetectedCode::fromJson))); + } - /** - * The notes field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> notes() { - return Json.optional(json, "notes", Conv.list(Conv.STRING)); - } + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } - /** - * The title field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional title() { - return Json.optional(json, "title", Conv.STRING); - } + /** Builds a {@link PoaVerifications}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** - * The gender field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> gender() { - return Json.optional(json, "gender", Conv.list(Conv.STRING)); - } + private Builder(PoaVerifications from) { + super(from); + } - /** - * The height field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional height() { - return Json.optional(json, "height", Conv.STRING); - } + /** + * Unique identifier for this POA step in the workflow graph. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } - /** - * The topics field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional topics() { - return Json.optional(json, "topics", Conv.STRING); - } + /** + * Feature-level status of this Proof of Address step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + * @param status the value + * @return this builder + */ + public Builder status(SessionDecision.PoaVerifications.Status status) { + return set("status", Conv.enumeration(SessionDecision.PoaVerifications.Status::of), status); + } - /** - * The weight field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional weight() { - return Json.optional(json, "weight", Conv.STRING); - } + /** + * URL of the proof of address document + * + * @param documentFile the value + * @return this builder + */ + public Builder documentFile(String documentFile) { + return set("document_file", Conv.STRING, documentFile); + } - /** - * The address field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional address() { - return Json.optional(json, "address", Conv.STRING); - } + /** + * Two-letter country code of the issuing state + * + * @param issuingState the value + * @return this builder + */ + public Builder issuingState(String issuingState) { + return set("issuing_state", Conv.STRING, issuingState); + } - /** - * The country field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional country() { - return Json.optional(json, "country", Conv.STRING); - } + /** + * Type of proof of address document + * + * @param documentType the value + * @return this builder + */ + public Builder documentType(String documentType) { + return set("document_type", Conv.STRING, documentType); + } - /** - * The website field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional website() { - return Json.optional(json, "website", Conv.STRING); - } + /** + * More specific classification of the POA document within its document_type, when one was detected. null otherwise. + * + * @param documentSubtype the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder documentSubtype(@Nullable String documentSubtype) { + return setNullable("document_subtype", Conv.STRING, documentSubtype); + } - /** - * The eyeColor field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional eyeColor() { - return Json.optional(json, "eyeColor", Conv.STRING); - } + /** + * Language code of the document + * + * @param documentLanguage the value + * @return this builder + */ + public Builder documentLanguage(String documentLanguage) { + return set("document_language", Conv.STRING, documentLanguage); + } + + /** + * File-forensics metadata extracted from the uploaded POA document. Always contains the keys below (values are null when not applicable, e.g. EXIF dates for PDFs). null when no metadata could be extracted. + * + * @param documentMetadata the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder documentMetadata(SessionDecision.PoaVerifications.@Nullable DocumentMetadata documentMetadata) { + return setNullable("document_metadata", Conv.model(SessionDecision.PoaVerifications.DocumentMetadata::fromJson), documentMetadata); + } - /** - * The keywords field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional keywords() { - return Json.optional(json, "keywords", Conv.STRING); - } + /** + * Name of the document issuer + * + * @param issuer the value + * @return this builder + */ + public Builder issuer(String issuer) { + return set("issuer", Conv.STRING, issuer); + } - /** - * The lastName field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> lastName() { - return Json.optional(json, "lastName", Conv.list(Conv.STRING)); - } + /** + * Date when the document was issued + * + * @param issueDate the value + * @return this builder + */ + public Builder issueDate(LocalDate issueDate) { + return set("issue_date", Conv.DATE, issueDate); + } - /** - * The position field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional position() { - return Json.optional(json, "position", Conv.STRING); - } + /** + * Expiration date of the document, if available + * + * @param expirationDate the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expirationDate(@Nullable LocalDate expirationDate) { + return setNullable("expiration_date", Conv.DATE, expirationDate); + } - /** - * The religion field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional religion() { - return Json.optional(json, "religion", Conv.STRING); - } + /** + * Raw address from the proof of address document + * + * @param poaAddress the value + * @return this builder + */ + public Builder poaAddress(String poaAddress) { + return set("poa_address", Conv.STRING, poaAddress); + } - /** - * The birthDate field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> birthDate() { - return Json.optional(json, "birthDate", Conv.list(Conv.STRING)); - } + /** + * Formatted address from the proof of address document + * + * @param poaFormattedAddress the value + * @return this builder + */ + public Builder poaFormattedAddress(String poaFormattedAddress) { + return set("poa_formatted_address", Conv.STRING, poaFormattedAddress); + } - /** - * The education field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> education() { - return Json.optional(json, "education", Conv.list(Conv.STRING)); - } + /** + * The poa_parsed_address field. + * + * @param poaParsedAddress the value + * @return this builder + */ + public Builder poaParsedAddress(SessionDecision.PoaVerifications.PoaParsedAddress poaParsedAddress) { + return set("poa_parsed_address", Conv.model(SessionDecision.PoaVerifications.PoaParsedAddress::fromJson), poaParsedAddress); + } - /** - * The ethnicity field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional ethnicity() { - return Json.optional(json, "ethnicity", Conv.STRING); - } + /** + * Account-holder / addressee name extracted from the POA document, used for name matching. null when no name could be extracted. + * + * @param nameOnDocument the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nameOnDocument(@Nullable String nameOnDocument) { + return setNullable("name_on_document", Conv.STRING, nameOnDocument); + } - /** - * The firstName field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> firstName() { - return Json.optional(json, "firstName", Conv.list(Conv.STRING)); - } + /** + * Similarity score (0-100) between name_on_document and the expected name supplied at session creation. null when either side is missing. + * + * @param nameMatchScoreExpectedDetails the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nameMatchScoreExpectedDetails(@Nullable Double nameMatchScoreExpectedDetails) { + return setNullable("name_match_score_expected_details", Conv.DOUBLE, nameMatchScoreExpectedDetails); + } - /** - * The hairColor field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional hairColor() { - return Json.optional(json, "hairColor", Conv.STRING); - } + /** + * Similarity score (0-100) between name_on_document and the name extracted by the ID verification step. null when either side is missing. + * + * @param nameMatchScoreIdVerification the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nameMatchScoreIdVerification(@Nullable Double nameMatchScoreIdVerification) { + return setNullable("name_match_score_id_verification", Conv.DOUBLE, nameMatchScoreIdVerification); + } - /** - * The weakAlias field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional weakAlias() { - return Json.optional(json, "weakAlias", Conv.STRING); - } + /** + * Expected raw address for verification + * + * @param expectedDetailsAddress the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expectedDetailsAddress(@Nullable String expectedDetailsAddress) { + return setNullable("expected_details_address", Conv.STRING, expectedDetailsAddress); + } - /** - * The birthPlace field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> birthPlace() { - return Json.optional(json, "birthPlace", Conv.list(Conv.STRING)); - } + /** + * Expected formatted address for verification + * + * @param expectedDetailsFormattedAddress the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expectedDetailsFormattedAddress(@Nullable String expectedDetailsFormattedAddress) { + return setNullable("expected_details_formatted_address", Conv.STRING, expectedDetailsFormattedAddress); + } - /** - * The modifiedAt field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional modifiedAt() { - return Json.optional(json, "modifiedAt", Conv.STRING); - } + /** + * Expected parsed address details for verification + * + * @param expectedDetailsParsedAddress the value + * @return this builder + */ + public Builder expectedDetailsParsedAddress(Map expectedDetailsParsedAddress) { + return setAny("expected_details_parsed_address", expectedDetailsParsedAddress); + } - /** - * The wikidataId field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional wikidataId() { - return Json.optional(json, "wikidataId", Conv.STRING); - } + /** + * Additional banking and contact details extracted from the document. + * + * @param extraFields the value + * @return this builder + */ + public Builder extraFields(SessionDecision.PoaVerifications.ExtraFields extraFields) { + return set("extra_fields", Conv.model(SessionDecision.PoaVerifications.ExtraFields::fromJson), extraFields); + } - /** - * The citizenship field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional citizenship() { - return Json.optional(json, "citizenship", Conv.STRING); - } + /** + * Additional proof of address files + * + * @param extraFiles the value + * @return this builder + */ + public Builder extraFiles(List extraFiles) { + return set("extra_files", Conv.list(Conv.STRING), extraFiles); + } - /** - * The nationality field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nationality() { - return Json.optional(json, "nationality", Conv.STRING); - } + /** + * The warnings field. + * + * @param warnings the value + * @return this builder + */ + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SessionDecision.PoaVerifications.Warnings::fromJson)), warnings); + } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** + * QR codes and barcodes detected across every processed page of the document. Empty when none are found. A located but undecodable QR code has parsed_payload: null. + * + * @param detectedCodes the value + * @return this builder + */ + public Builder detectedCodes(List detectedCodes) { + return set("detected_codes", Conv.list(Conv.model(DocumentAIDetectedCode::fromJson)), detectedCodes); + } - /** Builds a {@link Properties}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** + * Builds the value. + * + * @return a new immutable {@code PoaVerifications} + */ + public PoaVerifications build() { + return new PoaVerifications(buildJson(), buildFiles()); + } - private Builder(Properties from) { - super(from); - } + @Override + protected Builder self() { + return this; + } + } - /** - * The name field. - * - * @param name the value - * @return this builder - */ - public Builder name(List name) { - return set("name", Conv.list(Conv.STRING), name); - } + /** + * Feature-level status of this Proof of Address step. Independent of the top-level session status — a session can reach its overall decision while an individual feature stays In Review. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); - /** - * The alias field. - * - * @param alias the value - * @return this builder - */ - public Builder alias(List alias) { - return set("alias", Conv.list(Conv.STRING), alias); - } + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); - /** - * The notes field. - * - * @param notes the value - * @return this builder - */ - public Builder notes(List notes) { - return set("notes", Conv.list(Conv.STRING), notes); - } + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); - /** - * The title field. - * - * @param title the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder title(@Nullable String title) { - return setNullable("title", Conv.STRING, title); - } + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); - /** - * The gender field. - * - * @param gender the value - * @return this builder - */ - public Builder gender(List gender) { - return set("gender", Conv.list(Conv.STRING), gender); - } + /** The value {@code "Resub Requested"}. */ + public static final Status RESUB_REQUESTED = new Status("Resub Requested"); - /** - * The height field. - * - * @param height the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder height(@Nullable String height) { - return setNullable("height", Conv.STRING, height); - } + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); - /** - * The topics field. - * - * @param topics the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder topics(@Nullable String topics) { - return setNullable("topics", Conv.STRING, topics); - } + private Status(String value) { + super(value); + } - /** - * The weight field. - * - * @param weight the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder weight(@Nullable String weight) { - return setNullable("weight", Conv.STRING, weight); - } + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } - /** - * The address field. - * - * @param address the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder address(@Nullable String address) { - return setNullable("address", Conv.STRING, address); - } + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } - /** - * The country field. - * - * @param country the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder country(@Nullable String country) { - return setNullable("country", Conv.STRING, country); - } + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } - /** - * The website field. - * - * @param website the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder website(@Nullable String website) { - return setNullable("website", Conv.STRING, website); - } + /** + * File-forensics metadata extracted from the uploaded POA document. Always contains the keys below (values are null when not applicable, e.g. EXIF dates for PDFs). null when no metadata could be extracted. + */ + public static final class DocumentMetadata extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("file_size", "content_type", "creation_date", "modified_date", "overlay_manipulation", "creator", "producer", "software", "encryption", "is_signed", "is_tampered", "signature_info", "exif_original_date", "exif_digitized_date", "processed_by_known_editor", "has_different_creation_mod_date"); - /** - * The eyeColor field. - * - * @param eyeColor the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder eyeColor(@Nullable String eyeColor) { - return setNullable("eyeColor", Conv.STRING, eyeColor); - } + private DocumentMetadata(ObjectNode json, Map> files) { + super(json, files); + } - /** - * The keywords field. - * - * @param keywords the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder keywords(@Nullable String keywords) { - return setNullable("keywords", Conv.STRING, keywords); - } + /** + * Reads a {@code DocumentMetadata} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DocumentMetadata fromJson(JsonNode json) { + return new DocumentMetadata(Json.copyObject(json), Map.of()); + } - /** - * The lastName field. - * - * @param lastName the value - * @return this builder - */ - public Builder lastName(List lastName) { - return set("lastName", Conv.list(Conv.STRING), lastName); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * The position field. - * - * @param position the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder position(@Nullable String position) { - return setNullable("position", Conv.STRING, position); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * The religion field. - * - * @param religion the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder religion(@Nullable String religion) { - return setNullable("religion", Conv.STRING, religion); - } + /** + * Size of the document + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fileSize() { + return Json.optional(json, "file_size", Conv.LONG); + } - /** - * The birthDate field. - * - * @param birthDate the value - * @return this builder - */ - public Builder birthDate(List birthDate) { - return set("birthDate", Conv.list(Conv.STRING), birthDate); - } + /** + * Content type of the document + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional contentType() { + return Json.optional(json, "content_type", Conv.STRING); + } - /** - * The education field. - * - * @param education the value - * @return this builder - */ - public Builder education(List education) { - return set("education", Conv.list(Conv.STRING), education); - } + /** + * Date when the document was created + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional creationDate() { + return Json.optional(json, "creation_date", Conv.DATE); + } - /** - * The ethnicity field. - * - * @param ethnicity the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder ethnicity(@Nullable String ethnicity) { - return setNullable("ethnicity", Conv.STRING, ethnicity); - } + /** + * Date when the document was modified + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional modifiedDate() { + return Json.optional(json, "modified_date", Conv.DATE); + } - /** - * The firstName field. - * - * @param firstName the value - * @return this builder - */ - public Builder firstName(List firstName) { - return set("firstName", Conv.list(Conv.STRING), firstName); - } + /** + * PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional overlayManipulation() { + return Json.optional(json, "overlay_manipulation", Conv.model(SessionDecision.PoaVerifications.DocumentMetadata.OverlayManipulation::fromJson)); + } - /** - * The hairColor field. - * - * @param hairColor the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder hairColor(@Nullable String hairColor) { - return setNullable("hairColor", Conv.STRING, hairColor); - } + /** + * Creator application recorded in the document metadata. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional creator() { + return Json.optional(json, "creator", Conv.STRING); + } - /** - * The weakAlias field. - * - * @param weakAlias the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder weakAlias(@Nullable String weakAlias) { - return setNullable("weakAlias", Conv.STRING, weakAlias); - } + /** + * Producer software recorded in the PDF metadata. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional producer() { + return Json.optional(json, "producer", Conv.STRING); + } - /** - * The birthPlace field. - * - * @param birthPlace the value - * @return this builder - */ - public Builder birthPlace(List birthPlace) { - return set("birthPlace", Conv.list(Conv.STRING), birthPlace); - } + /** + * Editing/processing software recorded in the file metadata. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional software() { + return Json.optional(json, "software", Conv.STRING); + } - /** - * The modifiedAt field. - * - * @param modifiedAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder modifiedAt(@Nullable String modifiedAt) { - return setNullable("modifiedAt", Conv.STRING, modifiedAt); - } + /** + * Encryption scheme of the document, when encrypted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional encryption() { + return Json.optional(json, "encryption", Conv.STRING); + } - /** - * The wikidataId field. - * - * @param wikidataId the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder wikidataId(@Nullable String wikidataId) { - return setNullable("wikidataId", Conv.STRING, wikidataId); - } + /** + * Whether the PDF carries a digital signature. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isSigned() { + return Json.optional(json, "is_signed", Conv.BOOLEAN); + } - /** - * The citizenship field. - * - * @param citizenship the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder citizenship(@Nullable String citizenship) { - return setNullable("citizenship", Conv.STRING, citizenship); - } + /** + * Whether signature validation indicates the file was modified after signing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isTampered() { + return Json.optional(json, "is_tampered", Conv.BOOLEAN); + } - /** - * The nationality field. - * - * @param nationality the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder nationality(@Nullable String nationality) { - return setNullable("nationality", Conv.STRING, nationality); - } + /** + * Details of the digital signature(s), when present. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> signatureInfo() { + return Json.optional(json, "signature_info", Conv.OBJECT); + } - /** - * Builds the value. - * - * @return a new immutable {@code Properties} - */ - public Properties build() { - return new Properties(buildJson(), buildFiles()); - } + /** + * EXIF original capture date, for image uploads. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional exifOriginalDate() { + return Json.optional(json, "exif_original_date", Conv.STRING); + } - @Override - protected Builder self() { - return this; - } - } + /** + * EXIF digitization date, for image uploads. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional exifDigitizedDate() { + return Json.optional(json, "exif_digitized_date", Conv.STRING); } /** - * Review status for this hit. Can be 'Unreviewed', 'Confirmed Match', 'False Positive', or 'Inconclusive'. + * Whether the file metadata indicates processing by a known document editor. * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional processedByKnownEditor() { + return Json.optional(json, "processed_by_known_editor", Conv.BOOLEAN); + } + + /** + * Whether the creation and modification dates differ — a common manipulation signal. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public static final class ReviewStatus extends StringEnum { - /** The value {@code "Unreviewed"}. */ - public static final ReviewStatus UNREVIEWED = new ReviewStatus("Unreviewed"); + public Optional hasDifferentCreationModDate() { + return Json.optional(json, "has_different_creation_mod_date", Conv.BOOLEAN); + } - /** The value {@code "Confirmed Match"}. */ - public static final ReviewStatus CONFIRMED_MATCH = new ReviewStatus("Confirmed Match"); + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link DocumentMetadata}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** The value {@code "False Positive"}. */ - public static final ReviewStatus FALSE_POSITIVE = new ReviewStatus("False Positive"); + private Builder(DocumentMetadata from) { + super(from); + } - /** The value {@code "Inconclusive"}. */ - public static final ReviewStatus INCONCLUSIVE = new ReviewStatus("Inconclusive"); + /** + * Size of the document + * + * @param fileSize the value + * @return this builder + */ + public Builder fileSize(long fileSize) { + return set("file_size", Conv.LONG, fileSize); + } - private static final List KNOWN_VALUES = List.of(UNREVIEWED, CONFIRMED_MATCH, FALSE_POSITIVE, INCONCLUSIVE); + /** + * Content type of the document + * + * @param contentType the value + * @return this builder + */ + public Builder contentType(String contentType) { + return set("content_type", Conv.STRING, contentType); + } - private ReviewStatus(String value) { - super(value); + /** + * Date when the document was created + * + * @param creationDate the value + * @return this builder + */ + public Builder creationDate(LocalDate creationDate) { + return set("creation_date", Conv.DATE, creationDate); } /** - * Returns the constant for a known value, or a new instance for any other value. + * Date when the document was modified * - * @param value the string value - * @return the enum value + * @param modifiedDate the value + * @return this builder */ - public static ReviewStatus of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, ReviewStatus::new); + public Builder modifiedDate(LocalDate modifiedDate) { + return set("modified_date", Conv.DATE, modifiedDate); } /** - * Returns the values known to this SDK version. + * PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF. * - * @return the known values + * @param overlayManipulation the value, or {@code null} to send JSON null + * @return this builder */ - public static List knownValues() { - return KNOWN_VALUES; + public Builder overlayManipulation(SessionDecision.PoaVerifications.DocumentMetadata.@Nullable OverlayManipulation overlayManipulation) { + return setNullable("overlay_manipulation", Conv.model(SessionDecision.PoaVerifications.DocumentMetadata.OverlayManipulation::fromJson), overlayManipulation); } - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); + /** + * Creator application recorded in the document metadata. + * + * @param creator the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder creator(@Nullable String creator) { + return setNullable("creator", Conv.STRING, creator); } - } - /** Detailed breakdown of how the match_score was calculated. */ - public static final class ScoreBreakdown extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("name_score", "name_weight", "name_weight_normalized", "name_contribution", "dob_score", "dob_weight", "dob_weight_normalized", "dob_contribution", "country_score", "country_weight", "country_weight_normalized", "country_contribution", "document_number_match_type", "document_number_effect", "total_score"); + /** + * Producer software recorded in the PDF metadata. + * + * @param producer the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder producer(@Nullable String producer) { + return setNullable("producer", Conv.STRING, producer); + } - private ScoreBreakdown(ObjectNode json, Map> files) { - super(json, files); + /** + * Editing/processing software recorded in the file metadata. + * + * @param software the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder software(@Nullable String software) { + return setNullable("software", Conv.STRING, software); } /** - * Reads a {@code ScoreBreakdown} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. + * Encryption scheme of the document, when encrypted. * - * @param json a JSON object - * @return the parsed value + * @param encryption the value, or {@code null} to send JSON null + * @return this builder */ - public static ScoreBreakdown fromJson(JsonNode json) { - return new ScoreBreakdown(Json.copyObject(json), Map.of()); + public Builder encryption(@Nullable String encryption) { + return setNullable("encryption", Conv.STRING, encryption); } /** - * Returns an empty builder. + * Whether the PDF carries a digital signature. * - * @return a new builder + * @param isSigned the value, or {@code null} to send JSON null + * @return this builder */ - public static Builder builder() { - return new Builder(); + public Builder isSigned(@Nullable Boolean isSigned) { + return setNullable("is_signed", Conv.BOOLEAN, isSigned); } /** - * Returns a builder holding a copy of every field of this value. + * Whether signature validation indicates the file was modified after signing. * - * @return a new builder + * @param isTampered the value, or {@code null} to send JSON null + * @return this builder */ - public Builder toBuilder() { - return new Builder(this); + public Builder isTampered(@Nullable Boolean isTampered) { + return setNullable("is_tampered", Conv.BOOLEAN, isTampered); } /** - * Raw name similarity percentage (0-100). + * Details of the digital signature(s), when present. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param signatureInfo the value, or {@code null} to send JSON null + * @return this builder */ - public Optional nameScore() { - return Json.optional(json, "name_score", Conv.LONG); + public Builder signatureInfo(@Nullable Map signatureInfo) { + return setAny("signature_info", signatureInfo); } /** - * Original weight for name from config (0-100). + * EXIF original capture date, for image uploads. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param exifOriginalDate the value, or {@code null} to send JSON null + * @return this builder */ - public Optional nameWeight() { - return Json.optional(json, "name_weight", Conv.LONG); + public Builder exifOriginalDate(@Nullable String exifOriginalDate) { + return setNullable("exif_original_date", Conv.STRING, exifOriginalDate); } /** - * Normalized weight for name after re-weighting for missing data (0-100). + * EXIF digitization date, for image uploads. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param exifDigitizedDate the value, or {@code null} to send JSON null + * @return this builder */ - public Optional nameWeightNormalized() { - return Json.optional(json, "name_weight_normalized", Conv.DOUBLE); + public Builder exifDigitizedDate(@Nullable String exifDigitizedDate) { + return setNullable("exif_digitized_date", Conv.STRING, exifDigitizedDate); } /** - * Points contributed by name to the total score. + * Whether the file metadata indicates processing by a known document editor. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param processedByKnownEditor the value, or {@code null} to send JSON null + * @return this builder */ - public Optional nameContribution() { - return Json.optional(json, "name_contribution", Conv.DOUBLE); + public Builder processedByKnownEditor(@Nullable Boolean processedByKnownEditor) { + return setNullable("processed_by_known_editor", Conv.BOOLEAN, processedByKnownEditor); } /** - * DOB match score (-100 to 100). + * Whether the creation and modification dates differ — a common manipulation signal. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param hasDifferentCreationModDate the value, or {@code null} to send JSON null + * @return this builder */ - public Optional dobScore() { - return Json.optional(json, "dob_score", Conv.LONG); + public Builder hasDifferentCreationModDate(@Nullable Boolean hasDifferentCreationModDate) { + return setNullable("has_different_creation_mod_date", Conv.BOOLEAN, hasDifferentCreationModDate); } /** - * Original weight for DOB from config (0-100). + * Builds the value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new immutable {@code DocumentMetadata} */ - public Optional dobWeight() { - return Json.optional(json, "dob_weight", Conv.LONG); + public DocumentMetadata build() { + return new DocumentMetadata(buildJson(), buildFiles()); } - /** - * Normalized weight for DOB after re-weighting for missing data (0-100). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional dobWeightNormalized() { - return Json.optional(json, "dob_weight_normalized", Conv.DOUBLE); + @Override + protected Builder self() { + return this; } + } - /** - * Points contributed by DOB to the total score. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional dobContribution() { - return Json.optional(json, "dob_contribution", Conv.DOUBLE); + /** + * PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF. + */ + public static final class OverlayManipulation extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("detected", "analyzed", "signals", "manipulated_regions"); + + private OverlayManipulation(ObjectNode json, Map> files) { + super(json, files); } /** - * Country match score (-100 to 100). + * Reads a {@code OverlayManipulation} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param json a JSON object + * @return the parsed value */ - public Optional countryScore() { - return Json.optional(json, "country_score", Conv.LONG); + public static OverlayManipulation fromJson(JsonNode json) { + return new OverlayManipulation(Json.copyObject(json), Map.of()); } /** - * Original weight for country from config (0-100). + * Returns an empty builder. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new builder */ - public Optional countryWeight() { - return Json.optional(json, "country_weight", Conv.LONG); + public static Builder builder() { + return new Builder(); } /** - * Normalized weight for country after re-weighting for missing data (0-100). + * Returns a builder holding a copy of every field of this value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new builder */ - public Optional countryWeightNormalized() { - return Json.optional(json, "country_weight_normalized", Conv.DOUBLE); + public Builder toBuilder() { + return new Builder(this); } /** - * Points contributed by country to the total score. + * Whether overlay-text manipulation evidence was detected. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional countryContribution() { - return Json.optional(json, "country_contribution", Conv.DOUBLE); + public Optional detected() { + return Json.optional(json, "detected", Conv.BOOLEAN); } /** - * Result of document number 'Golden Key' evaluation. + * Whether the PDF could be analyzed. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional documentNumberMatchType() { - return Json.optional(json, "document_number_match_type", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.ScoreBreakdown.DocumentNumberMatchType::of)); + public Optional analyzed() { + return Json.optional(json, "analyzed", Conv.BOOLEAN); } /** - * Human-readable description of the document number effect on the score. + * Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional documentNumberEffect() { - return Json.optional(json, "document_number_effect", Conv.STRING); + public Optional> signals() { + return Json.optional(json, "signals", Conv.list(Conv.STRING)); } /** - * Final weighted match score (0-100). + * Suspected manipulated areas in PDF page coordinates. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional totalScore() { - return Json.optional(json, "total_score", Conv.LONG); + public Optional> manipulatedRegions() { + return Json.optional(json, "manipulated_regions", Conv.list(Conv.model(SessionDecision.PoaVerifications.DocumentMetadata.OverlayManipulation.ManipulatedRegions::fromJson))); } @Override @@ -20765,370 +16533,554 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link ScoreBreakdown}. */ + /** Builds a {@link OverlayManipulation}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(ScoreBreakdown from) { + private Builder(OverlayManipulation from) { super(from); } /** - * Raw name similarity percentage (0-100). + * Whether overlay-text manipulation evidence was detected. * - * @param nameScore the value + * @param detected the value * @return this builder */ - public Builder nameScore(long nameScore) { - return set("name_score", Conv.LONG, nameScore); + public Builder detected(boolean detected) { + return set("detected", Conv.BOOLEAN, detected); } /** - * Original weight for name from config (0-100). + * Whether the PDF could be analyzed. * - * @param nameWeight the value + * @param analyzed the value * @return this builder */ - public Builder nameWeight(long nameWeight) { - return set("name_weight", Conv.LONG, nameWeight); + public Builder analyzed(boolean analyzed) { + return set("analyzed", Conv.BOOLEAN, analyzed); } /** - * Normalized weight for name after re-weighting for missing data (0-100). + * Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation. * - * @param nameWeightNormalized the value + * @param signals the value * @return this builder */ - public Builder nameWeightNormalized(double nameWeightNormalized) { - return set("name_weight_normalized", Conv.DOUBLE, nameWeightNormalized); + public Builder signals(List signals) { + return set("signals", Conv.list(Conv.STRING), signals); } /** - * Points contributed by name to the total score. + * Suspected manipulated areas in PDF page coordinates. * - * @param nameContribution the value + * @param manipulatedRegions the value * @return this builder */ - public Builder nameContribution(double nameContribution) { - return set("name_contribution", Conv.DOUBLE, nameContribution); + public Builder manipulatedRegions(List manipulatedRegions) { + return set("manipulated_regions", Conv.list(Conv.model(SessionDecision.PoaVerifications.DocumentMetadata.OverlayManipulation.ManipulatedRegions::fromJson)), manipulatedRegions); } /** - * DOB match score (-100 to 100). + * Builds the value. * - * @param dobScore the value - * @return this builder + * @return a new immutable {@code OverlayManipulation} + */ + public OverlayManipulation build() { + return new OverlayManipulation(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class ManipulatedRegions extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("page", "x", "y", "width", "height", "page_width", "page_height"); + + private ManipulatedRegions(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ManipulatedRegions} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value */ - public Builder dobScore(long dobScore) { - return set("dob_score", Conv.LONG, dobScore); + public static ManipulatedRegions fromJson(JsonNode json) { + return new ManipulatedRegions(Json.copyObject(json), Map.of()); } /** - * Original weight for DOB from config (0-100). + * Returns an empty builder. * - * @param dobWeight the value - * @return this builder + * @return a new builder */ - public Builder dobWeight(long dobWeight) { - return set("dob_weight", Conv.LONG, dobWeight); + public static Builder builder() { + return new Builder(); } /** - * Normalized weight for DOB after re-weighting for missing data (0-100). + * Returns a builder holding a copy of every field of this value. * - * @param dobWeightNormalized the value - * @return this builder + * @return a new builder */ - public Builder dobWeightNormalized(double dobWeightNormalized) { - return set("dob_weight_normalized", Conv.DOUBLE, dobWeightNormalized); + public Builder toBuilder() { + return new Builder(this); } /** - * Points contributed by DOB to the total score. + * The page field. * - * @param dobContribution the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder dobContribution(double dobContribution) { - return set("dob_contribution", Conv.DOUBLE, dobContribution); + public Optional page() { + return Json.optional(json, "page", Conv.LONG); } /** - * Country match score (-100 to 100). + * The x field. * - * @param countryScore the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional x() { + return Json.optional(json, "x", Conv.DOUBLE); + } + + /** + * The y field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional y() { + return Json.optional(json, "y", Conv.DOUBLE); + } + + /** + * The width field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional width() { + return Json.optional(json, "width", Conv.DOUBLE); + } + + /** + * The height field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder countryScore(long countryScore) { - return set("country_score", Conv.LONG, countryScore); + public Optional height() { + return Json.optional(json, "height", Conv.DOUBLE); + } + + /** + * The page_width field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional pageWidth() { + return Json.optional(json, "page_width", Conv.DOUBLE); + } + + /** + * The page_height field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional pageHeight() { + return Json.optional(json, "page_height", Conv.DOUBLE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; } - /** - * Original weight for country from config (0-100). - * - * @param countryWeight the value - * @return this builder - */ - public Builder countryWeight(long countryWeight) { - return set("country_weight", Conv.LONG, countryWeight); - } + /** Builds a {@link ManipulatedRegions}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ManipulatedRegions from) { + super(from); + } + + /** + * The page field. + * + * @param page the value + * @return this builder + */ + public Builder page(long page) { + return set("page", Conv.LONG, page); + } + + /** + * The x field. + * + * @param x the value + * @return this builder + */ + public Builder x(double x) { + return set("x", Conv.DOUBLE, x); + } + + /** + * The y field. + * + * @param y the value + * @return this builder + */ + public Builder y(double y) { + return set("y", Conv.DOUBLE, y); + } + + /** + * The width field. + * + * @param width the value + * @return this builder + */ + public Builder width(double width) { + return set("width", Conv.DOUBLE, width); + } + + /** + * The height field. + * + * @param height the value + * @return this builder + */ + public Builder height(double height) { + return set("height", Conv.DOUBLE, height); + } + + /** + * The page_width field. + * + * @param pageWidth the value + * @return this builder + */ + public Builder pageWidth(double pageWidth) { + return set("page_width", Conv.DOUBLE, pageWidth); + } + + /** + * The page_height field. + * + * @param pageHeight the value + * @return this builder + */ + public Builder pageHeight(double pageHeight) { + return set("page_height", Conv.DOUBLE, pageHeight); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ManipulatedRegions} + */ + public ManipulatedRegions build() { + return new ManipulatedRegions(buildJson(), buildFiles()); + } - /** - * Normalized weight for country after re-weighting for missing data (0-100). - * - * @param countryWeightNormalized the value - * @return this builder - */ - public Builder countryWeightNormalized(double countryWeightNormalized) { - return set("country_weight_normalized", Conv.DOUBLE, countryWeightNormalized); + @Override + protected Builder self() { + return this; + } } + } + } + } - /** - * Points contributed by country to the total score. - * - * @param countryContribution the value - * @return this builder - */ - public Builder countryContribution(double countryContribution) { - return set("country_contribution", Conv.DOUBLE, countryContribution); - } + public static final class PoaParsedAddress extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("address_type", "street_1", "street_2", "city", "region", "country", "postal_code", "document_location"); - /** - * Result of document number 'Golden Key' evaluation. - * - * @param documentNumberMatchType the value - * @return this builder - */ - public Builder documentNumberMatchType(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.ScoreBreakdown.DocumentNumberMatchType documentNumberMatchType) { - return set("document_number_match_type", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.ScoreBreakdown.DocumentNumberMatchType::of), documentNumberMatchType); - } + private PoaParsedAddress(ObjectNode json, Map> files) { + super(json, files); + } - /** - * Human-readable description of the document number effect on the score. - * - * @param documentNumberEffect the value - * @return this builder - */ - public Builder documentNumberEffect(String documentNumberEffect) { - return set("document_number_effect", Conv.STRING, documentNumberEffect); - } + /** + * Reads a {@code PoaParsedAddress} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PoaParsedAddress fromJson(JsonNode json) { + return new PoaParsedAddress(Json.copyObject(json), Map.of()); + } - /** - * Final weighted match score (0-100). - * - * @param totalScore the value - * @return this builder - */ - public Builder totalScore(long totalScore) { - return set("total_score", Conv.LONG, totalScore); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * Builds the value. - * - * @return a new immutable {@code ScoreBreakdown} - */ - public ScoreBreakdown build() { - return new ScoreBreakdown(buildJson(), buildFiles()); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - @Override - protected Builder self() { - return this; - } - } + /** + * Type of address (Street, Avenue, etc) + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional addressType() { + return Json.optional(json, "address_type", Conv.STRING); + } - /** - * Result of document number 'Golden Key' evaluation. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class DocumentNumberMatchType extends StringEnum { - /** The value {@code "MATCH"}. */ - public static final DocumentNumberMatchType MATCH = new DocumentNumberMatchType("MATCH"); + /** + * Primary street address + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional street1() { + return Json.optional(json, "street_1", Conv.STRING); + } - /** The value {@code "NEUTRAL"}. */ - public static final DocumentNumberMatchType NEUTRAL = new DocumentNumberMatchType("NEUTRAL"); + /** + * Secondary street address + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional street2() { + return Json.optional(json, "street_2", Conv.STRING); + } - /** The value {@code "HARD_MISMATCH"}. */ - public static final DocumentNumberMatchType HARD_MISMATCH = new DocumentNumberMatchType("HARD_MISMATCH"); + /** + * City name + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional city() { + return Json.optional(json, "city", Conv.STRING); + } - private static final List KNOWN_VALUES = List.of(MATCH, NEUTRAL, HARD_MISMATCH); + /** + * Region or state name + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional region() { + return Json.optional(json, "region", Conv.STRING); + } - private DocumentNumberMatchType(String value) { - super(value); - } + /** + * Two-letter country code + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static DocumentNumberMatchType of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, DocumentNumberMatchType::new); - } + /** + * Postal code + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional postalCode() { + return Json.optional(json, "postal_code", Conv.STRING); + } - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } + /** + * The document_location field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentLocation() { + return Json.optional(json, "document_location", Conv.model(SessionDecision.PoaVerifications.PoaParsedAddress.DocumentLocation::fromJson)); + } - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } + @Override + protected Set knownFields() { + return KNOWN_FIELDS; } - public static final class PepMatches extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("aliases", "education", "list_name", "publisher", "source_url", "description", "matched_name", "pep_position", "date_of_birth", "other_sources", "place_of_birth"); + /** Builds a {@link PoaParsedAddress}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - private PepMatches(ObjectNode json, Map> files) { - super(json, files); + private Builder(PoaParsedAddress from) { + super(from); } /** - * Reads a {@code PepMatches} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. + * Type of address (Street, Avenue, etc) * - * @param json a JSON object - * @return the parsed value + * @param addressType the value + * @return this builder */ - public static PepMatches fromJson(JsonNode json) { - return new PepMatches(Json.copyObject(json), Map.of()); + public Builder addressType(String addressType) { + return set("address_type", Conv.STRING, addressType); } /** - * Returns an empty builder. + * Primary street address * - * @return a new builder + * @param street1 the value + * @return this builder */ - public static Builder builder() { - return new Builder(); + public Builder street1(String street1) { + return set("street_1", Conv.STRING, street1); } /** - * Returns a builder holding a copy of every field of this value. + * Secondary street address * - * @return a new builder + * @param street2 the value, or {@code null} to send JSON null + * @return this builder */ - public Builder toBuilder() { - return new Builder(this); + public Builder street2(@Nullable String street2) { + return setNullable("street_2", Conv.STRING, street2); } /** - * The aliases field. + * City name * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param city the value + * @return this builder */ - public Optional> aliases() { - return Json.optional(json, "aliases", Conv.list(Conv.STRING)); + public Builder city(String city) { + return set("city", Conv.STRING, city); } /** - * The education field. + * Region or state name * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param region the value + * @return this builder */ - public Optional> education() { - return Json.optional(json, "education", Conv.list(Conv.STRING)); + public Builder region(String region) { + return set("region", Conv.STRING, region); } /** - * The list_name field. + * Two-letter country code * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param country the value + * @return this builder */ - public Optional listName() { - return Json.optional(json, "list_name", Conv.STRING); + public Builder country(String country) { + return set("country", Conv.STRING, country); } /** - * The publisher field. + * Postal code * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param postalCode the value + * @return this builder */ - public Optional publisher() { - return Json.optional(json, "publisher", Conv.STRING); + public Builder postalCode(String postalCode) { + return set("postal_code", Conv.STRING, postalCode); } /** - * The source_url field. + * The document_location field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param documentLocation the value + * @return this builder */ - public Optional sourceUrl() { - return Json.optional(json, "source_url", Conv.STRING); + public Builder documentLocation(SessionDecision.PoaVerifications.PoaParsedAddress.DocumentLocation documentLocation) { + return set("document_location", Conv.model(SessionDecision.PoaVerifications.PoaParsedAddress.DocumentLocation::fromJson), documentLocation); } /** - * The description field. + * Builds the value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new immutable {@code PoaParsedAddress} */ - public Optional description() { - return Json.optional(json, "description", Conv.STRING); + public PoaParsedAddress build() { + return new PoaParsedAddress(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class DocumentLocation extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("latitude", "longitude"); + + private DocumentLocation(ObjectNode json, Map> files) { + super(json, files); } /** - * The matched_name field. + * Reads a {@code DocumentLocation} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param json a JSON object + * @return the parsed value */ - public Optional matchedName() { - return Json.optional(json, "matched_name", Conv.STRING); + public static DocumentLocation fromJson(JsonNode json) { + return new DocumentLocation(Json.copyObject(json), Map.of()); } /** - * The pep_position field. + * Returns an empty builder. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new builder */ - public Optional pepPosition() { - return Json.optional(json, "pep_position", Conv.STRING); + public static Builder builder() { + return new Builder(); } /** - * The date_of_birth field. + * Returns a builder holding a copy of every field of this value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new builder */ - public Optional dateOfBirth() { - return Json.optional(json, "date_of_birth", Conv.STRING); + public Builder toBuilder() { + return new Builder(this); } /** - * The other_sources field. + * Latitude coordinate * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> otherSources() { - return Json.optional(json, "other_sources", Conv.list(Conv.STRING)); + public Optional latitude() { + return Json.optional(json, "latitude", Conv.DOUBLE); } /** - * The place_of_birth field. + * Longitude coordinate * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional placeOfBirth() { - return Json.optional(json, "place_of_birth", Conv.STRING); + public Optional longitude() { + return Json.optional(json, "longitude", Conv.DOUBLE); } @Override @@ -21136,1262 +17088,1168 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link PepMatches}. */ + /** Builds a {@link DocumentLocation}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(PepMatches from) { + private Builder(DocumentLocation from) { super(from); } /** - * The aliases field. + * Latitude coordinate * - * @param aliases the value + * @param latitude the value * @return this builder */ - public Builder aliases(List aliases) { - return set("aliases", Conv.list(Conv.STRING), aliases); + public Builder latitude(double latitude) { + return set("latitude", Conv.DOUBLE, latitude); } /** - * The education field. + * Longitude coordinate * - * @param education the value + * @param longitude the value * @return this builder */ - public Builder education(List education) { - return set("education", Conv.list(Conv.STRING), education); + public Builder longitude(double longitude) { + return set("longitude", Conv.DOUBLE, longitude); } /** - * The list_name field. + * Builds the value. * - * @param listName the value - * @return this builder + * @return a new immutable {@code DocumentLocation} */ - public Builder listName(String listName) { - return set("list_name", Conv.STRING, listName); + public DocumentLocation build() { + return new DocumentLocation(buildJson(), buildFiles()); } - /** - * The publisher field. - * - * @param publisher the value - * @return this builder - */ - public Builder publisher(String publisher) { - return set("publisher", Conv.STRING, publisher); + @Override + protected Builder self() { + return this; } + } + } + } - /** - * The source_url field. - * - * @param sourceUrl the value - * @return this builder - */ - public Builder sourceUrl(String sourceUrl) { - return set("source_url", Conv.STRING, sourceUrl); - } + /** Additional banking and contact details extracted from the document. */ + public static final class ExtraFields extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("bank_account_number", "bank_iban", "bank_sort_code", "bank_routing_number", "bank_swift_bic", "bank_branch_name", "bank_branch_address", "document_phone_number", "additional_names"); - /** - * The description field. - * - * @param description the value - * @return this builder - */ - public Builder description(String description) { - return set("description", Conv.STRING, description); - } + private ExtraFields(ObjectNode json, Map> files) { + super(json, files); + } - /** - * The matched_name field. - * - * @param matchedName the value - * @return this builder - */ - public Builder matchedName(String matchedName) { - return set("matched_name", Conv.STRING, matchedName); - } + /** + * Reads a {@code ExtraFields} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ExtraFields fromJson(JsonNode json) { + return new ExtraFields(Json.copyObject(json), Map.of()); + } - /** - * The pep_position field. - * - * @param pepPosition the value - * @return this builder - */ - public Builder pepPosition(String pepPosition) { - return set("pep_position", Conv.STRING, pepPosition); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * The date_of_birth field. - * - * @param dateOfBirth the value - * @return this builder - */ - public Builder dateOfBirth(String dateOfBirth) { - return set("date_of_birth", Conv.STRING, dateOfBirth); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * The other_sources field. - * - * @param otherSources the value - * @return this builder - */ - public Builder otherSources(List otherSources) { - return set("other_sources", Conv.list(Conv.STRING), otherSources); - } + /** + * The bank_account_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bankAccountNumber() { + return Json.optional(json, "bank_account_number", Conv.STRING); + } - /** - * The place_of_birth field. - * - * @param placeOfBirth the value - * @return this builder - */ - public Builder placeOfBirth(String placeOfBirth) { - return set("place_of_birth", Conv.STRING, placeOfBirth); - } + /** + * The bank_iban field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bankIban() { + return Json.optional(json, "bank_iban", Conv.STRING); + } - /** - * Builds the value. - * - * @return a new immutable {@code PepMatches} - */ - public PepMatches build() { - return new PepMatches(buildJson(), buildFiles()); - } + /** + * The bank_sort_code field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bankSortCode() { + return Json.optional(json, "bank_sort_code", Conv.STRING); + } - @Override - protected Builder self() { - return this; - } - } + /** + * The bank_routing_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bankRoutingNumber() { + return Json.optional(json, "bank_routing_number", Conv.STRING); + } + + /** + * The bank_swift_bic field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bankSwiftBic() { + return Json.optional(json, "bank_swift_bic", Conv.STRING); + } + + /** + * The bank_branch_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bankBranchName() { + return Json.optional(json, "bank_branch_name", Conv.STRING); + } + + /** + * The bank_branch_address field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional bankBranchAddress() { + return Json.optional(json, "bank_branch_address", Conv.STRING); + } + + /** + * The document_phone_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentPhoneNumber() { + return Json.optional(json, "document_phone_number", Conv.STRING); + } + + /** + * The additional_names field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> additionalNames() { + return Json.optional(json, "additional_names", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; } - public static final class LinkedEntities extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("name", "active", "status", "details", "relation"); + /** Builds a {@link ExtraFields}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ExtraFields from) { + super(from); + } + + /** + * The bank_account_number field. + * + * @param bankAccountNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder bankAccountNumber(@Nullable String bankAccountNumber) { + return setNullable("bank_account_number", Conv.STRING, bankAccountNumber); + } - private LinkedEntities(ObjectNode json, Map> files) { - super(json, files); + /** + * The bank_iban field. + * + * @param bankIban the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder bankIban(@Nullable String bankIban) { + return setNullable("bank_iban", Conv.STRING, bankIban); } /** - * Reads a {@code LinkedEntities} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. + * The bank_sort_code field. * - * @param json a JSON object - * @return the parsed value + * @param bankSortCode the value, or {@code null} to send JSON null + * @return this builder */ - public static LinkedEntities fromJson(JsonNode json) { - return new LinkedEntities(Json.copyObject(json), Map.of()); + public Builder bankSortCode(@Nullable String bankSortCode) { + return setNullable("bank_sort_code", Conv.STRING, bankSortCode); } /** - * Returns an empty builder. + * The bank_routing_number field. * - * @return a new builder + * @param bankRoutingNumber the value, or {@code null} to send JSON null + * @return this builder */ - public static Builder builder() { - return new Builder(); + public Builder bankRoutingNumber(@Nullable String bankRoutingNumber) { + return setNullable("bank_routing_number", Conv.STRING, bankRoutingNumber); } /** - * Returns a builder holding a copy of every field of this value. + * The bank_swift_bic field. * - * @return a new builder + * @param bankSwiftBic the value, or {@code null} to send JSON null + * @return this builder */ - public Builder toBuilder() { - return new Builder(this); + public Builder bankSwiftBic(@Nullable String bankSwiftBic) { + return setNullable("bank_swift_bic", Conv.STRING, bankSwiftBic); } /** - * The name field. + * The bank_branch_name field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param bankBranchName the value, or {@code null} to send JSON null + * @return this builder */ - public Optional> name() { - return Json.optional(json, "name", Conv.list(Conv.STRING)); + public Builder bankBranchName(@Nullable String bankBranchName) { + return setNullable("bank_branch_name", Conv.STRING, bankBranchName); } /** - * The active field. + * The bank_branch_address field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param bankBranchAddress the value, or {@code null} to send JSON null + * @return this builder */ - public Optional> active() { - return Json.optional(json, "active", Conv.list(Conv.STRING)); + public Builder bankBranchAddress(@Nullable String bankBranchAddress) { + return setNullable("bank_branch_address", Conv.STRING, bankBranchAddress); } /** - * The status field. + * The document_phone_number field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param documentPhoneNumber the value, or {@code null} to send JSON null + * @return this builder */ - public Optional> status() { - return Json.optional(json, "status", Conv.list(Conv.STRING)); + public Builder documentPhoneNumber(@Nullable String documentPhoneNumber) { + return setNullable("document_phone_number", Conv.STRING, documentPhoneNumber); } /** - * The details field. + * The additional_names field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param additionalNames the value + * @return this builder */ - public Optional> details() { - return Json.optional(json, "details", Conv.list(Conv.STRING)); + public Builder additionalNames(List additionalNames) { + return set("additional_names", Conv.list(Conv.STRING), additionalNames); } /** - * The relation field. + * Builds the value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new immutable {@code ExtraFields} */ - public Optional> relation() { - return Json.optional(json, "relation", Conv.list(Conv.STRING)); + public ExtraFields build() { + return new ExtraFields(buildJson(), buildFiles()); } @Override - protected Set knownFields() { - return KNOWN_FIELDS; + protected Builder self() { + return this; } + } + } - /** Builds a {@link LinkedEntities}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(LinkedEntities from) { - super(from); - } - - /** - * The name field. - * - * @param name the value - * @return this builder - */ - public Builder name(List name) { - return set("name", Conv.list(Conv.STRING), name); - } + public static final class Warnings extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); - /** - * The active field. - * - * @param active the value - * @return this builder - */ - public Builder active(List active) { - return set("active", Conv.list(Conv.STRING), active); - } + private Warnings(ObjectNode json, Map> files) { + super(json, files); + } - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(List status) { - return set("status", Conv.list(Conv.STRING), status); - } + /** + * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Warnings fromJson(JsonNode json) { + return new Warnings(Json.copyObject(json), Map.of()); + } - /** - * The details field. - * - * @param details the value - * @return this builder - */ - public Builder details(List details) { - return set("details", Conv.list(Conv.STRING), details); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * The relation field. - * - * @param relation the value - * @return this builder - */ - public Builder relation(List relation) { - return set("relation", Conv.list(Conv.STRING), relation); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * Builds the value. - * - * @return a new immutable {@code LinkedEntities} - */ - public LinkedEntities build() { - return new LinkedEntities(buildJson(), buildFiles()); - } + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(SessionDecision.PoaVerifications.Warnings.Feature::of)); + } - @Override - protected Builder self() { - return this; - } - } + /** + * The risk field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.STRING); } - public static final class WarningMatches extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("list_name", "matched_name", "description", "publisher", "source_url", "other_sources", "countries", "created_at", "updated_at", "additional_data"); + /** + * The additional_data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> additionalData() { + return Json.optional(json, "additional_data", Conv.OBJECT); + } - private WarningMatches(ObjectNode json, Map> files) { - super(json, files); - } + /** + * The log_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional logType() { + return Json.optional(json, "log_type", Conv.STRING); + } - /** - * Reads a {@code WarningMatches} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static WarningMatches fromJson(JsonNode json) { - return new WarningMatches(Json.copyObject(json), Map.of()); - } + /** + * The short_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional shortDescription() { + return Json.optional(json, "short_description", Conv.STRING); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * The long_description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional longDescription() { + return Json.optional(json, "long_description", Conv.STRING); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * The workflow graph node ID that generated this warning + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } - /** - * Name of the warning list publisher - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional listName() { - return Json.optional(json, "list_name", Conv.STRING); - } + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } - /** - * Name matched in the warning list - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional matchedName() { - return Json.optional(json, "matched_name", Conv.STRING); + /** Builds a {@link Warnings}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Warnings from) { + super(from); } /** - * Description of the warning entry + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param feature the value + * @return this builder */ - public Optional description() { - return Json.optional(json, "description", Conv.STRING); + public Builder feature(SessionDecision.PoaVerifications.Warnings.Feature feature) { + return set("feature", Conv.enumeration(SessionDecision.PoaVerifications.Warnings.Feature::of), feature); } /** - * Publisher or source of the warning + * The risk field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param risk the value + * @return this builder */ - public Optional publisher() { - return Json.optional(json, "publisher", Conv.STRING); + public Builder risk(String risk) { + return set("risk", Conv.STRING, risk); } /** - * URL to the source of the warning + * The additional_data field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param additionalData the value, or {@code null} to send JSON null + * @return this builder */ - public Optional sourceUrl() { - return Json.optional(json, "source_url", Conv.STRING); + public Builder additionalData(@Nullable Map additionalData) { + return setAny("additional_data", additionalData); } /** - * Additional source URLs + * The log_type field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param logType the value + * @return this builder */ - public Optional> otherSources() { - return Json.optional(json, "other_sources", Conv.list(Conv.STRING)); + public Builder logType(String logType) { + return set("log_type", Conv.STRING, logType); } /** - * Countries associated with the warning + * The short_description field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param shortDescription the value + * @return this builder */ - public Optional> countries() { - return Json.optional(json, "countries", Conv.list(Conv.STRING)); + public Builder shortDescription(String shortDescription) { + return set("short_description", Conv.STRING, shortDescription); } /** - * Date the warning entry was created + * The long_description field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param longDescription the value + * @return this builder */ - public Optional createdAt() { - return Json.optional(json, "created_at", Conv.STRING); + public Builder longDescription(String longDescription) { + return set("long_description", Conv.STRING, longDescription); } /** - * Date the warning entry was last updated + * The workflow graph node ID that generated this warning * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param nodeId the value + * @return this builder */ - public Optional updatedAt() { - return Json.optional(json, "updated_at", Conv.STRING); + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); } /** - * Additional data from the warning source. May contain a data object with case_details array. + * Builds the value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new immutable {@code Warnings} */ - public Optional additionalData() { - return Json.optional(json, "additional_data", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData::fromJson)); + public Warnings build() { + return new Warnings(buildJson(), buildFiles()); } @Override - protected Set knownFields() { - return KNOWN_FIELDS; + protected Builder self() { + return this; } + } - /** Builds a {@link WarningMatches}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(WarningMatches from) { - super(from); - } - - /** - * Name of the warning list publisher - * - * @param listName the value - * @return this builder - */ - public Builder listName(String listName) { - return set("list_name", Conv.STRING, listName); - } - - /** - * Name matched in the warning list - * - * @param matchedName the value - * @return this builder - */ - public Builder matchedName(String matchedName) { - return set("matched_name", Conv.STRING, matchedName); - } - - /** - * Description of the warning entry - * - * @param description the value - * @return this builder - */ - public Builder description(String description) { - return set("description", Conv.STRING, description); - } - - /** - * Publisher or source of the warning - * - * @param publisher the value - * @return this builder - */ - public Builder publisher(String publisher) { - return set("publisher", Conv.STRING, publisher); - } + /** + * The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. PHONE inside phone_verifications[].warnings), but the enum above is authoritative across all feature warnings on the decision payload. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Feature extends StringEnum { + /** The value {@code "ID_VERIFICATION"}. */ + public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); - /** - * URL to the source of the warning - * - * @param sourceUrl the value - * @return this builder - */ - public Builder sourceUrl(String sourceUrl) { - return set("source_url", Conv.STRING, sourceUrl); - } + /** The value {@code "NFC"}. */ + public static final Feature NFC = new Feature("NFC"); - /** - * Additional source URLs - * - * @param otherSources the value - * @return this builder - */ - public Builder otherSources(List otherSources) { - return set("other_sources", Conv.list(Conv.STRING), otherSources); - } + /** The value {@code "LIVENESS"}. */ + public static final Feature LIVENESS = new Feature("LIVENESS"); - /** - * Countries associated with the warning - * - * @param countries the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder countries(@Nullable List countries) { - return setNullable("countries", Conv.list(Conv.STRING), countries); - } + /** The value {@code "FACEMATCH"}. */ + public static final Feature FACEMATCH = new Feature("FACEMATCH"); - /** - * Date the warning entry was created - * - * @param createdAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder createdAt(@Nullable String createdAt) { - return setNullable("created_at", Conv.STRING, createdAt); - } + /** The value {@code "PROOF_OF_ADDRESS"}. */ + public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); - /** - * Date the warning entry was last updated - * - * @param updatedAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder updatedAt(@Nullable String updatedAt) { - return setNullable("updated_at", Conv.STRING, updatedAt); - } + /** The value {@code "PHONE"}. */ + public static final Feature PHONE = new Feature("PHONE"); - /** - * Additional data from the warning source. May contain a data object with case_details array. - * - * @param additionalData the value - * @return this builder - */ - public Builder additionalData(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData additionalData) { - return set("additional_data", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData::fromJson), additionalData); - } + /** The value {@code "EMAIL"}. */ + public static final Feature EMAIL = new Feature("EMAIL"); - /** - * Builds the value. - * - * @return a new immutable {@code WarningMatches} - */ - public WarningMatches build() { - return new WarningMatches(buildJson(), buildFiles()); - } + /** The value {@code "AML"}. */ + public static final Feature AML = new Feature("AML"); - @Override - protected Builder self() { - return this; - } - } + /** The value {@code "LOCATION"}. */ + public static final Feature LOCATION = new Feature("LOCATION"); - /** - * Additional data from the warning source. May contain a data object with case_details array. - */ - public static final class AdditionalData extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("data"); + /** The value {@code "DATABASE_VALIDATION"}. */ + public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); - private AdditionalData(ObjectNode json, Map> files) { - super(json, files); - } + /** The value {@code "QUESTIONNAIRE"}. */ + public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); - /** - * Reads a {@code AdditionalData} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static AdditionalData fromJson(JsonNode json) { - return new AdditionalData(Json.copyObject(json), Map.of()); - } + /** The value {@code "DOCUMENT_AI"}. */ + public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** The value {@code "KYB_REGISTRY"}. */ + public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** The value {@code "KYB_DOCUMENTS"}. */ + public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); - /** - * The data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional data() { - return Json.optional(json, "data", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData.Data::fromJson)); - } + /** The value {@code "KYB_KEY_PEOPLE"}. */ + public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); - /** Builds a {@link AdditionalData}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + private Feature(String value) { + super(value); + } - private Builder(AdditionalData from) { - super(from); - } + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Feature of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); + } - /** - * The data field. - * - * @param data the value - * @return this builder - */ - public Builder data(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData.Data data) { - return set("data", Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData.Data::fromJson), data); - } + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } - /** - * Builds the value. - * - * @return a new immutable {@code AdditionalData} - */ - public AdditionalData build() { - return new AdditionalData(buildJson(), buildFiles()); - } + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + } - @Override - protected Builder self() { - return this; - } - } + /** Document AI results grouped by workflow node. */ + public static final class DocumentAiDocuments extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("status", "node_id", "items", "warnings"); - public static final class Data extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("case_details"); + private DocumentAiDocuments(ObjectNode json, Map> files) { + super(json, files); + } - private Data(ObjectNode json, Map> files) { - super(json, files); - } + /** + * Reads a {@code DocumentAiDocuments} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DocumentAiDocuments fromJson(JsonNode json) { + return new DocumentAiDocuments(Json.copyObject(json), Map.of()); + } - /** - * Reads a {@code Data} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Data fromJson(JsonNode json) { - return new Data(Json.copyObject(json), Map.of()); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * Aggregate Document AI status for this node, rolled up from its items[]. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.DocumentAiDocuments.Status::of)); + } - /** - * Details of warning cases associated with this match - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> caseDetails() { - return Json.optional(json, "case_details", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData.Data.CaseDetails::fromJson))); - } + /** + * Unique identifier for this Document AI step in the workflow graph. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** + * The items field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> items() { + return Json.optional(json, "items", Conv.list(Conv.model(SessionDecision.DocumentAiDocuments.Items::fromJson))); + } - /** Builds a {@link Data}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** + * Warning logs raised for this Document AI node. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional>> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.OBJECT)); + } - private Builder(Data from) { - super(from); - } + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } - /** - * Details of warning cases associated with this match - * - * @param caseDetails the value - * @return this builder - */ - public Builder caseDetails(List caseDetails) { - return set("case_details", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData.Data.CaseDetails::fromJson)), caseDetails); - } + /** Builds a {@link DocumentAiDocuments}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** - * Builds the value. - * - * @return a new immutable {@code Data} - */ - public Data build() { - return new Data(buildJson(), buildFiles()); - } + private Builder(DocumentAiDocuments from) { + super(from); + } - @Override - protected Builder self() { - return this; - } - } + /** + * Aggregate Document AI status for this node, rolled up from its items[]. + * + * @param status the value + * @return this builder + */ + public Builder status(SessionDecision.DocumentAiDocuments.Status status) { + return set("status", Conv.enumeration(SessionDecision.DocumentAiDocuments.Status::of), status); + } - public static final class CaseDetails extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("case_type"); + /** + * Unique identifier for this Document AI step in the workflow graph. + * + * @param nodeId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nodeId(@Nullable String nodeId) { + return setNullable("node_id", Conv.STRING, nodeId); + } - private CaseDetails(ObjectNode json, Map> files) { - super(json, files); - } + /** + * The items field. + * + * @param items the value + * @return this builder + */ + public Builder items(List items) { + return set("items", Conv.list(Conv.model(SessionDecision.DocumentAiDocuments.Items::fromJson)), items); + } - /** - * Reads a {@code CaseDetails} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static CaseDetails fromJson(JsonNode json) { - return new CaseDetails(Json.copyObject(json), Map.of()); - } + /** + * Warning logs raised for this Document AI node. + * + * @param warnings the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder warnings(@Nullable List> warnings) { + return setAny("warnings", warnings); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * Builds the value. + * + * @return a new immutable {@code DocumentAiDocuments} + */ + public DocumentAiDocuments build() { + return new DocumentAiDocuments(buildJson(), buildFiles()); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + @Override + protected Builder self() { + return this; + } + } - /** - * Type of the warning case - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional caseType() { - return Json.optional(json, "case_type", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData.Data.CaseDetails.CaseType::of)); - } + /** + * Aggregate Document AI status for this node, rolled up from its items[]. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); - /** Builds a {@link CaseDetails}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); - private Builder(CaseDetails from) { - super(from); - } + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); - /** - * Type of the warning case - * - * @param caseType the value - * @return this builder - */ - public Builder caseType(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData.Data.CaseDetails.CaseType caseType) { - return set("case_type", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Hits.WarningMatches.AdditionalData.Data.CaseDetails.CaseType::of), caseType); - } + private static final List KNOWN_VALUES = List.of(APPROVED, DECLINED, IN_REVIEW, NOT_FINISHED); - /** - * Builds the value. - * - * @return a new immutable {@code CaseDetails} - */ - public CaseDetails build() { - return new CaseDetails(buildJson(), buildFiles()); - } + private Status(String value) { + super(value); + } - @Override - protected Builder self() { - return this; - } - } + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } - /** - * Type of the warning case - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class CaseType extends StringEnum { - /** The value {@code "Accusation"}. */ - public static final CaseType ACCUSATION = new CaseType("Accusation"); + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } - /** The value {@code "Arrest"}. */ - public static final CaseType ARREST = new CaseType("Arrest"); + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } - /** The value {@code "Bribery"}. */ - public static final CaseType BRIBERY = new CaseType("Bribery"); + public static final class Items extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "node_id", "document_key", "original_filename", "file_size", "file_url", "status", "extracted_data", "detected_codes", "fields", "document_metadata", "cross_check_result", "created_at"); - /** The value {@code "Criminal"}. */ - public static final CaseType CRIMINAL = new CaseType("Criminal"); + private Items(ObjectNode json, Map> files) { + super(json, files); + } - /** The value {@code "Fraud"}. */ - public static final CaseType FRAUD = new CaseType("Fraud"); + /** + * Reads a {@code Items} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Items fromJson(JsonNode json) { + return new Items(Json.copyObject(json), Map.of()); + } - /** The value {@code "Illegal Fundraising"}. */ - public static final CaseType ILLEGAL_FUNDRAISING = new CaseType("Illegal Fundraising"); + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** The value {@code "Non-conviction Terror"}. */ - public static final CaseType NON_CONVICTION_TERROR = new CaseType("Non-conviction Terror"); + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** The value {@code "Proclaimed Offender"}. */ - public static final CaseType PROCLAIMED_OFFENDER = new CaseType("Proclaimed Offender"); + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } - /** The value {@code "Regulatory Enforcement"}. */ - public static final CaseType REGULATORY_ENFORCEMENT = new CaseType("Regulatory Enforcement"); + /** + * Workflow graph node that produced this document. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } - /** The value {@code "Terrorism"}. */ - public static final CaseType TERRORISM = new CaseType("Terrorism"); + /** + * Key of the configured Document AI document this file corresponds to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentKey() { + return Json.optional(json, "document_key", Conv.STRING); + } - /** The value {@code "Terrorism-related"}. */ - public static final CaseType TERRORISM_RELATED = new CaseType("Terrorism-related"); + /** + * Filename as uploaded by the user. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originalFilename() { + return Json.optional(json, "original_filename", Conv.STRING); + } - /** The value {@code "Terrorism-related listing"}. */ - public static final CaseType TERRORISM_RELATED_LISTING = new CaseType("Terrorism-related listing"); + /** + * File size in bytes. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fileSize() { + return Json.optional(json, "file_size", Conv.LONG); + } - private static final List KNOWN_VALUES = List.of(ACCUSATION, ARREST, BRIBERY, CRIMINAL, FRAUD, ILLEGAL_FUNDRAISING, NON_CONVICTION_TERROR, PROCLAIMED_OFFENDER, REGULATORY_ENFORCEMENT, TERRORISM, TERRORISM_RELATED, TERRORISM_RELATED_LISTING); + /** + * Short-lived presigned link to the uploaded file. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fileUrl() { + return Json.optional(json, "file_url", Conv.STRING); + } - private CaseType(String value) { - super(value); - } + /** + * Per-document Document AI status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.DocumentAiDocuments.Items.Status::of)); + } - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static CaseType of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, CaseType::new); - } + /** + * Values extracted by Document AI, keyed by the configured field key. Each value is a string, number, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> extractedData() { + return Json.optional(json, "extracted_data", Conv.OBJECT); + } - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } + /** + * QR codes and barcodes detected across every processed page. Empty when none are found. A located but undecodable QR code has parsed_payload: null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> detectedCodes() { + return Json.optional(json, "detected_codes", Conv.list(Conv.model(DocumentAIDetectedCode::fromJson))); + } - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - } - } + /** + * The configured extraction fields for this document. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> fields() { + return Json.optional(json, "fields", Conv.list(Conv.model(SessionDecision.DocumentAiDocuments.Items.Fields::fromJson))); } - public static final class SanctionMatches extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("list_name", "matched_name", "description", "reason", "source_url", "other_sources", "details", "legal_basis", "listed_on", "sanction_list", "sanction_program", "sanctioning_authority", "updated_on", "additional_data"); + /** + * File-forensics metadata extracted from the uploaded document (e.g. is_tampered). null when nothing could be extracted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> documentMetadata() { + return Json.optional(json, "document_metadata", Conv.OBJECT); + } - private SanctionMatches(ObjectNode json, Map> files) { - super(json, files); - } + /** + * Cross-reference of the extracted document fields against other session data. null when no cross-check was performed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> crossCheckResult() { + return Json.optional(json, "cross_check_result", Conv.OBJECT); + } - /** - * Reads a {@code SanctionMatches} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static SanctionMatches fromJson(JsonNode json) { - return new SanctionMatches(Json.copyObject(json), Map.of()); - } + /** + * The created_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.DATE_TIME); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); + /** Builds a {@link Items}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Items from) { + super(from); } /** - * Name(s) of the sanction list(s) + * The uuid field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param uuid the value + * @return this builder */ - public Optional> listName() { - return Json.optional(json, "list_name", Conv.list(Conv.STRING)); + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); } /** - * Name matched in the sanction list + * Workflow graph node that produced this document. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param nodeId the value, or {@code null} to send JSON null + * @return this builder */ - public Optional matchedName() { - return Json.optional(json, "matched_name", Conv.STRING); + public Builder nodeId(@Nullable String nodeId) { + return setNullable("node_id", Conv.STRING, nodeId); } /** - * Description of the sanction entry + * Key of the configured Document AI document this file corresponds to. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param documentKey the value + * @return this builder */ - public Optional description() { - return Json.optional(json, "description", Conv.STRING); + public Builder documentKey(String documentKey) { + return set("document_key", Conv.STRING, documentKey); } /** - * Free-text reason why the entity was sanctioned. Joined from the sanction list's reason entries. Examples: 'Executive Order 13224 (Terrorism)', 'Support for actions undermining Ukraine's sovereignty', 'Listed due to national security concerns.' + * Filename as uploaded by the user. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param originalFilename the value, or {@code null} to send JSON null + * @return this builder */ - public Optional reason() { - return Json.optional(json, "reason", Conv.STRING); + public Builder originalFilename(@Nullable String originalFilename) { + return setNullable("original_filename", Conv.STRING, originalFilename); } /** - * URL to the source of the sanction + * File size in bytes. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param fileSize the value, or {@code null} to send JSON null + * @return this builder */ - public Optional sourceUrl() { - return Json.optional(json, "source_url", Conv.STRING); + public Builder fileSize(@Nullable Long fileSize) { + return setNullable("file_size", Conv.LONG, fileSize); } /** - * Additional source URLs + * Short-lived presigned link to the uploaded file. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param fileUrl the value, or {@code null} to send JSON null + * @return this builder */ - public Optional> otherSources() { - return Json.optional(json, "other_sources", Conv.list(Conv.STRING)); + public Builder fileUrl(@Nullable String fileUrl) { + return setNullable("file_url", Conv.STRING, fileUrl); } /** - * Additional details about the sanction + * Per-document Document AI status. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param status the value + * @return this builder */ - public Optional> details() { - return Json.optional(json, "details", Conv.list(Conv.STRING)); + public Builder status(SessionDecision.DocumentAiDocuments.Items.Status status) { + return set("status", Conv.enumeration(SessionDecision.DocumentAiDocuments.Items.Status::of), status); } /** - * Legal basis for the sanction + * Values extracted by Document AI, keyed by the configured field key. Each value is a string, number, or null. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param extractedData the value, or {@code null} to send JSON null + * @return this builder */ - public Optional> legalBasis() { - return Json.optional(json, "legal_basis", Conv.list(Conv.STRING)); + public Builder extractedData(@Nullable Map extractedData) { + return setAny("extracted_data", extractedData); } /** - * Date(s) when the entity was listed + * QR codes and barcodes detected across every processed page. Empty when none are found. A located but undecodable QR code has parsed_payload: null. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param detectedCodes the value + * @return this builder */ - public Optional> listedOn() { - return Json.optional(json, "listed_on", Conv.list(Conv.STRING)); + public Builder detectedCodes(List detectedCodes) { + return set("detected_codes", Conv.list(Conv.model(DocumentAIDetectedCode::fromJson)), detectedCodes); } /** - * Sanction list identifier(s) + * The configured extraction fields for this document. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param fields the value + * @return this builder */ - public Optional> sanctionList() { - return Json.optional(json, "sanction_list", Conv.list(Conv.STRING)); + public Builder fields(List fields) { + return set("fields", Conv.list(Conv.model(SessionDecision.DocumentAiDocuments.Items.Fields::fromJson)), fields); } /** - * Sanction program(s) the entity is listed under + * File-forensics metadata extracted from the uploaded document (e.g. is_tampered). null when nothing could be extracted. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param documentMetadata the value, or {@code null} to send JSON null + * @return this builder */ - public Optional> sanctionProgram() { - return Json.optional(json, "sanction_program", Conv.list(Conv.STRING)); + public Builder documentMetadata(@Nullable Map documentMetadata) { + return setAny("document_metadata", documentMetadata); } /** - * Authority/authorities that imposed the sanction + * Cross-reference of the extracted document fields against other session data. null when no cross-check was performed. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param crossCheckResult the value, or {@code null} to send JSON null + * @return this builder */ - public Optional> sanctioningAuthority() { - return Json.optional(json, "sanctioning_authority", Conv.list(Conv.STRING)); + public Builder crossCheckResult(@Nullable Map crossCheckResult) { + return setAny("cross_check_result", crossCheckResult); } /** - * Date(s) when the sanction entry was last updated + * The created_at field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @param createdAt the value + * @return this builder */ - public Optional> updatedOn() { - return Json.optional(json, "updated_on", Conv.list(Conv.STRING)); + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); } /** - * Additional data from the sanction source not captured in the main fields + * Builds the value. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return a new immutable {@code Items} */ - public Optional> additionalData() { - return Json.optional(json, "additional_data", Conv.OBJECT); + public Items build() { + return new Items(buildJson(), buildFiles()); } @Override - protected Set knownFields() { - return KNOWN_FIELDS; + protected Builder self() { + return this; } + } - /** Builds a {@link SanctionMatches}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(SanctionMatches from) { - super(from); - } - - /** - * Name(s) of the sanction list(s) - * - * @param listName the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder listName(@Nullable List listName) { - return setNullable("list_name", Conv.list(Conv.STRING), listName); - } - - /** - * Name matched in the sanction list - * - * @param matchedName the value - * @return this builder - */ - public Builder matchedName(String matchedName) { - return set("matched_name", Conv.STRING, matchedName); - } - - /** - * Description of the sanction entry - * - * @param description the value - * @return this builder - */ - public Builder description(String description) { - return set("description", Conv.STRING, description); - } - - /** - * Free-text reason why the entity was sanctioned. Joined from the sanction list's reason entries. Examples: 'Executive Order 13224 (Terrorism)', 'Support for actions undermining Ukraine's sovereignty', 'Listed due to national security concerns.' - * - * @param reason the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder reason(@Nullable String reason) { - return setNullable("reason", Conv.STRING, reason); - } - - /** - * URL to the source of the sanction - * - * @param sourceUrl the value - * @return this builder - */ - public Builder sourceUrl(String sourceUrl) { - return set("source_url", Conv.STRING, sourceUrl); - } - - /** - * Additional source URLs - * - * @param otherSources the value - * @return this builder - */ - public Builder otherSources(List otherSources) { - return set("other_sources", Conv.list(Conv.STRING), otherSources); - } - - /** - * Additional details about the sanction - * - * @param details the value - * @return this builder - */ - public Builder details(List details) { - return set("details", Conv.list(Conv.STRING), details); - } - - /** - * Legal basis for the sanction - * - * @param legalBasis the value - * @return this builder - */ - public Builder legalBasis(List legalBasis) { - return set("legal_basis", Conv.list(Conv.STRING), legalBasis); - } + /** + * Per-document Document AI status. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); - /** - * Date(s) when the entity was listed - * - * @param listedOn the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder listedOn(@Nullable List listedOn) { - return setNullable("listed_on", Conv.list(Conv.STRING), listedOn); - } + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); - /** - * Sanction list identifier(s) - * - * @param sanctionList the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder sanctionList(@Nullable List sanctionList) { - return setNullable("sanction_list", Conv.list(Conv.STRING), sanctionList); - } + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); - /** - * Sanction program(s) the entity is listed under - * - * @param sanctionProgram the value - * @return this builder - */ - public Builder sanctionProgram(List sanctionProgram) { - return set("sanction_program", Conv.list(Conv.STRING), sanctionProgram); - } + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); - /** - * Authority/authorities that imposed the sanction - * - * @param sanctioningAuthority the value - * @return this builder - */ - public Builder sanctioningAuthority(List sanctioningAuthority) { - return set("sanctioning_authority", Conv.list(Conv.STRING), sanctioningAuthority); - } + private static final List KNOWN_VALUES = List.of(APPROVED, DECLINED, IN_REVIEW, NOT_FINISHED); - /** - * Date(s) when the sanction entry was last updated - * - * @param updatedOn the value - * @return this builder - */ - public Builder updatedOn(List updatedOn) { - return set("updated_on", Conv.list(Conv.STRING), updatedOn); - } + private Status(String value) { + super(value); + } - /** - * Additional data from the sanction source not captured in the main fields - * - * @param additionalData the value - * @return this builder - */ - public Builder additionalData(Map additionalData) { - return setAny("additional_data", additionalData); - } + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } - /** - * Builds the value. - * - * @return a new immutable {@code SanctionMatches} - */ - public SanctionMatches build() { - return new SanctionMatches(buildJson(), buildFiles()); - } + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } - @Override - protected Builder self() { - return this; - } + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); } } - public static final class AdverseMediaDetails extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("sentiment", "entity_type", "sentiment_score", "adverse_keywords"); + public static final class Fields extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("key", "name", "instruction", "type", "required"); - private AdverseMediaDetails(ObjectNode json, Map> files) { + private Fields(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code AdverseMediaDetails} from JSON. Unknown fields are kept and readable + * Reads a {@code Fields} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static AdverseMediaDetails fromJson(JsonNode json) { - return new AdverseMediaDetails(Json.copyObject(json), Map.of()); + public static Fields fromJson(JsonNode json) { + return new Fields(Json.copyObject(json), Map.of()); } /** @@ -22413,43 +18271,53 @@ public Builder toBuilder() { } /** - * The sentiment field. + * Stable field key used as the key in extracted_data. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional sentiment() { - return Json.optional(json, "sentiment", Conv.STRING); + public Optional key() { + return Json.optional(json, "key", Conv.STRING); } /** - * The entity_type field. + * Human-readable field name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * Extraction instruction shown to the model. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional entityType() { - return Json.optional(json, "entity_type", Conv.STRING); + public Optional instruction() { + return Json.optional(json, "instruction", Conv.STRING); } /** - * The sentiment_score field. + * Expected value type of the extracted field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional sentimentScore() { - return Json.optional(json, "sentiment_score", Conv.LONG); + public Optional type() { + return Json.optional(json, "type", Conv.enumeration(SessionDecision.DocumentAiDocuments.Items.Fields.Type::of)); } /** - * The adverse_keywords field. + * Whether this field is required. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> adverseKeywords() { - return Json.optional(json, "adverse_keywords", Conv.map(Conv.LONG)); + public Optional required() { + return Json.optional(json, "required", Conv.BOOLEAN); } @Override @@ -22457,372 +18325,468 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link AdverseMediaDetails}. */ + /** Builds a {@link Fields}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(AdverseMediaDetails from) { + private Builder(Fields from) { super(from); } /** - * The sentiment field. + * Stable field key used as the key in extracted_data. * - * @param sentiment the value + * @param key the value * @return this builder */ - public Builder sentiment(String sentiment) { - return set("sentiment", Conv.STRING, sentiment); + public Builder key(String key) { + return set("key", Conv.STRING, key); } /** - * The entity_type field. + * Human-readable field name. * - * @param entityType the value + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Extraction instruction shown to the model. + * + * @param instruction the value + * @return this builder + */ + public Builder instruction(String instruction) { + return set("instruction", Conv.STRING, instruction); + } + + /** + * Expected value type of the extracted field. + * + * @param type the value + * @return this builder + */ + public Builder type(SessionDecision.DocumentAiDocuments.Items.Fields.Type type) { + return set("type", Conv.enumeration(SessionDecision.DocumentAiDocuments.Items.Fields.Type::of), type); + } + + /** + * Whether this field is required. + * + * @param required the value * @return this builder */ - public Builder entityType(String entityType) { - return set("entity_type", Conv.STRING, entityType); + public Builder required(boolean required) { + return set("required", Conv.BOOLEAN, required); } /** - * The sentiment_score field. + * Builds the value. * - * @param sentimentScore the value - * @return this builder + * @return a new immutable {@code Fields} */ - public Builder sentimentScore(long sentimentScore) { - return set("sentiment_score", Conv.LONG, sentimentScore); + public Fields build() { + return new Fields(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Expected value type of the extracted field. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "text"}. */ + public static final Type TEXT = new Type("text"); + + /** The value {@code "number"}. */ + public static final Type NUMBER = new Type("number"); + + /** The value {@code "date"}. */ + public static final Type DATE = new Type("date"); + + private static final List KNOWN_VALUES = List.of(TEXT, NUMBER, DATE); + + private Type(String value) { + super(value); } /** - * The adverse_keywords field. + * Returns the constant for a known value, or a new instance for any other value. * - * @param adverseKeywords the value - * @return this builder + * @param value the string value + * @return the enum value */ - public Builder adverseKeywords(Map adverseKeywords) { - return set("adverse_keywords", Conv.map(Conv.LONG), adverseKeywords); + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); } /** - * Builds the value. + * Returns the values known to this SDK version. * - * @return a new immutable {@code AdverseMediaDetails} + * @return the known values */ - public AdverseMediaDetails build() { - return new AdverseMediaDetails(buildJson(), buildFiles()); + public static List knownValues() { + return KNOWN_VALUES; } @Override - protected Builder self() { - return this; + public boolean isKnown() { + return KNOWN_VALUES.contains(this); } } } + } + } - public static final class AdverseMediaMatches extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("country", "summary", "headline", "sentiment", "thumbnail", "source_url", "author_name", "other_sources", "adverse_keywords", "sentiment_score", "publication_date"); + public static final class QuestionnaireResponses extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "questionnaire_id", "title", "description", "languages", "default_language", "is_active", "is_simple_questionnaire", "questionnaire_group_id", "version", "published_at", "sections", "status"); - private AdverseMediaMatches(ObjectNode json, Map> files) { - super(json, files); - } + private QuestionnaireResponses(ObjectNode json, Map> files) { + super(json, files); + } - /** - * Reads a {@code AdverseMediaMatches} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static AdverseMediaMatches fromJson(JsonNode json) { - return new AdverseMediaMatches(Json.copyObject(json), Map.of()); - } + /** + * Reads a {@code QuestionnaireResponses} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static QuestionnaireResponses fromJson(JsonNode json) { + return new QuestionnaireResponses(Json.copyObject(json), Map.of()); + } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * The country field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional country() { - return Json.optional(json, "country", Conv.STRING); - } + /** + * Unique identifier for this questionnaire step in the workflow graph. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } - /** - * The summary field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional summary() { - return Json.optional(json, "summary", Conv.STRING); - } + /** + * Unique identifier for the questionnaire + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional questionnaireId() { + return Json.optional(json, "questionnaire_id", Conv.STRING); + } - /** - * The headline field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional headline() { - return Json.optional(json, "headline", Conv.STRING); - } + /** + * Title of the questionnaire + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional title() { + return Json.optional(json, "title", Conv.STRING); + } - /** - * The sentiment field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sentiment() { - return Json.optional(json, "sentiment", Conv.STRING); - } + /** + * Description of the questionnaire + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } - /** - * The thumbnail field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional thumbnail() { - return Json.optional(json, "thumbnail", Conv.STRING); - } + /** + * Supported languages + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> languages() { + return Json.optional(json, "languages", Conv.list(Conv.STRING)); + } - /** - * The source_url field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sourceUrl() { - return Json.optional(json, "source_url", Conv.STRING); - } + /** + * Default language + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional defaultLanguage() { + return Json.optional(json, "default_language", Conv.STRING); + } - /** - * The author_name field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional authorName() { - return Json.optional(json, "author_name", Conv.STRING); - } + /** + * Whether the questionnaire is active + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isActive() { + return Json.optional(json, "is_active", Conv.BOOLEAN); + } - /** - * The other_sources field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> otherSources() { - return Json.optional(json, "other_sources", Conv.list(Conv.STRING)); - } + /** + * Whether the questionnaire is a simple linear form (no branching graph). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isSimpleQuestionnaire() { + return Json.optional(json, "is_simple_questionnaire", Conv.BOOLEAN); + } + + /** + * Stable identifier of the questionnaire group across versions. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional questionnaireGroupId() { + return Json.optional(json, "questionnaire_group_id", Conv.STRING); + } + + /** + * Version number of the questionnaire that was answered. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional version() { + return Json.optional(json, "version", Conv.LONG); + } + + /** + * Timestamp at which this questionnaire version was published. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional publishedAt() { + return Json.optional(json, "published_at", Conv.DATE_TIME); + } - /** - * The adverse_keywords field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> adverseKeywords() { - return Json.optional(json, "adverse_keywords", Conv.list(Conv.STRING)); - } + /** + * The sections field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> sections() { + return Json.optional(json, "sections", Conv.list(Conv.model(SessionDecision.QuestionnaireResponses.Sections::fromJson))); + } - /** - * The sentiment_score field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sentimentScore() { - return Json.optional(json, "sentiment_score", Conv.LONG); - } + /** + * Status of the questionnaire + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } - /** - * The publication_date field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional publicationDate() { - return Json.optional(json, "publication_date", Conv.DATE_TIME); - } + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** Builds a {@link QuestionnaireResponses}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** Builds a {@link AdverseMediaMatches}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + private Builder(QuestionnaireResponses from) { + super(from); + } - private Builder(AdverseMediaMatches from) { - super(from); - } + /** + * Unique identifier for this questionnaire step in the workflow graph. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } - /** - * The country field. - * - * @param country the value - * @return this builder - */ - public Builder country(String country) { - return set("country", Conv.STRING, country); - } + /** + * Unique identifier for the questionnaire + * + * @param questionnaireId the value + * @return this builder + */ + public Builder questionnaireId(String questionnaireId) { + return set("questionnaire_id", Conv.STRING, questionnaireId); + } - /** - * The summary field. - * - * @param summary the value - * @return this builder - */ - public Builder summary(String summary) { - return set("summary", Conv.STRING, summary); - } + /** + * Title of the questionnaire + * + * @param title the value + * @return this builder + */ + public Builder title(String title) { + return set("title", Conv.STRING, title); + } - /** - * The headline field. - * - * @param headline the value - * @return this builder - */ - public Builder headline(String headline) { - return set("headline", Conv.STRING, headline); - } + /** + * Description of the questionnaire + * + * @param description the value + * @return this builder + */ + public Builder description(String description) { + return set("description", Conv.STRING, description); + } - /** - * The sentiment field. - * - * @param sentiment the value - * @return this builder - */ - public Builder sentiment(String sentiment) { - return set("sentiment", Conv.STRING, sentiment); - } + /** + * Supported languages + * + * @param languages the value + * @return this builder + */ + public Builder languages(List languages) { + return set("languages", Conv.list(Conv.STRING), languages); + } - /** - * The thumbnail field. - * - * @param thumbnail the value - * @return this builder - */ - public Builder thumbnail(String thumbnail) { - return set("thumbnail", Conv.STRING, thumbnail); - } + /** + * Default language + * + * @param defaultLanguage the value + * @return this builder + */ + public Builder defaultLanguage(String defaultLanguage) { + return set("default_language", Conv.STRING, defaultLanguage); + } - /** - * The source_url field. - * - * @param sourceUrl the value - * @return this builder - */ - public Builder sourceUrl(String sourceUrl) { - return set("source_url", Conv.STRING, sourceUrl); - } + /** + * Whether the questionnaire is active + * + * @param isActive the value + * @return this builder + */ + public Builder isActive(boolean isActive) { + return set("is_active", Conv.BOOLEAN, isActive); + } - /** - * The author_name field. - * - * @param authorName the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder authorName(@Nullable String authorName) { - return setNullable("author_name", Conv.STRING, authorName); - } + /** + * Whether the questionnaire is a simple linear form (no branching graph). + * + * @param isSimpleQuestionnaire the value + * @return this builder + */ + public Builder isSimpleQuestionnaire(boolean isSimpleQuestionnaire) { + return set("is_simple_questionnaire", Conv.BOOLEAN, isSimpleQuestionnaire); + } - /** - * The other_sources field. - * - * @param otherSources the value - * @return this builder - */ - public Builder otherSources(List otherSources) { - return set("other_sources", Conv.list(Conv.STRING), otherSources); - } + /** + * Stable identifier of the questionnaire group across versions. + * + * @param questionnaireGroupId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder questionnaireGroupId(@Nullable String questionnaireGroupId) { + return setNullable("questionnaire_group_id", Conv.STRING, questionnaireGroupId); + } - /** - * The adverse_keywords field. - * - * @param adverseKeywords the value - * @return this builder - */ - public Builder adverseKeywords(List adverseKeywords) { - return set("adverse_keywords", Conv.list(Conv.STRING), adverseKeywords); - } + /** + * Version number of the questionnaire that was answered. + * + * @param version the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder version(@Nullable Long version) { + return setNullable("version", Conv.LONG, version); + } - /** - * The sentiment_score field. - * - * @param sentimentScore the value - * @return this builder - */ - public Builder sentimentScore(long sentimentScore) { - return set("sentiment_score", Conv.LONG, sentimentScore); - } + /** + * Timestamp at which this questionnaire version was published. + * + * @param publishedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder publishedAt(@Nullable OffsetDateTime publishedAt) { + return setNullable("published_at", Conv.DATE_TIME, publishedAt); + } - /** - * The publication_date field. - * - * @param publicationDate the value - * @return this builder - */ - public Builder publicationDate(OffsetDateTime publicationDate) { - return set("publication_date", Conv.DATE_TIME, publicationDate); - } + /** + * The sections field. + * + * @param sections the value + * @return this builder + */ + public Builder sections(List sections) { + return set("sections", Conv.list(Conv.model(SessionDecision.QuestionnaireResponses.Sections::fromJson)), sections); + } - /** - * Builds the value. - * - * @return a new immutable {@code AdverseMediaMatches} - */ - public AdverseMediaMatches build() { - return new AdverseMediaMatches(buildJson(), buildFiles()); - } + /** + * Status of the questionnaire + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } - @Override - protected Builder self() { - return this; - } - } + /** + * Builds the value. + * + * @return a new immutable {@code QuestionnaireResponses} + */ + public QuestionnaireResponses build() { + return new QuestionnaireResponses(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; } } - public static final class ScreenedData extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("full_name", "nationality", "date_of_birth", "document_number"); + public static final class Sections extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("title", "description", "items"); - private ScreenedData(ObjectNode json, Map> files) { + private Sections(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code ScreenedData} from JSON. Unknown fields are kept and readable + * Reads a {@code Sections} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static ScreenedData fromJson(JsonNode json) { - return new ScreenedData(Json.copyObject(json), Map.of()); + public static Sections fromJson(JsonNode json) { + return new Sections(Json.copyObject(json), Map.of()); } /** @@ -22844,43 +18808,33 @@ public Builder toBuilder() { } /** - * The full_name field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional fullName() { - return Json.optional(json, "full_name", Conv.STRING); - } - - /** - * The nationality field. + * Section title * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional nationality() { - return Json.optional(json, "nationality", Conv.STRING); + public Optional title() { + return Json.optional(json, "title", Conv.STRING); } /** - * The date_of_birth field. + * Section description * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional dateOfBirth() { - return Json.optional(json, "date_of_birth", Conv.DATE); + public Optional description() { + return Json.optional(json, "description", Conv.STRING); } /** - * The document_number field. + * The items field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional documentNumber() { - return Json.optional(json, "document_number", Conv.STRING); + public Optional> items() { + return Json.optional(json, "items", Conv.list(Conv.model(SessionDecision.QuestionnaireResponses.Sections.Items::fromJson))); } @Override @@ -22888,61 +18842,51 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link ScreenedData}. */ + /** Builds a {@link Sections}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(ScreenedData from) { + private Builder(Sections from) { super(from); } /** - * The full_name field. - * - * @param fullName the value - * @return this builder - */ - public Builder fullName(String fullName) { - return set("full_name", Conv.STRING, fullName); - } - - /** - * The nationality field. + * Section title * - * @param nationality the value + * @param title the value, or {@code null} to send JSON null * @return this builder */ - public Builder nationality(String nationality) { - return set("nationality", Conv.STRING, nationality); + public Builder title(@Nullable String title) { + return setNullable("title", Conv.STRING, title); } /** - * The date_of_birth field. + * Section description * - * @param dateOfBirth the value + * @param description the value, or {@code null} to send JSON null * @return this builder */ - public Builder dateOfBirth(LocalDate dateOfBirth) { - return set("date_of_birth", Conv.DATE, dateOfBirth); + public Builder description(@Nullable String description) { + return setNullable("description", Conv.STRING, description); } /** - * The document_number field. + * The items field. * - * @param documentNumber the value, or {@code null} to send JSON null + * @param items the value * @return this builder */ - public Builder documentNumber(@Nullable String documentNumber) { - return setNullable("document_number", Conv.STRING, documentNumber); + public Builder items(List items) { + return set("items", Conv.list(Conv.model(SessionDecision.QuestionnaireResponses.Sections.Items::fromJson)), items); } /** * Builds the value. * - * @return a new immutable {@code ScreenedData} + * @return a new immutable {@code Sections} */ - public ScreenedData build() { - return new ScreenedData(buildJson(), buildFiles()); + public Sections build() { + return new Sections(buildJson(), buildFiles()); } @Override @@ -22950,291 +18894,569 @@ protected Builder self() { return this; } } - } - - public static final class Warnings extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("feature", "risk", "additional_data", "log_type", "short_description", "long_description", "node_id"); - - private Warnings(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Warnings fromJson(JsonNode json) { - return new Warnings(Json.copyObject(json), Map.of()); - } - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } + public static final class Items extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "value", "element_type", "is_required", "title", "description", "placeholder", "choices", "max_files", "answer", "required_if", "repeatable_config"); - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } + private Items(ObjectNode json, Map> files) { + super(json, files); + } - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Within an AML screening this is typically AML, but the enum above is authoritative across all feature warnings on the decision payload. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Warnings.Feature::of)); - } + /** + * Reads a {@code Items} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Items fromJson(JsonNode json) { + return new Items(Json.copyObject(json), Map.of()); + } - /** - * The risk field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional risk() { - return Json.optional(json, "risk", Conv.STRING); - } + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** - * The additional_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> additionalData() { - return Json.optional(json, "additional_data", Conv.OBJECT); - } + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** - * The log_type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional logType() { - return Json.optional(json, "log_type", Conv.STRING); - } + /** + * Unique identifier for the item + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } - /** - * The short_description field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional shortDescription() { - return Json.optional(json, "short_description", Conv.STRING); - } + /** + * Stable node identifier of this form element within the questionnaire graph. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional value() { + return Json.optional(json, "value", Conv.STRING); + } - /** - * The long_description field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional longDescription() { - return Json.optional(json, "long_description", Conv.STRING); - } + /** + * Type of the questionnaire element + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String elementType() { + return Json.required(json, "element_type", Conv.STRING); + } - /** - * The workflow graph node ID that generated this warning - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nodeId() { - return Json.optional(json, "node_id", Conv.STRING); - } + /** + * Whether the item is required + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isRequired() { + return Json.required(json, "is_required", Conv.BOOLEAN); + } - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } + /** + * Title of the item + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String title() { + return Json.required(json, "title", Conv.STRING); + } - /** Builds a {@link Warnings}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} + /** + * Description of the item + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } - private Builder(Warnings from) { - super(from); + /** + * Placeholder text + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional placeholder() { + return Json.optional(json, "placeholder", Conv.STRING); } /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Within an AML screening this is typically AML, but the enum above is authoritative across all feature warnings on the decision payload. + * Choices for dropdown or single choice elements * - * @param feature the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder feature(SessionsRetrieveDecisionResponse.AmlScreenings.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.AmlScreenings.Warnings.Feature::of), feature); + public Optional> choices() { + return Json.optional(json, "choices", Conv.list(Conv.model(SessionDecision.QuestionnaireResponses.Sections.Items.Choices::fromJson))); } /** - * The risk field. + * Maximum number of files allowed * - * @param risk the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder risk(String risk) { - return set("risk", Conv.STRING, risk); + public Optional maxFiles() { + return Json.optional(json, "max_files", Conv.LONG); } /** - * The additional_data field. + * Answer provided for the item * - * @param additionalData the value, or {@code null} to send JSON null - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder additionalData(@Nullable Map additionalData) { - return setAny("additional_data", additionalData); + public Optional answer() { + return Json.optional(json, "answer", Conv.model(SessionDecision.QuestionnaireResponses.Sections.Items.Answer::fromJson)); } /** - * The log_type field. + * Conditional-requirement rule, when the element is only required for certain answers. * - * @param logType the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder logType(String logType) { - return set("log_type", Conv.STRING, logType); + public Optional> requiredIf() { + return Json.optional(json, "required_if", Conv.OBJECT); } /** - * The short_description field. + * Configuration for repeatable element groups, when applicable. * - * @param shortDescription the value - * @return this builder + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Builder shortDescription(String shortDescription) { - return set("short_description", Conv.STRING, shortDescription); + public Optional> repeatableConfig() { + return Json.optional(json, "repeatable_config", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Items}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Items from) { + super(from); + } + + /** + * Unique identifier for the item + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Stable node identifier of this form element within the questionnaire graph. + * + * @param value the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder value(@Nullable String value) { + return setNullable("value", Conv.STRING, value); + } + + /** + * Type of the questionnaire element + * + * @param elementType the value + * @return this builder + */ + public Builder elementType(String elementType) { + return set("element_type", Conv.STRING, elementType); + } + + /** + * Whether the item is required + * + * @param isRequired the value + * @return this builder + */ + public Builder isRequired(boolean isRequired) { + return set("is_required", Conv.BOOLEAN, isRequired); + } + + /** + * Title of the item + * + * @param title the value + * @return this builder + */ + public Builder title(String title) { + return set("title", Conv.STRING, title); + } + + /** + * Description of the item + * + * @param description the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder description(@Nullable String description) { + return setNullable("description", Conv.STRING, description); + } + + /** + * Placeholder text + * + * @param placeholder the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder placeholder(@Nullable String placeholder) { + return setNullable("placeholder", Conv.STRING, placeholder); + } + + /** + * Choices for dropdown or single choice elements + * + * @param choices the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder choices(@Nullable List choices) { + return setNullable("choices", Conv.list(Conv.model(SessionDecision.QuestionnaireResponses.Sections.Items.Choices::fromJson)), choices); + } + + /** + * Maximum number of files allowed + * + * @param maxFiles the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder maxFiles(@Nullable Long maxFiles) { + return setNullable("max_files", Conv.LONG, maxFiles); + } + + /** + * Answer provided for the item + * + * @param answer the value + * @return this builder + */ + public Builder answer(SessionDecision.QuestionnaireResponses.Sections.Items.Answer answer) { + return set("answer", Conv.model(SessionDecision.QuestionnaireResponses.Sections.Items.Answer::fromJson), answer); + } + + /** + * Conditional-requirement rule, when the element is only required for certain answers. + * + * @param requiredIf the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder requiredIf(@Nullable Map requiredIf) { + return setAny("required_if", requiredIf); + } + + /** + * Configuration for repeatable element groups, when applicable. + * + * @param repeatableConfig the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder repeatableConfig(@Nullable Map repeatableConfig) { + return setAny("repeatable_config", repeatableConfig); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Items} + * @throws IllegalStateException when a required field is missing + */ + public Items build() { + require("uuid", false); + require("value", true); + require("element_type", false); + require("is_required", false); + require("title", false); + return new Items(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } } - /** - * The long_description field. - * - * @param longDescription the value - * @return this builder - */ - public Builder longDescription(String longDescription) { - return set("long_description", Conv.STRING, longDescription); - } + public static final class Choices extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("label", "value", "requires_text_input"); + + private Choices(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Choices} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Choices fromJson(JsonNode json) { + return new Choices(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The label field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String label() { + return Json.required(json, "label", Conv.STRING); + } + + /** + * The value field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String value() { + return Json.required(json, "value", Conv.STRING); + } + + /** + * Whether this choice requires additional text input + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional requiresTextInput() { + return Json.optional(json, "requires_text_input", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } - /** - * The workflow graph node ID that generated this warning - * - * @param nodeId the value - * @return this builder - */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); - } + /** Builds a {@link Choices}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** - * Builds the value. - * - * @return a new immutable {@code Warnings} - */ - public Warnings build() { - return new Warnings(buildJson(), buildFiles()); - } + private Builder(Choices from) { + super(from); + } - @Override - protected Builder self() { - return this; - } - } + /** + * The label field. + * + * @param label the value + * @return this builder + */ + public Builder label(String label) { + return set("label", Conv.STRING, label); + } - /** - * The feature that generated this warning. The value identifies the feature block the warning belongs to. Within an AML screening this is typically AML, but the enum above is authoritative across all feature warnings on the decision payload. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Feature extends StringEnum { - /** The value {@code "ID_VERIFICATION"}. */ - public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); + /** + * The value field. + * + * @param value the value + * @return this builder + */ + public Builder value(String value) { + return set("value", Conv.STRING, value); + } - /** The value {@code "NFC"}. */ - public static final Feature NFC = new Feature("NFC"); + /** + * Whether this choice requires additional text input + * + * @param requiresTextInput the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder requiresTextInput(@Nullable Boolean requiresTextInput) { + return setNullable("requires_text_input", Conv.BOOLEAN, requiresTextInput); + } - /** The value {@code "LIVENESS"}. */ - public static final Feature LIVENESS = new Feature("LIVENESS"); + /** + * Builds the value. + * + * @return a new immutable {@code Choices} + * @throws IllegalStateException when a required field is missing + */ + public Choices build() { + require("label", false); + require("value", false); + return new Choices(buildJson(), buildFiles()); + } - /** The value {@code "FACEMATCH"}. */ - public static final Feature FACEMATCH = new Feature("FACEMATCH"); + @Override + protected Builder self() { + return this; + } + } + } - /** The value {@code "PROOF_OF_ADDRESS"}. */ - public static final Feature PROOF_OF_ADDRESS = new Feature("PROOF_OF_ADDRESS"); + /** Answer provided for the item */ + public static final class Answer extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("value", "text", "files"); - /** The value {@code "PHONE"}. */ - public static final Feature PHONE = new Feature("PHONE"); + private Answer(ObjectNode json, Map> files) { + super(json, files); + } - /** The value {@code "EMAIL"}. */ - public static final Feature EMAIL = new Feature("EMAIL"); + /** + * Reads a {@code Answer} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Answer fromJson(JsonNode json) { + return new Answer(Json.copyObject(json), Map.of()); + } - /** The value {@code "AML"}. */ - public static final Feature AML = new Feature("AML"); + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } - /** The value {@code "LOCATION"}. */ - public static final Feature LOCATION = new Feature("LOCATION"); + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } - /** The value {@code "DATABASE_VALIDATION"}. */ - public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); + /** + * The value field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional value() { + return Json.optional(json, "value", Conv.STRING); + } - /** The value {@code "QUESTIONNAIRE"}. */ - public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); + /** + * The text field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional text() { + return Json.optional(json, "text", Conv.STRING); + } - /** The value {@code "DOCUMENT_AI"}. */ - public static final Feature DOCUMENT_AI = new Feature("DOCUMENT_AI"); + /** + * The files field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> files() { + return Json.optional(json, "files", Conv.list(Conv.STRING)); + } - /** The value {@code "KYB_REGISTRY"}. */ - public static final Feature KYB_REGISTRY = new Feature("KYB_REGISTRY"); + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } - /** The value {@code "KYB_DOCUMENTS"}. */ - public static final Feature KYB_DOCUMENTS = new Feature("KYB_DOCUMENTS"); + /** Builds a {@link Answer}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} - /** The value {@code "KYB_KEY_PEOPLE"}. */ - public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); + private Builder(Answer from) { + super(from); + } - private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACEMATCH, PROOF_OF_ADDRESS, PHONE, EMAIL, AML, LOCATION, DATABASE_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); + /** + * The value field. + * + * @param value the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder value(@Nullable String value) { + return setNullable("value", Conv.STRING, value); + } - private Feature(String value) { - super(value); - } + /** + * The text field. + * + * @param text the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder text(@Nullable String text) { + return setNullable("text", Conv.STRING, text); + } - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Feature of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); - } + /** + * The files field. + * + * @param files the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder files(@Nullable List files) { + return setNullable("files", Conv.list(Conv.STRING), files); + } - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } + /** + * Builds the value. + * + * @return a new immutable {@code Answer} + */ + public Answer build() { + return new Answer(buildJson(), buildFiles()); + } - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); + @Override + protected Builder self() { + return this; + } + } } } } @@ -23292,8 +19514,8 @@ public Optional nodeId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.IpAnalyses.Status::of)); } /** @@ -23492,8 +19714,8 @@ public Optional timeZoneOffset() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional ip() { - return Json.optional(json, "ip", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Ip::fromJson)); + public Optional ip() { + return Json.optional(json, "ip", Conv.model(SessionDecision.IpAnalyses.Ip::fromJson)); } /** @@ -23502,8 +19724,8 @@ public Optional ip() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional idDocument() { - return Json.optional(json, "id_document", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.IdDocument::fromJson)); + public Optional idDocument() { + return Json.optional(json, "id_document", Conv.model(SessionDecision.IpAnalyses.IdDocument::fromJson)); } /** @@ -23512,8 +19734,8 @@ public Optional idDocume * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional poaDocument() { - return Json.optional(json, "poa_document", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.PoaDocument::fromJson)); + public Optional poaDocument() { + return Json.optional(json, "poa_document", Conv.model(SessionDecision.IpAnalyses.PoaDocument::fromJson)); } /** @@ -23522,8 +19744,8 @@ public Optional poaDocu * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Warnings::fromJson))); + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SessionDecision.IpAnalyses.Warnings::fromJson))); } /** @@ -23532,8 +19754,8 @@ public Optional> warn * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> matches() { - return Json.optional(json, "matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Matches::fromJson))); + public Optional> matches() { + return Json.optional(json, "matches", Conv.list(Conv.model(SessionDecision.IpAnalyses.Matches::fromJson))); } @Override @@ -23565,8 +19787,8 @@ public Builder nodeId(String nodeId) { * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDecisionResponse.IpAnalyses.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Status::of), status); + public Builder status(SessionDecision.IpAnalyses.Status status) { + return set("status", Conv.enumeration(SessionDecision.IpAnalyses.Status::of), status); } /** @@ -23765,8 +19987,8 @@ public Builder timeZoneOffset(String timeZoneOffset) { * @param ip the value * @return this builder */ - public Builder ip(SessionsRetrieveDecisionResponse.IpAnalyses.Ip ip) { - return set("ip", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Ip::fromJson), ip); + public Builder ip(SessionDecision.IpAnalyses.Ip ip) { + return set("ip", Conv.model(SessionDecision.IpAnalyses.Ip::fromJson), ip); } /** @@ -23775,8 +19997,8 @@ public Builder ip(SessionsRetrieveDecisionResponse.IpAnalyses.Ip ip) { * @param idDocument the value * @return this builder */ - public Builder idDocument(SessionsRetrieveDecisionResponse.IpAnalyses.IdDocument idDocument) { - return set("id_document", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.IdDocument::fromJson), idDocument); + public Builder idDocument(SessionDecision.IpAnalyses.IdDocument idDocument) { + return set("id_document", Conv.model(SessionDecision.IpAnalyses.IdDocument::fromJson), idDocument); } /** @@ -23785,8 +20007,8 @@ public Builder idDocument(SessionsRetrieveDecisionResponse.IpAnalyses.IdDocument * @param poaDocument the value * @return this builder */ - public Builder poaDocument(SessionsRetrieveDecisionResponse.IpAnalyses.PoaDocument poaDocument) { - return set("poa_document", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.PoaDocument::fromJson), poaDocument); + public Builder poaDocument(SessionDecision.IpAnalyses.PoaDocument poaDocument) { + return set("poa_document", Conv.model(SessionDecision.IpAnalyses.PoaDocument::fromJson), poaDocument); } /** @@ -23795,8 +20017,8 @@ public Builder poaDocument(SessionsRetrieveDecisionResponse.IpAnalyses.PoaDocume * @param warnings the value * @return this builder */ - public Builder warnings(List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Warnings::fromJson)), warnings); + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SessionDecision.IpAnalyses.Warnings::fromJson)), warnings); } /** @@ -23805,8 +20027,8 @@ public Builder warnings(List matches) { - return set("matches", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Matches::fromJson)), matches); + public Builder matches(List matches) { + return set("matches", Conv.list(Conv.model(SessionDecision.IpAnalyses.Matches::fromJson)), matches); } /** @@ -23921,8 +20143,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional location() { - return Json.optional(json, "location", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Ip.Location::fromJson)); + public Optional location() { + return Json.optional(json, "location", Conv.model(SessionDecision.IpAnalyses.Ip.Location::fromJson)); } /** @@ -23964,8 +20186,8 @@ private Builder(Ip from) { * @param location the value, or {@code null} to send JSON null * @return this builder */ - public Builder location(SessionsRetrieveDecisionResponse.IpAnalyses.Ip.@Nullable Location location) { - return setNullable("location", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Ip.Location::fromJson), location); + public Builder location(SessionDecision.IpAnalyses.Ip.@Nullable Location location) { + return setNullable("location", Conv.model(SessionDecision.IpAnalyses.Ip.Location::fromJson), location); } /** @@ -24155,8 +20377,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional location() { - return Json.optional(json, "location", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.IdDocument.Location::fromJson)); + public Optional location() { + return Json.optional(json, "location", Conv.model(SessionDecision.IpAnalyses.IdDocument.Location::fromJson)); } /** @@ -24198,8 +20420,8 @@ private Builder(IdDocument from) { * @param location the value, or {@code null} to send JSON null * @return this builder */ - public Builder location(SessionsRetrieveDecisionResponse.IpAnalyses.IdDocument.@Nullable Location location) { - return setNullable("location", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.IdDocument.Location::fromJson), location); + public Builder location(SessionDecision.IpAnalyses.IdDocument.@Nullable Location location) { + return setNullable("location", Conv.model(SessionDecision.IpAnalyses.IdDocument.Location::fromJson), location); } /** @@ -24389,8 +20611,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional location() { - return Json.optional(json, "location", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.PoaDocument.Location::fromJson)); + public Optional location() { + return Json.optional(json, "location", Conv.model(SessionDecision.IpAnalyses.PoaDocument.Location::fromJson)); } /** @@ -24432,8 +20654,8 @@ private Builder(PoaDocument from) { * @param location the value, or {@code null} to send JSON null * @return this builder */ - public Builder location(SessionsRetrieveDecisionResponse.IpAnalyses.PoaDocument.@Nullable Location location) { - return setNullable("location", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.PoaDocument.Location::fromJson), location); + public Builder location(SessionDecision.IpAnalyses.PoaDocument.@Nullable Location location) { + return setNullable("location", Conv.model(SessionDecision.IpAnalyses.PoaDocument.Location::fromJson), location); } /** @@ -24620,8 +20842,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Warnings.Feature::of)); + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(SessionDecision.IpAnalyses.Warnings.Feature::of)); } /** @@ -24703,8 +20925,8 @@ private Builder(Warnings from) { * @param feature the value * @return this builder */ - public Builder feature(SessionsRetrieveDecisionResponse.IpAnalyses.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Warnings.Feature::of), feature); + public Builder feature(SessionDecision.IpAnalyses.Warnings.Feature feature) { + return set("feature", Conv.enumeration(SessionDecision.IpAnalyses.Warnings.Feature::of), feature); } /** @@ -24947,8 +21169,8 @@ public Optional verificationDate() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional matchType() { - return Json.optional(json, "match_type", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.MatchType::of)); + public Optional matchType() { + return Json.optional(json, "match_type", Conv.enumeration(SessionDecision.IpAnalyses.Matches.MatchType::of)); } /** @@ -24957,8 +21179,8 @@ public Optional m * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional matchSource() { - return Json.optional(json, "match_source", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.MatchSource::of)); + public Optional matchSource() { + return Json.optional(json, "match_source", Conv.enumeration(SessionDecision.IpAnalyses.Matches.MatchSource::of)); } /** @@ -25007,8 +21229,8 @@ public Optional apiService() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional source() { - return Json.optional(json, "source", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.Source::of)); + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(SessionDecision.IpAnalyses.Matches.Source::of)); } /** @@ -25017,8 +21239,8 @@ public Optional sour * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional deviceInfo() { - return Json.optional(json, "device_info", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.DeviceInfo::fromJson)); + public Optional deviceInfo() { + return Json.optional(json, "device_info", Conv.model(SessionDecision.IpAnalyses.Matches.DeviceInfo::fromJson)); } /** @@ -25027,8 +21249,8 @@ public Optional * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional locationInfo() { - return Json.optional(json, "location_info", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.LocationInfo::fromJson)); + public Optional locationInfo() { + return Json.optional(json, "location_info", Conv.model(SessionDecision.IpAnalyses.Matches.LocationInfo::fromJson)); } /** @@ -25047,8 +21269,8 @@ public Optional confidence() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional matchMode() { - return Json.optional(json, "match_mode", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.MatchMode::of)); + public Optional matchMode() { + return Json.optional(json, "match_mode", Conv.enumeration(SessionDecision.IpAnalyses.Matches.MatchMode::of)); } /** @@ -25140,8 +21362,8 @@ public Builder verificationDate(@Nullable OffsetDateTime verificationDate) { * @param matchType the value * @return this builder */ - public Builder matchType(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.MatchType matchType) { - return set("match_type", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.MatchType::of), matchType); + public Builder matchType(SessionDecision.IpAnalyses.Matches.MatchType matchType) { + return set("match_type", Conv.enumeration(SessionDecision.IpAnalyses.Matches.MatchType::of), matchType); } /** @@ -25150,8 +21372,8 @@ public Builder matchType(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.Mat * @param matchSource the value * @return this builder */ - public Builder matchSource(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.MatchSource matchSource) { - return set("match_source", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.MatchSource::of), matchSource); + public Builder matchSource(SessionDecision.IpAnalyses.Matches.MatchSource matchSource) { + return set("match_source", Conv.enumeration(SessionDecision.IpAnalyses.Matches.MatchSource::of), matchSource); } /** @@ -25200,8 +21422,8 @@ public Builder apiService(@Nullable String apiService) { * @param source the value * @return this builder */ - public Builder source(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.Source source) { - return set("source", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.Source::of), source); + public Builder source(SessionDecision.IpAnalyses.Matches.Source source) { + return set("source", Conv.enumeration(SessionDecision.IpAnalyses.Matches.Source::of), source); } /** @@ -25210,8 +21432,8 @@ public Builder source(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.Source * @param deviceInfo the value * @return this builder */ - public Builder deviceInfo(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.DeviceInfo deviceInfo) { - return set("device_info", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.DeviceInfo::fromJson), deviceInfo); + public Builder deviceInfo(SessionDecision.IpAnalyses.Matches.DeviceInfo deviceInfo) { + return set("device_info", Conv.model(SessionDecision.IpAnalyses.Matches.DeviceInfo::fromJson), deviceInfo); } /** @@ -25220,8 +21442,8 @@ public Builder deviceInfo(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.De * @param locationInfo the value * @return this builder */ - public Builder locationInfo(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.LocationInfo locationInfo) { - return set("location_info", Conv.model(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.LocationInfo::fromJson), locationInfo); + public Builder locationInfo(SessionDecision.IpAnalyses.Matches.LocationInfo locationInfo) { + return set("location_info", Conv.model(SessionDecision.IpAnalyses.Matches.LocationInfo::fromJson), locationInfo); } /** @@ -25240,8 +21462,8 @@ public Builder confidence(double confidence) { * @param matchMode the value * @return this builder */ - public Builder matchMode(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.MatchMode matchMode) { - return set("match_mode", Conv.enumeration(SessionsRetrieveDecisionResponse.IpAnalyses.Matches.MatchMode::of), matchMode); + public Builder matchMode(SessionDecision.IpAnalyses.Matches.MatchMode matchMode) { + return set("match_mode", Conv.enumeration(SessionDecision.IpAnalyses.Matches.MatchMode::of), matchMode); } /** @@ -25923,8 +22145,8 @@ public Optional issuingState() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional validationType() { - return Json.optional(json, "validation_type", Conv.enumeration(SessionsRetrieveDecisionResponse.DatabaseValidations.ValidationType::of)); + public Optional validationType() { + return Json.optional(json, "validation_type", Conv.enumeration(SessionDecision.DatabaseValidations.ValidationType::of)); } /** @@ -25933,8 +22155,8 @@ public Optional screenedData() { - return Json.optional(json, "screened_data", Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.ScreenedData::fromJson)); + public Optional screenedData() { + return Json.optional(json, "screened_data", Conv.model(SessionDecision.DatabaseValidations.ScreenedData::fromJson)); } /** @@ -25943,8 +22165,8 @@ public Optional> validations() { - return Json.optional(json, "validations", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.Validations::fromJson))); + public Optional> validations() { + return Json.optional(json, "validations", Conv.list(Conv.model(SessionDecision.DatabaseValidations.Validations::fromJson))); } /** @@ -25953,8 +22175,8 @@ public Optional> errors() { - return Json.optional(json, "errors", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.Errors::fromJson))); + public Optional> errors() { + return Json.optional(json, "errors", Conv.list(Conv.model(SessionDecision.DatabaseValidations.Errors::fromJson))); } /** @@ -25963,8 +22185,8 @@ public Optional matchType() { - return Json.optional(json, "match_type", Conv.enumeration(SessionsRetrieveDecisionResponse.DatabaseValidations.MatchType::of)); + public Optional matchType() { + return Json.optional(json, "match_type", Conv.enumeration(SessionDecision.DatabaseValidations.MatchType::of)); } /** @@ -25973,8 +22195,8 @@ public Optional * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.DatabaseValidations.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.DatabaseValidations.Status::of)); } /** @@ -25983,8 +22205,8 @@ public Optional sta * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> warnings() { - return Json.optional(json, "warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.Warnings::fromJson))); + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SessionDecision.DatabaseValidations.Warnings::fromJson))); } @Override @@ -26026,8 +22248,8 @@ public Builder issuingState(String issuingState) { * @param validationType the value * @return this builder */ - public Builder validationType(SessionsRetrieveDecisionResponse.DatabaseValidations.ValidationType validationType) { - return set("validation_type", Conv.enumeration(SessionsRetrieveDecisionResponse.DatabaseValidations.ValidationType::of), validationType); + public Builder validationType(SessionDecision.DatabaseValidations.ValidationType validationType) { + return set("validation_type", Conv.enumeration(SessionDecision.DatabaseValidations.ValidationType::of), validationType); } /** @@ -26036,8 +22258,8 @@ public Builder validationType(SessionsRetrieveDecisionResponse.DatabaseValidatio * @param screenedData the value * @return this builder */ - public Builder screenedData(SessionsRetrieveDecisionResponse.DatabaseValidations.ScreenedData screenedData) { - return set("screened_data", Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.ScreenedData::fromJson), screenedData); + public Builder screenedData(SessionDecision.DatabaseValidations.ScreenedData screenedData) { + return set("screened_data", Conv.model(SessionDecision.DatabaseValidations.ScreenedData::fromJson), screenedData); } /** @@ -26046,8 +22268,8 @@ public Builder screenedData(SessionsRetrieveDecisionResponse.DatabaseValidations * @param validations the value * @return this builder */ - public Builder validations(List validations) { - return set("validations", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.Validations::fromJson)), validations); + public Builder validations(List validations) { + return set("validations", Conv.list(Conv.model(SessionDecision.DatabaseValidations.Validations::fromJson)), validations); } /** @@ -26056,8 +22278,8 @@ public Builder validations(List errors) { - return set("errors", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.Errors::fromJson)), errors); + public Builder errors(List errors) { + return set("errors", Conv.list(Conv.model(SessionDecision.DatabaseValidations.Errors::fromJson)), errors); } /** @@ -26066,8 +22288,8 @@ public Builder errors(List warnings) { - return set("warnings", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.Warnings::fromJson)), warnings); + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SessionDecision.DatabaseValidations.Warnings::fromJson)), warnings); } /** @@ -26441,8 +22663,8 @@ public Optional serviceName() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional validation() { - return Json.optional(json, "validation", Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.Validations.Validation::fromJson)); + public Optional validation() { + return Json.optional(json, "validation", Conv.model(SessionDecision.DatabaseValidations.Validations.Validation::fromJson)); } /** @@ -26514,8 +22736,8 @@ public Builder serviceName(String serviceName) { * @param validation the value * @return this builder */ - public Builder validation(SessionsRetrieveDecisionResponse.DatabaseValidations.Validations.Validation validation) { - return set("validation", Conv.model(SessionsRetrieveDecisionResponse.DatabaseValidations.Validations.Validation::fromJson), validation); + public Builder validation(SessionDecision.DatabaseValidations.Validations.Validation validation) { + return set("validation", Conv.model(SessionDecision.DatabaseValidations.Validations.Validation::fromJson), validation); } /** @@ -26918,8 +23140,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional feature() { - return Json.optional(json, "feature", Conv.enumeration(SessionsRetrieveDecisionResponse.DatabaseValidations.Warnings.Feature::of)); + public Optional feature() { + return Json.optional(json, "feature", Conv.enumeration(SessionDecision.DatabaseValidations.Warnings.Feature::of)); } /** @@ -27001,8 +23223,8 @@ private Builder(Warnings from) { * @param feature the value * @return this builder */ - public Builder feature(SessionsRetrieveDecisionResponse.DatabaseValidations.Warnings.Feature feature) { - return set("feature", Conv.enumeration(SessionsRetrieveDecisionResponse.DatabaseValidations.Warnings.Feature::of), feature); + public Builder feature(SessionDecision.DatabaseValidations.Warnings.Feature feature) { + return set("feature", Conv.enumeration(SessionDecision.DatabaseValidations.Warnings.Feature::of), feature); } /** @@ -27384,8 +23606,8 @@ public Optional dataResolved() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional company() { - return Json.optional(json, "company", Conv.model(SessionsRetrieveDecisionResponse.RegistryChecks.Company::fromJson)); + public Optional company() { + return Json.optional(json, "company", Conv.model(SessionDecision.RegistryChecks.Company::fromJson)); } /** @@ -27457,8 +23679,8 @@ public Builder dataResolved(boolean dataResolved) { * @param company the value * @return this builder */ - public Builder company(SessionsRetrieveDecisionResponse.RegistryChecks.Company company) { - return set("company", Conv.model(SessionsRetrieveDecisionResponse.RegistryChecks.Company::fromJson), company); + public Builder company(SessionDecision.RegistryChecks.Company company) { + return set("company", Conv.model(SessionDecision.RegistryChecks.Company::fromJson), company); } /** @@ -27671,8 +23893,8 @@ public Optional taxNumber() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional riskLevel() { - return Json.optional(json, "risk_level", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.RiskLevel::of)); + public Optional riskLevel() { + return Json.optional(json, "risk_level", Conv.enumeration(SessionDecision.RegistryChecks.Company.RiskLevel::of)); } /** @@ -27821,8 +24043,8 @@ public Optional vatNumber() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional vatValidationStatus() { - return Json.optional(json, "vat_validation_status", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.VatValidationStatus::of)); + public Optional vatValidationStatus() { + return Json.optional(json, "vat_validation_status", Conv.enumeration(SessionDecision.RegistryChecks.Company.VatValidationStatus::of)); } /** @@ -27841,8 +24063,8 @@ public Optional vatValidatedNumber() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional vatValidationSource() { - return Json.optional(json, "vat_validation_source", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.VatValidationSource::of)); + public Optional vatValidationSource() { + return Json.optional(json, "vat_validation_source", Conv.enumeration(SessionDecision.RegistryChecks.Company.VatValidationSource::of)); } /** @@ -27891,8 +24113,8 @@ public Optional> financialSummary() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> officers() { - return Json.optional(json, "officers", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.RegistryChecks.Company.Officers::fromJson))); + public Optional> officers() { + return Json.optional(json, "officers", Conv.list(Conv.model(SessionDecision.RegistryChecks.Company.Officers::fromJson))); } /** @@ -27901,8 +24123,8 @@ public Optional> beneficialOwners() { - return Json.optional(json, "beneficial_owners", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.RegistryChecks.Company.BeneficialOwners::fromJson))); + public Optional> beneficialOwners() { + return Json.optional(json, "beneficial_owners", Conv.list(Conv.model(SessionDecision.RegistryChecks.Company.BeneficialOwners::fromJson))); } /** @@ -28174,8 +24396,8 @@ public Builder taxNumber(@Nullable String taxNumber) { * @param riskLevel the value, or {@code null} to send JSON null * @return this builder */ - public Builder riskLevel(SessionsRetrieveDecisionResponse.RegistryChecks.Company.@Nullable RiskLevel riskLevel) { - return setNullable("risk_level", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.RiskLevel::of), riskLevel); + public Builder riskLevel(SessionDecision.RegistryChecks.Company.@Nullable RiskLevel riskLevel) { + return setNullable("risk_level", Conv.enumeration(SessionDecision.RegistryChecks.Company.RiskLevel::of), riskLevel); } /** @@ -28324,8 +24546,8 @@ public Builder vatNumber(@Nullable String vatNumber) { * @param vatValidationStatus the value * @return this builder */ - public Builder vatValidationStatus(SessionsRetrieveDecisionResponse.RegistryChecks.Company.VatValidationStatus vatValidationStatus) { - return set("vat_validation_status", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.VatValidationStatus::of), vatValidationStatus); + public Builder vatValidationStatus(SessionDecision.RegistryChecks.Company.VatValidationStatus vatValidationStatus) { + return set("vat_validation_status", Conv.enumeration(SessionDecision.RegistryChecks.Company.VatValidationStatus::of), vatValidationStatus); } /** @@ -28344,8 +24566,8 @@ public Builder vatValidatedNumber(@Nullable String vatValidatedNumber) { * @param vatValidationSource the value, or {@code null} to send JSON null * @return this builder */ - public Builder vatValidationSource(SessionsRetrieveDecisionResponse.RegistryChecks.Company.@Nullable VatValidationSource vatValidationSource) { - return setNullable("vat_validation_source", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.VatValidationSource::of), vatValidationSource); + public Builder vatValidationSource(SessionDecision.RegistryChecks.Company.@Nullable VatValidationSource vatValidationSource) { + return setNullable("vat_validation_source", Conv.enumeration(SessionDecision.RegistryChecks.Company.VatValidationSource::of), vatValidationSource); } /** @@ -28394,8 +24616,8 @@ public Builder financialSummary(@Nullable Map financialSummary) { * @param officers the value * @return this builder */ - public Builder officers(List officers) { - return set("officers", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.RegistryChecks.Company.Officers::fromJson)), officers); + public Builder officers(List officers) { + return set("officers", Conv.list(Conv.model(SessionDecision.RegistryChecks.Company.Officers::fromJson)), officers); } /** @@ -28404,8 +24626,8 @@ public Builder officers(List beneficialOwners) { - return set("beneficial_owners", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.RegistryChecks.Company.BeneficialOwners::fromJson)), beneficialOwners); + public Builder beneficialOwners(List beneficialOwners) { + return set("beneficial_owners", Conv.list(Conv.model(SessionDecision.RegistryChecks.Company.BeneficialOwners::fromJson)), beneficialOwners); } /** @@ -28812,8 +25034,8 @@ public Optional isActive() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional kycStatus() { - return Json.optional(json, "kyc_status", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.Officers.KycStatus::of)); + public Optional kycStatus() { + return Json.optional(json, "kyc_status", Conv.enumeration(SessionDecision.RegistryChecks.Company.Officers.KycStatus::of)); } /** @@ -28985,8 +25207,8 @@ public Builder isActive(boolean isActive) { * @param kycStatus the value, or {@code null} to send JSON null * @return this builder */ - public Builder kycStatus(SessionsRetrieveDecisionResponse.RegistryChecks.Company.Officers.@Nullable KycStatus kycStatus) { - return setNullable("kyc_status", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.Officers.KycStatus::of), kycStatus); + public Builder kycStatus(SessionDecision.RegistryChecks.Company.Officers.@Nullable KycStatus kycStatus) { + return setNullable("kyc_status", Conv.enumeration(SessionDecision.RegistryChecks.Company.Officers.KycStatus::of), kycStatus); } /** @@ -29222,8 +25444,8 @@ public Optional lastName() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional entityType() { - return Json.optional(json, "entity_type", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.BeneficialOwners.EntityType::of)); + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.enumeration(SessionDecision.RegistryChecks.Company.BeneficialOwners.EntityType::of)); } /** @@ -29272,8 +25494,8 @@ public Optional isActive() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional kycStatus() { - return Json.optional(json, "kyc_status", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.BeneficialOwners.KycStatus::of)); + public Optional kycStatus() { + return Json.optional(json, "kyc_status", Conv.enumeration(SessionDecision.RegistryChecks.Company.BeneficialOwners.KycStatus::of)); } /** @@ -29465,8 +25687,8 @@ public Builder lastName(@Nullable String lastName) { * @param entityType the value * @return this builder */ - public Builder entityType(SessionsRetrieveDecisionResponse.RegistryChecks.Company.BeneficialOwners.EntityType entityType) { - return set("entity_type", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.BeneficialOwners.EntityType::of), entityType); + public Builder entityType(SessionDecision.RegistryChecks.Company.BeneficialOwners.EntityType entityType) { + return set("entity_type", Conv.enumeration(SessionDecision.RegistryChecks.Company.BeneficialOwners.EntityType::of), entityType); } /** @@ -29515,8 +25737,8 @@ public Builder isActive(boolean isActive) { * @param kycStatus the value, or {@code null} to send JSON null * @return this builder */ - public Builder kycStatus(SessionsRetrieveDecisionResponse.RegistryChecks.Company.BeneficialOwners.@Nullable KycStatus kycStatus) { - return setNullable("kyc_status", Conv.enumeration(SessionsRetrieveDecisionResponse.RegistryChecks.Company.BeneficialOwners.KycStatus::of), kycStatus); + public Builder kycStatus(SessionDecision.RegistryChecks.Company.BeneficialOwners.@Nullable KycStatus kycStatus) { + return setNullable("kyc_status", Conv.enumeration(SessionDecision.RegistryChecks.Company.BeneficialOwners.KycStatus::of), kycStatus); } /** @@ -29819,8 +26041,8 @@ public Optional nodeId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional registry() { - return Json.optional(json, "registry", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry::fromJson)); + public Optional registry() { + return Json.optional(json, "registry", Conv.model(SessionDecision.KeyPeopleChecks.Registry::fromJson)); } /** @@ -29829,8 +26051,8 @@ public Optional regis * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional submitted() { - return Json.optional(json, "submitted", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted::fromJson)); + public Optional submitted() { + return Json.optional(json, "submitted", Conv.model(SessionDecision.KeyPeopleChecks.Submitted::fromJson)); } /** @@ -29839,8 +26061,8 @@ public Optional subm * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional uboKycSummary() { - return Json.optional(json, "ubo_kyc_summary", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.UboKycSummary::fromJson)); + public Optional uboKycSummary() { + return Json.optional(json, "ubo_kyc_summary", Conv.model(SessionDecision.KeyPeopleChecks.UboKycSummary::fromJson)); } /** @@ -29892,8 +26114,8 @@ public Builder nodeId(@Nullable String nodeId) { * @param registry the value * @return this builder */ - public Builder registry(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry registry) { - return set("registry", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry::fromJson), registry); + public Builder registry(SessionDecision.KeyPeopleChecks.Registry registry) { + return set("registry", Conv.model(SessionDecision.KeyPeopleChecks.Registry::fromJson), registry); } /** @@ -29902,8 +26124,8 @@ public Builder registry(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registr * @param submitted the value * @return this builder */ - public Builder submitted(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted submitted) { - return set("submitted", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted::fromJson), submitted); + public Builder submitted(SessionDecision.KeyPeopleChecks.Submitted submitted) { + return set("submitted", Conv.model(SessionDecision.KeyPeopleChecks.Submitted::fromJson), submitted); } /** @@ -29912,8 +26134,8 @@ public Builder submitted(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submit * @param uboKycSummary the value, or {@code null} to send JSON null * @return this builder */ - public Builder uboKycSummary(SessionsRetrieveDecisionResponse.KeyPeopleChecks.@Nullable UboKycSummary uboKycSummary) { - return setNullable("ubo_kyc_summary", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.UboKycSummary::fromJson), uboKycSummary); + public Builder uboKycSummary(SessionDecision.KeyPeopleChecks.@Nullable UboKycSummary uboKycSummary) { + return setNullable("ubo_kyc_summary", Conv.model(SessionDecision.KeyPeopleChecks.UboKycSummary::fromJson), uboKycSummary); } /** @@ -29984,8 +26206,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> officers() { - return Json.optional(json, "officers", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers::fromJson))); + public Optional> officers() { + return Json.optional(json, "officers", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Registry.Officers::fromJson))); } /** @@ -29994,8 +26216,8 @@ public Optional> beneficialOwners() { - return Json.optional(json, "beneficial_owners", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners::fromJson))); + public Optional> beneficialOwners() { + return Json.optional(json, "beneficial_owners", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners::fromJson))); } @Override @@ -30017,8 +26239,8 @@ private Builder(Registry from) { * @param officers the value * @return this builder */ - public Builder officers(List officers) { - return set("officers", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers::fromJson)), officers); + public Builder officers(List officers) { + return set("officers", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Registry.Officers::fromJson)), officers); } /** @@ -30027,8 +26249,8 @@ public Builder officers(List beneficialOwners) { - return set("beneficial_owners", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners::fromJson)), beneficialOwners); + public Builder beneficialOwners(List beneficialOwners) { + return set("beneficial_owners", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners::fromJson)), beneficialOwners); } /** @@ -30118,8 +26340,8 @@ public Optional designation() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional role() { - return Json.optional(json, "role", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.Role::of)); + public Optional role() { + return Json.optional(json, "role", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.Role::of)); } /** @@ -30178,8 +26400,8 @@ public Optional address() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional amlStatus() { - return Json.optional(json, "aml_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.AmlStatus::of)); + public Optional amlStatus() { + return Json.optional(json, "aml_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.AmlStatus::of)); } /** @@ -30188,8 +26410,8 @@ public Optional kycStatus() { - return Json.optional(json, "kyc_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycStatus::of)); + public Optional kycStatus() { + return Json.optional(json, "kyc_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycStatus::of)); } /** @@ -30248,8 +26470,8 @@ public Optional verificationWorkflowLabel() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> verificationWorkflowFeatures() { - return Json.optional(json, "verification_workflow_features", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures::fromJson))); + public Optional> verificationWorkflowFeatures() { + return Json.optional(json, "verification_workflow_features", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures::fromJson))); } /** @@ -30308,8 +26530,8 @@ public Optional kycVerifiedAt() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional kycAmlCategories() { - return Json.optional(json, "kyc_aml_categories", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories::fromJson)); + public Optional kycAmlCategories() { + return Json.optional(json, "kyc_aml_categories", Conv.model(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories::fromJson)); } /** @@ -30421,8 +26643,8 @@ public Builder designation(@Nullable String designation) { * @param role the value * @return this builder */ - public Builder role(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.Role role) { - return set("role", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.Role::of), role); + public Builder role(SessionDecision.KeyPeopleChecks.Registry.Officers.Role role) { + return set("role", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.Role::of), role); } /** @@ -30481,8 +26703,8 @@ public Builder address(@Nullable String address) { * @param amlStatus the value, or {@code null} to send JSON null * @return this builder */ - public Builder amlStatus(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.@Nullable AmlStatus amlStatus) { - return setNullable("aml_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.AmlStatus::of), amlStatus); + public Builder amlStatus(SessionDecision.KeyPeopleChecks.Registry.Officers.@Nullable AmlStatus amlStatus) { + return setNullable("aml_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.AmlStatus::of), amlStatus); } /** @@ -30491,8 +26713,8 @@ public Builder amlStatus(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Regist * @param kycStatus the value, or {@code null} to send JSON null * @return this builder */ - public Builder kycStatus(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.@Nullable KycStatus kycStatus) { - return setNullable("kyc_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycStatus::of), kycStatus); + public Builder kycStatus(SessionDecision.KeyPeopleChecks.Registry.Officers.@Nullable KycStatus kycStatus) { + return setNullable("kyc_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycStatus::of), kycStatus); } /** @@ -30551,8 +26773,8 @@ public Builder verificationWorkflowLabel(@Nullable String verificationWorkflowLa * @param verificationWorkflowFeatures the value, or {@code null} to send JSON null * @return this builder */ - public Builder verificationWorkflowFeatures(@Nullable List verificationWorkflowFeatures) { - return setNullable("verification_workflow_features", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures::fromJson)), verificationWorkflowFeatures); + public Builder verificationWorkflowFeatures(@Nullable List verificationWorkflowFeatures) { + return setNullable("verification_workflow_features", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures::fromJson)), verificationWorkflowFeatures); } /** @@ -30611,8 +26833,8 @@ public Builder kycVerifiedAt(@Nullable OffsetDateTime kycVerifiedAt) { * @param kycAmlCategories the value, or {@code null} to send JSON null * @return this builder */ - public Builder kycAmlCategories(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.@Nullable KycAmlCategories kycAmlCategories) { - return setNullable("kyc_aml_categories", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories::fromJson), kycAmlCategories); + public Builder kycAmlCategories(SessionDecision.KeyPeopleChecks.Registry.Officers.@Nullable KycAmlCategories kycAmlCategories) { + return setNullable("kyc_aml_categories", Conv.model(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories::fromJson), kycAmlCategories); } /** @@ -30949,8 +27171,8 @@ public Optional feature() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures.Status::of)); } @Override @@ -30982,8 +27204,8 @@ public Builder feature(String feature) { * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures.Status::of), status); + public Builder status(SessionDecision.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures.Status status) { + return set("status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.VerificationWorkflowFeatures.Status::of), status); } /** @@ -31099,8 +27321,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional sanctions() { - return Json.optional(json, "sanctions", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Sanctions::of)); + public Optional sanctions() { + return Json.optional(json, "sanctions", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Sanctions::of)); } /** @@ -31109,8 +27331,8 @@ public Optional pep() { - return Json.optional(json, "pep", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Pep::of)); + public Optional pep() { + return Json.optional(json, "pep", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Pep::of)); } /** @@ -31119,8 +27341,8 @@ public Optional adverseMedia() { - return Json.optional(json, "adverse_media", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.AdverseMedia::of)); + public Optional adverseMedia() { + return Json.optional(json, "adverse_media", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.AdverseMedia::of)); } /** @@ -31129,8 +27351,8 @@ public Optional warnings() { - return Json.optional(json, "warnings", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Warnings::of)); + public Optional warnings() { + return Json.optional(json, "warnings", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Warnings::of)); } @Override @@ -31152,8 +27374,8 @@ private Builder(KycAmlCategories from) { * @param sanctions the value * @return this builder */ - public Builder sanctions(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Sanctions sanctions) { - return set("sanctions", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Sanctions::of), sanctions); + public Builder sanctions(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Sanctions sanctions) { + return set("sanctions", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Sanctions::of), sanctions); } /** @@ -31162,8 +27384,8 @@ public Builder sanctions(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Regist * @param pep the value * @return this builder */ - public Builder pep(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Pep pep) { - return set("pep", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Pep::of), pep); + public Builder pep(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Pep pep) { + return set("pep", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Pep::of), pep); } /** @@ -31172,8 +27394,8 @@ public Builder pep(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Off * @param adverseMedia the value * @return this builder */ - public Builder adverseMedia(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.AdverseMedia adverseMedia) { - return set("adverse_media", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.AdverseMedia::of), adverseMedia); + public Builder adverseMedia(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.AdverseMedia adverseMedia) { + return set("adverse_media", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.AdverseMedia::of), adverseMedia); } /** @@ -31182,8 +27404,8 @@ public Builder adverseMedia(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Reg * @param warnings the value * @return this builder */ - public Builder warnings(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Warnings warnings) { - return set("warnings", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Warnings::of), warnings); + public Builder warnings(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Warnings warnings) { + return set("warnings", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.Officers.KycAmlCategories.Warnings::of), warnings); } /** @@ -31503,8 +27725,8 @@ public Optional registrationNumber() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional entityType() { - return Json.optional(json, "entity_type", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.EntityType::of)); + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.EntityType::of)); } /** @@ -31573,8 +27795,8 @@ public Optional isActive() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional amlStatus() { - return Json.optional(json, "aml_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.AmlStatus::of)); + public Optional amlStatus() { + return Json.optional(json, "aml_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.AmlStatus::of)); } /** @@ -31583,8 +27805,8 @@ public Optional kycStatus() { - return Json.optional(json, "kyc_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycStatus::of)); + public Optional kycStatus() { + return Json.optional(json, "kyc_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycStatus::of)); } /** @@ -31643,8 +27865,8 @@ public Optional verificationWorkflowLabel() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> verificationWorkflowFeatures() { - return Json.optional(json, "verification_workflow_features", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures::fromJson))); + public Optional> verificationWorkflowFeatures() { + return Json.optional(json, "verification_workflow_features", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures::fromJson))); } /** @@ -31703,8 +27925,8 @@ public Optional kycVerifiedAt() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional kycAmlCategories() { - return Json.optional(json, "kyc_aml_categories", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories::fromJson)); + public Optional kycAmlCategories() { + return Json.optional(json, "kyc_aml_categories", Conv.model(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories::fromJson)); } /** @@ -31876,8 +28098,8 @@ public Builder registrationNumber(@Nullable String registrationNumber) { * @param entityType the value * @return this builder */ - public Builder entityType(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.EntityType entityType) { - return set("entity_type", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.EntityType::of), entityType); + public Builder entityType(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.EntityType entityType) { + return set("entity_type", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.EntityType::of), entityType); } /** @@ -31946,8 +28168,8 @@ public Builder isActive(boolean isActive) { * @param amlStatus the value, or {@code null} to send JSON null * @return this builder */ - public Builder amlStatus(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.@Nullable AmlStatus amlStatus) { - return setNullable("aml_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.AmlStatus::of), amlStatus); + public Builder amlStatus(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.@Nullable AmlStatus amlStatus) { + return setNullable("aml_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.AmlStatus::of), amlStatus); } /** @@ -31956,8 +28178,8 @@ public Builder amlStatus(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Regist * @param kycStatus the value, or {@code null} to send JSON null * @return this builder */ - public Builder kycStatus(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.@Nullable KycStatus kycStatus) { - return setNullable("kyc_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycStatus::of), kycStatus); + public Builder kycStatus(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.@Nullable KycStatus kycStatus) { + return setNullable("kyc_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycStatus::of), kycStatus); } /** @@ -32016,8 +28238,8 @@ public Builder verificationWorkflowLabel(@Nullable String verificationWorkflowLa * @param verificationWorkflowFeatures the value, or {@code null} to send JSON null * @return this builder */ - public Builder verificationWorkflowFeatures(@Nullable List verificationWorkflowFeatures) { - return setNullable("verification_workflow_features", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures::fromJson)), verificationWorkflowFeatures); + public Builder verificationWorkflowFeatures(@Nullable List verificationWorkflowFeatures) { + return setNullable("verification_workflow_features", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures::fromJson)), verificationWorkflowFeatures); } /** @@ -32076,8 +28298,8 @@ public Builder kycVerifiedAt(@Nullable OffsetDateTime kycVerifiedAt) { * @param kycAmlCategories the value, or {@code null} to send JSON null * @return this builder */ - public Builder kycAmlCategories(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.@Nullable KycAmlCategories kycAmlCategories) { - return setNullable("kyc_aml_categories", Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories::fromJson), kycAmlCategories); + public Builder kycAmlCategories(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.@Nullable KycAmlCategories kycAmlCategories) { + return setNullable("kyc_aml_categories", Conv.model(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories::fromJson), kycAmlCategories); } /** @@ -32368,8 +28590,8 @@ public Optional feature() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures.Status::of)); } @Override @@ -32401,8 +28623,8 @@ public Builder feature(String feature) { * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures.Status::of), status); + public Builder status(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures.Status status) { + return set("status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.VerificationWorkflowFeatures.Status::of), status); } /** @@ -32518,8 +28740,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional sanctions() { - return Json.optional(json, "sanctions", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Sanctions::of)); + public Optional sanctions() { + return Json.optional(json, "sanctions", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Sanctions::of)); } /** @@ -32528,8 +28750,8 @@ public Optional pep() { - return Json.optional(json, "pep", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Pep::of)); + public Optional pep() { + return Json.optional(json, "pep", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Pep::of)); } /** @@ -32538,8 +28760,8 @@ public Optional adverseMedia() { - return Json.optional(json, "adverse_media", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.AdverseMedia::of)); + public Optional adverseMedia() { + return Json.optional(json, "adverse_media", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.AdverseMedia::of)); } /** @@ -32548,8 +28770,8 @@ public Optional warnings() { - return Json.optional(json, "warnings", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Warnings::of)); + public Optional warnings() { + return Json.optional(json, "warnings", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Warnings::of)); } @Override @@ -32571,8 +28793,8 @@ private Builder(KycAmlCategories from) { * @param sanctions the value * @return this builder */ - public Builder sanctions(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Sanctions sanctions) { - return set("sanctions", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Sanctions::of), sanctions); + public Builder sanctions(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Sanctions sanctions) { + return set("sanctions", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Sanctions::of), sanctions); } /** @@ -32581,8 +28803,8 @@ public Builder sanctions(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Regist * @param pep the value * @return this builder */ - public Builder pep(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Pep pep) { - return set("pep", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Pep::of), pep); + public Builder pep(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Pep pep) { + return set("pep", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Pep::of), pep); } /** @@ -32591,8 +28813,8 @@ public Builder pep(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.Ben * @param adverseMedia the value * @return this builder */ - public Builder adverseMedia(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.AdverseMedia adverseMedia) { - return set("adverse_media", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.AdverseMedia::of), adverseMedia); + public Builder adverseMedia(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.AdverseMedia adverseMedia) { + return set("adverse_media", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.AdverseMedia::of), adverseMedia); } /** @@ -32601,8 +28823,8 @@ public Builder adverseMedia(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Reg * @param warnings the value * @return this builder */ - public Builder warnings(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Warnings warnings) { - return set("warnings", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Warnings::of), warnings); + public Builder warnings(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Warnings warnings) { + return set("warnings", Conv.enumeration(SessionDecision.KeyPeopleChecks.Registry.BeneficialOwners.KycAmlCategories.Warnings::of), warnings); } /** @@ -32836,8 +29058,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> parties() { - return Json.optional(json, "parties", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties::fromJson))); + public Optional> parties() { + return Json.optional(json, "parties", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Submitted.Parties::fromJson))); } @Override @@ -32859,8 +29081,8 @@ private Builder(Submitted from) { * @param parties the value * @return this builder */ - public Builder parties(List parties) { - return set("parties", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties::fromJson)), parties); + public Builder parties(List parties) { + return set("parties", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Submitted.Parties::fromJson)), parties); } /** @@ -32931,8 +29153,8 @@ public Optional uuid() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional entityType() { - return Json.optional(json, "entity_type", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.EntityType::of)); + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.EntityType::of)); } /** @@ -32941,8 +29163,8 @@ public Optional source() { - return Json.optional(json, "source", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.Source::of)); + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.Source::of)); } /** @@ -33091,8 +29313,8 @@ public Optional requiresVerification() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> roles() { - return Json.optional(json, "roles", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.Roles::fromJson))); + public Optional> roles() { + return Json.optional(json, "roles", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Submitted.Parties.Roles::fromJson))); } /** @@ -33111,8 +29333,8 @@ public Optional kycSessionId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional kycSessionStatus() { - return Json.optional(json, "kyc_session_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.KycSessionStatus::of)); + public Optional kycSessionStatus() { + return Json.optional(json, "kyc_session_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.KycSessionStatus::of)); } /** @@ -33141,8 +29363,8 @@ public Optional kybSubSessionId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional kybSubSessionStatus() { - return Json.optional(json, "kyb_sub_session_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.KybSubSessionStatus::of)); + public Optional kybSubSessionStatus() { + return Json.optional(json, "kyb_sub_session_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.KybSubSessionStatus::of)); } /** @@ -33191,8 +29413,8 @@ public Optional verificationWorkflowLabel() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> verificationWorkflowFeatures() { - return Json.optional(json, "verification_workflow_features", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures::fromJson))); + public Optional> verificationWorkflowFeatures() { + return Json.optional(json, "verification_workflow_features", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures::fromJson))); } /** @@ -33234,8 +29456,8 @@ public Builder uuid(String uuid) { * @param entityType the value * @return this builder */ - public Builder entityType(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.EntityType entityType) { - return set("entity_type", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.EntityType::of), entityType); + public Builder entityType(SessionDecision.KeyPeopleChecks.Submitted.Parties.EntityType entityType) { + return set("entity_type", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.EntityType::of), entityType); } /** @@ -33244,8 +29466,8 @@ public Builder entityType(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submi * @param source the value * @return this builder */ - public Builder source(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.Source source) { - return set("source", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.Source::of), source); + public Builder source(SessionDecision.KeyPeopleChecks.Submitted.Parties.Source source) { + return set("source", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.Source::of), source); } /** @@ -33394,8 +29616,8 @@ public Builder requiresVerification(boolean requiresVerification) { * @param roles the value * @return this builder */ - public Builder roles(List roles) { - return set("roles", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.Roles::fromJson)), roles); + public Builder roles(List roles) { + return set("roles", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Submitted.Parties.Roles::fromJson)), roles); } /** @@ -33414,8 +29636,8 @@ public Builder kycSessionId(@Nullable String kycSessionId) { * @param kycSessionStatus the value, or {@code null} to send JSON null * @return this builder */ - public Builder kycSessionStatus(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.@Nullable KycSessionStatus kycSessionStatus) { - return setNullable("kyc_session_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.KycSessionStatus::of), kycSessionStatus); + public Builder kycSessionStatus(SessionDecision.KeyPeopleChecks.Submitted.Parties.@Nullable KycSessionStatus kycSessionStatus) { + return setNullable("kyc_session_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.KycSessionStatus::of), kycSessionStatus); } /** @@ -33444,8 +29666,8 @@ public Builder kybSubSessionId(@Nullable String kybSubSessionId) { * @param kybSubSessionStatus the value, or {@code null} to send JSON null * @return this builder */ - public Builder kybSubSessionStatus(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.@Nullable KybSubSessionStatus kybSubSessionStatus) { - return setNullable("kyb_sub_session_status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.KybSubSessionStatus::of), kybSubSessionStatus); + public Builder kybSubSessionStatus(SessionDecision.KeyPeopleChecks.Submitted.Parties.@Nullable KybSubSessionStatus kybSubSessionStatus) { + return setNullable("kyb_sub_session_status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.KybSubSessionStatus::of), kybSubSessionStatus); } /** @@ -33494,8 +29716,8 @@ public Builder verificationWorkflowLabel(@Nullable String verificationWorkflowLa * @param verificationWorkflowFeatures the value, or {@code null} to send JSON null * @return this builder */ - public Builder verificationWorkflowFeatures(@Nullable List verificationWorkflowFeatures) { - return setNullable("verification_workflow_features", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures::fromJson)), verificationWorkflowFeatures); + public Builder verificationWorkflowFeatures(@Nullable List verificationWorkflowFeatures) { + return setNullable("verification_workflow_features", Conv.list(Conv.model(SessionDecision.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures::fromJson)), verificationWorkflowFeatures); } /** @@ -33647,8 +29869,8 @@ public Builder toBuilder() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional role() { - return Json.optional(json, "role", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.Roles.Role::of)); + public Optional role() { + return Json.optional(json, "role", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.Roles.Role::of)); } /** @@ -33690,8 +29912,8 @@ private Builder(Roles from) { * @param role the value * @return this builder */ - public Builder role(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.Roles.Role role) { - return set("role", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.Roles.Role::of), role); + public Builder role(SessionDecision.KeyPeopleChecks.Submitted.Parties.Roles.Role role) { + return set("role", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.Roles.Role::of), role); } /** @@ -34003,8 +30225,8 @@ public Optional feature() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures.Status::of)); } @Override @@ -34036,8 +30258,8 @@ public Builder feature(String feature) { * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures.Status::of), status); + public Builder status(SessionDecision.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures.Status status) { + return set("status", Conv.enumeration(SessionDecision.KeyPeopleChecks.Submitted.Parties.VerificationWorkflowFeatures.Status::of), status); } /** @@ -34320,8 +30542,8 @@ public Optional nodeId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> items() { - return Json.optional(json, "items", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.Items::fromJson))); + public Optional> items() { + return Json.optional(json, "items", Conv.list(Conv.model(SessionDecision.DocumentVerifications.Items::fromJson))); } /** @@ -34330,8 +30552,8 @@ public Optional> groups() { - return Json.optional(json, "groups", Conv.map(Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.GroupsValue::fromJson))); + public Optional> groups() { + return Json.optional(json, "groups", Conv.map(Conv.model(SessionDecision.DocumentVerifications.GroupsValue::fromJson))); } /** @@ -34393,8 +30615,8 @@ public Builder nodeId(@Nullable String nodeId) { * @param items the value * @return this builder */ - public Builder items(List items) { - return set("items", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.Items::fromJson)), items); + public Builder items(List items) { + return set("items", Conv.list(Conv.model(SessionDecision.DocumentVerifications.Items::fromJson)), items); } /** @@ -34403,8 +30625,8 @@ public Builder items(List groups) { - return set("groups", Conv.map(Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.GroupsValue::fromJson)), groups); + public Builder groups(Map groups) { + return set("groups", Conv.map(Conv.model(SessionDecision.DocumentVerifications.GroupsValue::fromJson)), groups); } /** @@ -34494,8 +30716,8 @@ public Optional uuid() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional documentType() { - return Json.optional(json, "document_type", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentType::of)); + public Optional documentType() { + return Json.optional(json, "document_type", Conv.enumeration(SessionDecision.DocumentVerifications.Items.DocumentType::of)); } /** @@ -34514,8 +30736,8 @@ public Optional documentSubtype() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional documentGroup() { - return Json.optional(json, "document_group", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentGroup::of)); + public Optional documentGroup() { + return Json.optional(json, "document_group", Conv.enumeration(SessionDecision.DocumentVerifications.Items.DocumentGroup::of)); } /** @@ -34554,8 +30776,8 @@ public Optional fileSize() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDecision.DocumentVerifications.Items.Status::of)); } /** @@ -34584,8 +30806,8 @@ public Optional> crossCheckResult() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional documentMetadata() { - return Json.optional(json, "document_metadata", Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentMetadata::fromJson)); + public Optional documentMetadata() { + return Json.optional(json, "document_metadata", Conv.model(SessionDecision.DocumentVerifications.Items.DocumentMetadata::fromJson)); } /** @@ -34667,8 +30889,8 @@ public Builder uuid(String uuid) { * @param documentType the value, or {@code null} to send JSON null * @return this builder */ - public Builder documentType(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.@Nullable DocumentType documentType) { - return setNullable("document_type", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentType::of), documentType); + public Builder documentType(SessionDecision.DocumentVerifications.Items.@Nullable DocumentType documentType) { + return setNullable("document_type", Conv.enumeration(SessionDecision.DocumentVerifications.Items.DocumentType::of), documentType); } /** @@ -34687,8 +30909,8 @@ public Builder documentSubtype(@Nullable String documentSubtype) { * @param documentGroup the value, or {@code null} to send JSON null * @return this builder */ - public Builder documentGroup(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.@Nullable DocumentGroup documentGroup) { - return setNullable("document_group", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentGroup::of), documentGroup); + public Builder documentGroup(SessionDecision.DocumentVerifications.Items.@Nullable DocumentGroup documentGroup) { + return setNullable("document_group", Conv.enumeration(SessionDecision.DocumentVerifications.Items.DocumentGroup::of), documentGroup); } /** @@ -34727,8 +30949,8 @@ public Builder fileSize(@Nullable Long fileSize) { * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.Status::of), status); + public Builder status(SessionDecision.DocumentVerifications.Items.Status status) { + return set("status", Conv.enumeration(SessionDecision.DocumentVerifications.Items.Status::of), status); } /** @@ -34757,8 +30979,8 @@ public Builder crossCheckResult(@Nullable Map crossCheckResult) { * @param documentMetadata the value, or {@code null} to send JSON null * @return this builder */ - public Builder documentMetadata(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.@Nullable DocumentMetadata documentMetadata) { - return setNullable("document_metadata", Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentMetadata::fromJson), documentMetadata); + public Builder documentMetadata(SessionDecision.DocumentVerifications.Items.@Nullable DocumentMetadata documentMetadata) { + return setNullable("document_metadata", Conv.model(SessionDecision.DocumentVerifications.Items.DocumentMetadata::fromJson), documentMetadata); } /** @@ -35177,8 +31399,8 @@ public Optional hasDifferentCreationModDate() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional overlayManipulation() { - return Json.optional(json, "overlay_manipulation", Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentMetadata.OverlayManipulation::fromJson)); + public Optional overlayManipulation() { + return Json.optional(json, "overlay_manipulation", Conv.model(SessionDecision.DocumentVerifications.Items.DocumentMetadata.OverlayManipulation::fromJson)); } @Override @@ -35350,8 +31572,8 @@ public Builder hasDifferentCreationModDate(@Nullable Boolean hasDifferentCreatio * @param overlayManipulation the value, or {@code null} to send JSON null * @return this builder */ - public Builder overlayManipulation(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentMetadata.@Nullable OverlayManipulation overlayManipulation) { - return setNullable("overlay_manipulation", Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentMetadata.OverlayManipulation::fromJson), overlayManipulation); + public Builder overlayManipulation(SessionDecision.DocumentVerifications.Items.DocumentMetadata.@Nullable OverlayManipulation overlayManipulation) { + return setNullable("overlay_manipulation", Conv.model(SessionDecision.DocumentVerifications.Items.DocumentMetadata.OverlayManipulation::fromJson), overlayManipulation); } /** @@ -35444,8 +31666,8 @@ public Optional> signals() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> manipulatedRegions() { - return Json.optional(json, "manipulated_regions", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentMetadata.OverlayManipulation.ManipulatedRegions::fromJson))); + public Optional> manipulatedRegions() { + return Json.optional(json, "manipulated_regions", Conv.list(Conv.model(SessionDecision.DocumentVerifications.Items.DocumentMetadata.OverlayManipulation.ManipulatedRegions::fromJson))); } @Override @@ -35497,8 +31719,8 @@ public Builder signals(List signals) { * @param manipulatedRegions the value * @return this builder */ - public Builder manipulatedRegions(List manipulatedRegions) { - return set("manipulated_regions", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.DocumentVerifications.Items.DocumentMetadata.OverlayManipulation.ManipulatedRegions::fromJson)), manipulatedRegions); + public Builder manipulatedRegions(List manipulatedRegions) { + return set("manipulated_regions", Conv.list(Conv.model(SessionDecision.DocumentVerifications.Items.DocumentMetadata.OverlayManipulation.ManipulatedRegions::fromJson)), manipulatedRegions); } /** @@ -36045,8 +32267,8 @@ public Optional previousStatus() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> nodes() { - return Json.optional(json, "nodes", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.Resubmissions.Nodes::fromJson))); + public Optional> nodes() { + return Json.optional(json, "nodes", Conv.list(Conv.model(SessionDecision.Resubmissions.Nodes::fromJson))); } /** @@ -36065,8 +32287,8 @@ public Optional> reasons() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional requestedBy() { - return Json.optional(json, "requested_by", Conv.model(SessionsRetrieveDecisionResponse.Resubmissions.RequestedBy::fromJson)); + public Optional requestedBy() { + return Json.optional(json, "requested_by", Conv.model(SessionDecision.Resubmissions.RequestedBy::fromJson)); } /** @@ -36128,8 +32350,8 @@ public Builder previousStatus(String previousStatus) { * @param nodes the value * @return this builder */ - public Builder nodes(List nodes) { - return set("nodes", Conv.list(Conv.model(SessionsRetrieveDecisionResponse.Resubmissions.Nodes::fromJson)), nodes); + public Builder nodes(List nodes) { + return set("nodes", Conv.list(Conv.model(SessionDecision.Resubmissions.Nodes::fromJson)), nodes); } /** @@ -36148,8 +32370,8 @@ public Builder reasons(@Nullable Map reasons) { * @param requestedBy the value, or {@code null} to send JSON null * @return this builder */ - public Builder requestedBy(SessionsRetrieveDecisionResponse.Resubmissions.@Nullable RequestedBy requestedBy) { - return setNullable("requested_by", Conv.model(SessionsRetrieveDecisionResponse.Resubmissions.RequestedBy::fromJson), requestedBy); + public Builder requestedBy(SessionDecision.Resubmissions.@Nullable RequestedBy requestedBy) { + return setNullable("requested_by", Conv.model(SessionDecision.Resubmissions.RequestedBy::fromJson), requestedBy); } /** diff --git a/src/main/java/me/didit/types/SessionsRetrieveDeletionJobResponse.java b/src/main/java/me/didit/types/SessionDeletionJob.java similarity index 91% rename from src/main/java/me/didit/types/SessionsRetrieveDeletionJobResponse.java rename to src/main/java/me/didit/types/SessionDeletionJob.java index 39bd7b4..898c9ab 100644 --- a/src/main/java/me/didit/types/SessionsRetrieveDeletionJobResponse.java +++ b/src/main/java/me/didit/types/SessionDeletionJob.java @@ -18,22 +18,22 @@ /** * A bulk session deletion running in the background: progress and counts per outcome, never which sessions it deleted. */ -public final class SessionsRetrieveDeletionJobResponse extends JsonModel { +public final class SessionDeletionJob extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "total", "processed", "outcomes", "deleted_child_verifications", "error", "created_at", "completed_at"); - private SessionsRetrieveDeletionJobResponse(ObjectNode json, Map> files) { + private SessionDeletionJob(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code SessionsRetrieveDeletionJobResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code SessionDeletionJob} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static SessionsRetrieveDeletionJobResponse fromJson(JsonNode json) { - return new SessionsRetrieveDeletionJobResponse(Json.copyObject(json), Map.of()); + public static SessionDeletionJob fromJson(JsonNode json) { + return new SessionDeletionJob(Json.copyObject(json), Map.of()); } /** @@ -70,8 +70,8 @@ public Optional jobId() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsRetrieveDeletionJobResponse.Status::of)); + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionDeletionJob.Status::of)); } /** @@ -100,8 +100,8 @@ public Optional processed() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(SessionsRetrieveDeletionJobResponse.Outcomes::fromJson)); + public Optional outcomes() { + return Json.optional(json, "outcomes", Conv.model(SessionDeletionJob.Outcomes::fromJson)); } /** @@ -149,11 +149,11 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link SessionsRetrieveDeletionJobResponse}. */ + /** Builds a {@link SessionDeletionJob}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(SessionsRetrieveDeletionJobResponse from) { + private Builder(SessionDeletionJob from) { super(from); } @@ -173,8 +173,8 @@ public Builder jobId(String jobId) { * @param status the value * @return this builder */ - public Builder status(SessionsRetrieveDeletionJobResponse.Status status) { - return set("status", Conv.enumeration(SessionsRetrieveDeletionJobResponse.Status::of), status); + public Builder status(SessionDeletionJob.Status status) { + return set("status", Conv.enumeration(SessionDeletionJob.Status::of), status); } /** @@ -203,8 +203,8 @@ public Builder processed(long processed) { * @param outcomes the value * @return this builder */ - public Builder outcomes(SessionsRetrieveDeletionJobResponse.Outcomes outcomes) { - return set("outcomes", Conv.model(SessionsRetrieveDeletionJobResponse.Outcomes::fromJson), outcomes); + public Builder outcomes(SessionDeletionJob.Outcomes outcomes) { + return set("outcomes", Conv.model(SessionDeletionJob.Outcomes::fromJson), outcomes); } /** @@ -250,10 +250,10 @@ public Builder completedAt(@Nullable OffsetDateTime completedAt) { /** * Builds the value. * - * @return a new immutable {@code SessionsRetrieveDeletionJobResponse} + * @return a new immutable {@code SessionDeletionJob} */ - public SessionsRetrieveDeletionJobResponse build() { - return new SessionsRetrieveDeletionJobResponse(buildJson(), buildFiles()); + public SessionDeletionJob build() { + return new SessionDeletionJob(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/SessionEvent.java b/src/main/java/me/didit/types/SessionEvent.java new file mode 100644 index 0000000..49a2c2e --- /dev/null +++ b/src/main/java/me/didit/types/SessionEvent.java @@ -0,0 +1,514 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** One entry of a verification session's event timeline. */ +public final class SessionEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "event_type", "created_at", "component", "feature_step", "step", "details", "ip_address", "user_agent", "device_info", "file_url", "sent_at"); + + private SessionEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionEvent fromJson(JsonNode json) { + return new SessionEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Event identifier. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * What happened. Reported by the device during the hosted flow or SDK: LINK_OPENED, STEP_RENDERED, BUTTON_CLICKED, FORM_SUBMITTED, FILE_UPLOADED, ATTEMPT_FAILED and LOG (debugging). Recorded by the server: SESSION_CREATED, SESSION_STATUS_CHANGED (details.old_status, details.current_status), FEATURE_STEP_CHANGED (details.old_feature_step, details.current_feature_step), WORKFLOW_BRANCH_EVALUATED and WORKFLOW_STATUS_NODE_REACHED (the workflow routing trace). SESSION_COMPLETED, NOTE_ADDED and MANUAL_REVIEW_STARTED can appear on older sessions. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionEvent.EventType eventType() { + return Json.required(json, "event_type", Conv.enumeration(SessionEvent.EventType::of)); + } + + /** + * When Didit stored the event. Events are stored asynchronously, a few seconds after sent_at. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + /** + * UI component that reported the event (device events only). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional component() { + return Json.optional(json, "component", Conv.STRING); + } + + /** + * Verification feature the session was on when the event happened. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional featureStep() { + return Json.optional(json, "feature_step", Conv.enumeration(SessionEvent.FeatureStep::of)); + } + + /** + * Screen within the feature, for example DOCUMENT_CAPTURE_FRONT or FACE_PASSIVE_LIVENESS. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional step() { + return Json.optional(json, "step", Conv.STRING); + } + + /** + * Event-specific data. The keys depend on event_type. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> details() { + return Json.optional(json, "details", Conv.OBJECT); + } + + /** + * IP address the device reported the event from. null for server-recorded events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipAddress() { + return Json.optional(json, "ip_address", Conv.STRING); + } + + /** + * User agent of the device. null for server-recorded events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional userAgent() { + return Json.optional(json, "user_agent", Conv.STRING); + } + + /** + * Device details the SDK or browser reported. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> deviceInfo() { + return Json.optional(json, "device_info", Conv.OBJECT); + } + + /** + * Short-lived presigned link to a file attached to the event (for example a captured image). null when there is none, and always null for a scoped API key without media: read. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fileUrl() { + return Json.optional(json, "file_url", Conv.STRING); + } + + /** + * When the event happened, as a Unix timestamp in milliseconds. Results are ordered by it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long sentAt() { + return Json.required(json, "sent_at", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionEvent from) { + super(from); + } + + /** + * Event identifier. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * What happened. Reported by the device during the hosted flow or SDK: LINK_OPENED, STEP_RENDERED, BUTTON_CLICKED, FORM_SUBMITTED, FILE_UPLOADED, ATTEMPT_FAILED and LOG (debugging). Recorded by the server: SESSION_CREATED, SESSION_STATUS_CHANGED (details.old_status, details.current_status), FEATURE_STEP_CHANGED (details.old_feature_step, details.current_feature_step), WORKFLOW_BRANCH_EVALUATED and WORKFLOW_STATUS_NODE_REACHED (the workflow routing trace). SESSION_COMPLETED, NOTE_ADDED and MANUAL_REVIEW_STARTED can appear on older sessions. + * + * @param eventType the value + * @return this builder + */ + public Builder eventType(SessionEvent.EventType eventType) { + return set("event_type", Conv.enumeration(SessionEvent.EventType::of), eventType); + } + + /** + * When Didit stored the event. Events are stored asynchronously, a few seconds after sent_at. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * UI component that reported the event (device events only). + * + * @param component the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder component(@Nullable String component) { + return setNullable("component", Conv.STRING, component); + } + + /** + * Verification feature the session was on when the event happened. + * + * @param featureStep the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder featureStep(SessionEvent.@Nullable FeatureStep featureStep) { + return setNullable("feature_step", Conv.enumeration(SessionEvent.FeatureStep::of), featureStep); + } + + /** + * Screen within the feature, for example DOCUMENT_CAPTURE_FRONT or FACE_PASSIVE_LIVENESS. + * + * @param step the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder step(@Nullable String step) { + return setNullable("step", Conv.STRING, step); + } + + /** + * Event-specific data. The keys depend on event_type. + * + * @param details the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder details(@Nullable Map details) { + return setAny("details", details); + } + + /** + * IP address the device reported the event from. null for server-recorded events. + * + * @param ipAddress the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ipAddress(@Nullable String ipAddress) { + return setNullable("ip_address", Conv.STRING, ipAddress); + } + + /** + * User agent of the device. null for server-recorded events. + * + * @param userAgent the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder userAgent(@Nullable String userAgent) { + return setNullable("user_agent", Conv.STRING, userAgent); + } + + /** + * Device details the SDK or browser reported. + * + * @param deviceInfo the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder deviceInfo(@Nullable Map deviceInfo) { + return setAny("device_info", deviceInfo); + } + + /** + * Short-lived presigned link to a file attached to the event (for example a captured image). null when there is none, and always null for a scoped API key without media: read. + * + * @param fileUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fileUrl(@Nullable String fileUrl) { + return setNullable("file_url", Conv.STRING, fileUrl); + } + + /** + * When the event happened, as a Unix timestamp in milliseconds. Results are ordered by it. + * + * @param sentAt the value + * @return this builder + */ + public Builder sentAt(long sentAt) { + return set("sent_at", Conv.LONG, sentAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionEvent} + * @throws IllegalStateException when a required field is missing + */ + public SessionEvent build() { + require("uuid", false); + require("event_type", false); + require("created_at", false); + require("component", true); + require("feature_step", true); + require("step", true); + require("details", true); + require("ip_address", true); + require("user_agent", true); + require("device_info", true); + require("file_url", true); + require("sent_at", false); + return new SessionEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * What happened. Reported by the device during the hosted flow or SDK: LINK_OPENED, STEP_RENDERED, BUTTON_CLICKED, FORM_SUBMITTED, FILE_UPLOADED, ATTEMPT_FAILED and LOG (debugging). Recorded by the server: SESSION_CREATED, SESSION_STATUS_CHANGED (details.old_status, details.current_status), FEATURE_STEP_CHANGED (details.old_feature_step, details.current_feature_step), WORKFLOW_BRANCH_EVALUATED and WORKFLOW_STATUS_NODE_REACHED (the workflow routing trace). SESSION_COMPLETED, NOTE_ADDED and MANUAL_REVIEW_STARTED can appear on older sessions. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EventType extends StringEnum { + /** The value {@code "SESSION_CREATED"}. */ + public static final EventType SESSION_CREATED = new EventType("SESSION_CREATED"); + + /** The value {@code "SESSION_COMPLETED"}. */ + public static final EventType SESSION_COMPLETED = new EventType("SESSION_COMPLETED"); + + /** The value {@code "SESSION_STATUS_CHANGED"}. */ + public static final EventType SESSION_STATUS_CHANGED = new EventType("SESSION_STATUS_CHANGED"); + + /** The value {@code "LINK_OPENED"}. */ + public static final EventType LINK_OPENED = new EventType("LINK_OPENED"); + + /** The value {@code "FORM_SUBMITTED"}. */ + public static final EventType FORM_SUBMITTED = new EventType("FORM_SUBMITTED"); + + /** The value {@code "BUTTON_CLICKED"}. */ + public static final EventType BUTTON_CLICKED = new EventType("BUTTON_CLICKED"); + + /** The value {@code "FILE_UPLOADED"}. */ + public static final EventType FILE_UPLOADED = new EventType("FILE_UPLOADED"); + + /** The value {@code "ATTEMPT_FAILED"}. */ + public static final EventType ATTEMPT_FAILED = new EventType("ATTEMPT_FAILED"); + + /** The value {@code "FEATURE_STEP_CHANGED"}. */ + public static final EventType FEATURE_STEP_CHANGED = new EventType("FEATURE_STEP_CHANGED"); + + /** The value {@code "STEP_RENDERED"}. */ + public static final EventType STEP_RENDERED = new EventType("STEP_RENDERED"); + + /** The value {@code "NOTE_ADDED"}. */ + public static final EventType NOTE_ADDED = new EventType("NOTE_ADDED"); + + /** The value {@code "MANUAL_REVIEW_STARTED"}. */ + public static final EventType MANUAL_REVIEW_STARTED = new EventType("MANUAL_REVIEW_STARTED"); + + /** The value {@code "WORKFLOW_BRANCH_EVALUATED"}. */ + public static final EventType WORKFLOW_BRANCH_EVALUATED = new EventType("WORKFLOW_BRANCH_EVALUATED"); + + /** The value {@code "WORKFLOW_STATUS_NODE_REACHED"}. */ + public static final EventType WORKFLOW_STATUS_NODE_REACHED = new EventType("WORKFLOW_STATUS_NODE_REACHED"); + + /** The value {@code "LOG"}. */ + public static final EventType LOG = new EventType("LOG"); + + private static final List KNOWN_VALUES = List.of(SESSION_CREATED, SESSION_COMPLETED, SESSION_STATUS_CHANGED, LINK_OPENED, FORM_SUBMITTED, BUTTON_CLICKED, FILE_UPLOADED, ATTEMPT_FAILED, FEATURE_STEP_CHANGED, STEP_RENDERED, NOTE_ADDED, MANUAL_REVIEW_STARTED, WORKFLOW_BRANCH_EVALUATED, WORKFLOW_STATUS_NODE_REACHED, LOG); + + private EventType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EventType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EventType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Verification feature the session was on when the event happened. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class FeatureStep extends StringEnum { + /** The value {@code "OCR"}. */ + public static final FeatureStep OCR = new FeatureStep("OCR"); + + /** The value {@code "OCR_BACK"}. */ + public static final FeatureStep OCR_BACK = new FeatureStep("OCR_BACK"); + + /** The value {@code "NFC"}. */ + public static final FeatureStep NFC = new FeatureStep("NFC"); + + /** The value {@code "AML"}. */ + public static final FeatureStep AML = new FeatureStep("AML"); + + /** The value {@code "FACE"}. */ + public static final FeatureStep FACE = new FeatureStep("FACE"); + + /** The value {@code "LIVENESS"}. */ + public static final FeatureStep LIVENESS = new FeatureStep("LIVENESS"); + + /** The value {@code "FACE_MATCH"}. */ + public static final FeatureStep FACE_MATCH = new FeatureStep("FACE_MATCH"); + + /** The value {@code "IP_ANALYSIS"}. */ + public static final FeatureStep IP_ANALYSIS = new FeatureStep("IP_ANALYSIS"); + + /** The value {@code "AGE_ESTIMATION"}. */ + public static final FeatureStep AGE_ESTIMATION = new FeatureStep("AGE_ESTIMATION"); + + /** The value {@code "PROOF_OF_ADDRESS"}. */ + public static final FeatureStep PROOF_OF_ADDRESS = new FeatureStep("PROOF_OF_ADDRESS"); + + /** The value {@code "PHONE_VERIFICATION"}. */ + public static final FeatureStep PHONE_VERIFICATION = new FeatureStep("PHONE_VERIFICATION"); + + /** The value {@code "EMAIL_VERIFICATION"}. */ + public static final FeatureStep EMAIL_VERIFICATION = new FeatureStep("EMAIL_VERIFICATION"); + + /** The value {@code "FACE_SEARCH"}. */ + public static final FeatureStep FACE_SEARCH = new FeatureStep("FACE_SEARCH"); + + /** The value {@code "DATABASE_VALIDATION"}. */ + public static final FeatureStep DATABASE_VALIDATION = new FeatureStep("DATABASE_VALIDATION"); + + /** The value {@code "QUESTIONNAIRE"}. */ + public static final FeatureStep QUESTIONNAIRE = new FeatureStep("QUESTIONNAIRE"); + + /** The value {@code "KYB_REGISTRY"}. */ + public static final FeatureStep KYB_REGISTRY = new FeatureStep("KYB_REGISTRY"); + + /** The value {@code "KYB_DOCUMENTS"}. */ + public static final FeatureStep KYB_DOCUMENTS = new FeatureStep("KYB_DOCUMENTS"); + + /** The value {@code "KYB_KEY_PEOPLE"}. */ + public static final FeatureStep KYB_KEY_PEOPLE = new FeatureStep("KYB_KEY_PEOPLE"); + + /** The value {@code "DOCUMENT_AI"}. */ + public static final FeatureStep DOCUMENT_AI = new FeatureStep("DOCUMENT_AI"); + + /** The value {@code "GEOLOCATION"}. */ + public static final FeatureStep GEOLOCATION = new FeatureStep("GEOLOCATION"); + + private static final List KNOWN_VALUES = List.of(OCR, OCR_BACK, NFC, AML, FACE, LIVENESS, FACE_MATCH, IP_ANALYSIS, AGE_ESTIMATION, PROOF_OF_ADDRESS, PHONE_VERIFICATION, EMAIL_VERIFICATION, FACE_SEARCH, DATABASE_VALIDATION, QUESTIONNAIRE, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE, DOCUMENT_AI, GEOLOCATION); + + private FeatureStep(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static FeatureStep of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, FeatureStep::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionSignDocument.java b/src/main/java/me/didit/types/SessionSignDocument.java new file mode 100644 index 0000000..04f415a --- /dev/null +++ b/src/main/java/me/didit/types/SessionSignDocument.java @@ -0,0 +1,161 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class SessionSignDocument extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("title", "file_name", "content_base64", "fields"); + + private SessionSignDocument(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionSignDocument} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionSignDocument fromJson(JsonNode json) { + return new SessionSignDocument(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Title shown to the signer. Defaults to file_name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional title() { + return Json.optional(json, "title", Conv.STRING); + } + + /** + * File name, ending in .pdf or .docx. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String fileName() { + return Json.required(json, "file_name", Conv.STRING); + } + + /** + * The file, base64-encoded. A PDF can have up to 20 MB and 100 pages, without a password, scripts or attached files. A Word file (.docx) can have up to 20 MB and is converted to PDF while the request runs, in at most 25 seconds; the PDF then has the same limits. Up to 5 Word files per session. A Word file with macros, tracked changes, links to external content or embedded objects is refused with signature_document_not_supported, and one that can't be converted returns signature_document_conversion_failed, each with a reason. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String contentBase64() { + return Json.required(json, "content_base64", Conv.STRING); + } + + /** + * Optional fields to place on this document, up to 200. Pages and positions refer to the PDF, or to the converted PDF for a Word file. Without fields, the signer reads the document and agrees to it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> fields() { + return Json.optional(json, "fields", Conv.list(Conv.model(SignatureField::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionSignDocument}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionSignDocument from) { + super(from); + } + + /** + * Title shown to the signer. Defaults to file_name. + * + * @param title the value + * @return this builder + */ + public Builder title(String title) { + return set("title", Conv.STRING, title); + } + + /** + * File name, ending in .pdf or .docx. + * + * @param fileName the value + * @return this builder + */ + public Builder fileName(String fileName) { + return set("file_name", Conv.STRING, fileName); + } + + /** + * The file, base64-encoded. A PDF can have up to 20 MB and 100 pages, without a password, scripts or attached files. A Word file (.docx) can have up to 20 MB and is converted to PDF while the request runs, in at most 25 seconds; the PDF then has the same limits. Up to 5 Word files per session. A Word file with macros, tracked changes, links to external content or embedded objects is refused with signature_document_not_supported, and one that can't be converted returns signature_document_conversion_failed, each with a reason. + * + * @param contentBase64 the value + * @return this builder + */ + public Builder contentBase64(String contentBase64) { + return set("content_base64", Conv.STRING, contentBase64); + } + + /** + * Optional fields to place on this document, up to 200. Pages and positions refer to the PDF, or to the converted PDF for a Word file. Without fields, the signer reads the document and agrees to it. + * + * @param fields the value + * @return this builder + */ + public Builder fields(List fields) { + return set("fields", Conv.list(Conv.model(SignatureField::fromJson)), fields); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionSignDocument} + * @throws IllegalStateException when a required field is missing + */ + public SessionSignDocument build() { + require("file_name", false); + require("content_base64", false); + return new SessionSignDocument(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionStatusUpdatedEvent.java b/src/main/java/me/didit/types/SessionStatusUpdatedEvent.java new file mode 100644 index 0000000..80f423a --- /dev/null +++ b/src/main/java/me/didit/types/SessionStatusUpdatedEvent.java @@ -0,0 +1,513 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * A verification session changed status. decision is sent only for Approved, Declined, In Review and Abandoned; it has the shape of GET /v3/session/{sessionId}/decision/ for destinations on webhook version 3 (the default), and the older reduced shape for version 1 or 2 destinations. Business sessions always receive the version 3 business decision. + */ +public final class SessionStatusUpdatedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "session_id", "business_session_id", "session_kind", "status", "trigger", "workflow_id", "workflow_version", "vendor_data", "vendor_business_id", "metadata", "decision", "resubmit_info", "sandbox_scenario", "webhook_type"); + + private SessionStatusUpdatedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionStatusUpdatedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionStatusUpdatedEvent fromJson(JsonNode json) { + return new SessionStatusUpdatedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * The verification session. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Business (KYB) sessions only: the same value as session_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional businessSessionId() { + return Json.optional(json, "business_session_id", Conv.STRING); + } + + /** + * Business (KYB) sessions only. User (KYC) session events carry no top-level session_kind; read decision.session_kind instead. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionKind() { + return Json.optional(json, "session_kind", Conv.enumeration(SessionWebhookEventData.SessionKind::of)); + } + + /** + * Session status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionWebhookEventData.Status status() { + return Json.required(json, "status", Conv.enumeration(SessionWebhookEventData.Status::of)); + } + + /** + * Present when the event was not produced by the verification flow itself, for example manual_review, manual_step_update, ongoing_monitoring, webhook_reconciliation, manual_webhook_recovery, kyb_registry_change or a console_* edit. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional trigger() { + return Json.optional(json, "trigger", Conv.STRING); + } + + /** + * The session's workflow. Omitted when the session has none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowId() { + return Json.optional(json, "workflow_id", Conv.STRING); + } + + /** + * Version of the workflow the session ran. Sent together with workflow_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowVersion() { + return Json.optional(json, "workflow_version", Conv.LONG); + } + + /** + * Your vendor_data for the session. Omitted when empty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * Business (KYB) sessions only: the business the session belongs to. Omitted when none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorBusinessId() { + return Json.optional(json, "vendor_business_id", Conv.STRING); + } + + /** + * Your session metadata. Omitted when empty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> metadata() { + return Json.optional(json, "metadata", Conv.OBJECT); + } + + /** + * The decision field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decision() { + return Json.optional(json, "decision", Conv.model(SessionDecision::fromJson)); + } + + /** + * Only when status is Resubmitted: the steps the user must redo and why. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional resubmitInfo() { + return Json.optional(json, "resubmit_info", Conv.model(SessionWebhookEventData.ResubmitInfo::fromJson)); + } + + /** + * The simulated outcome armed on a sandbox session, or null. Always null for live sessions. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sandboxScenario() { + return Json.optional(json, "sandbox_scenario", Conv.STRING); + } + + /** + * Always status.updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionStatusUpdatedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(SessionStatusUpdatedEvent.WebhookType::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionStatusUpdatedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionStatusUpdatedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * The verification session. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Business (KYB) sessions only: the same value as session_id. + * + * @param businessSessionId the value + * @return this builder + */ + public Builder businessSessionId(String businessSessionId) { + return set("business_session_id", Conv.STRING, businessSessionId); + } + + /** + * Business (KYB) sessions only. User (KYC) session events carry no top-level session_kind; read decision.session_kind instead. + * + * @param sessionKind the value + * @return this builder + */ + public Builder sessionKind(SessionWebhookEventData.SessionKind sessionKind) { + return set("session_kind", Conv.enumeration(SessionWebhookEventData.SessionKind::of), sessionKind); + } + + /** + * Session status after the change. + * + * @param status the value + * @return this builder + */ + public Builder status(SessionWebhookEventData.Status status) { + return set("status", Conv.enumeration(SessionWebhookEventData.Status::of), status); + } + + /** + * Present when the event was not produced by the verification flow itself, for example manual_review, manual_step_update, ongoing_monitoring, webhook_reconciliation, manual_webhook_recovery, kyb_registry_change or a console_* edit. + * + * @param trigger the value + * @return this builder + */ + public Builder trigger(String trigger) { + return set("trigger", Conv.STRING, trigger); + } + + /** + * The session's workflow. Omitted when the session has none. + * + * @param workflowId the value + * @return this builder + */ + public Builder workflowId(String workflowId) { + return set("workflow_id", Conv.STRING, workflowId); + } + + /** + * Version of the workflow the session ran. Sent together with workflow_id. + * + * @param workflowVersion the value + * @return this builder + */ + public Builder workflowVersion(long workflowVersion) { + return set("workflow_version", Conv.LONG, workflowVersion); + } + + /** + * Your vendor_data for the session. Omitted when empty. + * + * @param vendorData the value + * @return this builder + */ + public Builder vendorData(String vendorData) { + return set("vendor_data", Conv.STRING, vendorData); + } + + /** + * Business (KYB) sessions only: the business the session belongs to. Omitted when none. + * + * @param vendorBusinessId the value + * @return this builder + */ + public Builder vendorBusinessId(String vendorBusinessId) { + return set("vendor_business_id", Conv.STRING, vendorBusinessId); + } + + /** + * Your session metadata. Omitted when empty. + * + * @param metadata the value + * @return this builder + */ + public Builder metadata(Map metadata) { + return setAny("metadata", metadata); + } + + /** + * The decision field. + * + * @param decision the value + * @return this builder + */ + public Builder decision(SessionDecision decision) { + return set("decision", Conv.model(SessionDecision::fromJson), decision); + } + + /** + * Only when status is Resubmitted: the steps the user must redo and why. + * + * @param resubmitInfo the value + * @return this builder + */ + public Builder resubmitInfo(SessionWebhookEventData.ResubmitInfo resubmitInfo) { + return set("resubmit_info", Conv.model(SessionWebhookEventData.ResubmitInfo::fromJson), resubmitInfo); + } + + /** + * The simulated outcome armed on a sandbox session, or null. Always null for live sessions. + * + * @param sandboxScenario the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sandboxScenario(@Nullable String sandboxScenario) { + return setNullable("sandbox_scenario", Conv.STRING, sandboxScenario); + } + + /** + * Always status.updated. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(SessionStatusUpdatedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(SessionStatusUpdatedEvent.WebhookType::of), webhookType); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionStatusUpdatedEvent} + * @throws IllegalStateException when a required field is missing + */ + public SessionStatusUpdatedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("session_id", false); + require("status", false); + require("sandbox_scenario", true); + require("webhook_type", false); + return new SessionStatusUpdatedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always status.updated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "status.updated"}. */ + public static final WebhookType STATUS_UPDATED = new WebhookType("status.updated"); + + private static final List KNOWN_VALUES = List.of(STATUS_UPDATED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionWebhookDelivery.java b/src/main/java/me/didit/types/SessionWebhookDelivery.java new file mode 100644 index 0000000..e5a6838 --- /dev/null +++ b/src/main/java/me/didit/types/SessionWebhookDelivery.java @@ -0,0 +1,273 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** + * One webhook delivery attempt recorded for a verification session, with the exact request headers and body Didit sent. + */ +public final class SessionWebhookDelivery extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "session_id", "request_url", "response_status", "status", "webhook_type", "request_header", "request_body", "created_at"); + + private SessionWebhookDelivery(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionWebhookDelivery} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionWebhookDelivery fromJson(JsonNode json) { + return new SessionWebhookDelivery(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Delivery ID. Use it as {webhook_uuid} to get or resend this delivery. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * The session the delivery belongs to (the session in the path). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * URL the delivery was sent to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional requestUrl() { + return Json.optional(json, "request_url", Conv.STRING); + } + + /** + * HTTP status your endpoint returned. null while the delivery is queued and not yet attempted. When no HTTP answer was received Didit records a status of its own: 504 when your endpoint did not answer within 5 seconds, 503 for a connection failure, 400 when the URL was refused as unsafe (for example a private or internal address), and 404 for any other request error. 400 or above counts as a failed delivery. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional responseStatus() { + return Json.optional(json, "response_status", Conv.LONG); + } + + /** + * Session status when the event was generated, for example In Progress, Approved or Declined. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEvent webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(WebhookEvent::of)); + } + + /** + * JSON text (a string) of the request headers Didit sent: Content-Type, X-Signature, X-Signature-V2, X-Signature-Simple, X-Timestamp and User-Agent. Parse it with a JSON parser. It holds signatures, never the signing secret. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String requestHeader() { + return Json.required(json, "request_header", Conv.STRING); + } + + /** + * JSON text (a string) of the exact payload Didit sent, keys sorted, including event_id and timestamp. Parse it with a JSON parser; see Payload shape. For a scoped key without media: read, media URLs inside it are null; without sessions: write, session tokens and hosted verification links are null. A body changed that way no longer matches its signatures. Didit does not record the response body your endpoint returned. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String requestBody() { + return Json.required(json, "request_body", Conv.STRING); + } + + /** + * When the delivery was created. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionWebhookDelivery}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionWebhookDelivery from) { + super(from); + } + + /** + * Delivery ID. Use it as {webhook_uuid} to get or resend this delivery. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The session the delivery belongs to (the session in the path). + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * URL the delivery was sent to. + * + * @param requestUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder requestUrl(@Nullable String requestUrl) { + return setNullable("request_url", Conv.STRING, requestUrl); + } + + /** + * HTTP status your endpoint returned. null while the delivery is queued and not yet attempted. When no HTTP answer was received Didit records a status of its own: 504 when your endpoint did not answer within 5 seconds, 503 for a connection failure, 400 when the URL was refused as unsafe (for example a private or internal address), and 404 for any other request error. 400 or above counts as a failed delivery. + * + * @param responseStatus the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder responseStatus(@Nullable Long responseStatus) { + return setNullable("response_status", Conv.LONG, responseStatus); + } + + /** + * Session status when the event was generated, for example In Progress, Approved or Declined. + * + * @param status the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder status(@Nullable String status) { + return setNullable("status", Conv.STRING, status); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(WebhookEvent webhookType) { + return set("webhook_type", Conv.enumeration(WebhookEvent::of), webhookType); + } + + /** + * JSON text (a string) of the request headers Didit sent: Content-Type, X-Signature, X-Signature-V2, X-Signature-Simple, X-Timestamp and User-Agent. Parse it with a JSON parser. It holds signatures, never the signing secret. + * + * @param requestHeader the value + * @return this builder + */ + public Builder requestHeader(String requestHeader) { + return set("request_header", Conv.STRING, requestHeader); + } + + /** + * JSON text (a string) of the exact payload Didit sent, keys sorted, including event_id and timestamp. Parse it with a JSON parser; see Payload shape. For a scoped key without media: read, media URLs inside it are null; without sessions: write, session tokens and hosted verification links are null. A body changed that way no longer matches its signatures. Didit does not record the response body your endpoint returned. + * + * @param requestBody the value + * @return this builder + */ + public Builder requestBody(String requestBody) { + return set("request_body", Conv.STRING, requestBody); + } + + /** + * When the delivery was created. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionWebhookDelivery} + * @throws IllegalStateException when a required field is missing + */ + public SessionWebhookDelivery build() { + require("uuid", false); + require("session_id", false); + require("request_url", true); + require("response_status", true); + require("status", true); + require("webhook_type", false); + require("request_header", false); + require("request_body", false); + require("created_at", false); + return new SessionWebhookDelivery(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionWebhookEventData.java b/src/main/java/me/didit/types/SessionWebhookEventData.java new file mode 100644 index 0000000..7a4f8d9 --- /dev/null +++ b/src/main/java/me/didit/types/SessionWebhookEventData.java @@ -0,0 +1,673 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Session fields of status.updated and data.updated. Optional fields are omitted, never sent as null, when they do not apply. Business (KYB) sessions add business_session_id, session_kind and vendor_business_id. + */ +public final class SessionWebhookEventData extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "business_session_id", "session_kind", "status", "trigger", "workflow_id", "workflow_version", "vendor_data", "vendor_business_id", "metadata", "decision", "resubmit_info", "sandbox_scenario"); + + private SessionWebhookEventData(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionWebhookEventData} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionWebhookEventData fromJson(JsonNode json) { + return new SessionWebhookEventData(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The verification session. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Business (KYB) sessions only: the same value as session_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional businessSessionId() { + return Json.optional(json, "business_session_id", Conv.STRING); + } + + /** + * Business (KYB) sessions only. User (KYC) session events carry no top-level session_kind; read decision.session_kind instead. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionKind() { + return Json.optional(json, "session_kind", Conv.enumeration(SessionWebhookEventData.SessionKind::of)); + } + + /** + * Session status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionWebhookEventData.Status status() { + return Json.required(json, "status", Conv.enumeration(SessionWebhookEventData.Status::of)); + } + + /** + * Present when the event was not produced by the verification flow itself, for example manual_review, manual_step_update, ongoing_monitoring, webhook_reconciliation, manual_webhook_recovery, kyb_registry_change or a console_* edit. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional trigger() { + return Json.optional(json, "trigger", Conv.STRING); + } + + /** + * The session's workflow. Omitted when the session has none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowId() { + return Json.optional(json, "workflow_id", Conv.STRING); + } + + /** + * Version of the workflow the session ran. Sent together with workflow_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowVersion() { + return Json.optional(json, "workflow_version", Conv.LONG); + } + + /** + * Your vendor_data for the session. Omitted when empty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * Business (KYB) sessions only: the business the session belongs to. Omitted when none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorBusinessId() { + return Json.optional(json, "vendor_business_id", Conv.STRING); + } + + /** + * Your session metadata. Omitted when empty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> metadata() { + return Json.optional(json, "metadata", Conv.OBJECT); + } + + /** + * The decision field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decision() { + return Json.optional(json, "decision", Conv.model(SessionDecision::fromJson)); + } + + /** + * Only when status is Resubmitted: the steps the user must redo and why. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional resubmitInfo() { + return Json.optional(json, "resubmit_info", Conv.model(SessionWebhookEventData.ResubmitInfo::fromJson)); + } + + /** + * The simulated outcome armed on a sandbox session, or null. Always null for live sessions. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sandboxScenario() { + return Json.optional(json, "sandbox_scenario", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionWebhookEventData}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionWebhookEventData from) { + super(from); + } + + /** + * The verification session. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Business (KYB) sessions only: the same value as session_id. + * + * @param businessSessionId the value + * @return this builder + */ + public Builder businessSessionId(String businessSessionId) { + return set("business_session_id", Conv.STRING, businessSessionId); + } + + /** + * Business (KYB) sessions only. User (KYC) session events carry no top-level session_kind; read decision.session_kind instead. + * + * @param sessionKind the value + * @return this builder + */ + public Builder sessionKind(SessionWebhookEventData.SessionKind sessionKind) { + return set("session_kind", Conv.enumeration(SessionWebhookEventData.SessionKind::of), sessionKind); + } + + /** + * Session status after the change. + * + * @param status the value + * @return this builder + */ + public Builder status(SessionWebhookEventData.Status status) { + return set("status", Conv.enumeration(SessionWebhookEventData.Status::of), status); + } + + /** + * Present when the event was not produced by the verification flow itself, for example manual_review, manual_step_update, ongoing_monitoring, webhook_reconciliation, manual_webhook_recovery, kyb_registry_change or a console_* edit. + * + * @param trigger the value + * @return this builder + */ + public Builder trigger(String trigger) { + return set("trigger", Conv.STRING, trigger); + } + + /** + * The session's workflow. Omitted when the session has none. + * + * @param workflowId the value + * @return this builder + */ + public Builder workflowId(String workflowId) { + return set("workflow_id", Conv.STRING, workflowId); + } + + /** + * Version of the workflow the session ran. Sent together with workflow_id. + * + * @param workflowVersion the value + * @return this builder + */ + public Builder workflowVersion(long workflowVersion) { + return set("workflow_version", Conv.LONG, workflowVersion); + } + + /** + * Your vendor_data for the session. Omitted when empty. + * + * @param vendorData the value + * @return this builder + */ + public Builder vendorData(String vendorData) { + return set("vendor_data", Conv.STRING, vendorData); + } + + /** + * Business (KYB) sessions only: the business the session belongs to. Omitted when none. + * + * @param vendorBusinessId the value + * @return this builder + */ + public Builder vendorBusinessId(String vendorBusinessId) { + return set("vendor_business_id", Conv.STRING, vendorBusinessId); + } + + /** + * Your session metadata. Omitted when empty. + * + * @param metadata the value + * @return this builder + */ + public Builder metadata(Map metadata) { + return setAny("metadata", metadata); + } + + /** + * The decision field. + * + * @param decision the value + * @return this builder + */ + public Builder decision(SessionDecision decision) { + return set("decision", Conv.model(SessionDecision::fromJson), decision); + } + + /** + * Only when status is Resubmitted: the steps the user must redo and why. + * + * @param resubmitInfo the value + * @return this builder + */ + public Builder resubmitInfo(SessionWebhookEventData.ResubmitInfo resubmitInfo) { + return set("resubmit_info", Conv.model(SessionWebhookEventData.ResubmitInfo::fromJson), resubmitInfo); + } + + /** + * The simulated outcome armed on a sandbox session, or null. Always null for live sessions. + * + * @param sandboxScenario the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sandboxScenario(@Nullable String sandboxScenario) { + return setNullable("sandbox_scenario", Conv.STRING, sandboxScenario); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionWebhookEventData} + * @throws IllegalStateException when a required field is missing + */ + public SessionWebhookEventData build() { + require("session_id", false); + require("status", false); + require("sandbox_scenario", true); + return new SessionWebhookEventData(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Business (KYB) sessions only. User (KYC) session events carry no top-level session_kind; read decision.session_kind instead. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SessionKind extends StringEnum { + /** The value {@code "business"}. */ + public static final SessionKind BUSINESS = new SessionKind("business"); + + private static final List KNOWN_VALUES = List.of(BUSINESS); + + private SessionKind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SessionKind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SessionKind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Session status after the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Started"}. */ + public static final Status NOT_STARTED = new Status("Not Started"); + + /** The value {@code "In Progress"}. */ + public static final Status IN_PROGRESS = new Status("In Progress"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Expired"}. */ + public static final Status EXPIRED = new Status("Expired"); + + /** The value {@code "Abandoned"}. */ + public static final Status ABANDONED = new Status("Abandoned"); + + /** The value {@code "Kyc Expired"}. */ + public static final Status KYC_EXPIRED = new Status("Kyc Expired"); + + /** The value {@code "Resubmitted"}. */ + public static final Status RESUBMITTED = new Status("Resubmitted"); + + /** The value {@code "Awaiting User"}. */ + public static final Status AWAITING_USER = new Status("Awaiting User"); + + private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED, KYC_EXPIRED, RESUBMITTED, AWAITING_USER); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Only when status is Resubmitted: the steps the user must redo and why. + */ + public static final class ResubmitInfo extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("nodes_to_resubmit", "reasons"); + + private ResubmitInfo(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ResubmitInfo} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ResubmitInfo fromJson(JsonNode json) { + return new ResubmitInfo(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Workflow steps the user must redo. The same {node_id, feature} entries as nodes_to_resubmit in [Update Status](#patch-/v3/session/-sessionId-/update-status/). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List nodesToResubmit() { + return Json.required(json, "nodes_to_resubmit", Conv.list(Conv.model(SessionWebhookEventData.ResubmitInfo.NodesToResubmit::fromJson))); + } + + /** + * Reason per node_id in nodes_to_resubmit. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map reasons() { + return Json.required(json, "reasons", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ResubmitInfo}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ResubmitInfo from) { + super(from); + } + + /** + * Workflow steps the user must redo. The same {node_id, feature} entries as nodes_to_resubmit in [Update Status](#patch-/v3/session/-sessionId-/update-status/). + * + * @param nodesToResubmit the value + * @return this builder + */ + public Builder nodesToResubmit(List nodesToResubmit) { + return set("nodes_to_resubmit", Conv.list(Conv.model(SessionWebhookEventData.ResubmitInfo.NodesToResubmit::fromJson)), nodesToResubmit); + } + + /** + * Reason per node_id in nodes_to_resubmit. + * + * @param reasons the value + * @return this builder + */ + public Builder reasons(Map reasons) { + return setAny("reasons", reasons); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ResubmitInfo} + * @throws IllegalStateException when a required field is missing + */ + public ResubmitInfo build() { + require("nodes_to_resubmit", false); + require("reasons", false); + return new ResubmitInfo(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class NodesToResubmit extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "feature"); + + private NodesToResubmit(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code NodesToResubmit} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static NodesToResubmit fromJson(JsonNode json) { + return new NodesToResubmit(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Node identifier as it appears in the session's workflow definition (for example feature_ocr, feature_liveness). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String nodeId() { + return Json.required(json, "node_id", Conv.STRING); + } + + /** + * Feature type of the node, for example OCR, LIVENESS or PROOF_OF_ADDRESS. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String feature() { + return Json.required(json, "feature", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link NodesToResubmit}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(NodesToResubmit from) { + super(from); + } + + /** + * Node identifier as it appears in the session's workflow definition (for example feature_ocr, feature_liveness). + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * Feature type of the node, for example OCR, LIVENESS or PROOF_OF_ADDRESS. + * + * @param feature the value + * @return this builder + */ + public Builder feature(String feature) { + return set("feature", Conv.STRING, feature); + } + + /** + * Builds the value. + * + * @return a new immutable {@code NodesToResubmit} + * @throws IllegalStateException when a required field is missing + */ + public NodesToResubmit build() { + require("node_id", false); + require("feature", false); + return new NodesToResubmit(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } +} diff --git a/src/main/java/me/didit/types/SessionsAddImagesParams.java b/src/main/java/me/didit/types/SessionsAddImagesParams.java new file mode 100644 index 0000000..5469a4b --- /dev/null +++ b/src/main/java/me/didit/types/SessionsAddImagesParams.java @@ -0,0 +1,196 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code sessions.add_images}. */ +public final class SessionsAddImagesParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("file", "model_type", "file_password"); + + private SessionsAddImagesParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsAddImagesParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsAddImagesParams fromJson(JsonNode json) { + return new SessionsAddImagesParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The image or PDF. Allowed extensions: jpg, jpeg, png, webp, tiff, pdf. At most 15 MB as uploaded; the file is then compressed and rejected with 400 if it is still larger than 2 MB (2,097,152 bytes). Empty files are rejected. + * + * @return the file to upload, if set + */ + public Optional file() { + return file("file"); + } + + /** + * kyc adds the file to the ID verification result, poa to the Proof of Address result. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsAddImagesParams.ModelType modelType() { + return Json.required(json, "model_type", Conv.enumeration(SessionsAddImagesParams.ModelType::of)); + } + + /** + * Password of an encrypted PDF. Without it, or with a wrong one, an encrypted PDF returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filePassword() { + return Json.optional(json, "file_password", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsAddImagesParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsAddImagesParams from) { + super(from); + } + + /** + * The image or PDF. Allowed extensions: jpg, jpeg, png, webp, tiff, pdf. At most 15 MB as uploaded; the file is then compressed and rejected with 400 if it is still larger than 2 MB (2,097,152 bytes). Empty files are rejected. + * + * @param file the value + * @return this builder + */ + public Builder file(FileUpload file) { + return setFile("file", file); + } + + /** + * Uploads the file at {@code path} as {@code file}. + * + * @param path the file to read when the request is sent + * @return this builder + */ + public Builder file(Path path) { + return setFile("file", FileUpload.of(path)); + } + + /** + * kyc adds the file to the ID verification result, poa to the Proof of Address result. + * + * @param modelType the value + * @return this builder + */ + public Builder modelType(SessionsAddImagesParams.ModelType modelType) { + return set("model_type", Conv.enumeration(SessionsAddImagesParams.ModelType::of), modelType); + } + + /** + * Password of an encrypted PDF. Without it, or with a wrong one, an encrypted PDF returns 400. + * + * @param filePassword the value + * @return this builder + */ + public Builder filePassword(String filePassword) { + return set("file_password", Conv.STRING, filePassword); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsAddImagesParams} + * @throws IllegalStateException when a required field is missing + */ + public SessionsAddImagesParams build() { + requireFile("file"); + require("model_type", false); + return new SessionsAddImagesParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * kyc adds the file to the ID verification result, poa to the Proof of Address result. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ModelType extends StringEnum { + /** The value {@code "kyc"}. */ + public static final ModelType KYC = new ModelType("kyc"); + + /** The value {@code "poa"}. */ + public static final ModelType POA = new ModelType("poa"); + + private static final List KNOWN_VALUES = List.of(KYC, POA); + + private ModelType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ModelType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ModelType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionsAddImagesResponse.java b/src/main/java/me/didit/types/SessionsAddImagesResponse.java new file mode 100644 index 0000000..e8dd687 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsAddImagesResponse.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class SessionsAddImagesResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("extra_files"); + + private SessionsAddImagesResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsAddImagesResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsAddImagesResponse fromJson(JsonNode json) { + return new SessionsAddImagesResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Short-lived presigned links to all extra files of the result named by model_type, oldest first. Each link is null for a scoped API key without media: read. Read fresh links from the session decision later. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List extraFiles() { + return Json.required(json, "extra_files", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsAddImagesResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsAddImagesResponse from) { + super(from); + } + + /** + * Short-lived presigned links to all extra files of the result named by model_type, oldest first. Each link is null for a scoped API key without media: read. Read fresh links from the session decision later. + * + * @param extraFiles the value + * @return this builder + */ + public Builder extraFiles(List extraFiles) { + return set("extra_files", Conv.list(Conv.STRING), extraFiles); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsAddImagesResponse} + * @throws IllegalStateException when a required field is missing + */ + public SessionsAddImagesResponse build() { + require("extra_files", false); + return new SessionsAddImagesResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsBatchDeleteParams.java b/src/main/java/me/didit/types/SessionsBatchDeleteParams.java index 437ea3c..2b76dff 100644 --- a/src/main/java/me/didit/types/SessionsBatchDeleteParams.java +++ b/src/main/java/me/didit/types/SessionsBatchDeleteParams.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; import java.time.OffsetDateTime; import java.util.List; import java.util.Map; @@ -12,12 +13,13 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; +import me.didit.core.JsonUnion; import me.didit.core.StringEnum; import org.jspecify.annotations.Nullable; /** Parameters for {@code sessions.batch_delete}. */ public final class SessionsBatchDeleteParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("session_numbers", "delete_all", "retain_face_embeddings", "face_retention_days", "face_retention_deadline", "deletion_instruction", "instruction_id"); + private static final Set KNOWN_FIELDS = Set.of("session_numbers", "delete_all", "retain_face_embeddings", "face_retention_days", "face_retention_deadline", "deletion_instruction", "instruction_id", "filter_status", "filter_vendor_data", "filter_search", "filter_workflow_id", "filter_date_from", "filter_date_to", "filter_country", "filter_document_type", "filter_document_name", "filter_region", "filter_didit_internal_id", "filter_session_type", "filter_api_service", "filter_screened_full_name", "filter_warning", "filter_status_initialized_as_in_review", "filter_is_kyc_to_be_reviewed", "filter_euuid", "filter_organization_name"); private SessionsBatchDeleteParams(ObjectNode json, Map> files) { super(json, files); @@ -63,7 +65,7 @@ public Optional> sessionNumbers() { } /** - * When true, every remaining KYC session in the calling application is deleted and session_numbers is ignored. More than 20 matching sessions run as a deletion job (202) that covers only the sessions created before the request. Defaults to false. Irreversible; there is no confirmation step. + * When true, every remaining KYC session in the calling application is deleted and session_numbers is ignored, unless you narrow the selection with filter_* keys. More than 20 matching sessions run as a deletion job (202) that covers only the sessions created before the request. Defaults to false. Irreversible; there is no confirmation step. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -122,6 +124,196 @@ public Optional instructionId() { return Json.optional(json, "instruction_id", Conv.STRING); } + /** + * Narrows a delete_all to the sessions that match the status filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterStatus() { + return Json.optional(json, "filter_status", Conv.union(SessionsBatchDeleteParams.FilterStatus::fromJson)); + } + + /** + * Narrows a delete_all to the sessions that match the vendor_data filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated text, so several values are not matched as alternatives. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterVendorData() { + return Json.optional(json, "filter_vendor_data", Conv.STRING); + } + + /** + * Narrows a delete_all to the sessions that match the search filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated text, so several values are not matched as alternatives. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterSearch() { + return Json.optional(json, "filter_search", Conv.STRING); + } + + /** + * Narrows a delete_all to the sessions that match the workflow_id filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterWorkflowId() { + return Json.optional(json, "filter_workflow_id", Conv.union(SessionsBatchDeleteParams.FilterWorkflowId::fromJson)); + } + + /** + * Narrows a delete_all to the sessions that match the date_from filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterDateFrom() { + return Json.optional(json, "filter_date_from", Conv.DATE); + } + + /** + * Narrows a delete_all to the sessions that match the date_to filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterDateTo() { + return Json.optional(json, "filter_date_to", Conv.DATE); + } + + /** + * Narrows a delete_all to the sessions that match the country filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterCountry() { + return Json.optional(json, "filter_country", Conv.union(SessionsBatchDeleteParams.FilterCountry::fromJson)); + } + + /** + * Narrows a delete_all to the sessions that match the document_type filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterDocumentType() { + return Json.optional(json, "filter_document_type", Conv.union(SessionsBatchDeleteParams.FilterDocumentType::fromJson)); + } + + /** + * Narrows a delete_all to the sessions that match the document_name filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterDocumentName() { + return Json.optional(json, "filter_document_name", Conv.union(SessionsBatchDeleteParams.FilterDocumentName::fromJson)); + } + + /** + * Narrows a delete_all to the sessions that match the region filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterRegion() { + return Json.optional(json, "filter_region", Conv.union(SessionsBatchDeleteParams.FilterRegion::fromJson)); + } + + /** + * Narrows a delete_all to the sessions that match the didit_internal_id filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterDiditInternalId() { + return Json.optional(json, "filter_didit_internal_id", Conv.STRING); + } + + /** + * Narrows a delete_all to the sessions that match the session_type filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterSessionType() { + return Json.optional(json, "filter_session_type", Conv.union(SessionsBatchDeleteParams.FilterSessionType::fromJson)); + } + + /** + * Narrows a delete_all to the sessions that match the api_service filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterApiService() { + return Json.optional(json, "filter_api_service", Conv.STRING); + } + + /** + * Narrows a delete_all to the sessions that match the screened_full_name filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated text, so several values are not matched as alternatives. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterScreenedFullName() { + return Json.optional(json, "filter_screened_full_name", Conv.STRING); + } + + /** + * Narrows a delete_all to the sessions that match the warning filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterWarning() { + return Json.optional(json, "filter_warning", Conv.union(SessionsBatchDeleteParams.FilterWarning::fromJson)); + } + + /** + * Narrows a delete_all to the sessions that match the status_initialized_as_in_review filter of [List Sessions](/sessions-api/list-sessions), with the same values. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterStatusInitializedAsInReview() { + return Json.optional(json, "filter_status_initialized_as_in_review", Conv.BOOLEAN); + } + + /** + * Narrows a delete_all to the sessions that match the is_kyc_to_be_reviewed filter of [List Sessions](/sessions-api/list-sessions), with the same values. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterIsKycToBeReviewed() { + return Json.optional(json, "filter_is_kyc_to_be_reviewed", Conv.BOOLEAN); + } + + /** + * Narrows a delete_all to the sessions that match the euuid filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterEuuid() { + return Json.optional(json, "filter_euuid", Conv.STRING); + } + + /** + * Narrows a delete_all to the sessions that match the organization_name filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. With an API key it can only match your own organization's name, so it selects all of your sessions or none. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filterOrganizationName() { + return Json.optional(json, "filter_organization_name", Conv.union(SessionsBatchDeleteParams.FilterOrganizationName::fromJson)); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -146,7 +338,7 @@ public Builder sessionNumbers(List sessionNumbers) { } /** - * When true, every remaining KYC session in the calling application is deleted and session_numbers is ignored. More than 20 matching sessions run as a deletion job (202) that covers only the sessions created before the request. Defaults to false. Irreversible; there is no confirmation step. + * When true, every remaining KYC session in the calling application is deleted and session_numbers is ignored, unless you narrow the selection with filter_* keys. More than 20 matching sessions run as a deletion job (202) that covers only the sessions created before the request. Defaults to false. Irreversible; there is no confirmation step. * * @param deleteAll the value * @return this builder @@ -205,6 +397,196 @@ public Builder instructionId(String instructionId) { return set("instruction_id", Conv.STRING, instructionId); } + /** + * Narrows a delete_all to the sessions that match the status filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterStatus the value + * @return this builder + */ + public Builder filterStatus(SessionsBatchDeleteParams.FilterStatus filterStatus) { + return set("filter_status", Conv.union(SessionsBatchDeleteParams.FilterStatus::fromJson), filterStatus); + } + + /** + * Narrows a delete_all to the sessions that match the vendor_data filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated text, so several values are not matched as alternatives. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterVendorData the value + * @return this builder + */ + public Builder filterVendorData(String filterVendorData) { + return set("filter_vendor_data", Conv.STRING, filterVendorData); + } + + /** + * Narrows a delete_all to the sessions that match the search filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated text, so several values are not matched as alternatives. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterSearch the value + * @return this builder + */ + public Builder filterSearch(String filterSearch) { + return set("filter_search", Conv.STRING, filterSearch); + } + + /** + * Narrows a delete_all to the sessions that match the workflow_id filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterWorkflowId the value + * @return this builder + */ + public Builder filterWorkflowId(SessionsBatchDeleteParams.FilterWorkflowId filterWorkflowId) { + return set("filter_workflow_id", Conv.union(SessionsBatchDeleteParams.FilterWorkflowId::fromJson), filterWorkflowId); + } + + /** + * Narrows a delete_all to the sessions that match the date_from filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterDateFrom the value + * @return this builder + */ + public Builder filterDateFrom(LocalDate filterDateFrom) { + return set("filter_date_from", Conv.DATE, filterDateFrom); + } + + /** + * Narrows a delete_all to the sessions that match the date_to filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterDateTo the value + * @return this builder + */ + public Builder filterDateTo(LocalDate filterDateTo) { + return set("filter_date_to", Conv.DATE, filterDateTo); + } + + /** + * Narrows a delete_all to the sessions that match the country filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterCountry the value + * @return this builder + */ + public Builder filterCountry(SessionsBatchDeleteParams.FilterCountry filterCountry) { + return set("filter_country", Conv.union(SessionsBatchDeleteParams.FilterCountry::fromJson), filterCountry); + } + + /** + * Narrows a delete_all to the sessions that match the document_type filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterDocumentType the value + * @return this builder + */ + public Builder filterDocumentType(SessionsBatchDeleteParams.FilterDocumentType filterDocumentType) { + return set("filter_document_type", Conv.union(SessionsBatchDeleteParams.FilterDocumentType::fromJson), filterDocumentType); + } + + /** + * Narrows a delete_all to the sessions that match the document_name filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterDocumentName the value + * @return this builder + */ + public Builder filterDocumentName(SessionsBatchDeleteParams.FilterDocumentName filterDocumentName) { + return set("filter_document_name", Conv.union(SessionsBatchDeleteParams.FilterDocumentName::fromJson), filterDocumentName); + } + + /** + * Narrows a delete_all to the sessions that match the region filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterRegion the value + * @return this builder + */ + public Builder filterRegion(SessionsBatchDeleteParams.FilterRegion filterRegion) { + return set("filter_region", Conv.union(SessionsBatchDeleteParams.FilterRegion::fromJson), filterRegion); + } + + /** + * Narrows a delete_all to the sessions that match the didit_internal_id filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterDiditInternalId the value + * @return this builder + */ + public Builder filterDiditInternalId(String filterDiditInternalId) { + return set("filter_didit_internal_id", Conv.STRING, filterDiditInternalId); + } + + /** + * Narrows a delete_all to the sessions that match the session_type filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterSessionType the value + * @return this builder + */ + public Builder filterSessionType(SessionsBatchDeleteParams.FilterSessionType filterSessionType) { + return set("filter_session_type", Conv.union(SessionsBatchDeleteParams.FilterSessionType::fromJson), filterSessionType); + } + + /** + * Narrows a delete_all to the sessions that match the api_service filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterApiService the value + * @return this builder + */ + public Builder filterApiService(String filterApiService) { + return set("filter_api_service", Conv.STRING, filterApiService); + } + + /** + * Narrows a delete_all to the sessions that match the screened_full_name filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated text, so several values are not matched as alternatives. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterScreenedFullName the value + * @return this builder + */ + public Builder filterScreenedFullName(String filterScreenedFullName) { + return set("filter_screened_full_name", Conv.STRING, filterScreenedFullName); + } + + /** + * Narrows a delete_all to the sessions that match the warning filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterWarning the value + * @return this builder + */ + public Builder filterWarning(SessionsBatchDeleteParams.FilterWarning filterWarning) { + return set("filter_warning", Conv.union(SessionsBatchDeleteParams.FilterWarning::fromJson), filterWarning); + } + + /** + * Narrows a delete_all to the sessions that match the status_initialized_as_in_review filter of [List Sessions](/sessions-api/list-sessions), with the same values. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterStatusInitializedAsInReview the value + * @return this builder + */ + public Builder filterStatusInitializedAsInReview(boolean filterStatusInitializedAsInReview) { + return set("filter_status_initialized_as_in_review", Conv.BOOLEAN, filterStatusInitializedAsInReview); + } + + /** + * Narrows a delete_all to the sessions that match the is_kyc_to_be_reviewed filter of [List Sessions](/sessions-api/list-sessions), with the same values. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterIsKycToBeReviewed the value + * @return this builder + */ + public Builder filterIsKycToBeReviewed(boolean filterIsKycToBeReviewed) { + return set("filter_is_kyc_to_be_reviewed", Conv.BOOLEAN, filterIsKycToBeReviewed); + } + + /** + * Narrows a delete_all to the sessions that match the euuid filter of [List Sessions](/sessions-api/list-sessions), with the same value. One value, not a list: an array is joined into one comma-separated value, so several values are refused with 400. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterEuuid the value + * @return this builder + */ + public Builder filterEuuid(String filterEuuid) { + return set("filter_euuid", Conv.STRING, filterEuuid); + } + + /** + * Narrows a delete_all to the sessions that match the organization_name filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. With an API key it can only match your own organization's name, so it selects all of your sessions or none. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + * @param filterOrganizationName the value + * @return this builder + */ + public Builder filterOrganizationName(SessionsBatchDeleteParams.FilterOrganizationName filterOrganizationName) { + return set("filter_organization_name", Conv.union(SessionsBatchDeleteParams.FilterOrganizationName::fromJson), filterOrganizationName); + } + /** * Builds the value. * @@ -262,4 +644,706 @@ public boolean isKnown() { return KNOWN_VALUES.contains(this); } } + + /** + * Narrows a delete_all to the sessions that match the status filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + *

One of: {@code String}, {@code List}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FilterStatus extends JsonUnion { + private FilterStatus(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FilterStatus} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FilterStatus fromJson(JsonNode json) { + return new FilterStatus(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FilterStatus ofString(String value) { + return new FilterStatus(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code List} value. + * + * @param value the value + * @return the union + */ + public static FilterStatus ofList(List value) { + return new FilterStatus(Conv.list(Conv.STRING).write(value)); + } + + /** + * Whether this value is a {@code List}. + * + * @return true when it matches + */ + public boolean isList() { + return json.isArray(); + } + + /** + * Reads this value as a {@code List}. + * + * @return the value, or empty when {@link #isList()} is false + */ + public Optional> asList() { + return isList() ? Optional.of(Conv.list(Conv.STRING).read(json)) : Optional.empty(); + } + } + + /** + * Narrows a delete_all to the sessions that match the workflow_id filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + *

One of: {@code String}, {@code List}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FilterWorkflowId extends JsonUnion { + private FilterWorkflowId(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FilterWorkflowId} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FilterWorkflowId fromJson(JsonNode json) { + return new FilterWorkflowId(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FilterWorkflowId ofString(String value) { + return new FilterWorkflowId(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code List} value. + * + * @param value the value + * @return the union + */ + public static FilterWorkflowId ofList(List value) { + return new FilterWorkflowId(Conv.list(Conv.STRING).write(value)); + } + + /** + * Whether this value is a {@code List}. + * + * @return true when it matches + */ + public boolean isList() { + return json.isArray(); + } + + /** + * Reads this value as a {@code List}. + * + * @return the value, or empty when {@link #isList()} is false + */ + public Optional> asList() { + return isList() ? Optional.of(Conv.list(Conv.STRING).read(json)) : Optional.empty(); + } + } + + /** + * Narrows a delete_all to the sessions that match the country filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + *

One of: {@code String}, {@code List}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FilterCountry extends JsonUnion { + private FilterCountry(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FilterCountry} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FilterCountry fromJson(JsonNode json) { + return new FilterCountry(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FilterCountry ofString(String value) { + return new FilterCountry(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code List} value. + * + * @param value the value + * @return the union + */ + public static FilterCountry ofList(List value) { + return new FilterCountry(Conv.list(Conv.STRING).write(value)); + } + + /** + * Whether this value is a {@code List}. + * + * @return true when it matches + */ + public boolean isList() { + return json.isArray(); + } + + /** + * Reads this value as a {@code List}. + * + * @return the value, or empty when {@link #isList()} is false + */ + public Optional> asList() { + return isList() ? Optional.of(Conv.list(Conv.STRING).read(json)) : Optional.empty(); + } + } + + /** + * Narrows a delete_all to the sessions that match the document_type filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + *

One of: {@code String}, {@code List}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FilterDocumentType extends JsonUnion { + private FilterDocumentType(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FilterDocumentType} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FilterDocumentType fromJson(JsonNode json) { + return new FilterDocumentType(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FilterDocumentType ofString(String value) { + return new FilterDocumentType(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code List} value. + * + * @param value the value + * @return the union + */ + public static FilterDocumentType ofList(List value) { + return new FilterDocumentType(Conv.list(Conv.STRING).write(value)); + } + + /** + * Whether this value is a {@code List}. + * + * @return true when it matches + */ + public boolean isList() { + return json.isArray(); + } + + /** + * Reads this value as a {@code List}. + * + * @return the value, or empty when {@link #isList()} is false + */ + public Optional> asList() { + return isList() ? Optional.of(Conv.list(Conv.STRING).read(json)) : Optional.empty(); + } + } + + /** + * Narrows a delete_all to the sessions that match the document_name filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + *

One of: {@code String}, {@code List}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FilterDocumentName extends JsonUnion { + private FilterDocumentName(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FilterDocumentName} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FilterDocumentName fromJson(JsonNode json) { + return new FilterDocumentName(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FilterDocumentName ofString(String value) { + return new FilterDocumentName(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code List} value. + * + * @param value the value + * @return the union + */ + public static FilterDocumentName ofList(List value) { + return new FilterDocumentName(Conv.list(Conv.STRING).write(value)); + } + + /** + * Whether this value is a {@code List}. + * + * @return true when it matches + */ + public boolean isList() { + return json.isArray(); + } + + /** + * Reads this value as a {@code List}. + * + * @return the value, or empty when {@link #isList()} is false + */ + public Optional> asList() { + return isList() ? Optional.of(Conv.list(Conv.STRING).read(json)) : Optional.empty(); + } + } + + /** + * Narrows a delete_all to the sessions that match the region filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + *

One of: {@code String}, {@code List}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FilterRegion extends JsonUnion { + private FilterRegion(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FilterRegion} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FilterRegion fromJson(JsonNode json) { + return new FilterRegion(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FilterRegion ofString(String value) { + return new FilterRegion(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code List} value. + * + * @param value the value + * @return the union + */ + public static FilterRegion ofList(List value) { + return new FilterRegion(Conv.list(Conv.STRING).write(value)); + } + + /** + * Whether this value is a {@code List}. + * + * @return true when it matches + */ + public boolean isList() { + return json.isArray(); + } + + /** + * Reads this value as a {@code List}. + * + * @return the value, or empty when {@link #isList()} is false + */ + public Optional> asList() { + return isList() ? Optional.of(Conv.list(Conv.STRING).read(json)) : Optional.empty(); + } + } + + /** + * Narrows a delete_all to the sessions that match the session_type filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + *

One of: {@code String}, {@code List}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FilterSessionType extends JsonUnion { + private FilterSessionType(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FilterSessionType} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FilterSessionType fromJson(JsonNode json) { + return new FilterSessionType(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FilterSessionType ofString(String value) { + return new FilterSessionType(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code List} value. + * + * @param value the value + * @return the union + */ + public static FilterSessionType ofList(List value) { + return new FilterSessionType(Conv.list(Conv.STRING).write(value)); + } + + /** + * Whether this value is a {@code List}. + * + * @return true when it matches + */ + public boolean isList() { + return json.isArray(); + } + + /** + * Reads this value as a {@code List}. + * + * @return the value, or empty when {@link #isList()} is false + */ + public Optional> asList() { + return isList() ? Optional.of(Conv.list(Conv.STRING).read(json)) : Optional.empty(); + } + } + + /** + * Narrows a delete_all to the sessions that match the warning filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + *

One of: {@code String}, {@code List}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FilterWarning extends JsonUnion { + private FilterWarning(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FilterWarning} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FilterWarning fromJson(JsonNode json) { + return new FilterWarning(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FilterWarning ofString(String value) { + return new FilterWarning(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code List} value. + * + * @param value the value + * @return the union + */ + public static FilterWarning ofList(List value) { + return new FilterWarning(Conv.list(Conv.STRING).write(value)); + } + + /** + * Whether this value is a {@code List}. + * + * @return true when it matches + */ + public boolean isList() { + return json.isArray(); + } + + /** + * Reads this value as a {@code List}. + * + * @return the value, or empty when {@link #isList()} is false + */ + public Optional> asList() { + return isList() ? Optional.of(Conv.list(Conv.STRING).read(json)) : Optional.empty(); + } + } + + /** + * Narrows a delete_all to the sessions that match the organization_name filter of [List Sessions](/sessions-api/list-sessions), with the same values. A comma-separated string or an array. With an API key it can only match your own organization's name, so it selects all of your sessions or none. Ignored without delete_all. An empty value is ignored and does not narrow the selection. + * + *

One of: {@code String}, {@code List}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FilterOrganizationName extends JsonUnion { + private FilterOrganizationName(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FilterOrganizationName} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FilterOrganizationName fromJson(JsonNode json) { + return new FilterOrganizationName(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FilterOrganizationName ofString(String value) { + return new FilterOrganizationName(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code List} value. + * + * @param value the value + * @return the union + */ + public static FilterOrganizationName ofList(List value) { + return new FilterOrganizationName(Conv.list(Conv.STRING).write(value)); + } + + /** + * Whether this value is a {@code List}. + * + * @return true when it matches + */ + public boolean isList() { + return json.isArray(); + } + + /** + * Reads this value as a {@code List}. + * + * @return the value, or empty when {@link #isList()} is false + */ + public Optional> asList() { + return isList() ? Optional.of(Conv.list(Conv.STRING).read(json)) : Optional.empty(); + } + } } diff --git a/src/main/java/me/didit/types/SessionsBatchDeleteResponse.java b/src/main/java/me/didit/types/SessionsBatchDeleteResponse.java index 9206c50..0c149c4 100644 --- a/src/main/java/me/didit/types/SessionsBatchDeleteResponse.java +++ b/src/main/java/me/didit/types/SessionsBatchDeleteResponse.java @@ -65,8 +65,8 @@ public boolean isAccepted() { * * @return the body, or empty for another status */ - public Optional asAccepted() { - return isAccepted() ? Optional.of(Conv.model(SessionsBatchDeleteResponse.Accepted::fromJson).read(json)) : Optional.empty(); + public Optional asAccepted() { + return isAccepted() ? Optional.of(Conv.model(SessionDeletionJob::fromJson).read(json)) : Optional.empty(); } public static final class Ok extends JsonModel { @@ -390,487 +390,4 @@ public boolean isKnown() { } } } - - /** - * A bulk session deletion running in the background: progress and counts per outcome, never which sessions it deleted. - */ - public static final class Accepted extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "total", "processed", "outcomes", "deleted_child_verifications", "error", "created_at", "completed_at"); - - private Accepted(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Accepted} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Accepted fromJson(JsonNode json) { - return new Accepted(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Poll GET /v3/sessions/delete/{job_id}/ with this id. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * PENDING: accepted, not started yet. PROCESSING: deleting; processed and outcomes are updating. COMPLETED: every selected session is accounted for. FAILED: the job stopped early; see error. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(SessionsBatchDeleteResponse.Accepted.Status::of)); - } - - /** - * Sessions selected when the deletion was requested. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * Sessions the job has finished with so far. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * How many sessions ended in each outcome. Every key is always present. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(SessionsBatchDeleteResponse.Accepted.Outcomes::fromJson)); - } - - /** - * Verifications that the deleted business verifications created, deleted with them. Always 0 for a job started by POST /v3/sessions/delete/, which deletes KYC sessions only. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional deletedChildVerifications() { - return Json.optional(json, "deleted_child_verifications", Conv.LONG); - } - - /** - * null unless status is FAILED; then why the deletion stopped. The sessions the job had not reached are still in place: repeat the request to delete them. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * When the deletion was requested. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional createdAt() { - return Json.optional(json, "created_at", Conv.DATE_TIME); - } - - /** - * When the job finished; null while it runs. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional completedAt() { - return Json.optional(json, "completed_at", Conv.DATE_TIME); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Accepted}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Accepted from) { - super(from); - } - - /** - * Poll GET /v3/sessions/delete/{job_id}/ with this id. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * PENDING: accepted, not started yet. PROCESSING: deleting; processed and outcomes are updating. COMPLETED: every selected session is accounted for. FAILED: the job stopped early; see error. - * - * @param status the value - * @return this builder - */ - public Builder status(SessionsBatchDeleteResponse.Accepted.Status status) { - return set("status", Conv.enumeration(SessionsBatchDeleteResponse.Accepted.Status::of), status); - } - - /** - * Sessions selected when the deletion was requested. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * Sessions the job has finished with so far. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * How many sessions ended in each outcome. Every key is always present. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(SessionsBatchDeleteResponse.Accepted.Outcomes outcomes) { - return set("outcomes", Conv.model(SessionsBatchDeleteResponse.Accepted.Outcomes::fromJson), outcomes); - } - - /** - * Verifications that the deleted business verifications created, deleted with them. Always 0 for a job started by POST /v3/sessions/delete/, which deletes KYC sessions only. - * - * @param deletedChildVerifications the value - * @return this builder - */ - public Builder deletedChildVerifications(long deletedChildVerifications) { - return set("deleted_child_verifications", Conv.LONG, deletedChildVerifications); - } - - /** - * null unless status is FAILED; then why the deletion stopped. The sessions the job had not reached are still in place: repeat the request to delete them. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * When the deletion was requested. - * - * @param createdAt the value - * @return this builder - */ - public Builder createdAt(OffsetDateTime createdAt) { - return set("created_at", Conv.DATE_TIME, createdAt); - } - - /** - * When the job finished; null while it runs. - * - * @param completedAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder completedAt(@Nullable OffsetDateTime completedAt) { - return setNullable("completed_at", Conv.DATE_TIME, completedAt); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Accepted} - */ - public Accepted build() { - return new Accepted(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * PENDING: accepted, not started yet. PROCESSING: deleting; processed and outcomes are updating. COMPLETED: every selected session is accounted for. FAILED: the job stopped early; see error. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** How many sessions ended in each outcome. Every key is always present. */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("deleted", "none", "retained_with_user", "ineligible_no_vendor_user", "failed_retryable", "already_deleted"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Sessions deleted together with their face embedding. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional deleted() { - return Json.optional(json, "deleted", Conv.LONG); - } - - /** - * Sessions deleted that had no face embedding. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional none() { - return Json.optional(json, "none", Conv.LONG); - } - - /** - * Sessions deleted while one image-free biometric template was retained for their User, as the request or the application policy asked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional retainedWithUser() { - return Json.optional(json, "retained_with_user", Conv.LONG); - } - - /** - * Sessions deleted, with their face embedding, because retention was requested but they had no linked User. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional ineligibleNoVendorUser() { - return Json.optional(json, "ineligible_no_vendor_user", Conv.LONG); - } - - /** - * Sessions reported as not deleted. A job retries a group it could not delete by itself instead of counting it here. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedRetryable() { - return Json.optional(json, "failed_retryable", Conv.LONG); - } - - /** - * Selected sessions that were already deleted, for example by another request, when the job reached them. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyDeleted() { - return Json.optional(json, "already_deleted", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Sessions deleted together with their face embedding. - * - * @param deleted the value - * @return this builder - */ - public Builder deleted(long deleted) { - return set("deleted", Conv.LONG, deleted); - } - - /** - * Sessions deleted that had no face embedding. - * - * @param none the value - * @return this builder - */ - public Builder none(long none) { - return set("none", Conv.LONG, none); - } - - /** - * Sessions deleted while one image-free biometric template was retained for their User, as the request or the application policy asked. - * - * @param retainedWithUser the value - * @return this builder - */ - public Builder retainedWithUser(long retainedWithUser) { - return set("retained_with_user", Conv.LONG, retainedWithUser); - } - - /** - * Sessions deleted, with their face embedding, because retention was requested but they had no linked User. - * - * @param ineligibleNoVendorUser the value - * @return this builder - */ - public Builder ineligibleNoVendorUser(long ineligibleNoVendorUser) { - return set("ineligible_no_vendor_user", Conv.LONG, ineligibleNoVendorUser); - } - - /** - * Sessions reported as not deleted. A job retries a group it could not delete by itself instead of counting it here. - * - * @param failedRetryable the value - * @return this builder - */ - public Builder failedRetryable(long failedRetryable) { - return set("failed_retryable", Conv.LONG, failedRetryable); - } - - /** - * Selected sessions that were already deleted, for example by another request, when the job reached them. - * - * @param alreadyDeleted the value - * @return this builder - */ - public Builder alreadyDeleted(long alreadyDeleted) { - return set("already_deleted", Conv.LONG, alreadyDeleted); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } } diff --git a/src/main/java/me/didit/types/SessionsBulkUpdateAmlHitStatusResponse.java b/src/main/java/me/didit/types/SessionsBulkUpdateAmlHitStatusResponse.java new file mode 100644 index 0000000..3345e20 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsBulkUpdateAmlHitStatusResponse.java @@ -0,0 +1,120 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class SessionsBulkUpdateAmlHitStatusResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("message", "updated_hits"); + + private SessionsBulkUpdateAmlHitStatusResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsBulkUpdateAmlHitStatusResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsBulkUpdateAmlHitStatusResponse fromJson(JsonNode json) { + return new SessionsBulkUpdateAmlHitStatusResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Updated {n} hit(s) successfully, where n is the number of entries in hit_updates. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String message() { + return Json.required(json, "message", Conv.STRING); + } + + /** + * The hit_id of every entry in hit_updates, in request order, including hits whose status did not change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List updatedHits() { + return Json.required(json, "updated_hits", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsBulkUpdateAmlHitStatusResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsBulkUpdateAmlHitStatusResponse from) { + super(from); + } + + /** + * Updated {n} hit(s) successfully, where n is the number of entries in hit_updates. + * + * @param message the value + * @return this builder + */ + public Builder message(String message) { + return set("message", Conv.STRING, message); + } + + /** + * The hit_id of every entry in hit_updates, in request order, including hits whose status did not change. + * + * @param updatedHits the value + * @return this builder + */ + public Builder updatedHits(List updatedHits) { + return set("updated_hits", Conv.list(Conv.STRING), updatedHits); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsBulkUpdateAmlHitStatusResponse} + * @throws IllegalStateException when a required field is missing + */ + public SessionsBulkUpdateAmlHitStatusResponse build() { + require("message", false); + require("updated_hits", false); + return new SessionsBulkUpdateAmlHitStatusResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsCreateParams.java b/src/main/java/me/didit/types/SessionsCreateParams.java index 5269030..bddf661 100644 --- a/src/main/java/me/didit/types/SessionsCreateParams.java +++ b/src/main/java/me/didit/types/SessionsCreateParams.java @@ -12,12 +12,13 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; +import me.didit.core.JsonUnion; import me.didit.core.StringEnum; import org.jspecify.annotations.Nullable; /** Parameters for {@code sessions.create}. */ public final class SessionsCreateParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("workflow_id", "vendor_data", "callback", "callback_method", "metadata", "language", "contact_details", "expected_details", "portrait_image", "sandbox_scenario"); + private static final Set KNOWN_FIELDS = Set.of("workflow_id", "vendor_data", "callback", "callback_method", "metadata", "language", "contact_details", "expected_details", "portrait_image", "sandbox_scenario", "sign"); private SessionsCreateParams(ObjectNode json, Map> files) { super(json, files); @@ -73,7 +74,7 @@ public Optional vendorData() { } /** - * URL to redirect the user to after verification completes. Didit automatically appends verificationSessionId and status (Approved, Declined, In Review) as query parameters. Custom URL schemes (e.g. myapp://) are supported for mobile callbacks. If omitted, the workflow's default callback_url is used. + * URL to redirect the user to after verification completes. Didit automatically appends verificationSessionId and status (Approved, Declined, In Review) as query parameters. Custom URL schemes (e.g. myapp://) are supported for mobile callbacks. If omitted, the workflow's default callback_url is used. When the application has allowed domains (allowed_domains on PATCH /v3/webhook/), an http or https callback must be on one of those origins (same scheme, host and port), otherwise the request fails with 400 on callback: callback must be on one of the application's allowed domains: https://app.example.com. Custom URL schemes are always accepted. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -152,6 +153,16 @@ public Optional sandboxScenario() { return Json.optional(json, "sandbox_scenario", Conv.STRING); } + /** + * Electronic Signature input for this session. documents are the documents to sign, for a workflow whose Electronic Signature step takes documents from the session (signature_documents_source session or both): up to 20 documents and 50 MB in total. field_values fills the fields whose prefill is api.<key>, on workflow and session documents alike. Creating a session again for the same workflow_id and vendor_data returns the pending session; if that call sends sign, its documents, fields and values must be identical to the first ones, and anything different returns 400 with {"sign": "signature_document_changed"}. See [Electronic signature documents](/sessions-api/create-session#electronic-signature-documents). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sign() { + return Json.optional(json, "sign", Conv.model(SessionsCreateParams.Sign::fromJson)); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -186,7 +197,7 @@ public Builder vendorData(String vendorData) { } /** - * URL to redirect the user to after verification completes. Didit automatically appends verificationSessionId and status (Approved, Declined, In Review) as query parameters. Custom URL schemes (e.g. myapp://) are supported for mobile callbacks. If omitted, the workflow's default callback_url is used. + * URL to redirect the user to after verification completes. Didit automatically appends verificationSessionId and status (Approved, Declined, In Review) as query parameters. Custom URL schemes (e.g. myapp://) are supported for mobile callbacks. If omitted, the workflow's default callback_url is used. When the application has allowed domains (allowed_domains on PATCH /v3/webhook/), an http or https callback must be on one of those origins (same scheme, host and port), otherwise the request fails with 400 on callback: callback must be on one of the application's allowed domains: https://app.example.com. Custom URL schemes are always accepted. * * @param callback the value * @return this builder @@ -265,6 +276,16 @@ public Builder sandboxScenario(String sandboxScenario) { return set("sandbox_scenario", Conv.STRING, sandboxScenario); } + /** + * Electronic Signature input for this session. documents are the documents to sign, for a workflow whose Electronic Signature step takes documents from the session (signature_documents_source session or both): up to 20 documents and 50 MB in total. field_values fills the fields whose prefill is api.<key>, on workflow and session documents alike. Creating a session again for the same workflow_id and vendor_data returns the pending session; if that call sends sign, its documents, fields and values must be identical to the first ones, and anything different returns 400 with {"sign": "signature_document_changed"}. See [Electronic signature documents](/sessions-api/create-session#electronic-signature-documents). + * + * @param sign the value + * @return this builder + */ + public Builder sign(SessionsCreateParams.Sign sign) { + return set("sign", Conv.model(SessionsCreateParams.Sign::fromJson), sign); + } + /** * Builds the value. * @@ -1341,4 +1362,189 @@ public boolean isKnown() { } } } + + /** + * Electronic Signature input for this session. documents are the documents to sign, for a workflow whose Electronic Signature step takes documents from the session (signature_documents_source session or both): up to 20 documents and 50 MB in total. field_values fills the fields whose prefill is api.<key>, on workflow and session documents alike. Creating a session again for the same workflow_id and vendor_data returns the pending session; if that call sends sign, its documents, fields and values must be identical to the first ones, and anything different returns 400 with {"sign": "signature_document_changed"}. See [Electronic signature documents](/sessions-api/create-session#electronic-signature-documents). + */ + public static final class Sign extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("documents", "field_values"); + + private Sign(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Sign} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Sign fromJson(JsonNode json) { + return new Sign(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The documents field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> documents() { + return Json.optional(json, "documents", Conv.list(Conv.model(SessionSignDocument::fromJson))); + } + + /** + * Values keyed by the <key> of each api.<key> pre-fill source: strings of 1 to 500 characters for text and data fields, booleans for a checkbox. 400 with field_value_unknown for a key no field uses, field_value_missing for a required field without its value, and field_value_invalid for a value that fails its field's validation. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> fieldValues() { + return Json.optional(json, "field_values", Conv.map(Conv.union(SessionsCreateParams.Sign.FieldValuesValue::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Sign}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Sign from) { + super(from); + } + + /** + * The documents field. + * + * @param documents the value + * @return this builder + */ + public Builder documents(List documents) { + return set("documents", Conv.list(Conv.model(SessionSignDocument::fromJson)), documents); + } + + /** + * Values keyed by the <key> of each api.<key> pre-fill source: strings of 1 to 500 characters for text and data fields, booleans for a checkbox. 400 with field_value_unknown for a key no field uses, field_value_missing for a required field without its value, and field_value_invalid for a value that fails its field's validation. + * + * @param fieldValues the value + * @return this builder + */ + public Builder fieldValues(Map fieldValues) { + return set("field_values", Conv.map(Conv.union(SessionsCreateParams.Sign.FieldValuesValue::fromJson)), fieldValues); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Sign} + */ + public Sign build() { + return new Sign(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

One of: {@code String}, {@code Boolean}. Check with the {@code is...} + * methods and read with the {@code as...} methods. + */ + public static final class FieldValuesValue extends JsonUnion { + private FieldValuesValue(JsonNode json) { + super(json); + } + + /** + * Reads a {@code FieldValuesValue} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static FieldValuesValue fromJson(JsonNode json) { + return new FieldValuesValue(json.deepCopy()); + } + + /** + * Wraps a {@code String} value. + * + * @param value the value + * @return the union + */ + public static FieldValuesValue ofString(String value) { + return new FieldValuesValue(Conv.STRING.write(value)); + } + + /** + * Whether this value is a {@code String}. + * + * @return true when it matches + */ + public boolean isString() { + return json.isTextual(); + } + + /** + * Reads this value as a {@code String}. + * + * @return the value, or empty when {@link #isString()} is false + */ + public Optional asString() { + return isString() ? Optional.of(Conv.STRING.read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code Boolean} value. + * + * @param value the value + * @return the union + */ + public static FieldValuesValue ofBoolean(Boolean value) { + return new FieldValuesValue(Conv.BOOLEAN.write(value)); + } + + /** + * Whether this value is a {@code Boolean}. + * + * @return true when it matches + */ + public boolean isBoolean() { + return json.isBoolean(); + } + + /** + * Reads this value as a {@code Boolean}. + * + * @return the value, or empty when {@link #isBoolean()} is false + */ + public Optional asBoolean() { + return isBoolean() ? Optional.of(Conv.BOOLEAN.read(json)) : Optional.empty(); + } + } + } } diff --git a/src/main/java/me/didit/types/SessionsCreateResponse.java b/src/main/java/me/didit/types/SessionsCreateResponse.java index 4ded12a..9f82fbb 100644 --- a/src/main/java/me/didit/types/SessionsCreateResponse.java +++ b/src/main/java/me/didit/types/SessionsCreateResponse.java @@ -121,7 +121,7 @@ public SessionsCreateResponse.Status status() { } /** - * Final redirect URL the user is sent to after completing the flow. Didit appends ?verificationSessionId={session_id}&status={status} to this URL. Falls back to the workflow's configured callback URL when not provided in the request; null when neither is set. + * Final redirect URL the user is sent to after completing the flow. Didit appends ?verificationSessionId={session_id}&status={status} to this URL. Falls back to the workflow's configured callback URL when not provided in the request; null when neither is set. When the application has allowed domains and this URL is a web URL outside them (for example a workflow default set before the list changed), the hosted flow does not redirect the user to it. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -234,7 +234,7 @@ public Builder status(SessionsCreateResponse.Status status) { } /** - * Final redirect URL the user is sent to after completing the flow. Didit appends ?verificationSessionId={session_id}&status={status} to this URL. Falls back to the workflow's configured callback URL when not provided in the request; null when neither is set. + * Final redirect URL the user is sent to after completing the flow. Didit appends ?verificationSessionId={session_id}&status={status} to this URL. Falls back to the workflow's configured callback URL when not provided in the request; null when neither is set. When the application has allowed domains and this URL is a web URL outside them (for example a workflow default set before the list changed), the hosted flow does not redirect the user to it. * * @param callback the value, or {@code null} to send JSON null * @return this builder diff --git a/src/main/java/me/didit/types/SessionsExportAmlScreeningHistoryParams.java b/src/main/java/me/didit/types/SessionsExportAmlScreeningHistoryParams.java new file mode 100644 index 0000000..2f97117 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsExportAmlScreeningHistoryParams.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code sessions.export_aml_screening_history}. */ +public final class SessionsExportAmlScreeningHistoryParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id"); + + private SessionsExportAmlScreeningHistoryParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsExportAmlScreeningHistoryParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsExportAmlScreeningHistoryParams fromJson(JsonNode json) { + return new SessionsExportAmlScreeningHistoryParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Workflow graph node ID of the AML check. Needed only when the session ran more than one AML step; without it the most recent AML check is used. An unknown node returns 404. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsExportAmlScreeningHistoryParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsExportAmlScreeningHistoryParams from) { + super(from); + } + + /** + * Workflow graph node ID of the AML check. Needed only when the session ran more than one AML step; without it the most recent AML check is used. An unknown node returns 404. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsExportAmlScreeningHistoryParams} + */ + public SessionsExportAmlScreeningHistoryParams build() { + return new SessionsExportAmlScreeningHistoryParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsListAmlScreeningHistoryParams.java b/src/main/java/me/didit/types/SessionsListAmlScreeningHistoryParams.java new file mode 100644 index 0000000..7f3a320 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsListAmlScreeningHistoryParams.java @@ -0,0 +1,159 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code sessions.list_aml_screening_history}. */ +public final class SessionsListAmlScreeningHistoryParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id", "limit", "offset", "ordering"); + + private SessionsListAmlScreeningHistoryParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsListAmlScreeningHistoryParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsListAmlScreeningHistoryParams fromJson(JsonNode json) { + return new SessionsListAmlScreeningHistoryParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Workflow graph node ID of the AML check. Needed only when the session ran more than one AML step; without it the most recent AML check is used. An unknown node returns 404. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + /** + * Maximum number of screening runs per page. Defaults to 50. A value below 1 or a non-numeric value falls back to 50; a value above 1,000,000 returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Number of results to skip before the first returned one. Defaults to 0; a negative or non-numeric value falls back to 0, a value above 1,000,000 returns 400. Prefer following the next and previous URLs. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + /** + * Optional sort override. The default order is -completed_at. Pass a result field name (for example completed_at, started_at, outcome or score) to sort ascending, prefix it with - to sort descending, and separate several fields with commas. Unknown fields are ignored. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ordering() { + return Json.optional(json, "ordering", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsListAmlScreeningHistoryParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsListAmlScreeningHistoryParams from) { + super(from); + } + + /** + * Workflow graph node ID of the AML check. Needed only when the session ran more than one AML step; without it the most recent AML check is used. An unknown node returns 404. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * Maximum number of screening runs per page. Defaults to 50. A value below 1 or a non-numeric value falls back to 50; a value above 1,000,000 returns 400. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Number of results to skip before the first returned one. Defaults to 0; a negative or non-numeric value falls back to 0, a value above 1,000,000 returns 400. Prefer following the next and previous URLs. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Optional sort override. The default order is -completed_at. Pass a result field name (for example completed_at, started_at, outcome or score) to sort ascending, prefix it with - to sort descending, and separate several fields with commas. Unknown fields are ignored. + * + * @param ordering the value + * @return this builder + */ + public Builder ordering(String ordering) { + return set("ordering", Conv.STRING, ordering); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsListAmlScreeningHistoryParams} + */ + public SessionsListAmlScreeningHistoryParams build() { + return new SessionsListAmlScreeningHistoryParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsListAmlScreeningHistoryResponse.java b/src/main/java/me/didit/types/SessionsListAmlScreeningHistoryResponse.java new file mode 100644 index 0000000..97158b9 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsListAmlScreeningHistoryResponse.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class SessionsListAmlScreeningHistoryResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private SessionsListAmlScreeningHistoryResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsListAmlScreeningHistoryResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsListAmlScreeningHistoryResponse fromJson(JsonNode json) { + return new SessionsListAmlScreeningHistoryResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Number of runs, exact up to 100 and capped at 100 above that. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(AMLScreeningRun::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsListAmlScreeningHistoryResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsListAmlScreeningHistoryResponse from) { + super(from); + } + + /** + * Number of runs, exact up to 100 and capped at 100 above that. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(AMLScreeningRun::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsListAmlScreeningHistoryResponse} + * @throws IllegalStateException when a required field is missing + */ + public SessionsListAmlScreeningHistoryResponse build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new SessionsListAmlScreeningHistoryResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsListEventsParams.java b/src/main/java/me/didit/types/SessionsListEventsParams.java new file mode 100644 index 0000000..f9e82a5 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsListEventsParams.java @@ -0,0 +1,139 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code sessions.list_events}. */ +public final class SessionsListEventsParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("limit", "offset", "ordering"); + + private SessionsListEventsParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsListEventsParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsListEventsParams fromJson(JsonNode json) { + return new SessionsListEventsParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Maximum number of events per page. Defaults to 50. A value below 1 or a non-numeric value falls back to 50; a value above 1,000,000 returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Number of results to skip before the first returned one. Defaults to 0; a negative or non-numeric value falls back to 0, a value above 1,000,000 returns 400. Prefer following the next and previous URLs. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + /** + * Optional sort override. The default order is -sent_at. Pass a result field name (for example sent_at, created_at, event_type, feature_step or step) to sort ascending, prefix it with - to sort descending, and separate several fields with commas. Unknown fields are ignored. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ordering() { + return Json.optional(json, "ordering", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsListEventsParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsListEventsParams from) { + super(from); + } + + /** + * Maximum number of events per page. Defaults to 50. A value below 1 or a non-numeric value falls back to 50; a value above 1,000,000 returns 400. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Number of results to skip before the first returned one. Defaults to 0; a negative or non-numeric value falls back to 0, a value above 1,000,000 returns 400. Prefer following the next and previous URLs. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Optional sort override. The default order is -sent_at. Pass a result field name (for example sent_at, created_at, event_type, feature_step or step) to sort ascending, prefix it with - to sort descending, and separate several fields with commas. Unknown fields are ignored. + * + * @param ordering the value + * @return this builder + */ + public Builder ordering(String ordering) { + return set("ordering", Conv.STRING, ordering); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsListEventsParams} + */ + public SessionsListEventsParams build() { + return new SessionsListEventsParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsListEventsResponse.java b/src/main/java/me/didit/types/SessionsListEventsResponse.java new file mode 100644 index 0000000..08e5e9e --- /dev/null +++ b/src/main/java/me/didit/types/SessionsListEventsResponse.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class SessionsListEventsResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private SessionsListEventsResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsListEventsResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsListEventsResponse fromJson(JsonNode json) { + return new SessionsListEventsResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Total number of events of the session. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(SessionEvent::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsListEventsResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsListEventsResponse from) { + super(from); + } + + /** + * Total number of events of the session. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(SessionEvent::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsListEventsResponse} + * @throws IllegalStateException when a required field is missing + */ + public SessionsListEventsResponse build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new SessionsListEventsResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsListFeatureOverridesResponse.java b/src/main/java/me/didit/types/SessionsListFeatureOverridesResponse.java new file mode 100644 index 0000000..1b4cba1 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsListFeatureOverridesResponse.java @@ -0,0 +1,165 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class SessionsListFeatureOverridesResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "person_in_flow", "flow_finished_at", "overrides"); + + private SessionsListFeatureOverridesResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsListFeatureOverridesResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsListFeatureOverridesResponse fromJson(JsonNode json) { + return new SessionsListFeatureOverridesResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The session_id field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Whether the person is still in the flow, so a new override with defer_until_flow_finishes: true would wait. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean personInFlow() { + return Json.required(json, "person_in_flow", Conv.BOOLEAN); + } + + /** + * When the person last finished the flow. null while they are still in it (a resubmission included) and when the session closed before they finished (abandoned, expired or decided mid-flow). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional flowFinishedAt() { + return Json.optional(json, "flow_finished_at", Conv.DATE_TIME); + } + + /** + * Every override of the session, newest first. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List overrides() { + return Json.required(json, "overrides", Conv.list(Conv.model(FeatureStatusOverride::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsListFeatureOverridesResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsListFeatureOverridesResponse from) { + super(from); + } + + /** + * The session_id field. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Whether the person is still in the flow, so a new override with defer_until_flow_finishes: true would wait. + * + * @param personInFlow the value + * @return this builder + */ + public Builder personInFlow(boolean personInFlow) { + return set("person_in_flow", Conv.BOOLEAN, personInFlow); + } + + /** + * When the person last finished the flow. null while they are still in it (a resubmission included) and when the session closed before they finished (abandoned, expired or decided mid-flow). + * + * @param flowFinishedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder flowFinishedAt(@Nullable OffsetDateTime flowFinishedAt) { + return setNullable("flow_finished_at", Conv.DATE_TIME, flowFinishedAt); + } + + /** + * Every override of the session, newest first. + * + * @param overrides the value + * @return this builder + */ + public Builder overrides(List overrides) { + return set("overrides", Conv.list(Conv.model(FeatureStatusOverride::fromJson)), overrides); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsListFeatureOverridesResponse} + * @throws IllegalStateException when a required field is missing + */ + public SessionsListFeatureOverridesResponse build() { + require("session_id", false); + require("person_in_flow", false); + require("flow_finished_at", true); + require("overrides", false); + return new SessionsListFeatureOverridesResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsListResponse.java b/src/main/java/me/didit/types/SessionsListResponse.java index 41c577d..d079edc 100644 --- a/src/main/java/me/didit/types/SessionsListResponse.java +++ b/src/main/java/me/didit/types/SessionsListResponse.java @@ -13,7 +13,6 @@ import me.didit.core.Json; import me.didit.core.JsonModel; import me.didit.core.JsonUnion; -import me.didit.core.StringEnum; import org.jspecify.annotations.Nullable; public final class SessionsListResponse extends JsonModel { @@ -261,8 +260,8 @@ public OffsetDateTime createdAt() { * @param value the value * @return the union */ - public static Results ofUser(SessionsListResponse.Results.User value) { - return new Results(Conv.model(SessionsListResponse.Results.User::fromJson).write(value)); + public static Results ofUser(UserSessionListItem value) { + return new Results(Conv.model(UserSessionListItem::fromJson).write(value)); } /** @@ -281,8 +280,8 @@ public boolean isUser() { * * @return the value, or empty when {@link #isUser()} is false */ - public Optional asUser() { - return isUser() ? Optional.of(Conv.model(SessionsListResponse.Results.User::fromJson).read(json)) : Optional.empty(); + public Optional asUser() { + return isUser() ? Optional.of(Conv.model(UserSessionListItem::fromJson).read(json)) : Optional.empty(); } /** @@ -291,8 +290,8 @@ public Optional asUser() { * @param value the value * @return the union */ - public static Results ofBusiness(SessionsListResponse.Results.Business value) { - return new Results(Conv.model(SessionsListResponse.Results.Business::fromJson).write(value)); + public static Results ofBusiness(BusinessSessionListItem value) { + return new Results(Conv.model(BusinessSessionListItem::fromJson).write(value)); } /** @@ -311,2734 +310,8 @@ public boolean isBusiness() { * * @return the value, or empty when {@link #isBusiness()} is false */ - public Optional asBusiness() { - return isBusiness() ? Optional.of(Conv.model(SessionsListResponse.Results.Business::fromJson).read(json)) : Optional.empty(); - } - - /** - * Summary row for a User Verification (KYC) session. Identity fields (full_name, country, document_type, portrait_image, contact fields) are populated progressively as the user completes verification steps, and are null until the relevant step finishes. Fields restricted by the workflow's response-attributes configuration are also returned as null. - */ - public static final class User extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("session_id", "session_kind", "session_number", "session_url", "portrait_image", "document_type", "full_name", "country", "status", "vendor_data", "didit_internal_id", "created_at", "features", "phone_number", "email_address", "additional_details"); - - private User(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code User} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static User fromJson(JsonNode json) { - return new User(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Unique identifier of the verification session. Use it with GET /v3/session/{sessionId}/decision/ to fetch the full decision payload. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String sessionId() { - return Json.required(json, "session_id", Conv.STRING); - } - - /** - * Row discriminator. Always "user" for KYC rows; Business Verification (KYB) rows have "business" and a different shape. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public SessionsListResponse.Results.User.SessionKind sessionKind() { - return Json.required(json, "session_kind", Conv.enumeration(SessionsListResponse.Results.User.SessionKind::of)); - } - - /** - * Human-friendly sequential number of the session within your application. Also matched by the search parameter. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long sessionNumber() { - return Json.required(json, "session_number", Conv.LONG); - } - - /** - * Hosted verification URL the end user opens to complete the flow. null when the session has no hosted URL. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sessionUrl() { - return Json.optional(json, "session_url", Conv.STRING); - } - - /** - * Expiring (presigned) URL of the best available portrait: the NFC chip photo when available, otherwise the ID document portrait, otherwise the liveness reference image. null until one of those exists. Download promptly; the link expires. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional portraitImage() { - return Json.optional(json, "portrait_image", Conv.STRING); - } - - /** - * Human-readable type of the verified ID document. null before document verification completes, and also null after completion when the verified code has no display mapping (SSC, WP, VISA, PSC, BC, OTHER). Note: the document_type query parameter filters by short codes (P, ID, DL, RP, ...), not by these display values. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentType() { - return Json.optional(json, "document_type", Conv.enumeration(SessionsListResponse.Results.User.DocumentType::of)); - } - - /** - * Best available full name for the session, resolved in order: ID document OCR, AML screened data, database-validation screened data, proof-of-address document, then the expected details provided at session creation. null when none is available yet. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional fullName() { - return Json.optional(json, "full_name", Conv.STRING); - } - - /** - * ISO 3166-1 alpha-3 country associated with the session, resolved in order: ID document issuing state, database-validation issuing state, proof-of-address issuing state, expected details, then the verified phone's country. May differ from nationality. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional country() { - return Json.optional(json, "country", Conv.STRING); - } - - /** - * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page for the lifecycle. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public SessionsListResponse.Results.User.Status status() { - return Json.required(json, "status", Conv.enumeration(SessionsListResponse.Results.User.Status::of)); - } - - /** - * Your own identifier for the end user, echoed back exactly as sent at session creation. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); - } - - /** - * Didit's internal identifier for the verified user profile behind this session. Stable across multiple sessions of the same user; filterable via the didit_internal_id query parameter. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional diditInternalId() { - return Json.optional(json, "didit_internal_id", Conv.STRING); - } - - /** - * UTC timestamp when the session was created. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public OffsetDateTime createdAt() { - return Json.required(json, "created_at", Conv.DATE_TIME); - } - - /** - * Per-feature status for every verification step configured in the session's workflow, in workflow order. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public List features() { - return Json.required(json, "features", Conv.list(Conv.model(SessionsListResponse.Results.User.Features::fromJson))); - } - - /** - * Phone associated with the session: the verified phone when phone verification ran, otherwise the contact phone provided at session creation. null when neither exists. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional phoneNumber() { - return Json.optional(json, "phone_number", Conv.model(SessionsListResponse.Results.User.PhoneNumber::fromJson)); - } - - /** - * Email associated with the session: the verified email when email verification ran, otherwise the contact email provided at session creation. null when neither exists. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional emailAddress() { - return Json.optional(json, "email_address", Conv.model(SessionsListResponse.Results.User.EmailAddress::fromJson)); - } - - /** - * Compact per-feature extras. Only features that produced data appear as keys; null when no feature produced extras. Fields excluded by the workflow's response-attributes configuration are omitted. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional additionalDetails() { - return Json.optional(json, "additional_details", Conv.model(SessionsListResponse.Results.User.AdditionalDetails::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link User}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(User from) { - super(from); - } - - /** - * Unique identifier of the verification session. Use it with GET /v3/session/{sessionId}/decision/ to fetch the full decision payload. - * - * @param sessionId the value - * @return this builder - */ - public Builder sessionId(String sessionId) { - return set("session_id", Conv.STRING, sessionId); - } - - /** - * Row discriminator. Always "user" for KYC rows; Business Verification (KYB) rows have "business" and a different shape. - * - * @param sessionKind the value - * @return this builder - */ - public Builder sessionKind(SessionsListResponse.Results.User.SessionKind sessionKind) { - return set("session_kind", Conv.enumeration(SessionsListResponse.Results.User.SessionKind::of), sessionKind); - } - - /** - * Human-friendly sequential number of the session within your application. Also matched by the search parameter. - * - * @param sessionNumber the value - * @return this builder - */ - public Builder sessionNumber(long sessionNumber) { - return set("session_number", Conv.LONG, sessionNumber); - } - - /** - * Hosted verification URL the end user opens to complete the flow. null when the session has no hosted URL. - * - * @param sessionUrl the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder sessionUrl(@Nullable String sessionUrl) { - return setNullable("session_url", Conv.STRING, sessionUrl); - } - - /** - * Expiring (presigned) URL of the best available portrait: the NFC chip photo when available, otherwise the ID document portrait, otherwise the liveness reference image. null until one of those exists. Download promptly; the link expires. - * - * @param portraitImage the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder portraitImage(@Nullable String portraitImage) { - return setNullable("portrait_image", Conv.STRING, portraitImage); - } - - /** - * Human-readable type of the verified ID document. null before document verification completes, and also null after completion when the verified code has no display mapping (SSC, WP, VISA, PSC, BC, OTHER). Note: the document_type query parameter filters by short codes (P, ID, DL, RP, ...), not by these display values. - * - * @param documentType the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder documentType(SessionsListResponse.Results.User.@Nullable DocumentType documentType) { - return setNullable("document_type", Conv.enumeration(SessionsListResponse.Results.User.DocumentType::of), documentType); - } - - /** - * Best available full name for the session, resolved in order: ID document OCR, AML screened data, database-validation screened data, proof-of-address document, then the expected details provided at session creation. null when none is available yet. - * - * @param fullName the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder fullName(@Nullable String fullName) { - return setNullable("full_name", Conv.STRING, fullName); - } - - /** - * ISO 3166-1 alpha-3 country associated with the session, resolved in order: ID document issuing state, database-validation issuing state, proof-of-address issuing state, expected details, then the verified phone's country. May differ from nationality. - * - * @param country the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder country(@Nullable String country) { - return setNullable("country", Conv.STRING, country); - } - - /** - * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page for the lifecycle. - * - * @param status the value - * @return this builder - */ - public Builder status(SessionsListResponse.Results.User.Status status) { - return set("status", Conv.enumeration(SessionsListResponse.Results.User.Status::of), status); - } - - /** - * Your own identifier for the end user, echoed back exactly as sent at session creation. - * - * @param vendorData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder vendorData(@Nullable String vendorData) { - return setNullable("vendor_data", Conv.STRING, vendorData); - } - - /** - * Didit's internal identifier for the verified user profile behind this session. Stable across multiple sessions of the same user; filterable via the didit_internal_id query parameter. - * - * @param diditInternalId the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder diditInternalId(@Nullable String diditInternalId) { - return setNullable("didit_internal_id", Conv.STRING, diditInternalId); - } - - /** - * UTC timestamp when the session was created. - * - * @param createdAt the value - * @return this builder - */ - public Builder createdAt(OffsetDateTime createdAt) { - return set("created_at", Conv.DATE_TIME, createdAt); - } - - /** - * Per-feature status for every verification step configured in the session's workflow, in workflow order. - * - * @param features the value - * @return this builder - */ - public Builder features(List features) { - return set("features", Conv.list(Conv.model(SessionsListResponse.Results.User.Features::fromJson)), features); - } - - /** - * Phone associated with the session: the verified phone when phone verification ran, otherwise the contact phone provided at session creation. null when neither exists. - * - * @param phoneNumber the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder phoneNumber(SessionsListResponse.Results.User.@Nullable PhoneNumber phoneNumber) { - return setNullable("phone_number", Conv.model(SessionsListResponse.Results.User.PhoneNumber::fromJson), phoneNumber); - } - - /** - * Email associated with the session: the verified email when email verification ran, otherwise the contact email provided at session creation. null when neither exists. - * - * @param emailAddress the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder emailAddress(SessionsListResponse.Results.User.@Nullable EmailAddress emailAddress) { - return setNullable("email_address", Conv.model(SessionsListResponse.Results.User.EmailAddress::fromJson), emailAddress); - } - - /** - * Compact per-feature extras. Only features that produced data appear as keys; null when no feature produced extras. Fields excluded by the workflow's response-attributes configuration are omitted. - * - * @param additionalDetails the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder additionalDetails(SessionsListResponse.Results.User.@Nullable AdditionalDetails additionalDetails) { - return setNullable("additional_details", Conv.model(SessionsListResponse.Results.User.AdditionalDetails::fromJson), additionalDetails); - } - - /** - * Builds the value. - * - * @return a new immutable {@code User} - * @throws IllegalStateException when a required field is missing - */ - public User build() { - require("session_id", false); - require("session_kind", false); - require("session_number", false); - require("session_url", true); - require("portrait_image", true); - require("document_type", true); - require("full_name", true); - require("country", true); - require("status", false); - require("vendor_data", true); - require("didit_internal_id", true); - require("created_at", false); - require("features", false); - require("phone_number", true); - require("email_address", true); - require("additional_details", true); - return new User(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Row discriminator. Always "user" for KYC rows; Business Verification (KYB) rows have "business" and a different shape. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class SessionKind extends StringEnum { - /** The value {@code "user"}. */ - public static final SessionKind USER = new SessionKind("user"); - - private static final List KNOWN_VALUES = List.of(USER); - - private SessionKind(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static SessionKind of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, SessionKind::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Human-readable type of the verified ID document. null before document verification completes, and also null after completion when the verified code has no display mapping (SSC, WP, VISA, PSC, BC, OTHER). Note: the document_type query parameter filters by short codes (P, ID, DL, RP, ...), not by these display values. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class DocumentType extends StringEnum { - /** The value {@code "Passport"}. */ - public static final DocumentType PASSPORT = new DocumentType("Passport"); - - /** The value {@code "Identity Card"}. */ - public static final DocumentType IDENTITY_CARD = new DocumentType("Identity Card"); - - /** The value {@code "Driver's License"}. */ - public static final DocumentType DRIVER_S_LICENSE = new DocumentType("Driver's License"); - - /** The value {@code "Residence Permit"}. */ - public static final DocumentType RESIDENCE_PERMIT = new DocumentType("Residence Permit"); - - /** The value {@code "Health Insurance Card"}. */ - public static final DocumentType HEALTH_INSURANCE_CARD = new DocumentType("Health Insurance Card"); - - /** The value {@code "Tax Card"}. */ - public static final DocumentType TAX_CARD = new DocumentType("Tax Card"); - - private static final List KNOWN_VALUES = List.of(PASSPORT, IDENTITY_CARD, DRIVER_S_LICENSE, RESIDENCE_PERMIT, HEALTH_INSURANCE_CARD, TAX_CARD); - - private DocumentType(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static DocumentType of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, DocumentType::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page for the lifecycle. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Not Started"}. */ - public static final Status NOT_STARTED = new Status("Not Started"); - - /** The value {@code "In Progress"}. */ - public static final Status IN_PROGRESS = new Status("In Progress"); - - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); - - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); - - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); - - /** The value {@code "Expired"}. */ - public static final Status EXPIRED = new Status("Expired"); - - /** The value {@code "Abandoned"}. */ - public static final Status ABANDONED = new Status("Abandoned"); - - /** The value {@code "Kyc Expired"}. */ - public static final Status KYC_EXPIRED = new Status("Kyc Expired"); - - /** The value {@code "Resubmitted"}. */ - public static final Status RESUBMITTED = new Status("Resubmitted"); - - /** The value {@code "Awaiting User"}. */ - public static final Status AWAITING_USER = new Status("Awaiting User"); - - private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED, KYC_EXPIRED, RESUBMITTED, AWAITING_USER); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - public static final class Features extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("feature", "status"); - - private Features(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Features} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Features fromJson(JsonNode json) { - return new Features(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Verification feature identifier. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public SessionsListResponse.Results.User.Features.Feature feature() { - return Json.required(json, "feature", Conv.enumeration(SessionsListResponse.Results.User.Features.Feature::of)); - } - - /** - * Status of this feature. Not Finished until the step completes. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public SessionsListResponse.Results.User.Features.Status status() { - return Json.required(json, "status", Conv.enumeration(SessionsListResponse.Results.User.Features.Status::of)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Features}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Features from) { - super(from); - } - - /** - * Verification feature identifier. - * - * @param feature the value - * @return this builder - */ - public Builder feature(SessionsListResponse.Results.User.Features.Feature feature) { - return set("feature", Conv.enumeration(SessionsListResponse.Results.User.Features.Feature::of), feature); - } - - /** - * Status of this feature. Not Finished until the step completes. - * - * @param status the value - * @return this builder - */ - public Builder status(SessionsListResponse.Results.User.Features.Status status) { - return set("status", Conv.enumeration(SessionsListResponse.Results.User.Features.Status::of), status); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Features} - * @throws IllegalStateException when a required field is missing - */ - public Features build() { - require("feature", false); - require("status", false); - return new Features(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Verification feature identifier. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Feature extends StringEnum { - /** The value {@code "ID_VERIFICATION"}. */ - public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); - - /** The value {@code "NFC"}. */ - public static final Feature NFC = new Feature("NFC"); - - /** The value {@code "LIVENESS"}. */ - public static final Feature LIVENESS = new Feature("LIVENESS"); - - /** The value {@code "FACE_MATCH"}. */ - public static final Feature FACE_MATCH = new Feature("FACE_MATCH"); - - /** The value {@code "POA"}. */ - public static final Feature POA = new Feature("POA"); - - /** The value {@code "QUESTIONNAIRE"}. */ - public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); - - /** The value {@code "EMAIL_VERIFICATION"}. */ - public static final Feature EMAIL_VERIFICATION = new Feature("EMAIL_VERIFICATION"); - - /** The value {@code "PHONE"}. */ - public static final Feature PHONE = new Feature("PHONE"); - - /** The value {@code "AML"}. */ - public static final Feature AML = new Feature("AML"); - - /** The value {@code "IP_ANALYSIS"}. */ - public static final Feature IP_ANALYSIS = new Feature("IP_ANALYSIS"); - - /** The value {@code "AGE_ESTIMATION"}. */ - public static final Feature AGE_ESTIMATION = new Feature("AGE_ESTIMATION"); - - /** The value {@code "DATABASE_VALIDATION"}. */ - public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); - - private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACE_MATCH, POA, QUESTIONNAIRE, EMAIL_VERIFICATION, PHONE, AML, IP_ANALYSIS, AGE_ESTIMATION, DATABASE_VALIDATION); - - private Feature(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Feature of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Status of this feature. Not Finished until the step completes. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Not Finished"}. */ - public static final Status NOT_FINISHED = new Status("Not Finished"); - - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); - - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); - - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); - - /** The value {@code "Resub Requested"}. */ - public static final Status RESUB_REQUESTED = new Status("Resub Requested"); - - private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** - * Phone associated with the session: the verified phone when phone verification ran, otherwise the contact phone provided at session creation. null when neither exists. - */ - public static final class PhoneNumber extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("number", "is_verified"); - - private PhoneNumber(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code PhoneNumber} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static PhoneNumber fromJson(JsonNode json) { - return new PhoneNumber(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Phone number in E.164 format. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String number() { - return Json.required(json, "number", Conv.STRING); - } - - /** - * true only when the phone-verification feature approved this number. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public boolean isVerified() { - return Json.required(json, "is_verified", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link PhoneNumber}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(PhoneNumber from) { - super(from); - } - - /** - * Phone number in E.164 format. - * - * @param number the value - * @return this builder - */ - public Builder number(String number) { - return set("number", Conv.STRING, number); - } - - /** - * true only when the phone-verification feature approved this number. - * - * @param isVerified the value - * @return this builder - */ - public Builder isVerified(boolean isVerified) { - return set("is_verified", Conv.BOOLEAN, isVerified); - } - - /** - * Builds the value. - * - * @return a new immutable {@code PhoneNumber} - * @throws IllegalStateException when a required field is missing - */ - public PhoneNumber build() { - require("number", false); - require("is_verified", false); - return new PhoneNumber(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** - * Email associated with the session: the verified email when email verification ran, otherwise the contact email provided at session creation. null when neither exists. - */ - public static final class EmailAddress extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("email", "is_verified"); - - private EmailAddress(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code EmailAddress} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static EmailAddress fromJson(JsonNode json) { - return new EmailAddress(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The email field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String email() { - return Json.required(json, "email", Conv.STRING); - } - - /** - * true only when the email-verification feature approved this address. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public boolean isVerified() { - return Json.required(json, "is_verified", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link EmailAddress}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(EmailAddress from) { - super(from); - } - - /** - * The email field. - * - * @param email the value - * @return this builder - */ - public Builder email(String email) { - return set("email", Conv.STRING, email); - } - - /** - * true only when the email-verification feature approved this address. - * - * @param isVerified the value - * @return this builder - */ - public Builder isVerified(boolean isVerified) { - return set("is_verified", Conv.BOOLEAN, isVerified); - } - - /** - * Builds the value. - * - * @return a new immutable {@code EmailAddress} - * @throws IllegalStateException when a required field is missing - */ - public EmailAddress build() { - require("email", false); - require("is_verified", false); - return new EmailAddress(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** - * Compact per-feature extras. Only features that produced data appear as keys; null when no feature produced extras. Fields excluded by the workflow's response-attributes configuration are omitted. - */ - public static final class AdditionalDetails extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("aml", "database_validation", "poa", "liveness", "face_match", "phone_verification", "email_verification"); - - private AdditionalDetails(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code AdditionalDetails} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static AdditionalDetails fromJson(JsonNode json) { - return new AdditionalDetails(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Present when AML screening ran. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional aml() { - return Json.optional(json, "aml", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.Aml::fromJson)); - } - - /** - * Present when database validation ran. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional databaseValidation() { - return Json.optional(json, "database_validation", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.DatabaseValidation::fromJson)); - } - - /** - * Present when proof of address ran. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional poa() { - return Json.optional(json, "poa", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.Poa::fromJson)); - } - - /** - * Present when a liveness check ran. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional liveness() { - return Json.optional(json, "liveness", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.Liveness::fromJson)); - } - - /** - * Present when face match ran. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional faceMatch() { - return Json.optional(json, "face_match", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.FaceMatch::fromJson)); - } - - /** - * Present when phone verification ran. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional phoneVerification() { - return Json.optional(json, "phone_verification", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.PhoneVerification::fromJson)); - } - - /** - * Present when email verification ran. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional emailVerification() { - return Json.optional(json, "email_verification", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.EmailVerification::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link AdditionalDetails}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(AdditionalDetails from) { - super(from); - } - - /** - * Present when AML screening ran. - * - * @param aml the value - * @return this builder - */ - public Builder aml(SessionsListResponse.Results.User.AdditionalDetails.Aml aml) { - return set("aml", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.Aml::fromJson), aml); - } - - /** - * Present when database validation ran. - * - * @param databaseValidation the value - * @return this builder - */ - public Builder databaseValidation(SessionsListResponse.Results.User.AdditionalDetails.DatabaseValidation databaseValidation) { - return set("database_validation", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.DatabaseValidation::fromJson), databaseValidation); - } - - /** - * Present when proof of address ran. - * - * @param poa the value - * @return this builder - */ - public Builder poa(SessionsListResponse.Results.User.AdditionalDetails.Poa poa) { - return set("poa", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.Poa::fromJson), poa); - } - - /** - * Present when a liveness check ran. - * - * @param liveness the value - * @return this builder - */ - public Builder liveness(SessionsListResponse.Results.User.AdditionalDetails.Liveness liveness) { - return set("liveness", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.Liveness::fromJson), liveness); - } - - /** - * Present when face match ran. - * - * @param faceMatch the value - * @return this builder - */ - public Builder faceMatch(SessionsListResponse.Results.User.AdditionalDetails.FaceMatch faceMatch) { - return set("face_match", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.FaceMatch::fromJson), faceMatch); - } - - /** - * Present when phone verification ran. - * - * @param phoneVerification the value - * @return this builder - */ - public Builder phoneVerification(SessionsListResponse.Results.User.AdditionalDetails.PhoneVerification phoneVerification) { - return set("phone_verification", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.PhoneVerification::fromJson), phoneVerification); - } - - /** - * Present when email verification ran. - * - * @param emailVerification the value - * @return this builder - */ - public Builder emailVerification(SessionsListResponse.Results.User.AdditionalDetails.EmailVerification emailVerification) { - return set("email_verification", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.EmailVerification::fromJson), emailVerification); - } - - /** - * Builds the value. - * - * @return a new immutable {@code AdditionalDetails} - */ - public AdditionalDetails build() { - return new AdditionalDetails(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** Present when AML screening ran. */ - public static final class Aml extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("total_hits", "entity_type", "is_adverse_media_fetched", "screened_data"); - - private Aml(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Aml} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Aml fromJson(JsonNode json) { - return new Aml(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Number of AML screening hits. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional totalHits() { - return Json.optional(json, "total_hits", Conv.LONG); - } - - /** - * Screened entity type. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional entityType() { - return Json.optional(json, "entity_type", Conv.STRING); - } - - /** - * The is_adverse_media_fetched field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isAdverseMediaFetched() { - return Json.optional(json, "is_adverse_media_fetched", Conv.BOOLEAN); - } - - /** - * Subset of the data that was screened. Only non-empty keys are included. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedData() { - return Json.optional(json, "screened_data", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.Aml.ScreenedData::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Aml}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Aml from) { - super(from); - } - - /** - * Number of AML screening hits. - * - * @param totalHits the value - * @return this builder - */ - public Builder totalHits(long totalHits) { - return set("total_hits", Conv.LONG, totalHits); - } - - /** - * Screened entity type. - * - * @param entityType the value - * @return this builder - */ - public Builder entityType(String entityType) { - return set("entity_type", Conv.STRING, entityType); - } - - /** - * The is_adverse_media_fetched field. - * - * @param isAdverseMediaFetched the value - * @return this builder - */ - public Builder isAdverseMediaFetched(boolean isAdverseMediaFetched) { - return set("is_adverse_media_fetched", Conv.BOOLEAN, isAdverseMediaFetched); - } - - /** - * Subset of the data that was screened. Only non-empty keys are included. - * - * @param screenedData the value - * @return this builder - */ - public Builder screenedData(SessionsListResponse.Results.User.AdditionalDetails.Aml.ScreenedData screenedData) { - return set("screened_data", Conv.model(SessionsListResponse.Results.User.AdditionalDetails.Aml.ScreenedData::fromJson), screenedData); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Aml} - */ - public Aml build() { - return new Aml(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** Subset of the data that was screened. Only non-empty keys are included. */ - public static final class ScreenedData extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("nationality", "document_number", "date_of_birth"); - - private ScreenedData(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code ScreenedData} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static ScreenedData fromJson(JsonNode json) { - return new ScreenedData(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The nationality field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nationality() { - return Json.optional(json, "nationality", Conv.STRING); - } - - /** - * The document_number field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentNumber() { - return Json.optional(json, "document_number", Conv.STRING); - } - - /** - * The date_of_birth field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional dateOfBirth() { - return Json.optional(json, "date_of_birth", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link ScreenedData}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(ScreenedData from) { - super(from); - } - - /** - * The nationality field. - * - * @param nationality the value - * @return this builder - */ - public Builder nationality(String nationality) { - return set("nationality", Conv.STRING, nationality); - } - - /** - * The document_number field. - * - * @param documentNumber the value - * @return this builder - */ - public Builder documentNumber(String documentNumber) { - return set("document_number", Conv.STRING, documentNumber); - } - - /** - * The date_of_birth field. - * - * @param dateOfBirth the value - * @return this builder - */ - public Builder dateOfBirth(String dateOfBirth) { - return set("date_of_birth", Conv.STRING, dateOfBirth); - } - - /** - * Builds the value. - * - * @return a new immutable {@code ScreenedData} - */ - public ScreenedData build() { - return new ScreenedData(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - - /** Present when database validation ran. */ - public static final class DatabaseValidation extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("validation_type", "match_type", "identification_number"); - - private DatabaseValidation(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code DatabaseValidation} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static DatabaseValidation fromJson(JsonNode json) { - return new DatabaseValidation(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The validation_type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional validationType() { - return Json.optional(json, "validation_type", Conv.STRING); - } - - /** - * The match_type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional matchType() { - return Json.optional(json, "match_type", Conv.STRING); - } - - /** - * Tax, personal, or document number that was validated (first available). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional identificationNumber() { - return Json.optional(json, "identification_number", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link DatabaseValidation}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(DatabaseValidation from) { - super(from); - } - - /** - * The validation_type field. - * - * @param validationType the value - * @return this builder - */ - public Builder validationType(String validationType) { - return set("validation_type", Conv.STRING, validationType); - } - - /** - * The match_type field. - * - * @param matchType the value - * @return this builder - */ - public Builder matchType(String matchType) { - return set("match_type", Conv.STRING, matchType); - } - - /** - * Tax, personal, or document number that was validated (first available). - * - * @param identificationNumber the value - * @return this builder - */ - public Builder identificationNumber(String identificationNumber) { - return set("identification_number", Conv.STRING, identificationNumber); - } - - /** - * Builds the value. - * - * @return a new immutable {@code DatabaseValidation} - */ - public DatabaseValidation build() { - return new DatabaseValidation(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Present when proof of address ran. */ - public static final class Poa extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("document_type", "issuer", "address", "name_on_document"); - - private Poa(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Poa} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Poa fromJson(JsonNode json) { - return new Poa(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The document_type field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional documentType() { - return Json.optional(json, "document_type", Conv.STRING); - } - - /** - * The issuer field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional issuer() { - return Json.optional(json, "issuer", Conv.STRING); - } - - /** - * The address field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional address() { - return Json.optional(json, "address", Conv.STRING); - } - - /** - * The name_on_document field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional nameOnDocument() { - return Json.optional(json, "name_on_document", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Poa}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Poa from) { - super(from); - } - - /** - * The document_type field. - * - * @param documentType the value - * @return this builder - */ - public Builder documentType(String documentType) { - return set("document_type", Conv.STRING, documentType); - } - - /** - * The issuer field. - * - * @param issuer the value - * @return this builder - */ - public Builder issuer(String issuer) { - return set("issuer", Conv.STRING, issuer); - } - - /** - * The address field. - * - * @param address the value - * @return this builder - */ - public Builder address(String address) { - return set("address", Conv.STRING, address); - } - - /** - * The name_on_document field. - * - * @param nameOnDocument the value - * @return this builder - */ - public Builder nameOnDocument(String nameOnDocument) { - return set("name_on_document", Conv.STRING, nameOnDocument); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Poa} - */ - public Poa build() { - return new Poa(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Present when a liveness check ran. */ - public static final class Liveness extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("method", "score"); - - private Liveness(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Liveness} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Liveness fromJson(JsonNode json) { - return new Liveness(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The method field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional method() { - return Json.optional(json, "method", Conv.STRING); - } - - /** - * Liveness confidence score, rounded to 2 decimals. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional score() { - return Json.optional(json, "score", Conv.DOUBLE); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Liveness}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Liveness from) { - super(from); - } - - /** - * The method field. - * - * @param method the value - * @return this builder - */ - public Builder method(String method) { - return set("method", Conv.STRING, method); - } - - /** - * Liveness confidence score, rounded to 2 decimals. - * - * @param score the value - * @return this builder - */ - public Builder score(double score) { - return set("score", Conv.DOUBLE, score); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Liveness} - */ - public Liveness build() { - return new Liveness(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Present when face match ran. */ - public static final class FaceMatch extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("score", "source_image", "target_image"); - - private FaceMatch(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code FaceMatch} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static FaceMatch fromJson(JsonNode json) { - return new FaceMatch(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Face-match similarity score, rounded to 2 decimals. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional score() { - return Json.optional(json, "score", Conv.DOUBLE); - } - - /** - * Expiring (presigned) URL of the source face image. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sourceImage() { - return Json.optional(json, "source_image", Conv.STRING); - } - - /** - * Expiring (presigned) URL of the target face image. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional targetImage() { - return Json.optional(json, "target_image", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link FaceMatch}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(FaceMatch from) { - super(from); - } - - /** - * Face-match similarity score, rounded to 2 decimals. - * - * @param score the value - * @return this builder - */ - public Builder score(double score) { - return set("score", Conv.DOUBLE, score); - } - - /** - * Expiring (presigned) URL of the source face image. - * - * @param sourceImage the value - * @return this builder - */ - public Builder sourceImage(String sourceImage) { - return set("source_image", Conv.STRING, sourceImage); - } - - /** - * Expiring (presigned) URL of the target face image. - * - * @param targetImage the value - * @return this builder - */ - public Builder targetImage(String targetImage) { - return set("target_image", Conv.STRING, targetImage); - } - - /** - * Builds the value. - * - * @return a new immutable {@code FaceMatch} - */ - public FaceMatch build() { - return new FaceMatch(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Present when phone verification ran. */ - public static final class PhoneVerification extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("country_code", "is_temporary", "is_ported", "is_blocked"); - - private PhoneVerification(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code PhoneVerification} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static PhoneVerification fromJson(JsonNode json) { - return new PhoneVerification(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * ISO 3166-1 alpha-2 country of the phone number. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional countryCode() { - return Json.optional(json, "country_code", Conv.STRING); - } - - /** - * The is_temporary field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isTemporary() { - return Json.optional(json, "is_temporary", Conv.BOOLEAN); - } - - /** - * The is_ported field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isPorted() { - return Json.optional(json, "is_ported", Conv.BOOLEAN); - } - - /** - * The is_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isBlocked() { - return Json.optional(json, "is_blocked", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link PhoneVerification}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(PhoneVerification from) { - super(from); - } - - /** - * ISO 3166-1 alpha-2 country of the phone number. - * - * @param countryCode the value - * @return this builder - */ - public Builder countryCode(String countryCode) { - return set("country_code", Conv.STRING, countryCode); - } - - /** - * The is_temporary field. - * - * @param isTemporary the value - * @return this builder - */ - public Builder isTemporary(boolean isTemporary) { - return set("is_temporary", Conv.BOOLEAN, isTemporary); - } - - /** - * The is_ported field. - * - * @param isPorted the value - * @return this builder - */ - public Builder isPorted(boolean isPorted) { - return set("is_ported", Conv.BOOLEAN, isPorted); - } - - /** - * The is_blocked field. - * - * @param isBlocked the value - * @return this builder - */ - public Builder isBlocked(boolean isBlocked) { - return set("is_blocked", Conv.BOOLEAN, isBlocked); - } - - /** - * Builds the value. - * - * @return a new immutable {@code PhoneVerification} - */ - public PhoneVerification build() { - return new PhoneVerification(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Present when email verification ran. */ - public static final class EmailVerification extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("is_breached", "is_disposable", "is_undeliverable", "is_blocklisted"); - - private EmailVerification(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code EmailVerification} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static EmailVerification fromJson(JsonNode json) { - return new EmailVerification(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The is_breached field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isBreached() { - return Json.optional(json, "is_breached", Conv.BOOLEAN); - } - - /** - * The is_disposable field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isDisposable() { - return Json.optional(json, "is_disposable", Conv.BOOLEAN); - } - - /** - * The is_undeliverable field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isUndeliverable() { - return Json.optional(json, "is_undeliverable", Conv.BOOLEAN); - } - - /** - * The is_blocklisted field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isBlocklisted() { - return Json.optional(json, "is_blocklisted", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link EmailVerification}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(EmailVerification from) { - super(from); - } - - /** - * The is_breached field. - * - * @param isBreached the value - * @return this builder - */ - public Builder isBreached(boolean isBreached) { - return set("is_breached", Conv.BOOLEAN, isBreached); - } - - /** - * The is_disposable field. - * - * @param isDisposable the value - * @return this builder - */ - public Builder isDisposable(boolean isDisposable) { - return set("is_disposable", Conv.BOOLEAN, isDisposable); - } - - /** - * The is_undeliverable field. - * - * @param isUndeliverable the value - * @return this builder - */ - public Builder isUndeliverable(boolean isUndeliverable) { - return set("is_undeliverable", Conv.BOOLEAN, isUndeliverable); - } - - /** - * The is_blocklisted field. - * - * @param isBlocklisted the value - * @return this builder - */ - public Builder isBlocklisted(boolean isBlocklisted) { - return set("is_blocklisted", Conv.BOOLEAN, isBlocklisted); - } - - /** - * Builds the value. - * - * @return a new immutable {@code EmailVerification} - */ - public EmailVerification build() { - return new EmailVerification(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - } - - /** - * Summary row for a Business Verification (KYB) session. Returned only when session_kind is business or all. - */ - public static final class Business extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("session_id", "session_kind", "session_number", "status", "vendor_data", "created_at", "workflow_id", "workflow_type", "workflow_label", "company_name", "registration_number", "country"); - - private Business(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Business} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Business fromJson(JsonNode json) { - return new Business(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Unique identifier of the business verification session. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String sessionId() { - return Json.required(json, "session_id", Conv.STRING); - } - - /** - * Row discriminator. Always "business" for KYB rows. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public SessionsListResponse.Results.Business.SessionKind sessionKind() { - return Json.required(json, "session_kind", Conv.enumeration(SessionsListResponse.Results.Business.SessionKind::of)); - } - - /** - * Human-friendly sequential number of the business session within your application. Numbered independently from KYC sessions. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long sessionNumber() { - return Json.required(json, "session_number", Conv.LONG); - } - - /** - * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public SessionsListResponse.Results.Business.Status status() { - return Json.required(json, "status", Conv.enumeration(SessionsListResponse.Results.Business.Status::of)); - } - - /** - * Your own identifier for the business, echoed back exactly as sent at session creation. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); - } - - /** - * UTC timestamp when the session was created. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public OffsetDateTime createdAt() { - return Json.required(json, "created_at", Conv.DATE_TIME); - } - - /** - * Identifier of the workflow this session was created with. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional workflowId() { - return Json.optional(json, "workflow_id", Conv.STRING); - } - - /** - * Workflow type. kyb for business workflows. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional workflowType() { - return Json.optional(json, "workflow_type", Conv.STRING); - } - - /** - * Display name of the workflow. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional workflowLabel() { - return Json.optional(json, "workflow_label", Conv.STRING); - } - - /** - * Name of the company being verified, from the registry company linked to the session. null until a company is selected. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional companyName() { - return Json.optional(json, "company_name", Conv.STRING); - } - - /** - * Company registration number from the registry company linked to the session. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional registrationNumber() { - return Json.optional(json, "registration_number", Conv.STRING); - } - - /** - * Country code of the registry company linked to the session. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional country() { - return Json.optional(json, "country", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Business}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Business from) { - super(from); - } - - /** - * Unique identifier of the business verification session. - * - * @param sessionId the value - * @return this builder - */ - public Builder sessionId(String sessionId) { - return set("session_id", Conv.STRING, sessionId); - } - - /** - * Row discriminator. Always "business" for KYB rows. - * - * @param sessionKind the value - * @return this builder - */ - public Builder sessionKind(SessionsListResponse.Results.Business.SessionKind sessionKind) { - return set("session_kind", Conv.enumeration(SessionsListResponse.Results.Business.SessionKind::of), sessionKind); - } - - /** - * Human-friendly sequential number of the business session within your application. Numbered independently from KYC sessions. - * - * @param sessionNumber the value - * @return this builder - */ - public Builder sessionNumber(long sessionNumber) { - return set("session_number", Conv.LONG, sessionNumber); - } - - /** - * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page. - * - * @param status the value - * @return this builder - */ - public Builder status(SessionsListResponse.Results.Business.Status status) { - return set("status", Conv.enumeration(SessionsListResponse.Results.Business.Status::of), status); - } - - /** - * Your own identifier for the business, echoed back exactly as sent at session creation. - * - * @param vendorData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder vendorData(@Nullable String vendorData) { - return setNullable("vendor_data", Conv.STRING, vendorData); - } - - /** - * UTC timestamp when the session was created. - * - * @param createdAt the value - * @return this builder - */ - public Builder createdAt(OffsetDateTime createdAt) { - return set("created_at", Conv.DATE_TIME, createdAt); - } - - /** - * Identifier of the workflow this session was created with. - * - * @param workflowId the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder workflowId(@Nullable String workflowId) { - return setNullable("workflow_id", Conv.STRING, workflowId); - } - - /** - * Workflow type. kyb for business workflows. - * - * @param workflowType the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder workflowType(@Nullable String workflowType) { - return setNullable("workflow_type", Conv.STRING, workflowType); - } - - /** - * Display name of the workflow. - * - * @param workflowLabel the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder workflowLabel(@Nullable String workflowLabel) { - return setNullable("workflow_label", Conv.STRING, workflowLabel); - } - - /** - * Name of the company being verified, from the registry company linked to the session. null until a company is selected. - * - * @param companyName the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder companyName(@Nullable String companyName) { - return setNullable("company_name", Conv.STRING, companyName); - } - - /** - * Company registration number from the registry company linked to the session. - * - * @param registrationNumber the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder registrationNumber(@Nullable String registrationNumber) { - return setNullable("registration_number", Conv.STRING, registrationNumber); - } - - /** - * Country code of the registry company linked to the session. - * - * @param country the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder country(@Nullable String country) { - return setNullable("country", Conv.STRING, country); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Business} - * @throws IllegalStateException when a required field is missing - */ - public Business build() { - require("session_id", false); - require("session_kind", false); - require("session_number", false); - require("status", false); - require("vendor_data", true); - require("created_at", false); - require("workflow_id", true); - require("workflow_type", true); - require("workflow_label", true); - require("company_name", true); - require("registration_number", true); - require("country", true); - return new Business(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * Row discriminator. Always "business" for KYB rows. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class SessionKind extends StringEnum { - /** The value {@code "business"}. */ - public static final SessionKind BUSINESS = new SessionKind("business"); - - private static final List KNOWN_VALUES = List.of(BUSINESS); - - private SessionKind(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static SessionKind of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, SessionKind::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "Not Started"}. */ - public static final Status NOT_STARTED = new Status("Not Started"); - - /** The value {@code "In Progress"}. */ - public static final Status IN_PROGRESS = new Status("In Progress"); - - /** The value {@code "Approved"}. */ - public static final Status APPROVED = new Status("Approved"); - - /** The value {@code "Declined"}. */ - public static final Status DECLINED = new Status("Declined"); - - /** The value {@code "In Review"}. */ - public static final Status IN_REVIEW = new Status("In Review"); - - /** The value {@code "Expired"}. */ - public static final Status EXPIRED = new Status("Expired"); - - /** The value {@code "Abandoned"}. */ - public static final Status ABANDONED = new Status("Abandoned"); - - /** The value {@code "Kyc Expired"}. */ - public static final Status KYC_EXPIRED = new Status("Kyc Expired"); - - /** The value {@code "Resubmitted"}. */ - public static final Status RESUBMITTED = new Status("Resubmitted"); - - /** The value {@code "Awaiting User"}. */ - public static final Status AWAITING_USER = new Status("Awaiting User"); - - private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED, KYC_EXPIRED, RESUBMITTED, AWAITING_USER); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } + public Optional asBusiness() { + return isBusiness() ? Optional.of(Conv.model(BusinessSessionListItem::fromJson).read(json)) : Optional.empty(); } } } diff --git a/src/main/java/me/didit/types/SessionsPreviewDecisionEmailParams.java b/src/main/java/me/didit/types/SessionsPreviewDecisionEmailParams.java new file mode 100644 index 0000000..4384c99 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsPreviewDecisionEmailParams.java @@ -0,0 +1,165 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code sessions.preview_decision_email}. */ +public final class SessionsPreviewDecisionEmailParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("new_status", "email_language"); + + private SessionsPreviewDecisionEmailParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsPreviewDecisionEmailParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsPreviewDecisionEmailParams fromJson(JsonNode json) { + return new SessionsPreviewDecisionEmailParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The decision whose email you want to preview. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsPreviewDecisionEmailParams.NewStatus newStatus() { + return Json.required(json, "new_status", Conv.enumeration(SessionsPreviewDecisionEmailParams.NewStatus::of)); + } + + /** + * Language of the email, the same default as Update Session Status. A language that is not offered falls back to English; language in the response says which one is used. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailLanguage() { + return Json.optional(json, "email_language", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsPreviewDecisionEmailParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsPreviewDecisionEmailParams from) { + super(from); + } + + /** + * The decision whose email you want to preview. + * + * @param newStatus the value + * @return this builder + */ + public Builder newStatus(SessionsPreviewDecisionEmailParams.NewStatus newStatus) { + return set("new_status", Conv.enumeration(SessionsPreviewDecisionEmailParams.NewStatus::of), newStatus); + } + + /** + * Language of the email, the same default as Update Session Status. A language that is not offered falls back to English; language in the response says which one is used. + * + * @param emailLanguage the value + * @return this builder + */ + public Builder emailLanguage(String emailLanguage) { + return set("email_language", Conv.STRING, emailLanguage); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsPreviewDecisionEmailParams} + * @throws IllegalStateException when a required field is missing + */ + public SessionsPreviewDecisionEmailParams build() { + require("new_status", false); + return new SessionsPreviewDecisionEmailParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * The decision whose email you want to preview. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NewStatus extends StringEnum { + /** The value {@code "Approved"}. */ + public static final NewStatus APPROVED = new NewStatus("Approved"); + + /** The value {@code "Declined"}. */ + public static final NewStatus DECLINED = new NewStatus("Declined"); + + private static final List KNOWN_VALUES = List.of(APPROVED, DECLINED); + + private NewStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NewStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NewStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionsPreviewDecisionEmailResponse.java b/src/main/java/me/didit/types/SessionsPreviewDecisionEmailResponse.java new file mode 100644 index 0000000..d1d27a4 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsPreviewDecisionEmailResponse.java @@ -0,0 +1,471 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class SessionsPreviewDecisionEmailResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("recipient", "recipient_source", "sender", "sender_name", "sender_email", "sender_kind", "subject", "html", "language", "will_be_sent", "not_sent_reason", "preview_token"); + + private SessionsPreviewDecisionEmailResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsPreviewDecisionEmailResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsPreviewDecisionEmailResponse fromJson(JsonNode json) { + return new SessionsPreviewDecisionEmailResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The address the email goes to. An empty string when no address is known for the person: then no email can be sent and preview_token is empty too. Pass it as email_address when you confirm. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String recipient() { + return Json.required(json, "recipient", Conv.STRING); + } + + /** + * Where recipient comes from. email_verification: the address the person verified in an email verification step of the flow. contact_details: the address given in contact_details when the session was created. An empty string when there is no address. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsPreviewDecisionEmailResponse.RecipientSource recipientSource() { + return Json.required(json, "recipient_source", Conv.enumeration(SessionsPreviewDecisionEmailResponse.RecipientSource::of)); + } + + /** + * The From header the email goes out with. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sender() { + return Json.required(json, "sender", Conv.STRING); + } + + /** + * The display name part of sender. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String senderName() { + return Json.required(json, "sender_name", Conv.STRING); + } + + /** + * The address part of sender. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String senderEmail() { + return Json.required(json, "sender_email", Conv.STRING); + } + + /** + * customer: the email goes out from your application's verified sender domain (white label). didit: it goes out from Didit's own sender. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsPreviewDecisionEmailResponse.SenderKind senderKind() { + return Json.required(json, "sender_kind", Conv.enumeration(SessionsPreviewDecisionEmailResponse.SenderKind::of)); + } + + /** + * The subject line exactly as it is sent. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String subject() { + return Json.required(json, "subject", Conv.STRING); + } + + /** + * The email's HTML exactly as it is sent, with its logo embedded as a data URL. Render it in a sandboxed frame, never directly in your application DOM. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String html() { + return Json.required(json, "html", Conv.STRING); + } + + /** + * The language the email is written in. English (en) when the email_language you asked for is not offered. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String language() { + return Json.required(json, "language", Conv.STRING); + } + + /** + * Whether confirming the change with send_email: true sends this email. false when not_sent_reason is set. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean willBeSent() { + return Json.required(json, "will_be_sent", Conv.BOOLEAN); + } + + /** + * Why confirming would send no email, or null when it will be sent. status_unchanged: the session already has this status, so the update changes nothing and sends nothing. notification_emails_disabled: the session was created without contact_details.send_notification_emails: true, so no decision email goes to the address given in contact_details. no_recipient: no address is known for the person. sandbox: a sandbox session whose recipient has not opted into sandbox email delivery, so the send is only simulated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional notSentReason() { + return Json.optional(json, "not_sent_reason", Conv.enumeration(SessionsPreviewDecisionEmailResponse.NotSentReason::of)); + } + + /** + * Signed receipt for this exact email, valid for 15 minutes. Pass it as email_preview_token to [Update Session Status](/sessions-api/update-status). An empty string when there is no recipient. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String previewToken() { + return Json.required(json, "preview_token", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsPreviewDecisionEmailResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsPreviewDecisionEmailResponse from) { + super(from); + } + + /** + * The address the email goes to. An empty string when no address is known for the person: then no email can be sent and preview_token is empty too. Pass it as email_address when you confirm. + * + * @param recipient the value + * @return this builder + */ + public Builder recipient(String recipient) { + return set("recipient", Conv.STRING, recipient); + } + + /** + * Where recipient comes from. email_verification: the address the person verified in an email verification step of the flow. contact_details: the address given in contact_details when the session was created. An empty string when there is no address. + * + * @param recipientSource the value + * @return this builder + */ + public Builder recipientSource(SessionsPreviewDecisionEmailResponse.RecipientSource recipientSource) { + return set("recipient_source", Conv.enumeration(SessionsPreviewDecisionEmailResponse.RecipientSource::of), recipientSource); + } + + /** + * The From header the email goes out with. + * + * @param sender the value + * @return this builder + */ + public Builder sender(String sender) { + return set("sender", Conv.STRING, sender); + } + + /** + * The display name part of sender. + * + * @param senderName the value + * @return this builder + */ + public Builder senderName(String senderName) { + return set("sender_name", Conv.STRING, senderName); + } + + /** + * The address part of sender. + * + * @param senderEmail the value + * @return this builder + */ + public Builder senderEmail(String senderEmail) { + return set("sender_email", Conv.STRING, senderEmail); + } + + /** + * customer: the email goes out from your application's verified sender domain (white label). didit: it goes out from Didit's own sender. + * + * @param senderKind the value + * @return this builder + */ + public Builder senderKind(SessionsPreviewDecisionEmailResponse.SenderKind senderKind) { + return set("sender_kind", Conv.enumeration(SessionsPreviewDecisionEmailResponse.SenderKind::of), senderKind); + } + + /** + * The subject line exactly as it is sent. + * + * @param subject the value + * @return this builder + */ + public Builder subject(String subject) { + return set("subject", Conv.STRING, subject); + } + + /** + * The email's HTML exactly as it is sent, with its logo embedded as a data URL. Render it in a sandboxed frame, never directly in your application DOM. + * + * @param html the value + * @return this builder + */ + public Builder html(String html) { + return set("html", Conv.STRING, html); + } + + /** + * The language the email is written in. English (en) when the email_language you asked for is not offered. + * + * @param language the value + * @return this builder + */ + public Builder language(String language) { + return set("language", Conv.STRING, language); + } + + /** + * Whether confirming the change with send_email: true sends this email. false when not_sent_reason is set. + * + * @param willBeSent the value + * @return this builder + */ + public Builder willBeSent(boolean willBeSent) { + return set("will_be_sent", Conv.BOOLEAN, willBeSent); + } + + /** + * Why confirming would send no email, or null when it will be sent. status_unchanged: the session already has this status, so the update changes nothing and sends nothing. notification_emails_disabled: the session was created without contact_details.send_notification_emails: true, so no decision email goes to the address given in contact_details. no_recipient: no address is known for the person. sandbox: a sandbox session whose recipient has not opted into sandbox email delivery, so the send is only simulated. + * + * @param notSentReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder notSentReason(SessionsPreviewDecisionEmailResponse.@Nullable NotSentReason notSentReason) { + return setNullable("not_sent_reason", Conv.enumeration(SessionsPreviewDecisionEmailResponse.NotSentReason::of), notSentReason); + } + + /** + * Signed receipt for this exact email, valid for 15 minutes. Pass it as email_preview_token to [Update Session Status](/sessions-api/update-status). An empty string when there is no recipient. + * + * @param previewToken the value + * @return this builder + */ + public Builder previewToken(String previewToken) { + return set("preview_token", Conv.STRING, previewToken); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsPreviewDecisionEmailResponse} + * @throws IllegalStateException when a required field is missing + */ + public SessionsPreviewDecisionEmailResponse build() { + require("recipient", false); + require("recipient_source", false); + require("sender", false); + require("sender_name", false); + require("sender_email", false); + require("sender_kind", false); + require("subject", false); + require("html", false); + require("language", false); + require("will_be_sent", false); + require("not_sent_reason", true); + require("preview_token", false); + return new SessionsPreviewDecisionEmailResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Where recipient comes from. email_verification: the address the person verified in an email verification step of the flow. contact_details: the address given in contact_details when the session was created. An empty string when there is no address. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class RecipientSource extends StringEnum { + /** The value {@code "email_verification"}. */ + public static final RecipientSource EMAIL_VERIFICATION = new RecipientSource("email_verification"); + + /** The value {@code "contact_details"}. */ + public static final RecipientSource CONTACT_DETAILS = new RecipientSource("contact_details"); + + /** The value {@code ""}. */ + public static final RecipientSource EMPTY = new RecipientSource(""); + + private static final List KNOWN_VALUES = List.of(EMAIL_VERIFICATION, CONTACT_DETAILS, EMPTY); + + private RecipientSource(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static RecipientSource of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RecipientSource::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * customer: the email goes out from your application's verified sender domain (white label). didit: it goes out from Didit's own sender. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SenderKind extends StringEnum { + /** The value {@code "customer"}. */ + public static final SenderKind CUSTOMER = new SenderKind("customer"); + + /** The value {@code "didit"}. */ + public static final SenderKind DIDIT = new SenderKind("didit"); + + private static final List KNOWN_VALUES = List.of(CUSTOMER, DIDIT); + + private SenderKind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SenderKind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SenderKind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Why confirming would send no email, or null when it will be sent. status_unchanged: the session already has this status, so the update changes nothing and sends nothing. notification_emails_disabled: the session was created without contact_details.send_notification_emails: true, so no decision email goes to the address given in contact_details. no_recipient: no address is known for the person. sandbox: a sandbox session whose recipient has not opted into sandbox email delivery, so the send is only simulated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NotSentReason extends StringEnum { + /** The value {@code "status_unchanged"}. */ + public static final NotSentReason STATUS_UNCHANGED = new NotSentReason("status_unchanged"); + + /** The value {@code "notification_emails_disabled"}. */ + public static final NotSentReason NOTIFICATION_EMAILS_DISABLED = new NotSentReason("notification_emails_disabled"); + + /** The value {@code "no_recipient"}. */ + public static final NotSentReason NO_RECIPIENT = new NotSentReason("no_recipient"); + + /** The value {@code "sandbox"}. */ + public static final NotSentReason SANDBOX = new NotSentReason("sandbox"); + + private static final List KNOWN_VALUES = List.of(STATUS_UNCHANGED, NOTIFICATION_EMAILS_DISABLED, NO_RECIPIENT, SANDBOX); + + private NotSentReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NotSentReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NotSentReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionsPreviewResubmissionParams.java b/src/main/java/me/didit/types/SessionsPreviewResubmissionParams.java index e6be30c..d5485df 100644 --- a/src/main/java/me/didit/types/SessionsPreviewResubmissionParams.java +++ b/src/main/java/me/didit/types/SessionsPreviewResubmissionParams.java @@ -61,13 +61,13 @@ public Optional> nodesTo } /** - * Recipient shown in the preview. No email is sent. + * Optional. Send the email to this address instead of the person's own. Without it, the recipient is the address the person verified in the flow, else the one given in contact_details. No email is sent by this call. * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public String emailAddress() { - return Json.required(json, "email_address", Conv.STRING); + public Optional emailAddress() { + return Json.optional(json, "email_address", Conv.STRING); } /** @@ -104,7 +104,7 @@ public Builder nodesToResubmit(Listcontact_details. No email is sent by this call. * * @param emailAddress the value * @return this builder @@ -127,10 +127,8 @@ public Builder emailLanguage(String emailLanguage) { * Builds the value. * * @return a new immutable {@code SessionsPreviewResubmissionParams} - * @throws IllegalStateException when a required field is missing */ public SessionsPreviewResubmissionParams build() { - require("email_address", false); return new SessionsPreviewResubmissionParams(buildJson(), buildFiles()); } diff --git a/src/main/java/me/didit/types/SessionsPreviewResubmissionResponse.java b/src/main/java/me/didit/types/SessionsPreviewResubmissionResponse.java index c1fdf92..39d7c08 100644 --- a/src/main/java/me/didit/types/SessionsPreviewResubmissionResponse.java +++ b/src/main/java/me/didit/types/SessionsPreviewResubmissionResponse.java @@ -5,15 +5,17 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import me.didit.core.Conv; import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; public final class SessionsPreviewResubmissionResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("recipient", "sender", "subject", "html", "nodes_to_resubmit", "requires_user_action", "preview_token"); + private static final Set KNOWN_FIELDS = Set.of("recipient", "recipient_source", "sender", "sender_name", "sender_email", "sender_kind", "subject", "html", "language", "will_be_sent", "not_sent_reason", "nodes_to_resubmit", "requires_user_action", "preview_token"); private SessionsPreviewResubmissionResponse(ObjectNode json, Map> files) { super(json, files); @@ -49,7 +51,7 @@ public Builder toBuilder() { } /** - * The recipient field. + * The address the email goes to. An empty string when no address is known for the person: then no email can be sent and preview_token is empty too. Pass it as email_address when you confirm. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -59,7 +61,17 @@ public String recipient() { } /** - * The sender field. + * Where recipient comes from. request: the email_address you passed. email_verification: the address the person verified in an email verification step of the flow. contact_details: the address given in contact_details when the session was created. An empty string when there is no address. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsPreviewResubmissionResponse.RecipientSource recipientSource() { + return Json.required(json, "recipient_source", Conv.enumeration(SessionsPreviewResubmissionResponse.RecipientSource::of)); + } + + /** + * The From header the email goes out with. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -69,7 +81,37 @@ public String sender() { } /** - * The subject field. + * The display name part of sender. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String senderName() { + return Json.required(json, "sender_name", Conv.STRING); + } + + /** + * The address part of sender. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String senderEmail() { + return Json.required(json, "sender_email", Conv.STRING); + } + + /** + * customer: the email goes out from your application's verified sender domain (white label). didit: it goes out from Didit's own sender. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsPreviewResubmissionResponse.SenderKind senderKind() { + return Json.required(json, "sender_kind", Conv.enumeration(SessionsPreviewResubmissionResponse.SenderKind::of)); + } + + /** + * The subject line exactly as it is sent. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -79,7 +121,7 @@ public String subject() { } /** - * Rendered email. Inline image attachments use data URLs for preview. + * The email's HTML exactly as it is sent, with its logo embedded as a data URL. Render it in a sandboxed frame, never directly in your application DOM. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -88,6 +130,36 @@ public String html() { return Json.required(json, "html", Conv.STRING); } + /** + * The language the email is written in. English (en) when the email_language you asked for is not offered. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String language() { + return Json.required(json, "language", Conv.STRING); + } + + /** + * Whether confirming the change with send_email: true sends this email. false when not_sent_reason is set. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean willBeSent() { + return Json.required(json, "will_be_sent", Conv.BOOLEAN); + } + + /** + * Why confirming would send no email, or null when it will be sent. no_user_action: every selected step runs without the person, so the resubmission finishes on its own and no email goes out. no_recipient: no address is known for the person and you passed none. sandbox: a sandbox session whose recipient has not opted into sandbox email delivery, so the send is only simulated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional notSentReason() { + return Json.optional(json, "not_sent_reason", Conv.enumeration(SessionsPreviewResubmissionResponse.NotSentReason::of)); + } + /** * The nodes_to_resubmit field. * @@ -109,7 +181,7 @@ public boolean requiresUserAction() { } /** - * Signed confirmation receipt, valid for 15 minutes. Pass it as email_preview_token when confirming the same message. + * Signed receipt for this exact email, valid for 15 minutes. Pass it as email_preview_token to [Update Session Status](/sessions-api/update-status). An empty string when there is no recipient. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -132,7 +204,7 @@ private Builder(SessionsPreviewResubmissionResponse from) { } /** - * The recipient field. + * The address the email goes to. An empty string when no address is known for the person: then no email can be sent and preview_token is empty too. Pass it as email_address when you confirm. * * @param recipient the value * @return this builder @@ -142,7 +214,17 @@ public Builder recipient(String recipient) { } /** - * The sender field. + * Where recipient comes from. request: the email_address you passed. email_verification: the address the person verified in an email verification step of the flow. contact_details: the address given in contact_details when the session was created. An empty string when there is no address. + * + * @param recipientSource the value + * @return this builder + */ + public Builder recipientSource(SessionsPreviewResubmissionResponse.RecipientSource recipientSource) { + return set("recipient_source", Conv.enumeration(SessionsPreviewResubmissionResponse.RecipientSource::of), recipientSource); + } + + /** + * The From header the email goes out with. * * @param sender the value * @return this builder @@ -152,7 +234,37 @@ public Builder sender(String sender) { } /** - * The subject field. + * The display name part of sender. + * + * @param senderName the value + * @return this builder + */ + public Builder senderName(String senderName) { + return set("sender_name", Conv.STRING, senderName); + } + + /** + * The address part of sender. + * + * @param senderEmail the value + * @return this builder + */ + public Builder senderEmail(String senderEmail) { + return set("sender_email", Conv.STRING, senderEmail); + } + + /** + * customer: the email goes out from your application's verified sender domain (white label). didit: it goes out from Didit's own sender. + * + * @param senderKind the value + * @return this builder + */ + public Builder senderKind(SessionsPreviewResubmissionResponse.SenderKind senderKind) { + return set("sender_kind", Conv.enumeration(SessionsPreviewResubmissionResponse.SenderKind::of), senderKind); + } + + /** + * The subject line exactly as it is sent. * * @param subject the value * @return this builder @@ -162,7 +274,7 @@ public Builder subject(String subject) { } /** - * Rendered email. Inline image attachments use data URLs for preview. + * The email's HTML exactly as it is sent, with its logo embedded as a data URL. Render it in a sandboxed frame, never directly in your application DOM. * * @param html the value * @return this builder @@ -171,6 +283,36 @@ public Builder html(String html) { return set("html", Conv.STRING, html); } + /** + * The language the email is written in. English (en) when the email_language you asked for is not offered. + * + * @param language the value + * @return this builder + */ + public Builder language(String language) { + return set("language", Conv.STRING, language); + } + + /** + * Whether confirming the change with send_email: true sends this email. false when not_sent_reason is set. + * + * @param willBeSent the value + * @return this builder + */ + public Builder willBeSent(boolean willBeSent) { + return set("will_be_sent", Conv.BOOLEAN, willBeSent); + } + + /** + * Why confirming would send no email, or null when it will be sent. no_user_action: every selected step runs without the person, so the resubmission finishes on its own and no email goes out. no_recipient: no address is known for the person and you passed none. sandbox: a sandbox session whose recipient has not opted into sandbox email delivery, so the send is only simulated. + * + * @param notSentReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder notSentReason(SessionsPreviewResubmissionResponse.@Nullable NotSentReason notSentReason) { + return setNullable("not_sent_reason", Conv.enumeration(SessionsPreviewResubmissionResponse.NotSentReason::of), notSentReason); + } + /** * The nodes_to_resubmit field. * @@ -192,7 +334,7 @@ public Builder requiresUserAction(boolean requiresUserAction) { } /** - * Signed confirmation receipt, valid for 15 minutes. Pass it as email_preview_token when confirming the same message. + * Signed receipt for this exact email, valid for 15 minutes. Pass it as email_preview_token to [Update Session Status](/sessions-api/update-status). An empty string when there is no recipient. * * @param previewToken the value * @return this builder @@ -209,9 +351,16 @@ public Builder previewToken(String previewToken) { */ public SessionsPreviewResubmissionResponse build() { require("recipient", false); + require("recipient_source", false); require("sender", false); + require("sender_name", false); + require("sender_email", false); + require("sender_kind", false); require("subject", false); require("html", false); + require("language", false); + require("will_be_sent", false); + require("not_sent_reason", true); require("nodes_to_resubmit", false); require("requires_user_action", false); require("preview_token", false); @@ -224,6 +373,144 @@ protected Builder self() { } } + /** + * Where recipient comes from. request: the email_address you passed. email_verification: the address the person verified in an email verification step of the flow. contact_details: the address given in contact_details when the session was created. An empty string when there is no address. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class RecipientSource extends StringEnum { + /** The value {@code "email_verification"}. */ + public static final RecipientSource EMAIL_VERIFICATION = new RecipientSource("email_verification"); + + /** The value {@code "contact_details"}. */ + public static final RecipientSource CONTACT_DETAILS = new RecipientSource("contact_details"); + + /** The value {@code "request"}. */ + public static final RecipientSource REQUEST = new RecipientSource("request"); + + /** The value {@code ""}. */ + public static final RecipientSource EMPTY = new RecipientSource(""); + + private static final List KNOWN_VALUES = List.of(EMAIL_VERIFICATION, CONTACT_DETAILS, REQUEST, EMPTY); + + private RecipientSource(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static RecipientSource of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RecipientSource::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * customer: the email goes out from your application's verified sender domain (white label). didit: it goes out from Didit's own sender. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SenderKind extends StringEnum { + /** The value {@code "customer"}. */ + public static final SenderKind CUSTOMER = new SenderKind("customer"); + + /** The value {@code "didit"}. */ + public static final SenderKind DIDIT = new SenderKind("didit"); + + private static final List KNOWN_VALUES = List.of(CUSTOMER, DIDIT); + + private SenderKind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SenderKind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SenderKind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Why confirming would send no email, or null when it will be sent. no_user_action: every selected step runs without the person, so the resubmission finishes on its own and no email goes out. no_recipient: no address is known for the person and you passed none. sandbox: a sandbox session whose recipient has not opted into sandbox email delivery, so the send is only simulated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NotSentReason extends StringEnum { + /** The value {@code "no_user_action"}. */ + public static final NotSentReason NO_USER_ACTION = new NotSentReason("no_user_action"); + + /** The value {@code "no_recipient"}. */ + public static final NotSentReason NO_RECIPIENT = new NotSentReason("no_recipient"); + + /** The value {@code "sandbox"}. */ + public static final NotSentReason SANDBOX = new NotSentReason("sandbox"); + + private static final List KNOWN_VALUES = List.of(NO_USER_ACTION, NO_RECIPIENT, SANDBOX); + + private NotSentReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NotSentReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NotSentReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + public static final class NodesToResubmit extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("node_id", "feature"); diff --git a/src/main/java/me/didit/types/BusinessSessionsListNetworksResponse.java b/src/main/java/me/didit/types/SessionsSignaturesDownloadEvidenceParams.java similarity index 51% rename from src/main/java/me/didit/types/BusinessSessionsListNetworksResponse.java rename to src/main/java/me/didit/types/SessionsSignaturesDownloadEvidenceParams.java index f721f55..ce0c842 100644 --- a/src/main/java/me/didit/types/BusinessSessionsListNetworksResponse.java +++ b/src/main/java/me/didit/types/SessionsSignaturesDownloadEvidenceParams.java @@ -12,22 +12,23 @@ import me.didit.core.Json; import me.didit.core.JsonModel; -public final class BusinessSessionsListNetworksResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("results"); +/** Parameters for {@code sessions.signatures.download_evidence}. */ +public final class SessionsSignaturesDownloadEvidenceParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("signature_id"); - private BusinessSessionsListNetworksResponse(ObjectNode json, Map> files) { + private SessionsSignaturesDownloadEvidenceParams(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code BusinessSessionsListNetworksResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code SessionsSignaturesDownloadEvidenceParams} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static BusinessSessionsListNetworksResponse fromJson(JsonNode json) { - return new BusinessSessionsListNetworksResponse(Json.copyObject(json), Map.of()); + public static SessionsSignaturesDownloadEvidenceParams fromJson(JsonNode json) { + return new SessionsSignaturesDownloadEvidenceParams(Json.copyObject(json), Map.of()); } /** @@ -49,13 +50,13 @@ public Builder toBuilder() { } /** - * The results field. + * id of the signer's item in signatures[]. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(EntityNetworkMembership::fromJson))); + public Optional signatureId() { + return Json.optional(json, "signature_id", Conv.STRING); } @Override @@ -63,31 +64,31 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link BusinessSessionsListNetworksResponse}. */ + /** Builds a {@link SessionsSignaturesDownloadEvidenceParams}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(BusinessSessionsListNetworksResponse from) { + private Builder(SessionsSignaturesDownloadEvidenceParams from) { super(from); } /** - * The results field. + * id of the signer's item in signatures[]. * - * @param results the value + * @param signatureId the value * @return this builder */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(EntityNetworkMembership::fromJson)), results); + public Builder signatureId(String signatureId) { + return set("signature_id", Conv.STRING, signatureId); } /** * Builds the value. * - * @return a new immutable {@code BusinessSessionsListNetworksResponse} + * @return a new immutable {@code SessionsSignaturesDownloadEvidenceParams} */ - public BusinessSessionsListNetworksResponse build() { - return new BusinessSessionsListNetworksResponse(buildJson(), buildFiles()); + public SessionsSignaturesDownloadEvidenceParams build() { + return new SessionsSignaturesDownloadEvidenceParams(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/SessionsSignaturesValidateParams.java b/src/main/java/me/didit/types/SessionsSignaturesValidateParams.java new file mode 100644 index 0000000..c508670 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsSignaturesValidateParams.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code sessions.signatures.validate}. */ +public final class SessionsSignaturesValidateParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("signature_id"); + + private SessionsSignaturesValidateParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsSignaturesValidateParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsSignaturesValidateParams fromJson(JsonNode json) { + return new SessionsSignaturesValidateParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * id of one signer's item in signatures[]. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional signatureId() { + return Json.optional(json, "signature_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsSignaturesValidateParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsSignaturesValidateParams from) { + super(from); + } + + /** + * id of one signer's item in signatures[]. + * + * @param signatureId the value + * @return this builder + */ + public Builder signatureId(String signatureId) { + return set("signature_id", Conv.STRING, signatureId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsSignaturesValidateParams} + */ + public SessionsSignaturesValidateParams build() { + return new SessionsSignaturesValidateParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsSignaturesValidateResponse.java b/src/main/java/me/didit/types/SessionsSignaturesValidateResponse.java new file mode 100644 index 0000000..f8a0347 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsSignaturesValidateResponse.java @@ -0,0 +1,98 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class SessionsSignaturesValidateResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("signatures"); + + private SessionsSignaturesValidateResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsSignaturesValidateResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsSignaturesValidateResponse fromJson(JsonNode json) { + return new SessionsSignaturesValidateResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The signatures field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> signatures() { + return Json.optional(json, "signatures", Conv.list(Conv.model(SignatureResult::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsSignaturesValidateResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsSignaturesValidateResponse from) { + super(from); + } + + /** + * The signatures field. + * + * @param signatures the value + * @return this builder + */ + public Builder signatures(List signatures) { + return set("signatures", Conv.list(Conv.model(SignatureResult::fromJson)), signatures); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsSignaturesValidateResponse} + */ + public SessionsSignaturesValidateResponse build() { + return new SessionsSignaturesValidateResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/SessionsUpdateAmlHitStatusResponse.java b/src/main/java/me/didit/types/SessionsUpdateAmlHitStatusResponse.java new file mode 100644 index 0000000..2184fb3 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsUpdateAmlHitStatusResponse.java @@ -0,0 +1,191 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class SessionsUpdateAmlHitStatusResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("message", "hit_id", "review_status"); + + private SessionsUpdateAmlHitStatusResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsUpdateAmlHitStatusResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsUpdateAmlHitStatusResponse fromJson(JsonNode json) { + return new SessionsUpdateAmlHitStatusResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Always Hit status updated successfully. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String message() { + return Json.required(json, "message", Conv.STRING); + } + + /** + * The updated hit, with surrounding whitespace removed. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String hitId() { + return Json.required(json, "hit_id", Conv.STRING); + } + + /** + * The review status now stored on the hit. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsUpdateAmlHitStatusResponse.ReviewStatus reviewStatus() { + return Json.required(json, "review_status", Conv.enumeration(SessionsUpdateAmlHitStatusResponse.ReviewStatus::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsUpdateAmlHitStatusResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsUpdateAmlHitStatusResponse from) { + super(from); + } + + /** + * Always Hit status updated successfully. + * + * @param message the value + * @return this builder + */ + public Builder message(String message) { + return set("message", Conv.STRING, message); + } + + /** + * The updated hit, with surrounding whitespace removed. + * + * @param hitId the value + * @return this builder + */ + public Builder hitId(String hitId) { + return set("hit_id", Conv.STRING, hitId); + } + + /** + * The review status now stored on the hit. + * + * @param reviewStatus the value + * @return this builder + */ + public Builder reviewStatus(SessionsUpdateAmlHitStatusResponse.ReviewStatus reviewStatus) { + return set("review_status", Conv.enumeration(SessionsUpdateAmlHitStatusResponse.ReviewStatus::of), reviewStatus); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsUpdateAmlHitStatusResponse} + * @throws IllegalStateException when a required field is missing + */ + public SessionsUpdateAmlHitStatusResponse build() { + require("message", false); + require("hit_id", false); + require("review_status", false); + return new SessionsUpdateAmlHitStatusResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * The review status now stored on the hit. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ReviewStatus extends StringEnum { + /** The value {@code "Unreviewed"}. */ + public static final ReviewStatus UNREVIEWED = new ReviewStatus("Unreviewed"); + + /** The value {@code "Confirmed Match"}. */ + public static final ReviewStatus CONFIRMED_MATCH = new ReviewStatus("Confirmed Match"); + + /** The value {@code "False Positive"}. */ + public static final ReviewStatus FALSE_POSITIVE = new ReviewStatus("False Positive"); + + /** The value {@code "Inconclusive"}. */ + public static final ReviewStatus INCONCLUSIVE = new ReviewStatus("Inconclusive"); + + private static final List KNOWN_VALUES = List.of(UNREVIEWED, CONFIRMED_MATCH, FALSE_POSITIVE, INCONCLUSIVE); + + private ReviewStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ReviewStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ReviewStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionsUpdateAmlStatusParams.java b/src/main/java/me/didit/types/SessionsUpdateAmlStatusParams.java new file mode 100644 index 0000000..6070b70 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsUpdateAmlStatusParams.java @@ -0,0 +1,194 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code sessions.update_aml_status}. */ +public final class SessionsUpdateAmlStatusParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("node_id_query", "status", "node_id"); + + private SessionsUpdateAmlStatusParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsUpdateAmlStatusParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsUpdateAmlStatusParams fromJson(JsonNode json) { + return new SessionsUpdateAmlStatusParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Workflow graph node ID of the AML check to update. Needed only when the session ran more than one AML step. Takes precedence over node_id in the body. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeIdQuery() { + return Json.optional(json, "node_id_query", Conv.STRING); + } + + /** + * New status of the AML check. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsUpdateAmlStatusParams.Status status() { + return Json.required(json, "status", Conv.enumeration(SessionsUpdateAmlStatusParams.Status::of)); + } + + /** + * Workflow graph node ID of the AML check, as an alternative to the query parameter. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsUpdateAmlStatusParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsUpdateAmlStatusParams from) { + super(from); + } + + /** + * Workflow graph node ID of the AML check to update. Needed only when the session ran more than one AML step. Takes precedence over node_id in the body. + * + * @param nodeIdQuery the value + * @return this builder + */ + public Builder nodeIdQuery(String nodeIdQuery) { + return set("node_id_query", Conv.STRING, nodeIdQuery); + } + + /** + * New status of the AML check. + * + * @param status the value + * @return this builder + */ + public Builder status(SessionsUpdateAmlStatusParams.Status status) { + return set("status", Conv.enumeration(SessionsUpdateAmlStatusParams.Status::of), status); + } + + /** + * Workflow graph node ID of the AML check, as an alternative to the query parameter. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsUpdateAmlStatusParams} + * @throws IllegalStateException when a required field is missing + */ + public SessionsUpdateAmlStatusParams build() { + require("status", false); + return new SessionsUpdateAmlStatusParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * New status of the AML check. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Resub Requested"}. */ + public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionsUpdateAmlStatusResponse.java b/src/main/java/me/didit/types/SessionsUpdateAmlStatusResponse.java new file mode 100644 index 0000000..4f8b1e7 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsUpdateAmlStatusResponse.java @@ -0,0 +1,246 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class SessionsUpdateAmlStatusResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("message", "status", "previous_status"); + + private SessionsUpdateAmlStatusResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsUpdateAmlStatusResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsUpdateAmlStatusResponse fromJson(JsonNode json) { + return new SessionsUpdateAmlStatusResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Always AML status updated successfully. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String message() { + return Json.required(json, "message", Conv.STRING); + } + + /** + * Status of the AML check now. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsUpdateAmlStatusResponse.Status status() { + return Json.required(json, "status", Conv.enumeration(SessionsUpdateAmlStatusResponse.Status::of)); + } + + /** + * Status before the call. Equal to status when nothing changed. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsUpdateAmlStatusResponse.PreviousStatus previousStatus() { + return Json.required(json, "previous_status", Conv.enumeration(SessionsUpdateAmlStatusResponse.PreviousStatus::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsUpdateAmlStatusResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsUpdateAmlStatusResponse from) { + super(from); + } + + /** + * Always AML status updated successfully. + * + * @param message the value + * @return this builder + */ + public Builder message(String message) { + return set("message", Conv.STRING, message); + } + + /** + * Status of the AML check now. + * + * @param status the value + * @return this builder + */ + public Builder status(SessionsUpdateAmlStatusResponse.Status status) { + return set("status", Conv.enumeration(SessionsUpdateAmlStatusResponse.Status::of), status); + } + + /** + * Status before the call. Equal to status when nothing changed. + * + * @param previousStatus the value + * @return this builder + */ + public Builder previousStatus(SessionsUpdateAmlStatusResponse.PreviousStatus previousStatus) { + return set("previous_status", Conv.enumeration(SessionsUpdateAmlStatusResponse.PreviousStatus::of), previousStatus); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsUpdateAmlStatusResponse} + * @throws IllegalStateException when a required field is missing + */ + public SessionsUpdateAmlStatusResponse build() { + require("message", false); + require("status", false); + require("previous_status", false); + return new SessionsUpdateAmlStatusResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Status of the AML check now. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Resub Requested"}. */ + public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Status before the call. Equal to status when nothing changed. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class PreviousStatus extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final PreviousStatus NOT_FINISHED = new PreviousStatus("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final PreviousStatus APPROVED = new PreviousStatus("Approved"); + + /** The value {@code "Declined"}. */ + public static final PreviousStatus DECLINED = new PreviousStatus("Declined"); + + /** The value {@code "In Review"}. */ + public static final PreviousStatus IN_REVIEW = new PreviousStatus("In Review"); + + /** The value {@code "Resub Requested"}. */ + public static final PreviousStatus RESUB_REQUESTED = new PreviousStatus("Resub Requested"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private PreviousStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static PreviousStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, PreviousStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionsUpdateDocumentAiDataParams.java b/src/main/java/me/didit/types/SessionsUpdateDocumentAiDataParams.java new file mode 100644 index 0000000..a71f4d5 --- /dev/null +++ b/src/main/java/me/didit/types/SessionsUpdateDocumentAiDataParams.java @@ -0,0 +1,169 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Parameters for {@code sessions.update_document_ai_data}. */ +public final class SessionsUpdateDocumentAiDataParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("extracted_data", "status"); + + private SessionsUpdateDocumentAiDataParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsUpdateDocumentAiDataParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsUpdateDocumentAiDataParams fromJson(JsonNode json) { + return new SessionsUpdateDocumentAiDataParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Field keys (the keys you configured for the document) and their corrected values. Must be a JSON object; merged key by key into the stored data. Leave it out rather than sending null, which returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> extractedData() { + return Json.optional(json, "extracted_data", Conv.OBJECT); + } + + /** + * New review status of the document. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SessionsUpdateDocumentAiDataParams.Status::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsUpdateDocumentAiDataParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsUpdateDocumentAiDataParams from) { + super(from); + } + + /** + * Field keys (the keys you configured for the document) and their corrected values. Must be a JSON object; merged key by key into the stored data. Leave it out rather than sending null, which returns 400. + * + * @param extractedData the value + * @return this builder + */ + public Builder extractedData(Map extractedData) { + return setAny("extracted_data", extractedData); + } + + /** + * New review status of the document. + * + * @param status the value + * @return this builder + */ + public Builder status(SessionsUpdateDocumentAiDataParams.Status status) { + return set("status", Conv.enumeration(SessionsUpdateDocumentAiDataParams.Status::of), status); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsUpdateDocumentAiDataParams} + */ + public SessionsUpdateDocumentAiDataParams build() { + return new SessionsUpdateDocumentAiDataParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * New review status of the document. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); + + private static final List KNOWN_VALUES = List.of(APPROVED, DECLINED, IN_REVIEW, NOT_FINISHED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionsUpdateDocumentAiDataResponse.java b/src/main/java/me/didit/types/SessionsUpdateDocumentAiDataResponse.java new file mode 100644 index 0000000..609a19f --- /dev/null +++ b/src/main/java/me/didit/types/SessionsUpdateDocumentAiDataResponse.java @@ -0,0 +1,259 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class SessionsUpdateDocumentAiDataResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "document_key", "original_filename", "file_url", "status", "extracted_data"); + + private SessionsUpdateDocumentAiDataResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SessionsUpdateDocumentAiDataResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SessionsUpdateDocumentAiDataResponse fromJson(JsonNode json) { + return new SessionsUpdateDocumentAiDataResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Document identifier. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * The configured document this upload corresponds to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String documentKey() { + return Json.required(json, "document_key", Conv.STRING); + } + + /** + * File name the document was uploaded with. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originalFilename() { + return Json.optional(json, "original_filename", Conv.STRING); + } + + /** + * Storage path of the uploaded file. It is not a download link: read the presigned file_url from the session decision to download the file. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fileUrl() { + return Json.optional(json, "file_url", Conv.STRING); + } + + /** + * Review status of the document. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SessionsUpdateDocumentAiDataResponse.Status status() { + return Json.required(json, "status", Conv.enumeration(SessionsUpdateDocumentAiDataResponse.Status::of)); + } + + /** + * All extracted fields after the merge, keyed by field key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> extractedData() { + return Json.optional(json, "extracted_data", Conv.OBJECT); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SessionsUpdateDocumentAiDataResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SessionsUpdateDocumentAiDataResponse from) { + super(from); + } + + /** + * Document identifier. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The configured document this upload corresponds to. + * + * @param documentKey the value + * @return this builder + */ + public Builder documentKey(String documentKey) { + return set("document_key", Conv.STRING, documentKey); + } + + /** + * File name the document was uploaded with. + * + * @param originalFilename the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder originalFilename(@Nullable String originalFilename) { + return setNullable("original_filename", Conv.STRING, originalFilename); + } + + /** + * Storage path of the uploaded file. It is not a download link: read the presigned file_url from the session decision to download the file. + * + * @param fileUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fileUrl(@Nullable String fileUrl) { + return setNullable("file_url", Conv.STRING, fileUrl); + } + + /** + * Review status of the document. + * + * @param status the value + * @return this builder + */ + public Builder status(SessionsUpdateDocumentAiDataResponse.Status status) { + return set("status", Conv.enumeration(SessionsUpdateDocumentAiDataResponse.Status::of), status); + } + + /** + * All extracted fields after the merge, keyed by field key. + * + * @param extractedData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder extractedData(@Nullable Map extractedData) { + return setAny("extracted_data", extractedData); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SessionsUpdateDocumentAiDataResponse} + * @throws IllegalStateException when a required field is missing + */ + public SessionsUpdateDocumentAiDataResponse build() { + require("uuid", false); + require("document_key", false); + require("original_filename", true); + require("file_url", true); + require("status", false); + require("extracted_data", true); + return new SessionsUpdateDocumentAiDataResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Review status of the document. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Resub Requested"}. */ + public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SessionsUpdateFeatureStatusParams.java b/src/main/java/me/didit/types/SessionsUpdateFeatureStatusParams.java index c524bc5..4e539b6 100644 --- a/src/main/java/me/didit/types/SessionsUpdateFeatureStatusParams.java +++ b/src/main/java/me/didit/types/SessionsUpdateFeatureStatusParams.java @@ -15,7 +15,7 @@ /** Parameters for {@code sessions.update_feature_status}. */ public final class SessionsUpdateFeatureStatusParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("new_status", "comment"); + private static final Set KNOWN_FIELDS = Set.of("new_status", "comment", "feature_type", "defer_until_flow_finishes"); private SessionsUpdateFeatureStatusParams(ObjectNode json, Map> files) { super(json, files); @@ -70,6 +70,26 @@ public Optional comment() { return Json.optional(json, "comment", Conv.STRING); } + /** + * Which kind of step to target. Needed on non-graph workflows (nodeId = default), where every step shares no node; optional otherwise. An unknown value returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional featureType() { + return Json.optional(json, "feature_type", Conv.enumeration(SessionsUpdateFeatureStatusParams.FeatureType::of)); + } + + /** + * While the person is still in the flow, store the override as pending_until_flow_finishes and apply it when the flow finishes, instead of at once. Omitted or false, the override applies at once. On a session whose flow already ended it applies at once either way. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deferUntilFlowFinishes() { + return Json.optional(json, "defer_until_flow_finishes", Conv.BOOLEAN); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -103,6 +123,26 @@ public Builder comment(String comment) { return set("comment", Conv.STRING, comment); } + /** + * Which kind of step to target. Needed on non-graph workflows (nodeId = default), where every step shares no node; optional otherwise. An unknown value returns 400. + * + * @param featureType the value + * @return this builder + */ + public Builder featureType(SessionsUpdateFeatureStatusParams.FeatureType featureType) { + return set("feature_type", Conv.enumeration(SessionsUpdateFeatureStatusParams.FeatureType::of), featureType); + } + + /** + * While the person is still in the flow, store the override as pending_until_flow_finishes and apply it when the flow finishes, instead of at once. Omitted or false, the override applies at once. On a session whose flow already ended it applies at once either way. + * + * @param deferUntilFlowFinishes the value + * @return this builder + */ + public Builder deferUntilFlowFinishes(boolean deferUntilFlowFinishes) { + return set("defer_until_flow_finishes", Conv.BOOLEAN, deferUntilFlowFinishes); + } + /** * Builds the value. * @@ -165,4 +205,122 @@ public boolean isKnown() { return KNOWN_VALUES.contains(this); } } + + /** + * Which kind of step to target. Needed on non-graph workflows (nodeId = default), where every step shares no node; optional otherwise. An unknown value returns 400. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class FeatureType extends StringEnum { + /** The value {@code "ID_VERIFICATION"}. */ + public static final FeatureType ID_VERIFICATION = new FeatureType("ID_VERIFICATION"); + + /** The value {@code "OCR"}. */ + public static final FeatureType OCR = new FeatureType("OCR"); + + /** The value {@code "OCR_BACK"}. */ + public static final FeatureType OCR_BACK = new FeatureType("OCR_BACK"); + + /** The value {@code "NFC"}. */ + public static final FeatureType NFC = new FeatureType("NFC"); + + /** The value {@code "LIVENESS"}. */ + public static final FeatureType LIVENESS = new FeatureType("LIVENESS"); + + /** The value {@code "FACE"}. */ + public static final FeatureType FACE = new FeatureType("FACE"); + + /** The value {@code "AGE_ESTIMATION"}. */ + public static final FeatureType AGE_ESTIMATION = new FeatureType("AGE_ESTIMATION"); + + /** The value {@code "AGE_VERIFICATION"}. */ + public static final FeatureType AGE_VERIFICATION = new FeatureType("AGE_VERIFICATION"); + + /** The value {@code "FACE_SEARCH"}. */ + public static final FeatureType FACE_SEARCH = new FeatureType("FACE_SEARCH"); + + /** The value {@code "FACE_MATCH"}. */ + public static final FeatureType FACE_MATCH = new FeatureType("FACE_MATCH"); + + /** The value {@code "IP_ANALYSIS"}. */ + public static final FeatureType IP_ANALYSIS = new FeatureType("IP_ANALYSIS"); + + /** The value {@code "AML"}. */ + public static final FeatureType AML = new FeatureType("AML"); + + /** The value {@code "PHONE"}. */ + public static final FeatureType PHONE = new FeatureType("PHONE"); + + /** The value {@code "PHONE_VERIFICATION"}. */ + public static final FeatureType PHONE_VERIFICATION = new FeatureType("PHONE_VERIFICATION"); + + /** The value {@code "EMAIL"}. */ + public static final FeatureType EMAIL = new FeatureType("EMAIL"); + + /** The value {@code "EMAIL_VERIFICATION"}. */ + public static final FeatureType EMAIL_VERIFICATION = new FeatureType("EMAIL_VERIFICATION"); + + /** The value {@code "POA"}. */ + public static final FeatureType POA = new FeatureType("POA"); + + /** The value {@code "PROOF_OF_ADDRESS"}. */ + public static final FeatureType PROOF_OF_ADDRESS = new FeatureType("PROOF_OF_ADDRESS"); + + /** The value {@code "DATABASE_VALIDATION"}. */ + public static final FeatureType DATABASE_VALIDATION = new FeatureType("DATABASE_VALIDATION"); + + /** The value {@code "DATA_VALIDATION"}. */ + public static final FeatureType DATA_VALIDATION = new FeatureType("DATA_VALIDATION"); + + /** The value {@code "QUESTIONNAIRE"}. */ + public static final FeatureType QUESTIONNAIRE = new FeatureType("QUESTIONNAIRE"); + + /** The value {@code "DOCUMENT_AI"}. */ + public static final FeatureType DOCUMENT_AI = new FeatureType("DOCUMENT_AI"); + + /** The value {@code "KYB_REGISTRY"}. */ + public static final FeatureType KYB_REGISTRY = new FeatureType("KYB_REGISTRY"); + + /** The value {@code "KYB_DOCUMENTS"}. */ + public static final FeatureType KYB_DOCUMENTS = new FeatureType("KYB_DOCUMENTS"); + + /** The value {@code "KYB_KEY_PEOPLE"}. */ + public static final FeatureType KYB_KEY_PEOPLE = new FeatureType("KYB_KEY_PEOPLE"); + + /** The value {@code "BANK_VERIFICATION"}. */ + public static final FeatureType BANK_VERIFICATION = new FeatureType("BANK_VERIFICATION"); + + /** The value {@code "GEOLOCATION"}. */ + public static final FeatureType GEOLOCATION = new FeatureType("GEOLOCATION"); + + private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, OCR, OCR_BACK, NFC, LIVENESS, FACE, AGE_ESTIMATION, AGE_VERIFICATION, FACE_SEARCH, FACE_MATCH, IP_ANALYSIS, AML, PHONE, PHONE_VERIFICATION, EMAIL, EMAIL_VERIFICATION, POA, PROOF_OF_ADDRESS, DATABASE_VALIDATION, DATA_VALIDATION, QUESTIONNAIRE, DOCUMENT_AI, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE, BANK_VERIFICATION, GEOLOCATION); + + private FeatureType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static FeatureType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, FeatureType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } } diff --git a/src/main/java/me/didit/types/SessionsUpdateFeatureStatusResponse.java b/src/main/java/me/didit/types/SessionsUpdateFeatureStatusResponse.java index 88f6f33..86e870e 100644 --- a/src/main/java/me/didit/types/SessionsUpdateFeatureStatusResponse.java +++ b/src/main/java/me/didit/types/SessionsUpdateFeatureStatusResponse.java @@ -11,9 +11,10 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; public final class SessionsUpdateFeatureStatusResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("session_id", "node_id", "feature_type", "new_status"); + private static final Set KNOWN_FIELDS = Set.of("session_id", "node_id", "feature_type", "new_status", "override"); private SessionsUpdateFeatureStatusResponse(ObjectNode json, Map> files) { super(json, files); @@ -51,15 +52,15 @@ public Builder toBuilder() { /** * The session_id field. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public Optional sessionId() { - return Json.optional(json, "session_id", Conv.STRING); + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); } /** - * The node_id field. + * The updated step's workflow node, or null for a step without a node (the default path in a non-graph workflow). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -71,21 +72,31 @@ public Optional nodeId() { /** * Model name of the updated feature (e.g. KYC, Face, FaceMatch, AML, POA). * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public Optional featureType() { - return Json.optional(json, "feature_type", Conv.STRING); + public String featureType() { + return Json.required(json, "feature_type", Conv.STRING); } /** - * The new_status field. + * The check's stored status: the requested one once the override applied, the unchanged one while the override waits for the flow to finish. * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public Optional newStatus() { - return Json.optional(json, "new_status", Conv.STRING); + public String newStatus() { + return Json.required(json, "new_status", Conv.STRING); + } + + /** + * One override of a check (feature) status made with PATCH /v3/session/{sessionId}/features/{nodeId}/update-status/: what it changes, whether it applied or still waits for the person to finish the flow, who asked and why. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public FeatureStatusOverride override() { + return Json.required(json, "override", Conv.model(FeatureStatusOverride::fromJson)); } @Override @@ -112,13 +123,13 @@ public Builder sessionId(String sessionId) { } /** - * The node_id field. + * The updated step's workflow node, or null for a step without a node (the default path in a non-graph workflow). * - * @param nodeId the value + * @param nodeId the value, or {@code null} to send JSON null * @return this builder */ - public Builder nodeId(String nodeId) { - return set("node_id", Conv.STRING, nodeId); + public Builder nodeId(@Nullable String nodeId) { + return setNullable("node_id", Conv.STRING, nodeId); } /** @@ -132,7 +143,7 @@ public Builder featureType(String featureType) { } /** - * The new_status field. + * The check's stored status: the requested one once the override applied, the unchanged one while the override waits for the flow to finish. * * @param newStatus the value * @return this builder @@ -141,12 +152,28 @@ public Builder newStatus(String newStatus) { return set("new_status", Conv.STRING, newStatus); } + /** + * One override of a check (feature) status made with PATCH /v3/session/{sessionId}/features/{nodeId}/update-status/: what it changes, whether it applied or still waits for the person to finish the flow, who asked and why. + * + * @param override the value + * @return this builder + */ + public Builder override(FeatureStatusOverride override) { + return set("override", Conv.model(FeatureStatusOverride::fromJson), override); + } + /** * Builds the value. * * @return a new immutable {@code SessionsUpdateFeatureStatusResponse} + * @throws IllegalStateException when a required field is missing */ public SessionsUpdateFeatureStatusResponse build() { + require("session_id", false); + require("node_id", true); + require("feature_type", false); + require("new_status", false); + require("override", false); return new SessionsUpdateFeatureStatusResponse(buildJson(), buildFiles()); } diff --git a/src/main/java/me/didit/types/SessionsUpdateStatusParams.java b/src/main/java/me/didit/types/SessionsUpdateStatusParams.java index 24a197f..4b73727 100644 --- a/src/main/java/me/didit/types/SessionsUpdateStatusParams.java +++ b/src/main/java/me/didit/types/SessionsUpdateStatusParams.java @@ -111,7 +111,7 @@ public Optional emailLanguage() { } /** - * Optional signed receipt from Preview Resubmission Email. When supplied with send_email=true and new_status=Resubmitted, the recipient, language, checks, current session state, sender and message must still match the preview. A changed or expired receipt returns 409 before changing the session or sending email. Existing integrations may omit this field. + * Optional receipt from [Preview Decision Email](/sessions-api/preview-decision-email) (for Approved and Declined) or [Preview Resubmission Email](/sessions-api/preview-resubmission) (for Resubmitted): its preview_token. Valid only with send_email: true; without it the request returns 400. The recipient, language, session status, sender and email (and, for a resubmission, the selected steps) must still match the preview, and the receipt expires after 15 minutes. Otherwise the request returns 409 before the session changes or an email is sent. Existing integrations may omit this field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -194,7 +194,7 @@ public Builder emailLanguage(String emailLanguage) { } /** - * Optional signed receipt from Preview Resubmission Email. When supplied with send_email=true and new_status=Resubmitted, the recipient, language, checks, current session state, sender and message must still match the preview. A changed or expired receipt returns 409 before changing the session or sending email. Existing integrations may omit this field. + * Optional receipt from [Preview Decision Email](/sessions-api/preview-decision-email) (for Approved and Declined) or [Preview Resubmission Email](/sessions-api/preview-resubmission) (for Resubmitted): its preview_token. Valid only with send_email: true; without it the request returns 400. The recipient, language, session status, sender and email (and, for a resubmission, the selected steps) must still match the preview, and the receipt expires after 15 minutes. Otherwise the request returns 409 before the session changes or an email is sent. Existing integrations may omit this field. * * @param emailPreviewToken the value * @return this builder diff --git a/src/main/java/me/didit/types/SignatureField.java b/src/main/java/me/didit/types/SignatureField.java new file mode 100644 index 0000000..a2336ca --- /dev/null +++ b/src/main/java/me/didit/types/SignatureField.java @@ -0,0 +1,582 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * A field to place on a session document: which signer fills it, how, and where. Give either a position (page, x, y, width and height) or an anchor. Positions are fractions of the page as it's displayed, from its top-left corner, rounded to 4 decimals, and the field must fit inside the page. A document takes up to 200 fields and one signer up to 500, and every role with fields on a document needs a signature or initials field there. See [Fields on the document](/core-technology/electronic-signature/overview#fields-on-the-document). + */ +public final class SignatureField extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("key", "type", "role", "page", "x", "y", "width", "height", "anchor", "required", "label", "placeholder", "validation", "prefill", "editable_when_prefilled", "checked_by_default", "date_format"); + + private SignatureField(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SignatureField} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SignatureField fromJson(JsonNode json) { + return new SignatureField(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Names the field in the results and in sign.field_values. Unique within the document. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String key() { + return Json.required(json, "key", Conv.STRING); + } + + /** + * What the field holds. The signer applies their adopted signature and initials, types text, ticks a checkbox, and types an email when the session has no verified email. Didit fills full_name (the verified name), date_signed (the date of signing, in the signer's time zone), data (from prefill) and a verified email, and shows them read-only. There is no date picker type: use text. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public SignatureField.Type type() { + return Json.required(json, "type", Conv.enumeration(SignatureField.Type::of)); + } + + /** + * Who fills the field, with the signature_signer values. A step fills and stamps only the fields of its own signer. Defaults to the step's signer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional role() { + return Json.optional(json, "role", Conv.enumeration(SignatureField.Role::of)); + } + + /** + * Page number, from 1 to the document's page count. For a Word document, a page of the converted PDF. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional page() { + return Json.optional(json, "page", Conv.LONG); + } + + /** + * Left edge, as a fraction of the page width. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional x() { + return Json.optional(json, "x", Conv.DOUBLE); + } + + /** + * Top edge, as a fraction of the page height. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional y() { + return Json.optional(json, "y", Conv.DOUBLE); + } + + /** + * Width, as a fraction of the page width. A signature is at least 0.12 wide. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional width() { + return Json.optional(json, "width", Conv.DOUBLE); + } + + /** + * Height, as a fraction of the page height. A signature is at least 0.035 high. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional height() { + return Json.optional(json, "height", Conv.DOUBLE); + } + + /** + * Places a field by a piece of text in the document instead of a position. Didit doesn't remove the text from the PDF, so write it in the same color as the page background. When the text isn't found, the call returns 400 with field_anchor_not_found. See [Place fields by anchor text](/sessions-api/create-session#place-fields-by-anchor-text). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional anchor() { + return Json.optional(json, "anchor", Conv.model(SignatureFieldAnchor::fromJson)); + } + + /** + * signature and initials are required unless you set false. full_name, date_signed and data are always required. The signer can't finish a document until its required fields have a value, and a required checkbox must be ticked. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional required() { + return Json.optional(json, "required", Conv.BOOLEAN); + } + + /** + * Shown to the signer and in the results. Defaults from the type. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional label() { + return Json.optional(json, "label", Conv.STRING); + } + + /** + * text and email only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional placeholder() { + return Json.optional(json, "placeholder", Conv.STRING); + } + + /** + * Rules for a text field's value. The console preview, the signing flow and the API apply the same rules. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional validation() { + return Json.optional(json, "validation", Conv.model(SignatureFieldValidation::fromJson)); + } + + /** + * Where the value comes from: identity.full_name, identity.first_name, identity.last_name, identity.date_of_birth (DD/MM/YYYY), identity.nationality, identity.document_number, identity.document_type, identity.issuing_country, identity.address, contact.email, contact.phone, company.name, company.registration_number, company.address, company.country (KYB), session.vendor_data, metadata.<key> (a key of the session's metadata) or api.<key> (a key of sign.field_values). Required for data. full_name always uses identity.full_name and email uses contact.email. The values of data, full_name and verified email fields are fixed when the signer starts signing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional prefill() { + return Json.optional(json, "prefill", Conv.STRING); + } + + /** + * text and email only: the signer can change a pre-filled value. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional editableWhenPrefilled() { + return Json.optional(json, "editable_when_prefilled", Conv.BOOLEAN); + } + + /** + * checkbox only: the box starts ticked. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional checkedByDefault() { + return Json.optional(json, "checked_by_default", Conv.BOOLEAN); + } + + /** + * date_signed only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateFormat() { + return Json.optional(json, "date_format", Conv.enumeration(SignatureField.DateFormat::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SignatureField}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SignatureField from) { + super(from); + } + + /** + * Names the field in the results and in sign.field_values. Unique within the document. + * + * @param key the value + * @return this builder + */ + public Builder key(String key) { + return set("key", Conv.STRING, key); + } + + /** + * What the field holds. The signer applies their adopted signature and initials, types text, ticks a checkbox, and types an email when the session has no verified email. Didit fills full_name (the verified name), date_signed (the date of signing, in the signer's time zone), data (from prefill) and a verified email, and shows them read-only. There is no date picker type: use text. + * + * @param type the value + * @return this builder + */ + public Builder type(SignatureField.Type type) { + return set("type", Conv.enumeration(SignatureField.Type::of), type); + } + + /** + * Who fills the field, with the signature_signer values. A step fills and stamps only the fields of its own signer. Defaults to the step's signer. + * + * @param role the value + * @return this builder + */ + public Builder role(SignatureField.Role role) { + return set("role", Conv.enumeration(SignatureField.Role::of), role); + } + + /** + * Page number, from 1 to the document's page count. For a Word document, a page of the converted PDF. + * + * @param page the value + * @return this builder + */ + public Builder page(long page) { + return set("page", Conv.LONG, page); + } + + /** + * Left edge, as a fraction of the page width. + * + * @param x the value + * @return this builder + */ + public Builder x(double x) { + return set("x", Conv.DOUBLE, x); + } + + /** + * Top edge, as a fraction of the page height. + * + * @param y the value + * @return this builder + */ + public Builder y(double y) { + return set("y", Conv.DOUBLE, y); + } + + /** + * Width, as a fraction of the page width. A signature is at least 0.12 wide. + * + * @param width the value + * @return this builder + */ + public Builder width(double width) { + return set("width", Conv.DOUBLE, width); + } + + /** + * Height, as a fraction of the page height. A signature is at least 0.035 high. + * + * @param height the value + * @return this builder + */ + public Builder height(double height) { + return set("height", Conv.DOUBLE, height); + } + + /** + * Places a field by a piece of text in the document instead of a position. Didit doesn't remove the text from the PDF, so write it in the same color as the page background. When the text isn't found, the call returns 400 with field_anchor_not_found. See [Place fields by anchor text](/sessions-api/create-session#place-fields-by-anchor-text). + * + * @param anchor the value + * @return this builder + */ + public Builder anchor(SignatureFieldAnchor anchor) { + return set("anchor", Conv.model(SignatureFieldAnchor::fromJson), anchor); + } + + /** + * signature and initials are required unless you set false. full_name, date_signed and data are always required. The signer can't finish a document until its required fields have a value, and a required checkbox must be ticked. + * + * @param required the value + * @return this builder + */ + public Builder required(boolean required) { + return set("required", Conv.BOOLEAN, required); + } + + /** + * Shown to the signer and in the results. Defaults from the type. + * + * @param label the value + * @return this builder + */ + public Builder label(String label) { + return set("label", Conv.STRING, label); + } + + /** + * text and email only. + * + * @param placeholder the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder placeholder(@Nullable String placeholder) { + return setNullable("placeholder", Conv.STRING, placeholder); + } + + /** + * Rules for a text field's value. The console preview, the signing flow and the API apply the same rules. + * + * @param validation the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder validation(@Nullable SignatureFieldValidation validation) { + return setNullable("validation", Conv.model(SignatureFieldValidation::fromJson), validation); + } + + /** + * Where the value comes from: identity.full_name, identity.first_name, identity.last_name, identity.date_of_birth (DD/MM/YYYY), identity.nationality, identity.document_number, identity.document_type, identity.issuing_country, identity.address, contact.email, contact.phone, company.name, company.registration_number, company.address, company.country (KYB), session.vendor_data, metadata.<key> (a key of the session's metadata) or api.<key> (a key of sign.field_values). Required for data. full_name always uses identity.full_name and email uses contact.email. The values of data, full_name and verified email fields are fixed when the signer starts signing. + * + * @param prefill the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder prefill(@Nullable String prefill) { + return setNullable("prefill", Conv.STRING, prefill); + } + + /** + * text and email only: the signer can change a pre-filled value. + * + * @param editableWhenPrefilled the value + * @return this builder + */ + public Builder editableWhenPrefilled(boolean editableWhenPrefilled) { + return set("editable_when_prefilled", Conv.BOOLEAN, editableWhenPrefilled); + } + + /** + * checkbox only: the box starts ticked. + * + * @param checkedByDefault the value + * @return this builder + */ + public Builder checkedByDefault(boolean checkedByDefault) { + return set("checked_by_default", Conv.BOOLEAN, checkedByDefault); + } + + /** + * date_signed only. + * + * @param dateFormat the value + * @return this builder + */ + public Builder dateFormat(SignatureField.DateFormat dateFormat) { + return set("date_format", Conv.enumeration(SignatureField.DateFormat::of), dateFormat); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SignatureField} + * @throws IllegalStateException when a required field is missing + */ + public SignatureField build() { + require("key", false); + require("type", false); + return new SignatureField(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * What the field holds. The signer applies their adopted signature and initials, types text, ticks a checkbox, and types an email when the session has no verified email. Didit fills full_name (the verified name), date_signed (the date of signing, in the signer's time zone), data (from prefill) and a verified email, and shows them read-only. There is no date picker type: use text. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "signature"}. */ + public static final Type SIGNATURE = new Type("signature"); + + /** The value {@code "initials"}. */ + public static final Type INITIALS = new Type("initials"); + + /** The value {@code "full_name"}. */ + public static final Type FULL_NAME = new Type("full_name"); + + /** The value {@code "date_signed"}. */ + public static final Type DATE_SIGNED = new Type("date_signed"); + + /** The value {@code "text"}. */ + public static final Type TEXT = new Type("text"); + + /** The value {@code "checkbox"}. */ + public static final Type CHECKBOX = new Type("checkbox"); + + /** The value {@code "email"}. */ + public static final Type EMAIL = new Type("email"); + + /** The value {@code "data"}. */ + public static final Type DATA = new Type("data"); + + private static final List KNOWN_VALUES = List.of(SIGNATURE, INITIALS, FULL_NAME, DATE_SIGNED, TEXT, CHECKBOX, EMAIL, DATA); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Who fills the field, with the signature_signer values. A step fills and stamps only the fields of its own signer. Defaults to the step's signer. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Role extends StringEnum { + /** The value {@code "verified_person"}. */ + public static final Role VERIFIED_PERSON = new Role("verified_person"); + + /** The value {@code "legal_representative"}. */ + public static final Role LEGAL_REPRESENTATIVE = new Role("legal_representative"); + + /** The value {@code "key_people"}. */ + public static final Role KEY_PEOPLE = new Role("key_people"); + + private static final List KNOWN_VALUES = List.of(VERIFIED_PERSON, LEGAL_REPRESENTATIVE, KEY_PEOPLE); + + private Role(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Role of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Role::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * date_signed only. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DateFormat extends StringEnum { + /** The value {@code "DD/MM/YYYY"}. */ + public static final DateFormat DD_MM_YYYY = new DateFormat("DD/MM/YYYY"); + + /** The value {@code "MM/DD/YYYY"}. */ + public static final DateFormat MM_DD_YYYY = new DateFormat("MM/DD/YYYY"); + + /** The value {@code "YYYY-MM-DD"}. */ + public static final DateFormat YYYY_MM_DD = new DateFormat("YYYY-MM-DD"); + + /** The value {@code "D MMMM YYYY"}. */ + public static final DateFormat D_MMMM_YYYY = new DateFormat("D MMMM YYYY"); + + private static final List KNOWN_VALUES = List.of(DD_MM_YYYY, MM_DD_YYYY, YYYY_MM_DD, D_MMMM_YYYY); + + private DateFormat(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DateFormat of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DateFormat::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SignatureFieldAnchor.java b/src/main/java/me/didit/types/SignatureFieldAnchor.java new file mode 100644 index 0000000..013e770 --- /dev/null +++ b/src/main/java/me/didit/types/SignatureFieldAnchor.java @@ -0,0 +1,247 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * Places a field by a piece of text in the document instead of a position. Didit doesn't remove the text from the PDF, so write it in the same color as the page background. When the text isn't found, the call returns 400 with field_anchor_not_found. See [Place fields by anchor text](/sessions-api/create-session#place-fields-by-anchor-text). + */ +public final class SignatureFieldAnchor extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("text", "occurrence", "offset_x", "offset_y", "width", "height"); + + private SignatureFieldAnchor(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SignatureFieldAnchor} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SignatureFieldAnchor fromJson(JsonNode json) { + return new SignatureFieldAnchor(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Text searched in the document. For a Word file, in the converted PDF. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String text() { + return Json.required(json, "text", Conv.STRING); + } + + /** + * all places one field per match; the keys after the first get _2, _3 and so on. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional occurrence() { + return Json.optional(json, "occurrence", Conv.enumeration(SignatureFieldAnchor.Occurrence::of)); + } + + /** + * Added to the left edge of the text, as a fraction of the page width. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offsetX() { + return Json.optional(json, "offset_x", Conv.DOUBLE); + } + + /** + * Added to the top edge of the text, as a fraction of the page height. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offsetY() { + return Json.optional(json, "offset_y", Conv.DOUBLE); + } + + /** + * Width of the field, as a fraction of the page width. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional width() { + return Json.optional(json, "width", Conv.DOUBLE); + } + + /** + * Height of the field, as a fraction of the page height. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional height() { + return Json.optional(json, "height", Conv.DOUBLE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SignatureFieldAnchor}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SignatureFieldAnchor from) { + super(from); + } + + /** + * Text searched in the document. For a Word file, in the converted PDF. + * + * @param text the value + * @return this builder + */ + public Builder text(String text) { + return set("text", Conv.STRING, text); + } + + /** + * all places one field per match; the keys after the first get _2, _3 and so on. + * + * @param occurrence the value + * @return this builder + */ + public Builder occurrence(SignatureFieldAnchor.Occurrence occurrence) { + return set("occurrence", Conv.enumeration(SignatureFieldAnchor.Occurrence::of), occurrence); + } + + /** + * Added to the left edge of the text, as a fraction of the page width. + * + * @param offsetX the value + * @return this builder + */ + public Builder offsetX(double offsetX) { + return set("offset_x", Conv.DOUBLE, offsetX); + } + + /** + * Added to the top edge of the text, as a fraction of the page height. + * + * @param offsetY the value + * @return this builder + */ + public Builder offsetY(double offsetY) { + return set("offset_y", Conv.DOUBLE, offsetY); + } + + /** + * Width of the field, as a fraction of the page width. + * + * @param width the value + * @return this builder + */ + public Builder width(double width) { + return set("width", Conv.DOUBLE, width); + } + + /** + * Height of the field, as a fraction of the page height. + * + * @param height the value + * @return this builder + */ + public Builder height(double height) { + return set("height", Conv.DOUBLE, height); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SignatureFieldAnchor} + * @throws IllegalStateException when a required field is missing + */ + public SignatureFieldAnchor build() { + require("text", false); + return new SignatureFieldAnchor(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * all places one field per match; the keys after the first get _2, _3 and so on. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Occurrence extends StringEnum { + /** The value {@code "first"}. */ + public static final Occurrence FIRST = new Occurrence("first"); + + /** The value {@code "all"}. */ + public static final Occurrence ALL = new Occurrence("all"); + + private static final List KNOWN_VALUES = List.of(FIRST, ALL); + + private Occurrence(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Occurrence of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Occurrence::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SignatureFieldResult.java b/src/main/java/me/didit/types/SignatureFieldResult.java new file mode 100644 index 0000000..82b8946 --- /dev/null +++ b/src/main/java/me/didit/types/SignatureFieldResult.java @@ -0,0 +1,610 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * A field of a signed document, with its value on this signer's copy. See [Fields](/core-technology/electronic-signature/report-electronic-signature#fields). + */ +public final class SignatureFieldResult extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("key", "type", "role", "label", "page", "x", "y", "width", "height", "required", "value", "display_value", "value_source", "filled_at", "image_url", "value_unavailable_reason"); + + private SignatureFieldResult(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SignatureFieldResult} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SignatureFieldResult fromJson(JsonNode json) { + return new SignatureFieldResult(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The field's key, as used in sign.field_values. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional key() { + return Json.optional(json, "key", Conv.STRING); + } + + /** + * The type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional type() { + return Json.optional(json, "type", Conv.enumeration(SignatureFieldResult.Type::of)); + } + + /** + * The role field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional role() { + return Json.optional(json, "role", Conv.enumeration(SignatureFieldResult.Role::of)); + } + + /** + * The label field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional label() { + return Json.optional(json, "label", Conv.STRING); + } + + /** + * Page number, from 1. For a Word document, a page of the converted PDF. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional page() { + return Json.optional(json, "page", Conv.LONG); + } + + /** + * Left edge, as a fraction of the page width. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional x() { + return Json.optional(json, "x", Conv.DOUBLE); + } + + /** + * Top edge, as a fraction of the page height. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional y() { + return Json.optional(json, "y", Conv.DOUBLE); + } + + /** + * Width, as a fraction of the page width. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional width() { + return Json.optional(json, "width", Conv.DOUBLE); + } + + /** + * Height, as a fraction of the page height. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional height() { + return Json.optional(json, "height", Conv.DOUBLE); + } + + /** + * The required field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional required() { + return Json.optional(json, "required", Conv.BOOLEAN); + } + + /** + * A string for text, email, full_name, date_signed and data, a boolean for a checkbox, and true for an applied signature or initials. null when empty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional value() { + return Json.optional(json, "value", Conv.ANY); + } + + /** + * The value as stamped on the page: dates in the field's date_format, Yes or No for a checkbox, Signed for a signature or initials. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional displayValue() { + return Json.optional(json, "display_value", Conv.STRING); + } + + /** + * Where the value came from: signer (typed, ticked or applied by the signer), identity (the verified ID), contact (the verified email or phone), company (the verified company), session (vendor_data), metadata, api (sign.field_values) or signing_time (the date signed). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional valueSource() { + return Json.optional(json, "value_source", Conv.enumeration(SignatureFieldResult.ValueSource::of)); + } + + /** + * The filled_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional filledAt() { + return Json.optional(json, "filled_at", Conv.DATE_TIME); + } + + /** + * Signature and initials only: the path of the applied image. See [Download a signature image](/sessions-api/electronic-signature/download-field-image). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional imageUrl() { + return Json.optional(json, "image_url", Conv.STRING); + } + + /** + * source_empty when a pre-filled field had no value in the session and signature_field_data_missing_action let the signer sign anyway. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional valueUnavailableReason() { + return Json.optional(json, "value_unavailable_reason", Conv.enumeration(SignatureFieldResult.ValueUnavailableReason::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SignatureFieldResult}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SignatureFieldResult from) { + super(from); + } + + /** + * The field's key, as used in sign.field_values. + * + * @param key the value + * @return this builder + */ + public Builder key(String key) { + return set("key", Conv.STRING, key); + } + + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(SignatureFieldResult.Type type) { + return set("type", Conv.enumeration(SignatureFieldResult.Type::of), type); + } + + /** + * The role field. + * + * @param role the value + * @return this builder + */ + public Builder role(SignatureFieldResult.Role role) { + return set("role", Conv.enumeration(SignatureFieldResult.Role::of), role); + } + + /** + * The label field. + * + * @param label the value + * @return this builder + */ + public Builder label(String label) { + return set("label", Conv.STRING, label); + } + + /** + * Page number, from 1. For a Word document, a page of the converted PDF. + * + * @param page the value + * @return this builder + */ + public Builder page(long page) { + return set("page", Conv.LONG, page); + } + + /** + * Left edge, as a fraction of the page width. + * + * @param x the value + * @return this builder + */ + public Builder x(double x) { + return set("x", Conv.DOUBLE, x); + } + + /** + * Top edge, as a fraction of the page height. + * + * @param y the value + * @return this builder + */ + public Builder y(double y) { + return set("y", Conv.DOUBLE, y); + } + + /** + * Width, as a fraction of the page width. + * + * @param width the value + * @return this builder + */ + public Builder width(double width) { + return set("width", Conv.DOUBLE, width); + } + + /** + * Height, as a fraction of the page height. + * + * @param height the value + * @return this builder + */ + public Builder height(double height) { + return set("height", Conv.DOUBLE, height); + } + + /** + * The required field. + * + * @param required the value + * @return this builder + */ + public Builder required(boolean required) { + return set("required", Conv.BOOLEAN, required); + } + + /** + * A string for text, email, full_name, date_signed and data, a boolean for a checkbox, and true for an applied signature or initials. null when empty. + * + * @param value the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder value(@Nullable Object value) { + return setAny("value", value); + } + + /** + * The value as stamped on the page: dates in the field's date_format, Yes or No for a checkbox, Signed for a signature or initials. + * + * @param displayValue the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder displayValue(@Nullable String displayValue) { + return setNullable("display_value", Conv.STRING, displayValue); + } + + /** + * Where the value came from: signer (typed, ticked or applied by the signer), identity (the verified ID), contact (the verified email or phone), company (the verified company), session (vendor_data), metadata, api (sign.field_values) or signing_time (the date signed). + * + * @param valueSource the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder valueSource(SignatureFieldResult.@Nullable ValueSource valueSource) { + return setNullable("value_source", Conv.enumeration(SignatureFieldResult.ValueSource::of), valueSource); + } + + /** + * The filled_at field. + * + * @param filledAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder filledAt(@Nullable OffsetDateTime filledAt) { + return setNullable("filled_at", Conv.DATE_TIME, filledAt); + } + + /** + * Signature and initials only: the path of the applied image. See [Download a signature image](/sessions-api/electronic-signature/download-field-image). + * + * @param imageUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder imageUrl(@Nullable String imageUrl) { + return setNullable("image_url", Conv.STRING, imageUrl); + } + + /** + * source_empty when a pre-filled field had no value in the session and signature_field_data_missing_action let the signer sign anyway. + * + * @param valueUnavailableReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder valueUnavailableReason(SignatureFieldResult.@Nullable ValueUnavailableReason valueUnavailableReason) { + return setNullable("value_unavailable_reason", Conv.enumeration(SignatureFieldResult.ValueUnavailableReason::of), valueUnavailableReason); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SignatureFieldResult} + */ + public SignatureFieldResult build() { + return new SignatureFieldResult(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "signature"}. */ + public static final Type SIGNATURE = new Type("signature"); + + /** The value {@code "initials"}. */ + public static final Type INITIALS = new Type("initials"); + + /** The value {@code "full_name"}. */ + public static final Type FULL_NAME = new Type("full_name"); + + /** The value {@code "date_signed"}. */ + public static final Type DATE_SIGNED = new Type("date_signed"); + + /** The value {@code "text"}. */ + public static final Type TEXT = new Type("text"); + + /** The value {@code "checkbox"}. */ + public static final Type CHECKBOX = new Type("checkbox"); + + /** The value {@code "email"}. */ + public static final Type EMAIL = new Type("email"); + + /** The value {@code "data"}. */ + public static final Type DATA = new Type("data"); + + private static final List KNOWN_VALUES = List.of(SIGNATURE, INITIALS, FULL_NAME, DATE_SIGNED, TEXT, CHECKBOX, EMAIL, DATA); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Role extends StringEnum { + /** The value {@code "verified_person"}. */ + public static final Role VERIFIED_PERSON = new Role("verified_person"); + + /** The value {@code "legal_representative"}. */ + public static final Role LEGAL_REPRESENTATIVE = new Role("legal_representative"); + + /** The value {@code "key_people"}. */ + public static final Role KEY_PEOPLE = new Role("key_people"); + + private static final List KNOWN_VALUES = List.of(VERIFIED_PERSON, LEGAL_REPRESENTATIVE, KEY_PEOPLE); + + private Role(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Role of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Role::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Where the value came from: signer (typed, ticked or applied by the signer), identity (the verified ID), contact (the verified email or phone), company (the verified company), session (vendor_data), metadata, api (sign.field_values) or signing_time (the date signed). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ValueSource extends StringEnum { + /** The value {@code "signer"}. */ + public static final ValueSource SIGNER = new ValueSource("signer"); + + /** The value {@code "identity"}. */ + public static final ValueSource IDENTITY = new ValueSource("identity"); + + /** The value {@code "contact"}. */ + public static final ValueSource CONTACT = new ValueSource("contact"); + + /** The value {@code "company"}. */ + public static final ValueSource COMPANY = new ValueSource("company"); + + /** The value {@code "session"}. */ + public static final ValueSource SESSION = new ValueSource("session"); + + /** The value {@code "metadata"}. */ + public static final ValueSource METADATA = new ValueSource("metadata"); + + /** The value {@code "api"}. */ + public static final ValueSource API = new ValueSource("api"); + + /** The value {@code "signing_time"}. */ + public static final ValueSource SIGNING_TIME = new ValueSource("signing_time"); + + private static final List KNOWN_VALUES = List.of(SIGNER, IDENTITY, CONTACT, COMPANY, SESSION, METADATA, API, SIGNING_TIME); + + private ValueSource(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ValueSource of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ValueSource::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * source_empty when a pre-filled field had no value in the session and signature_field_data_missing_action let the signer sign anyway. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ValueUnavailableReason extends StringEnum { + /** The value {@code "source_empty"}. */ + public static final ValueUnavailableReason SOURCE_EMPTY = new ValueUnavailableReason("source_empty"); + + private static final List KNOWN_VALUES = List.of(SOURCE_EMPTY); + + private ValueUnavailableReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ValueUnavailableReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ValueUnavailableReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SignatureFieldValidation.java b/src/main/java/me/didit/types/SignatureFieldValidation.java new file mode 100644 index 0000000..cf2f15c --- /dev/null +++ b/src/main/java/me/didit/types/SignatureFieldValidation.java @@ -0,0 +1,235 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Rules for a text field's value. The console preview, the signing flow and the API apply the same rules. + */ +public final class SignatureFieldValidation extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("kind", "min_length", "max_length", "pattern", "message"); + + private SignatureFieldValidation(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SignatureFieldValidation} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SignatureFieldValidation fromJson(JsonNode json) { + return new SignatureFieldValidation(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * letters: letters in any script, spaces, hyphens and apostrophes. number: digits with an optional decimal separator. pattern: the value must match pattern. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(SignatureFieldValidation.Kind::of)); + } + + /** + * From 0 up to max_length. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional minLength() { + return Json.optional(json, "min_length", Conv.LONG); + } + + /** + * The max_length field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional maxLength() { + return Json.optional(json, "max_length", Conv.LONG); + } + + /** + * Required with kind: "pattern". A regular expression the whole value must match. Lookarounds and backreferences aren't supported. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional pattern() { + return Json.optional(json, "pattern", Conv.STRING); + } + + /** + * Shown to the signer instead of the default error. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional message() { + return Json.optional(json, "message", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SignatureFieldValidation}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SignatureFieldValidation from) { + super(from); + } + + /** + * letters: letters in any script, spaces, hyphens and apostrophes. number: digits with an optional decimal separator. pattern: the value must match pattern. + * + * @param kind the value + * @return this builder + */ + public Builder kind(SignatureFieldValidation.Kind kind) { + return set("kind", Conv.enumeration(SignatureFieldValidation.Kind::of), kind); + } + + /** + * From 0 up to max_length. + * + * @param minLength the value + * @return this builder + */ + public Builder minLength(long minLength) { + return set("min_length", Conv.LONG, minLength); + } + + /** + * The max_length field. + * + * @param maxLength the value + * @return this builder + */ + public Builder maxLength(long maxLength) { + return set("max_length", Conv.LONG, maxLength); + } + + /** + * Required with kind: "pattern". A regular expression the whole value must match. Lookarounds and backreferences aren't supported. + * + * @param pattern the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder pattern(@Nullable String pattern) { + return setNullable("pattern", Conv.STRING, pattern); + } + + /** + * Shown to the signer instead of the default error. + * + * @param message the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder message(@Nullable String message) { + return setNullable("message", Conv.STRING, message); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SignatureFieldValidation} + */ + public SignatureFieldValidation build() { + return new SignatureFieldValidation(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * letters: letters in any script, spaces, hyphens and apostrophes. number: digits with an optional decimal separator. pattern: the value must match pattern. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "any"}. */ + public static final Kind ANY = new Kind("any"); + + /** The value {@code "letters"}. */ + public static final Kind LETTERS = new Kind("letters"); + + /** The value {@code "number"}. */ + public static final Kind NUMBER = new Kind("number"); + + /** The value {@code "email"}. */ + public static final Kind EMAIL = new Kind("email"); + + /** The value {@code "pattern"}. */ + public static final Kind PATTERN = new Kind("pattern"); + + private static final List KNOWN_VALUES = List.of(ANY, LETTERS, NUMBER, EMAIL, PATTERN); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SignatureResult.java b/src/main/java/me/didit/types/SignatureResult.java new file mode 100644 index 0000000..2e56149 --- /dev/null +++ b/src/main/java/me/didit/types/SignatureResult.java @@ -0,0 +1,3653 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One signer's result for one Electronic Signature step. See the [Electronic Signature report](/core-technology/electronic-signature/report-electronic-signature). + */ +public final class SignatureResult extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "node_id", "status", "verdict", "level", "format", "profile", "signer", "identity_used", "certificate", "confirmation", "timestamp", "documents", "adopted_signature", "electronic_signature_consent_at", "evidence_file", "warnings", "validation", "declined_at", "declined_action", "signed_at", "expires_at", "email_sent_at", "email_unavailable_reason", "ip_address", "place", "place_unavailable_reason"); + + private SignatureResult(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code SignatureResult} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static SignatureResult fromJson(JsonNode json) { + return new SignatureResult(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Pass it as signature_id to download this signer's evidence or validate their signature. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The workflow step that produced it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + /** + * Where the signer is in the step. Only signed means the documents are signed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SignatureResult.Status::of)); + } + + /** + * The step's outcome. A warning sets it through its rule; custom status rules apply after. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verdict() { + return Json.optional(json, "verdict", Conv.enumeration(SignatureResult.Verdict::of)); + } + + /** + * aes. qes (Qualified Electronic Signature, QES) is coming soon. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional level() { + return Json.optional(json, "level", Conv.enumeration(SignatureResult.Level::of)); + } + + /** + * The format field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional format() { + return Json.optional(json, "format", Conv.enumeration(SignatureResult.Format::of)); + } + + /** + * PAdES-B-LT once signed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional profile() { + return Json.optional(json, "profile", Conv.STRING); + } + + /** + * The signer field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional signer() { + return Json.optional(json, "signer", Conv.model(SignatureResult.Signer::fromJson)); + } + + /** + * The identity_used field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional identityUsed() { + return Json.optional(json, "identity_used", Conv.model(SignatureResult.IdentityUsed::fromJson)); + } + + /** + * The certificate the documents were signed with. null until signed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional certificate() { + return Json.optional(json, "certificate", Conv.model(SignatureResult.Certificate::fromJson)); + } + + /** + * The confirmation field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional confirmation() { + return Json.optional(json, "confirmation", Conv.model(SignatureResult.Confirmation::fromJson)); + } + + /** + * The trusted timestamp in the signature. null until signed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional timestamp() { + return Json.optional(json, "timestamp", Conv.model(SignatureResult.Timestamp::fromJson)); + } + + /** + * The documents field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> documents() { + return Json.optional(json, "documents", Conv.list(Conv.model(SignatureResult.Documents::fromJson))); + } + + /** + * The signature and initials the signer adopted for signature and initials fields. null until the signer adopts one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional adoptedSignature() { + return Json.optional(json, "adopted_signature", Conv.model(SignatureResult.AdoptedSignature::fromJson)); + } + + /** + * When the signer agreed, on the review screen, to sign the documents electronically. Set when any document has fields. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional electronicSignatureConsentAt() { + return Json.optional(json, "electronic_signature_consent_at", Conv.DATE_TIME); + } + + /** + * Path of the evidence PDF, once signed. See [Download the signature evidence](/sessions-api/electronic-signature/download-evidence). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional evidenceFile() { + return Json.optional(json, "evidence_file", Conv.STRING); + } + + /** + * Warnings raised for this signer. See [Electronic Signature warnings](/core-technology/electronic-signature/warnings-electronic-signature). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.model(SignatureResult.Warnings::fromJson))); + } + + /** + * The last cryptographic check of the signed documents. null until the documents are signed. If Didit can't confirm the outcome of signing, it's {"valid": null, "reason": "signature_provider_outcome_unknown", "checked_at": ...} and status stays signing: treat it as not signed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional validation() { + return Json.optional(json, "validation", Conv.model(SignatureResult.Validation::fromJson)); + } + + /** + * The declined_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional declinedAt() { + return Json.optional(json, "declined_at", Conv.DATE_TIME); + } + + /** + * The declined_action field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional declinedAction() { + return Json.optional(json, "declined_action", Conv.enumeration(SignatureResult.DeclinedAction::of)); + } + + /** + * The signed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional signedAt() { + return Json.optional(json, "signed_at", Conv.DATE_TIME); + } + + /** + * When the signing link stops working. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expiresAt() { + return Json.optional(json, "expires_at", Conv.DATE_TIME); + } + + /** + * The email_sent_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailSentAt() { + return Json.optional(json, "email_sent_at", Conv.DATE_TIME); + } + + /** + * The email_unavailable_reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailUnavailableReason() { + return Json.optional(json, "email_unavailable_reason", Conv.STRING); + } + + /** + * The signer's IP address when they asked for their code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ipAddress() { + return Json.optional(json, "ip_address", Conv.STRING); + } + + /** + * Not collected: always null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional place() { + return Json.optional(json, "place", Conv.STRING); + } + + /** + * The place_unavailable_reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional placeUnavailableReason() { + return Json.optional(json, "place_unavailable_reason", Conv.enumeration(SignatureResult.PlaceUnavailableReason::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link SignatureResult}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(SignatureResult from) { + super(from); + } + + /** + * Pass it as signature_id to download this signer's evidence or validate their signature. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The workflow step that produced it. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * Where the signer is in the step. Only signed means the documents are signed. + * + * @param status the value + * @return this builder + */ + public Builder status(SignatureResult.Status status) { + return set("status", Conv.enumeration(SignatureResult.Status::of), status); + } + + /** + * The step's outcome. A warning sets it through its rule; custom status rules apply after. + * + * @param verdict the value + * @return this builder + */ + public Builder verdict(SignatureResult.Verdict verdict) { + return set("verdict", Conv.enumeration(SignatureResult.Verdict::of), verdict); + } + + /** + * aes. qes (Qualified Electronic Signature, QES) is coming soon. + * + * @param level the value + * @return this builder + */ + public Builder level(SignatureResult.Level level) { + return set("level", Conv.enumeration(SignatureResult.Level::of), level); + } + + /** + * The format field. + * + * @param format the value + * @return this builder + */ + public Builder format(SignatureResult.Format format) { + return set("format", Conv.enumeration(SignatureResult.Format::of), format); + } + + /** + * PAdES-B-LT once signed. + * + * @param profile the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder profile(@Nullable String profile) { + return setNullable("profile", Conv.STRING, profile); + } + + /** + * The signer field. + * + * @param signer the value + * @return this builder + */ + public Builder signer(SignatureResult.Signer signer) { + return set("signer", Conv.model(SignatureResult.Signer::fromJson), signer); + } + + /** + * The identity_used field. + * + * @param identityUsed the value + * @return this builder + */ + public Builder identityUsed(SignatureResult.IdentityUsed identityUsed) { + return set("identity_used", Conv.model(SignatureResult.IdentityUsed::fromJson), identityUsed); + } + + /** + * The certificate the documents were signed with. null until signed. + * + * @param certificate the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder certificate(SignatureResult.@Nullable Certificate certificate) { + return setNullable("certificate", Conv.model(SignatureResult.Certificate::fromJson), certificate); + } + + /** + * The confirmation field. + * + * @param confirmation the value + * @return this builder + */ + public Builder confirmation(SignatureResult.Confirmation confirmation) { + return set("confirmation", Conv.model(SignatureResult.Confirmation::fromJson), confirmation); + } + + /** + * The trusted timestamp in the signature. null until signed. + * + * @param timestamp the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder timestamp(SignatureResult.@Nullable Timestamp timestamp) { + return setNullable("timestamp", Conv.model(SignatureResult.Timestamp::fromJson), timestamp); + } + + /** + * The documents field. + * + * @param documents the value + * @return this builder + */ + public Builder documents(List documents) { + return set("documents", Conv.list(Conv.model(SignatureResult.Documents::fromJson)), documents); + } + + /** + * The signature and initials the signer adopted for signature and initials fields. null until the signer adopts one. + * + * @param adoptedSignature the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder adoptedSignature(SignatureResult.@Nullable AdoptedSignature adoptedSignature) { + return setNullable("adopted_signature", Conv.model(SignatureResult.AdoptedSignature::fromJson), adoptedSignature); + } + + /** + * When the signer agreed, on the review screen, to sign the documents electronically. Set when any document has fields. + * + * @param electronicSignatureConsentAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder electronicSignatureConsentAt(@Nullable OffsetDateTime electronicSignatureConsentAt) { + return setNullable("electronic_signature_consent_at", Conv.DATE_TIME, electronicSignatureConsentAt); + } + + /** + * Path of the evidence PDF, once signed. See [Download the signature evidence](/sessions-api/electronic-signature/download-evidence). + * + * @param evidenceFile the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder evidenceFile(@Nullable String evidenceFile) { + return setNullable("evidence_file", Conv.STRING, evidenceFile); + } + + /** + * Warnings raised for this signer. See [Electronic Signature warnings](/core-technology/electronic-signature/warnings-electronic-signature). + * + * @param warnings the value + * @return this builder + */ + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.model(SignatureResult.Warnings::fromJson)), warnings); + } + + /** + * The last cryptographic check of the signed documents. null until the documents are signed. If Didit can't confirm the outcome of signing, it's {"valid": null, "reason": "signature_provider_outcome_unknown", "checked_at": ...} and status stays signing: treat it as not signed. + * + * @param validation the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder validation(SignatureResult.@Nullable Validation validation) { + return setNullable("validation", Conv.model(SignatureResult.Validation::fromJson), validation); + } + + /** + * The declined_at field. + * + * @param declinedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder declinedAt(@Nullable OffsetDateTime declinedAt) { + return setNullable("declined_at", Conv.DATE_TIME, declinedAt); + } + + /** + * The declined_action field. + * + * @param declinedAction the value + * @return this builder + */ + public Builder declinedAction(SignatureResult.DeclinedAction declinedAction) { + return set("declined_action", Conv.enumeration(SignatureResult.DeclinedAction::of), declinedAction); + } + + /** + * The signed_at field. + * + * @param signedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder signedAt(@Nullable OffsetDateTime signedAt) { + return setNullable("signed_at", Conv.DATE_TIME, signedAt); + } + + /** + * When the signing link stops working. + * + * @param expiresAt the value + * @return this builder + */ + public Builder expiresAt(OffsetDateTime expiresAt) { + return set("expires_at", Conv.DATE_TIME, expiresAt); + } + + /** + * The email_sent_at field. + * + * @param emailSentAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder emailSentAt(@Nullable OffsetDateTime emailSentAt) { + return setNullable("email_sent_at", Conv.DATE_TIME, emailSentAt); + } + + /** + * The email_unavailable_reason field. + * + * @param emailUnavailableReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder emailUnavailableReason(@Nullable String emailUnavailableReason) { + return setNullable("email_unavailable_reason", Conv.STRING, emailUnavailableReason); + } + + /** + * The signer's IP address when they asked for their code. + * + * @param ipAddress the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ipAddress(@Nullable String ipAddress) { + return setNullable("ip_address", Conv.STRING, ipAddress); + } + + /** + * Not collected: always null. + * + * @param place the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder place(@Nullable String place) { + return setNullable("place", Conv.STRING, place); + } + + /** + * The place_unavailable_reason field. + * + * @param placeUnavailableReason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder placeUnavailableReason(SignatureResult.@Nullable PlaceUnavailableReason placeUnavailableReason) { + return setNullable("place_unavailable_reason", Conv.enumeration(SignatureResult.PlaceUnavailableReason::of), placeUnavailableReason); + } + + /** + * Builds the value. + * + * @return a new immutable {@code SignatureResult} + */ + public SignatureResult build() { + return new SignatureResult(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Where the signer is in the step. Only signed means the documents are signed. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "intro"}. */ + public static final Status INTRO = new Status("intro"); + + /** The value {@code "review"}. */ + public static final Status REVIEW = new Status("review"); + + /** The value {@code "code"}. */ + public static final Status CODE = new Status("code"); + + /** The value {@code "signing"}. */ + public static final Status SIGNING = new Status("signing"); + + /** The value {@code "signed"}. */ + public static final Status SIGNED = new Status("signed"); + + /** The value {@code "declined"}. */ + public static final Status DECLINED = new Status("declined"); + + private static final List KNOWN_VALUES = List.of(INTRO, REVIEW, CODE, SIGNING, SIGNED, DECLINED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The step's outcome. A warning sets it through its rule; custom status rules apply after. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Verdict extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Verdict NOT_FINISHED = new Verdict("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final Verdict APPROVED = new Verdict("Approved"); + + /** The value {@code "In Review"}. */ + public static final Verdict IN_REVIEW = new Verdict("In Review"); + + /** The value {@code "Declined"}. */ + public static final Verdict DECLINED = new Verdict("Declined"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, IN_REVIEW, DECLINED); + + private Verdict(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Verdict of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Verdict::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * aes. qes (Qualified Electronic Signature, QES) is coming soon. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Level extends StringEnum { + /** The value {@code "aes"}. */ + public static final Level AES = new Level("aes"); + + /** The value {@code "qes"}. */ + public static final Level QES = new Level("qes"); + + private static final List KNOWN_VALUES = List.of(AES, QES); + + private Level(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Level of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Level::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Format extends StringEnum { + /** The value {@code "pades"}. */ + public static final Format PADES = new Format("pades"); + + private static final List KNOWN_VALUES = List.of(PADES); + + private Format(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Format of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Format::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class Signer extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "capacity", "company", "role", "roles", "identity_session_id"); + + private Signer(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Signer} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Signer fromJson(JsonNode json) { + return new Signer(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Verified name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * The capacity field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional capacity() { + return Json.optional(json, "capacity", Conv.enumeration(SignatureResult.Signer.Capacity::of)); + } + + /** + * The company field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional company() { + return Json.optional(json, "company", Conv.STRING); + } + + /** + * The key person's designation, as declared. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional role() { + return Json.optional(json, "role", Conv.STRING); + } + + /** + * The key person's structured roles, as declared for the company. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> roles() { + return Json.optional(json, "roles", Conv.list(Conv.STRING)); + } + + /** + * The KYC session that verified the signer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional identitySessionId() { + return Json.optional(json, "identity_session_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Signer}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Signer from) { + super(from); + } + + /** + * Verified name. + * + * @param name the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder name(@Nullable String name) { + return setNullable("name", Conv.STRING, name); + } + + /** + * The capacity field. + * + * @param capacity the value + * @return this builder + */ + public Builder capacity(SignatureResult.Signer.Capacity capacity) { + return set("capacity", Conv.enumeration(SignatureResult.Signer.Capacity::of), capacity); + } + + /** + * The company field. + * + * @param company the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder company(@Nullable String company) { + return setNullable("company", Conv.STRING, company); + } + + /** + * The key person's designation, as declared. + * + * @param role the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder role(@Nullable String role) { + return setNullable("role", Conv.STRING, role); + } + + /** + * The key person's structured roles, as declared for the company. + * + * @param roles the value + * @return this builder + */ + public Builder roles(List roles) { + return set("roles", Conv.list(Conv.STRING), roles); + } + + /** + * The KYC session that verified the signer. + * + * @param identitySessionId the value + * @return this builder + */ + public Builder identitySessionId(String identitySessionId) { + return set("identity_session_id", Conv.STRING, identitySessionId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Signer} + */ + public Signer build() { + return new Signer(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Capacity extends StringEnum { + /** The value {@code "self"}. */ + public static final Capacity SELF = new Capacity("self"); + + /** The value {@code "company"}. */ + public static final Capacity COMPANY = new Capacity("company"); + + private static final List KNOWN_VALUES = List.of(SELF, COMPANY); + + private Capacity(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Capacity of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Capacity::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + public static final class IdentityUsed extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "kyc_id", "verified", "expected_name"); + + private IdentityUsed(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code IdentityUsed} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static IdentityUsed fromJson(JsonNode json) { + return new IdentityUsed(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The session_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * The kyc_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kycId() { + return Json.optional(json, "kyc_id", Conv.STRING); + } + + /** + * The verified field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verified() { + return Json.optional(json, "verified", Conv.BOOLEAN); + } + + /** + * The name the signer had to match. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expectedName() { + return Json.optional(json, "expected_name", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link IdentityUsed}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(IdentityUsed from) { + super(from); + } + + /** + * The session_id field. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * The kyc_id field. + * + * @param kycId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder kycId(@Nullable String kycId) { + return setNullable("kyc_id", Conv.STRING, kycId); + } + + /** + * The verified field. + * + * @param verified the value + * @return this builder + */ + public Builder verified(boolean verified) { + return set("verified", Conv.BOOLEAN, verified); + } + + /** + * The name the signer had to match. + * + * @param expectedName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expectedName(@Nullable String expectedName) { + return setNullable("expected_name", Conv.STRING, expectedName); + } + + /** + * Builds the value. + * + * @return a new immutable {@code IdentityUsed} + */ + public IdentityUsed build() { + return new IdentityUsed(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** The certificate the documents were signed with. null until signed. */ + public static final class Certificate extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("issuer", "issued_to", "serial", "valid_from", "valid_until"); + + private Certificate(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Certificate} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Certificate fromJson(JsonNode json) { + return new Certificate(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The issuer field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional issuer() { + return Json.optional(json, "issuer", Conv.STRING); + } + + /** + * The issued_to field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional issuedTo() { + return Json.optional(json, "issued_to", Conv.STRING); + } + + /** + * Serial number, hexadecimal. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional serial() { + return Json.optional(json, "serial", Conv.STRING); + } + + /** + * The valid_from field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional validFrom() { + return Json.optional(json, "valid_from", Conv.DATE_TIME); + } + + /** + * The valid_until field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional validUntil() { + return Json.optional(json, "valid_until", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Certificate}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Certificate from) { + super(from); + } + + /** + * The issuer field. + * + * @param issuer the value + * @return this builder + */ + public Builder issuer(String issuer) { + return set("issuer", Conv.STRING, issuer); + } + + /** + * The issued_to field. + * + * @param issuedTo the value + * @return this builder + */ + public Builder issuedTo(String issuedTo) { + return set("issued_to", Conv.STRING, issuedTo); + } + + /** + * Serial number, hexadecimal. + * + * @param serial the value + * @return this builder + */ + public Builder serial(String serial) { + return set("serial", Conv.STRING, serial); + } + + /** + * The valid_from field. + * + * @param validFrom the value + * @return this builder + */ + public Builder validFrom(OffsetDateTime validFrom) { + return set("valid_from", Conv.DATE_TIME, validFrom); + } + + /** + * The valid_until field. + * + * @param validUntil the value + * @return this builder + */ + public Builder validUntil(OffsetDateTime validUntil) { + return set("valid_until", Conv.DATE_TIME, validUntil); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Certificate} + */ + public Certificate build() { + return new Certificate(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Confirmation extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("channel", "destination", "confirmed_at", "attempts"); + + private Confirmation(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Confirmation} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Confirmation fromJson(JsonNode json) { + return new Confirmation(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The channel field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional channel() { + return Json.optional(json, "channel", Conv.enumeration(SignatureResult.Confirmation.Channel::of)); + } + + /** + * The masked contact the code went to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional destination() { + return Json.optional(json, "destination", Conv.STRING); + } + + /** + * The confirmed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional confirmedAt() { + return Json.optional(json, "confirmed_at", Conv.DATE_TIME); + } + + /** + * Codes entered, the right one included. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional attempts() { + return Json.optional(json, "attempts", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Confirmation}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Confirmation from) { + super(from); + } + + /** + * The channel field. + * + * @param channel the value + * @return this builder + */ + public Builder channel(SignatureResult.Confirmation.Channel channel) { + return set("channel", Conv.enumeration(SignatureResult.Confirmation.Channel::of), channel); + } + + /** + * The masked contact the code went to. + * + * @param destination the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder destination(@Nullable String destination) { + return setNullable("destination", Conv.STRING, destination); + } + + /** + * The confirmed_at field. + * + * @param confirmedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder confirmedAt(@Nullable OffsetDateTime confirmedAt) { + return setNullable("confirmed_at", Conv.DATE_TIME, confirmedAt); + } + + /** + * Codes entered, the right one included. + * + * @param attempts the value + * @return this builder + */ + public Builder attempts(long attempts) { + return set("attempts", Conv.LONG, attempts); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Confirmation} + */ + public Confirmation build() { + return new Confirmation(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Channel extends StringEnum { + /** The value {@code "email"}. */ + public static final Channel EMAIL = new Channel("email"); + + /** The value {@code "sms"}. */ + public static final Channel SMS = new Channel("sms"); + + private static final List KNOWN_VALUES = List.of(EMAIL, SMS); + + private Channel(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Channel of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Channel::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** The trusted timestamp in the signature. null until signed. */ + public static final class Timestamp extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("time", "qualified", "valid"); + + private Timestamp(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Timestamp} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Timestamp fromJson(JsonNode json) { + return new Timestamp(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The time field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional time() { + return Json.optional(json, "time", Conv.DATE_TIME); + } + + /** + * Always false for AES. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional qualified() { + return Json.optional(json, "qualified", Conv.BOOLEAN); + } + + /** + * The valid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional valid() { + return Json.optional(json, "valid", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Timestamp}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Timestamp from) { + super(from); + } + + /** + * The time field. + * + * @param time the value + * @return this builder + */ + public Builder time(OffsetDateTime time) { + return set("time", Conv.DATE_TIME, time); + } + + /** + * Always false for AES. + * + * @param qualified the value + * @return this builder + */ + public Builder qualified(boolean qualified) { + return set("qualified", Conv.BOOLEAN, qualified); + } + + /** + * The valid field. + * + * @param valid the value + * @return this builder + */ + public Builder valid(boolean valid) { + return set("valid", Conv.BOOLEAN, valid); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Timestamp} + */ + public Timestamp build() { + return new Timestamp(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Documents extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "uuid", "title", "file_name", "pages", "size_bytes", "sha256_original", "sha256_signed", "status", "opened_at", "read_at", "read_to_end", "consented_at", "consent", "signed_file", "mode", "source_format", "conversion", "fields"); + + private Documents(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Documents} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Documents fromJson(JsonNode json) { + return new Documents(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The document on this signer's copy. Use it to download the signed file. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * Same as id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * The title field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional title() { + return Json.optional(json, "title", Conv.STRING); + } + + /** + * The file_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fileName() { + return Json.optional(json, "file_name", Conv.STRING); + } + + /** + * Pages of the document signed. For Word, of the converted PDF. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional pages() { + return Json.optional(json, "pages", Conv.LONG); + } + + /** + * The size_bytes field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sizeBytes() { + return Json.optional(json, "size_bytes", Conv.LONG); + } + + /** + * SHA-256 of the document as the signer was shown it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sha256Original() { + return Json.optional(json, "sha256_original", Conv.STRING); + } + + /** + * SHA-256 of the signed PDF, once signed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sha256Signed() { + return Json.optional(json, "sha256_signed", Conv.STRING); + } + + /** + * not_signed: opened but not signed. not_reached: never opened. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(SignatureResult.Documents.Status::of)); + } + + /** + * The opened_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional openedAt() { + return Json.optional(json, "opened_at", Conv.DATE_TIME); + } + + /** + * When the signer reached the end of a document without fields, when the step requires it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional readAt() { + return Json.optional(json, "read_at", Conv.DATE_TIME); + } + + /** + * The read_to_end field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional readToEnd() { + return Json.optional(json, "read_to_end", Conv.BOOLEAN); + } + + /** + * When the signer agreed to a document without fields, or finished the fields of a document with fields. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional consentedAt() { + return Json.optional(json, "consented_at", Conv.DATE_TIME); + } + + /** + * The consent field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional consent() { + return Json.optional(json, "consent", Conv.BOOLEAN); + } + + /** + * Path of the signed PDF, once signed. See [Download a signed document](/sessions-api/electronic-signature/download-signed-document). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional signedFile() { + return Json.optional(json, "signed_file", Conv.STRING); + } + + /** + * fields for a document with fields, read_and_agree for a document without. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional mode() { + return Json.optional(json, "mode", Conv.enumeration(SignatureResult.Documents.Mode::of)); + } + + /** + * pdf, or docx for a Word document Didit converted to PDF. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceFormat() { + return Json.optional(json, "source_format", Conv.enumeration(SignatureResult.Documents.SourceFormat::of)); + } + + /** + * null for a PDF. For a Word document, the original file and the conversion. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional conversion() { + return Json.optional(json, "conversion", Conv.model(SignatureResult.Documents.Conversion::fromJson)); + } + + /** + * Fields on the document, with their values. Empty for a document without fields. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> fields() { + return Json.optional(json, "fields", Conv.list(Conv.model(SignatureFieldResult::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Documents}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Documents from) { + super(from); + } + + /** + * The document on this signer's copy. Use it to download the signed file. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * Same as id. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The title field. + * + * @param title the value + * @return this builder + */ + public Builder title(String title) { + return set("title", Conv.STRING, title); + } + + /** + * The file_name field. + * + * @param fileName the value + * @return this builder + */ + public Builder fileName(String fileName) { + return set("file_name", Conv.STRING, fileName); + } + + /** + * Pages of the document signed. For Word, of the converted PDF. + * + * @param pages the value + * @return this builder + */ + public Builder pages(long pages) { + return set("pages", Conv.LONG, pages); + } + + /** + * The size_bytes field. + * + * @param sizeBytes the value + * @return this builder + */ + public Builder sizeBytes(long sizeBytes) { + return set("size_bytes", Conv.LONG, sizeBytes); + } + + /** + * SHA-256 of the document as the signer was shown it. + * + * @param sha256Original the value + * @return this builder + */ + public Builder sha256Original(String sha256Original) { + return set("sha256_original", Conv.STRING, sha256Original); + } + + /** + * SHA-256 of the signed PDF, once signed. + * + * @param sha256Signed the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sha256Signed(@Nullable String sha256Signed) { + return setNullable("sha256_signed", Conv.STRING, sha256Signed); + } + + /** + * not_signed: opened but not signed. not_reached: never opened. + * + * @param status the value + * @return this builder + */ + public Builder status(SignatureResult.Documents.Status status) { + return set("status", Conv.enumeration(SignatureResult.Documents.Status::of), status); + } + + /** + * The opened_at field. + * + * @param openedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder openedAt(@Nullable OffsetDateTime openedAt) { + return setNullable("opened_at", Conv.DATE_TIME, openedAt); + } + + /** + * When the signer reached the end of a document without fields, when the step requires it. + * + * @param readAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder readAt(@Nullable OffsetDateTime readAt) { + return setNullable("read_at", Conv.DATE_TIME, readAt); + } + + /** + * The read_to_end field. + * + * @param readToEnd the value + * @return this builder + */ + public Builder readToEnd(boolean readToEnd) { + return set("read_to_end", Conv.BOOLEAN, readToEnd); + } + + /** + * When the signer agreed to a document without fields, or finished the fields of a document with fields. + * + * @param consentedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder consentedAt(@Nullable OffsetDateTime consentedAt) { + return setNullable("consented_at", Conv.DATE_TIME, consentedAt); + } + + /** + * The consent field. + * + * @param consent the value + * @return this builder + */ + public Builder consent(boolean consent) { + return set("consent", Conv.BOOLEAN, consent); + } + + /** + * Path of the signed PDF, once signed. See [Download a signed document](/sessions-api/electronic-signature/download-signed-document). + * + * @param signedFile the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder signedFile(@Nullable String signedFile) { + return setNullable("signed_file", Conv.STRING, signedFile); + } + + /** + * fields for a document with fields, read_and_agree for a document without. + * + * @param mode the value + * @return this builder + */ + public Builder mode(SignatureResult.Documents.Mode mode) { + return set("mode", Conv.enumeration(SignatureResult.Documents.Mode::of), mode); + } + + /** + * pdf, or docx for a Word document Didit converted to PDF. + * + * @param sourceFormat the value + * @return this builder + */ + public Builder sourceFormat(SignatureResult.Documents.SourceFormat sourceFormat) { + return set("source_format", Conv.enumeration(SignatureResult.Documents.SourceFormat::of), sourceFormat); + } + + /** + * null for a PDF. For a Word document, the original file and the conversion. + * + * @param conversion the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder conversion(SignatureResult.Documents.@Nullable Conversion conversion) { + return setNullable("conversion", Conv.model(SignatureResult.Documents.Conversion::fromJson), conversion); + } + + /** + * Fields on the document, with their values. Empty for a document without fields. + * + * @param fields the value + * @return this builder + */ + public Builder fields(List fields) { + return set("fields", Conv.list(Conv.model(SignatureFieldResult::fromJson)), fields); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Documents} + */ + public Documents build() { + return new Documents(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * not_signed: opened but not signed. not_reached: never opened. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "signed"}. */ + public static final Status SIGNED = new Status("signed"); + + /** The value {@code "declined"}. */ + public static final Status DECLINED = new Status("declined"); + + /** The value {@code "not_signed"}. */ + public static final Status NOT_SIGNED = new Status("not_signed"); + + /** The value {@code "not_reached"}. */ + public static final Status NOT_REACHED = new Status("not_reached"); + + private static final List KNOWN_VALUES = List.of(SIGNED, DECLINED, NOT_SIGNED, NOT_REACHED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * fields for a document with fields, read_and_agree for a document without. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Mode extends StringEnum { + /** The value {@code "fields"}. */ + public static final Mode FIELDS = new Mode("fields"); + + /** The value {@code "read_and_agree"}. */ + public static final Mode READ_AND_AGREE = new Mode("read_and_agree"); + + private static final List KNOWN_VALUES = List.of(FIELDS, READ_AND_AGREE); + + private Mode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Mode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Mode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * pdf, or docx for a Word document Didit converted to PDF. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SourceFormat extends StringEnum { + /** The value {@code "pdf"}. */ + public static final SourceFormat PDF = new SourceFormat("pdf"); + + /** The value {@code "docx"}. */ + public static final SourceFormat DOCX = new SourceFormat("docx"); + + private static final List KNOWN_VALUES = List.of(PDF, DOCX); + + private SourceFormat(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SourceFormat of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SourceFormat::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** null for a PDF. For a Word document, the original file and the conversion. */ + public static final class Conversion extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("from", "file_name", "sha256", "size_bytes", "converter"); + + private Conversion(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Conversion} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Conversion fromJson(JsonNode json) { + return new Conversion(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The from field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional from() { + return Json.optional(json, "from", Conv.enumeration(SignatureResult.Documents.Conversion.From::of)); + } + + /** + * Your original Word file name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fileName() { + return Json.optional(json, "file_name", Conv.STRING); + } + + /** + * SHA-256 of your original Word file. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sha256() { + return Json.optional(json, "sha256", Conv.STRING); + } + + /** + * Size of your original Word file. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sizeBytes() { + return Json.optional(json, "size_bytes", Conv.LONG); + } + + /** + * Version of the converter that produced the PDF. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional converter() { + return Json.optional(json, "converter", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Conversion}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Conversion from) { + super(from); + } + + /** + * The from field. + * + * @param from the value + * @return this builder + */ + public Builder from(SignatureResult.Documents.Conversion.From from) { + return set("from", Conv.enumeration(SignatureResult.Documents.Conversion.From::of), from); + } + + /** + * Your original Word file name. + * + * @param fileName the value + * @return this builder + */ + public Builder fileName(String fileName) { + return set("file_name", Conv.STRING, fileName); + } + + /** + * SHA-256 of your original Word file. + * + * @param sha256 the value + * @return this builder + */ + public Builder sha256(String sha256) { + return set("sha256", Conv.STRING, sha256); + } + + /** + * Size of your original Word file. + * + * @param sizeBytes the value + * @return this builder + */ + public Builder sizeBytes(long sizeBytes) { + return set("size_bytes", Conv.LONG, sizeBytes); + } + + /** + * Version of the converter that produced the PDF. + * + * @param converter the value + * @return this builder + */ + public Builder converter(String converter) { + return set("converter", Conv.STRING, converter); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Conversion} + */ + public Conversion build() { + return new Conversion(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class From extends StringEnum { + /** The value {@code "docx"}. */ + public static final From DOCX = new From("docx"); + + private static final List KNOWN_VALUES = List.of(DOCX); + + private From(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static From of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, From::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + } + + /** + * The signature and initials the signer adopted for signature and initials fields. null until the signer adopts one. + */ + public static final class AdoptedSignature extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("method", "font", "adopted_at"); + + private AdoptedSignature(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AdoptedSignature} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AdoptedSignature fromJson(JsonNode json) { + return new AdoptedSignature(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * typed: the verified name in a handwriting style. drawn: drawn with a finger or a mouse. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional method() { + return Json.optional(json, "method", Conv.enumeration(SignatureResult.AdoptedSignature.Method::of)); + } + + /** + * The handwriting style of a typed signature. null when drawn. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional font() { + return Json.optional(json, "font", Conv.STRING); + } + + /** + * The adopted_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional adoptedAt() { + return Json.optional(json, "adopted_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AdoptedSignature}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AdoptedSignature from) { + super(from); + } + + /** + * typed: the verified name in a handwriting style. drawn: drawn with a finger or a mouse. + * + * @param method the value + * @return this builder + */ + public Builder method(SignatureResult.AdoptedSignature.Method method) { + return set("method", Conv.enumeration(SignatureResult.AdoptedSignature.Method::of), method); + } + + /** + * The handwriting style of a typed signature. null when drawn. + * + * @param font the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder font(@Nullable String font) { + return setNullable("font", Conv.STRING, font); + } + + /** + * The adopted_at field. + * + * @param adoptedAt the value + * @return this builder + */ + public Builder adoptedAt(OffsetDateTime adoptedAt) { + return set("adopted_at", Conv.DATE_TIME, adoptedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AdoptedSignature} + */ + public AdoptedSignature build() { + return new AdoptedSignature(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * typed: the verified name in a handwriting style. drawn: drawn with a finger or a mouse. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Method extends StringEnum { + /** The value {@code "typed"}. */ + public static final Method TYPED = new Method("typed"); + + /** The value {@code "drawn"}. */ + public static final Method DRAWN = new Method("drawn"); + + private static final List KNOWN_VALUES = List.of(TYPED, DRAWN); + + private Method(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Method of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Method::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + public static final class Warnings extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("code", "node_id"); + + private Warnings(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Warnings} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Warnings fromJson(JsonNode json) { + return new Warnings(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The code field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional code() { + return Json.optional(json, "code", Conv.enumeration(SignatureResult.Warnings.Code::of)); + } + + /** + * The node_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nodeId() { + return Json.optional(json, "node_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Warnings}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Warnings from) { + super(from); + } + + /** + * The code field. + * + * @param code the value + * @return this builder + */ + public Builder code(SignatureResult.Warnings.Code code) { + return set("code", Conv.enumeration(SignatureResult.Warnings.Code::of), code); + } + + /** + * The node_id field. + * + * @param nodeId the value + * @return this builder + */ + public Builder nodeId(String nodeId) { + return set("node_id", Conv.STRING, nodeId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Warnings} + */ + public Warnings build() { + return new Warnings(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Code extends StringEnum { + /** The value {@code "SIGNATURE_DECLINED"}. */ + public static final Code SIGNATURE_DECLINED = new Code("SIGNATURE_DECLINED"); + + /** The value {@code "SIGNATURE_OTP_FAILED"}. */ + public static final Code SIGNATURE_OTP_FAILED = new Code("SIGNATURE_OTP_FAILED"); + + /** The value {@code "SIGNATURE_NAME_MISMATCH"}. */ + public static final Code SIGNATURE_NAME_MISMATCH = new Code("SIGNATURE_NAME_MISMATCH"); + + /** The value {@code "SIGNATURE_IDENTITY_NOT_VERIFIED"}. */ + public static final Code SIGNATURE_IDENTITY_NOT_VERIFIED = new Code("SIGNATURE_IDENTITY_NOT_VERIFIED"); + + /** The value {@code "SIGNATURE_DOCUMENT_CHANGED"}. */ + public static final Code SIGNATURE_DOCUMENT_CHANGED = new Code("SIGNATURE_DOCUMENT_CHANGED"); + + /** The value {@code "SIGNATURE_FIELD_DATA_MISSING"}. */ + public static final Code SIGNATURE_FIELD_DATA_MISSING = new Code("SIGNATURE_FIELD_DATA_MISSING"); + + private static final List KNOWN_VALUES = List.of(SIGNATURE_DECLINED, SIGNATURE_OTP_FAILED, SIGNATURE_NAME_MISMATCH, SIGNATURE_IDENTITY_NOT_VERIFIED, SIGNATURE_DOCUMENT_CHANGED, SIGNATURE_FIELD_DATA_MISSING); + + private Code(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Code of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Code::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * The last cryptographic check of the signed documents. null until the documents are signed. If Didit can't confirm the outcome of signing, it's {"valid": null, "reason": "signature_provider_outcome_unknown", "checked_at": ...} and status stays signing: treat it as not signed. + */ + public static final class Validation extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("valid", "reason", "checked_at", "documents"); + + private Validation(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Validation} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Validation fromJson(JsonNode json) { + return new Validation(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * true when every document is valid. null when the outcome of signing couldn't be confirmed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional valid() { + return Json.optional(json, "valid", Conv.BOOLEAN); + } + + /** + * Set when valid is null: signature_provider_outcome_unknown. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * The checked_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional checkedAt() { + return Json.optional(json, "checked_at", Conv.DATE_TIME); + } + + /** + * The documents field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> documents() { + return Json.optional(json, "documents", Conv.list(Conv.model(SignatureResult.Validation.Documents::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Validation}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Validation from) { + super(from); + } + + /** + * true when every document is valid. null when the outcome of signing couldn't be confirmed. + * + * @param valid the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder valid(@Nullable Boolean valid) { + return setNullable("valid", Conv.BOOLEAN, valid); + } + + /** + * Set when valid is null: signature_provider_outcome_unknown. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * The checked_at field. + * + * @param checkedAt the value + * @return this builder + */ + public Builder checkedAt(OffsetDateTime checkedAt) { + return set("checked_at", Conv.DATE_TIME, checkedAt); + } + + /** + * The documents field. + * + * @param documents the value + * @return this builder + */ + public Builder documents(List documents) { + return set("documents", Conv.list(Conv.model(SignatureResult.Validation.Documents::fromJson)), documents); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Validation} + */ + public Validation build() { + return new Validation(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Documents extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "checked_at", "valid", "integrity", "trusted", "revoked", "trusted_list", "certificate", "timestamp"); + + private Documents(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Documents} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Documents fromJson(JsonNode json) { + return new Documents(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The checked_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional checkedAt() { + return Json.optional(json, "checked_at", Conv.DATE_TIME); + } + + /** + * true when the signature and the timestamp are both valid and trusted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional valid() { + return Json.optional(json, "valid", Conv.BOOLEAN); + } + + /** + * true when the document is unchanged since signing. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional integrity() { + return Json.optional(json, "integrity", Conv.BOOLEAN); + } + + /** + * true when the signing certificate chains to a trusted root. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional trusted() { + return Json.optional(json, "trusted", Conv.BOOLEAN); + } + + /** + * The revoked field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional revoked() { + return Json.optional(json, "revoked", Conv.BOOLEAN); + } + + /** + * EU Trusted List check. For AES: {"status": "not_applicable", "reason": "aes_not_qualified"}. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional trustedList() { + return Json.optional(json, "trusted_list", Conv.model(SignatureResult.Validation.Documents.TrustedList::fromJson)); + } + + /** + * The certificate the documents were signed with. null until signed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional certificate() { + return Json.optional(json, "certificate", Conv.model(SignatureResult.Validation.Documents.Certificate::fromJson)); + } + + /** + * The trusted timestamp in the signature. null until signed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional timestamp() { + return Json.optional(json, "timestamp", Conv.model(SignatureResult.Validation.Documents.Timestamp::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Documents}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Documents from) { + super(from); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The checked_at field. + * + * @param checkedAt the value + * @return this builder + */ + public Builder checkedAt(OffsetDateTime checkedAt) { + return set("checked_at", Conv.DATE_TIME, checkedAt); + } + + /** + * true when the signature and the timestamp are both valid and trusted. + * + * @param valid the value + * @return this builder + */ + public Builder valid(boolean valid) { + return set("valid", Conv.BOOLEAN, valid); + } + + /** + * true when the document is unchanged since signing. + * + * @param integrity the value + * @return this builder + */ + public Builder integrity(boolean integrity) { + return set("integrity", Conv.BOOLEAN, integrity); + } + + /** + * true when the signing certificate chains to a trusted root. + * + * @param trusted the value + * @return this builder + */ + public Builder trusted(boolean trusted) { + return set("trusted", Conv.BOOLEAN, trusted); + } + + /** + * The revoked field. + * + * @param revoked the value + * @return this builder + */ + public Builder revoked(boolean revoked) { + return set("revoked", Conv.BOOLEAN, revoked); + } + + /** + * EU Trusted List check. For AES: {"status": "not_applicable", "reason": "aes_not_qualified"}. + * + * @param trustedList the value + * @return this builder + */ + public Builder trustedList(SignatureResult.Validation.Documents.TrustedList trustedList) { + return set("trusted_list", Conv.model(SignatureResult.Validation.Documents.TrustedList::fromJson), trustedList); + } + + /** + * The certificate the documents were signed with. null until signed. + * + * @param certificate the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder certificate(SignatureResult.Validation.Documents.@Nullable Certificate certificate) { + return setNullable("certificate", Conv.model(SignatureResult.Validation.Documents.Certificate::fromJson), certificate); + } + + /** + * The trusted timestamp in the signature. null until signed. + * + * @param timestamp the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder timestamp(SignatureResult.Validation.Documents.@Nullable Timestamp timestamp) { + return setNullable("timestamp", Conv.model(SignatureResult.Validation.Documents.Timestamp::fromJson), timestamp); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Documents} + */ + public Documents build() { + return new Documents(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * EU Trusted List check. For AES: {"status": "not_applicable", "reason": "aes_not_qualified"}. + */ + public static final class TrustedList extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("status", "reason"); + + private TrustedList(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TrustedList} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TrustedList fromJson(JsonNode json) { + return new TrustedList(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * The reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TrustedList}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TrustedList from) { + super(from); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * The reason field. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TrustedList} + */ + public TrustedList build() { + return new TrustedList(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** The certificate the documents were signed with. null until signed. */ + public static final class Certificate extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("issuer", "issued_to", "serial", "valid_from", "valid_until"); + + private Certificate(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Certificate} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Certificate fromJson(JsonNode json) { + return new Certificate(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The issuer field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional issuer() { + return Json.optional(json, "issuer", Conv.STRING); + } + + /** + * The issued_to field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional issuedTo() { + return Json.optional(json, "issued_to", Conv.STRING); + } + + /** + * Serial number, hexadecimal. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional serial() { + return Json.optional(json, "serial", Conv.STRING); + } + + /** + * The valid_from field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional validFrom() { + return Json.optional(json, "valid_from", Conv.DATE_TIME); + } + + /** + * The valid_until field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional validUntil() { + return Json.optional(json, "valid_until", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Certificate}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Certificate from) { + super(from); + } + + /** + * The issuer field. + * + * @param issuer the value + * @return this builder + */ + public Builder issuer(String issuer) { + return set("issuer", Conv.STRING, issuer); + } + + /** + * The issued_to field. + * + * @param issuedTo the value + * @return this builder + */ + public Builder issuedTo(String issuedTo) { + return set("issued_to", Conv.STRING, issuedTo); + } + + /** + * Serial number, hexadecimal. + * + * @param serial the value + * @return this builder + */ + public Builder serial(String serial) { + return set("serial", Conv.STRING, serial); + } + + /** + * The valid_from field. + * + * @param validFrom the value + * @return this builder + */ + public Builder validFrom(OffsetDateTime validFrom) { + return set("valid_from", Conv.DATE_TIME, validFrom); + } + + /** + * The valid_until field. + * + * @param validUntil the value + * @return this builder + */ + public Builder validUntil(OffsetDateTime validUntil) { + return set("valid_until", Conv.DATE_TIME, validUntil); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Certificate} + */ + public Certificate build() { + return new Certificate(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** The trusted timestamp in the signature. null until signed. */ + public static final class Timestamp extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("time", "qualified", "valid"); + + private Timestamp(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Timestamp} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Timestamp fromJson(JsonNode json) { + return new Timestamp(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The time field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional time() { + return Json.optional(json, "time", Conv.DATE_TIME); + } + + /** + * Always false for AES. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional qualified() { + return Json.optional(json, "qualified", Conv.BOOLEAN); + } + + /** + * The valid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional valid() { + return Json.optional(json, "valid", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Timestamp}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Timestamp from) { + super(from); + } + + /** + * The time field. + * + * @param time the value + * @return this builder + */ + public Builder time(OffsetDateTime time) { + return set("time", Conv.DATE_TIME, time); + } + + /** + * Always false for AES. + * + * @param qualified the value + * @return this builder + */ + public Builder qualified(boolean qualified) { + return set("qualified", Conv.BOOLEAN, qualified); + } + + /** + * The valid field. + * + * @param valid the value + * @return this builder + */ + public Builder valid(boolean valid) { + return set("valid", Conv.BOOLEAN, valid); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Timestamp} + */ + public Timestamp build() { + return new Timestamp(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DeclinedAction extends StringEnum { + /** The value {@code "DECLINE"}. */ + public static final DeclinedAction DECLINE = new DeclinedAction("DECLINE"); + + /** The value {@code "REVIEW"}. */ + public static final DeclinedAction REVIEW = new DeclinedAction("REVIEW"); + + /** The value {@code "NO_ACTION"}. */ + public static final DeclinedAction NO_ACTION = new DeclinedAction("NO_ACTION"); + + private static final List KNOWN_VALUES = List.of(DECLINE, REVIEW, NO_ACTION); + + private DeclinedAction(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DeclinedAction of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DeclinedAction::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class PlaceUnavailableReason extends StringEnum { + /** The value {@code "not_collected"}. */ + public static final PlaceUnavailableReason NOT_COLLECTED = new PlaceUnavailableReason("not_collected"); + + private static final List KNOWN_VALUES = List.of(NOT_COLLECTED); + + private PlaceUnavailableReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static PlaceUnavailableReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, PlaceUnavailableReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/SimpleWorkflowFeature.java b/src/main/java/me/didit/types/SimpleWorkflowFeature.java index 6de4181..88132bc 100644 --- a/src/main/java/me/didit/types/SimpleWorkflowFeature.java +++ b/src/main/java/me/didit/types/SimpleWorkflowFeature.java @@ -60,7 +60,7 @@ public SimpleWorkflowFeature.Feature feature() { } /** - * Feature-specific settings. For QUESTIONNAIRE, include questionnaire_uuid with the questionnaire_id returned by the questionnaire create endpoint. For OCR, methods selects which ID Verification methods each country may use (document capture, non-doc lookup against a government or other authoritative source, and digital identity wallets); omit it and every country is document capture only. + * Feature-specific settings. For QUESTIONNAIRE, include questionnaire_uuid with the questionnaire_id returned by the questionnaire create endpoint. For OCR, methods selects which ID Verification methods each country may use (document capture, non-doc lookup against a government or other authoritative source, and digital identity wallets); omit it and every country is document capture only. For SIGNATURE, see [Electronic Signature settings](/management-api/workflows/feature-configs#electronic-signature): signature_level accepts aes only for now, and workflow documents and their fields are managed in the console, so through the API send documents and fields per session with signature_documents_source: "session". * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -103,7 +103,7 @@ public Builder feature(SimpleWorkflowFeature.Feature feature) { } /** - * Feature-specific settings. For QUESTIONNAIRE, include questionnaire_uuid with the questionnaire_id returned by the questionnaire create endpoint. For OCR, methods selects which ID Verification methods each country may use (document capture, non-doc lookup against a government or other authoritative source, and digital identity wallets); omit it and every country is document capture only. + * Feature-specific settings. For QUESTIONNAIRE, include questionnaire_uuid with the questionnaire_id returned by the questionnaire create endpoint. For OCR, methods selects which ID Verification methods each country may use (document capture, non-doc lookup against a government or other authoritative source, and digital identity wallets); omit it and every country is document capture only. For SIGNATURE, see [Electronic Signature settings](/management-api/workflows/feature-configs#electronic-signature): signature_level accepts aes only for now, and workflow documents and their fields are managed in the console, so through the API send documents and fields per session with signature_documents_source: "session". * * @param config the value * @return this builder @@ -193,7 +193,10 @@ public static final class Feature extends StringEnum { /** The value {@code "KYB_KEY_PEOPLE"}. */ public static final Feature KYB_KEY_PEOPLE = new Feature("KYB_KEY_PEOPLE"); - private static final List KNOWN_VALUES = List.of(OCR, NFC, LIVENESS, FACE_MATCH, PROOF_OF_ADDRESS, QUESTIONNAIRE, DOCUMENT_AI, PHONE_VERIFICATION, EMAIL_VERIFICATION, DATABASE_VALIDATION, AML, IP_ANALYSIS, AGE_ESTIMATION, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE); + /** The value {@code "SIGNATURE"}. */ + public static final Feature SIGNATURE = new Feature("SIGNATURE"); + + private static final List KNOWN_VALUES = List.of(OCR, NFC, LIVENESS, FACE_MATCH, PROOF_OF_ADDRESS, QUESTIONNAIRE, DOCUMENT_AI, PHONE_VERIFICATION, EMAIL_VERIFICATION, DATABASE_VALIDATION, AML, IP_ANALYSIS, AGE_ESTIMATION, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE, SIGNATURE); private Feature(String value) { super(value); diff --git a/src/main/java/me/didit/types/TransactionCreatedEvent.java b/src/main/java/me/didit/types/TransactionCreatedEvent.java new file mode 100644 index 0000000..911ac65 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionCreatedEvent.java @@ -0,0 +1,684 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.JsonUnion; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** A transaction was created and screened. Sent for live applications only. */ +public final class TransactionCreatedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "webhook_type", "transaction_id", "txn_id", "status", "score", "severity", "amount", "currency", "direction", "action_required"); + + private TransactionCreatedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionCreatedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionCreatedEvent fromJson(JsonNode json) { + return new TransactionCreatedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * Always transaction.created. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionCreatedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(TransactionCreatedEvent.WebhookType::of)); + } + + /** + * Didit's identifier of the transaction. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String transactionId() { + return Json.required(json, "transaction_id", Conv.STRING); + } + + /** + * The transaction_id you supplied at create time, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txnId() { + return Json.optional(json, "txn_id", Conv.STRING); + } + + /** + * Monitoring status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionCreatedEvent.Status status() { + return Json.required(json, "status", Conv.enumeration(TransactionCreatedEvent.Status::of)); + } + + /** + * Risk score accumulated by rules. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long score() { + return Json.required(json, "score", Conv.LONG); + } + + /** + * Risk severity, or null until a rule or provider sets one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional severity() { + return Json.optional(json, "severity", Conv.enumeration(TransactionCreatedEvent.Severity::of)); + } + + /** + * Transaction amount as a decimal string. Unlike the API it is not normalized, so it can carry trailing zeros (up to 18 decimal places). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String amount() { + return Json.required(json, "amount", Conv.STRING); + } + + /** + * Currency code or crypto asset symbol of amount. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String currency() { + return Json.required(json, "currency", Conv.STRING); + } + + /** + * Direction relative to the subject. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionCreatedEvent.Direction direction() { + return Json.required(json, "direction", Conv.enumeration(TransactionCreatedEvent.Direction::of)); + } + + /** + * Pending end-user action, or null. Same object as action_required on the transaction; session_token is a credential, so never log it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actionRequired() { + return Json.optional(json, "action_required", Conv.union(TransactionCreatedEvent.ActionRequired::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionCreatedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionCreatedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * Always transaction.created. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(TransactionCreatedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(TransactionCreatedEvent.WebhookType::of), webhookType); + } + + /** + * Didit's identifier of the transaction. + * + * @param transactionId the value + * @return this builder + */ + public Builder transactionId(String transactionId) { + return set("transaction_id", Conv.STRING, transactionId); + } + + /** + * The transaction_id you supplied at create time, or null. + * + * @param txnId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder txnId(@Nullable String txnId) { + return setNullable("txn_id", Conv.STRING, txnId); + } + + /** + * Monitoring status after the change. + * + * @param status the value + * @return this builder + */ + public Builder status(TransactionCreatedEvent.Status status) { + return set("status", Conv.enumeration(TransactionCreatedEvent.Status::of), status); + } + + /** + * Risk score accumulated by rules. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * Risk severity, or null until a rule or provider sets one. + * + * @param severity the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder severity(TransactionCreatedEvent.@Nullable Severity severity) { + return setNullable("severity", Conv.enumeration(TransactionCreatedEvent.Severity::of), severity); + } + + /** + * Transaction amount as a decimal string. Unlike the API it is not normalized, so it can carry trailing zeros (up to 18 decimal places). + * + * @param amount the value + * @return this builder + */ + public Builder amount(String amount) { + return set("amount", Conv.STRING, amount); + } + + /** + * Currency code or crypto asset symbol of amount. + * + * @param currency the value + * @return this builder + */ + public Builder currency(String currency) { + return set("currency", Conv.STRING, currency); + } + + /** + * Direction relative to the subject. + * + * @param direction the value + * @return this builder + */ + public Builder direction(TransactionCreatedEvent.Direction direction) { + return set("direction", Conv.enumeration(TransactionCreatedEvent.Direction::of), direction); + } + + /** + * Pending end-user action, or null. Same object as action_required on the transaction; session_token is a credential, so never log it. + * + * @param actionRequired the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actionRequired(TransactionCreatedEvent.@Nullable ActionRequired actionRequired) { + return setNullable("action_required", Conv.union(TransactionCreatedEvent.ActionRequired::fromJson), actionRequired); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionCreatedEvent} + * @throws IllegalStateException when a required field is missing + */ + public TransactionCreatedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("webhook_type", false); + require("transaction_id", false); + require("txn_id", true); + require("status", false); + require("score", false); + require("severity", true); + require("amount", false); + require("currency", false); + require("direction", false); + require("action_required", true); + return new TransactionCreatedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always transaction.created. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "transaction.created"}. */ + public static final WebhookType TRANSACTION_CREATED = new WebhookType("transaction.created"); + + private static final List KNOWN_VALUES = List.of(TRANSACTION_CREATED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Monitoring status after the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "APPROVED"}. */ + public static final Status APPROVED = new Status("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Status DECLINED = new Status("DECLINED"); + + /** The value {@code "AWAITING_USER"}. */ + public static final Status AWAITING_USER = new Status("AWAITING_USER"); + + private static final List KNOWN_VALUES = List.of(APPROVED, IN_REVIEW, DECLINED, AWAITING_USER); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Risk severity, or null until a rule or provider sets one. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Severity extends StringEnum { + /** The value {@code "UNKNOWN"}. */ + public static final Severity UNKNOWN = new Severity("UNKNOWN"); + + /** The value {@code "LOW"}. */ + public static final Severity LOW = new Severity("LOW"); + + /** The value {@code "MEDIUM"}. */ + public static final Severity MEDIUM = new Severity("MEDIUM"); + + /** The value {@code "HIGH"}. */ + public static final Severity HIGH = new Severity("HIGH"); + + /** The value {@code "CRITICAL"}. */ + public static final Severity CRITICAL = new Severity("CRITICAL"); + + private static final List KNOWN_VALUES = List.of(UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL); + + private Severity(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Severity of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Severity::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Direction relative to the subject. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Direction extends StringEnum { + /** The value {@code "INBOUND"}. */ + public static final Direction INBOUND = new Direction("INBOUND"); + + /** The value {@code "OUTBOUND"}. */ + public static final Direction OUTBOUND = new Direction("OUTBOUND"); + + private static final List KNOWN_VALUES = List.of(INBOUND, OUTBOUND); + + private Direction(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Direction of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Direction::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Pending end-user action, or null. Same object as action_required on the transaction; session_token is a credential, so never log it. + * + *

One of: {@code VerificationSession}, {@code WalletOwnership}. Check with the {@code is...} + * methods and read with the {@code as...} methods; the fields every variant has are readable directly. + */ + public static final class ActionRequired extends JsonUnion { + private ActionRequired(JsonNode json) { + super(json); + } + + /** + * Reads a {@code ActionRequired} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static ActionRequired fromJson(JsonNode json) { + return new ActionRequired(json.deepCopy()); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String type() { + return Json.required(json, "type", Conv.STRING); + } + + /** + * Hosted verification URL to open for the end user. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String url() { + return Json.required(json, "url", Conv.STRING); + } + + /** + * Wraps a {@code VerificationSession} value. + * + * @param value the value + * @return the union + */ + public static ActionRequired ofVerificationSession(TransactionVerificationSessionAction value) { + return new ActionRequired(Conv.model(TransactionVerificationSessionAction::fromJson).write(value)); + } + + /** + * Whether this value is a {@code VerificationSession}. + * + * Verification session + * + * @return true when it matches + */ + public boolean isVerificationSession() { + return Json.hasValue(json, "type", "verification_session"); + } + + /** + * Reads this value as a {@code VerificationSession}. + * + * @return the value, or empty when {@link #isVerificationSession()} is false + */ + public Optional asVerificationSession() { + return isVerificationSession() ? Optional.of(Conv.model(TransactionVerificationSessionAction::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code WalletOwnership} value. + * + * @param value the value + * @return the union + */ + public static ActionRequired ofWalletOwnership(TransactionWalletOwnershipAction value) { + return new ActionRequired(Conv.model(TransactionWalletOwnershipAction::fromJson).write(value)); + } + + /** + * Whether this value is a {@code WalletOwnership}. + * + * Wallet ownership + * + * @return true when it matches + */ + public boolean isWalletOwnership() { + return Json.hasValue(json, "type", "wallet_ownership"); + } + + /** + * Reads this value as a {@code WalletOwnership}. + * + * @return the value, or empty when {@link #isWalletOwnership()} is false + */ + public Optional asWalletOwnership() { + return isWalletOwnership() ? Optional.of(Conv.model(TransactionWalletOwnershipAction::fromJson).read(json)) : Optional.empty(); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionDetail.java b/src/main/java/me/didit/types/TransactionDetail.java index 25c20a8..73b28e1 100644 --- a/src/main/java/me/didit/types/TransactionDetail.java +++ b/src/main/java/me/didit/types/TransactionDetail.java @@ -407,13 +407,13 @@ public Optional> providerResults() { } /** - * Travel Rule transfer or check. Populated when travel_rule_details was submitted, and also when Travel Rule is enabled and the transaction was created with transaction_category: travelRule (Didit initiates the exchange itself, even without travel_rule_details). null when no transfer or check exists. + * Travel Rule transfer or check. Populated when travel_rule_details was submitted, and also when Travel Rule is enabled and the transaction was created with transaction_category: travelRule (Didit initiates the exchange itself, even without travel_rule_details). null when no transfer or check exists. On an inbound transfer, the counterparty's originator_data and beneficiary_data stay masked (data_masked: true) until wallet ownership is confirmed. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional travelRule() { - return Json.optional(json, "travel_rule", Conv.model(TransactionDetail.TravelRule::fromJson)); + public Optional travelRule() { + return Json.optional(json, "travel_rule", Conv.model(TransactionTravelRule::fromJson)); } /** @@ -820,13 +820,13 @@ public Builder providerResults(List providerR } /** - * Travel Rule transfer or check. Populated when travel_rule_details was submitted, and also when Travel Rule is enabled and the transaction was created with transaction_category: travelRule (Didit initiates the exchange itself, even without travel_rule_details). null when no transfer or check exists. + * Travel Rule transfer or check. Populated when travel_rule_details was submitted, and also when Travel Rule is enabled and the transaction was created with transaction_category: travelRule (Didit initiates the exchange itself, even without travel_rule_details). null when no transfer or check exists. On an inbound transfer, the counterparty's originator_data and beneficiary_data stay masked (data_masked: true) until wallet ownership is confirmed. * * @param travelRule the value, or {@code null} to send JSON null * @return this builder */ - public Builder travelRule(TransactionDetail.@Nullable TravelRule travelRule) { - return setNullable("travel_rule", Conv.model(TransactionDetail.TravelRule::fromJson), travelRule); + public Builder travelRule(@Nullable TransactionTravelRule travelRule) { + return setNullable("travel_rule", Conv.model(TransactionTravelRule::fromJson), travelRule); } /** @@ -3847,7 +3847,7 @@ public Optional actionSummary() { } /** - * Evaluation detail: scope_match, conditions[] and aggregations[]. Each condition entry carries field, operator, expected_value, the current_value read from the transaction, matched, group_index and group_logic. Each aggregation entry (velocity / count / sum) carries metric, field, operator, expected_value, the computed_value the aggregate evaluated to, window, filters, matched and sample_references; deferred async aggregations have computed_value: null and deferred: true. + * Evaluation detail: scope_match, conditions[] and aggregations[]. Each condition entry carries field, operator, expected_value, the current_value read from the transaction, matched, group_index and group_logic. Each aggregation entry (velocity / count / sum) carries metric, field, operator, expected_value, the computed_value the aggregate evaluated to, window, filters, matched and sample_references; deferred async aggregations have computed_value: null and deferred: true. A nested condition group is one conditions[] entry with operator any_of or all_of, field null, and its members' results under its own conditions. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -4000,7 +4000,7 @@ public Builder actionSummary(@Nullable String actionSummary) { } /** - * Evaluation detail: scope_match, conditions[] and aggregations[]. Each condition entry carries field, operator, expected_value, the current_value read from the transaction, matched, group_index and group_logic. Each aggregation entry (velocity / count / sum) carries metric, field, operator, expected_value, the computed_value the aggregate evaluated to, window, filters, matched and sample_references; deferred async aggregations have computed_value: null and deferred: true. + * Evaluation detail: scope_match, conditions[] and aggregations[]. Each condition entry carries field, operator, expected_value, the current_value read from the transaction, matched, group_index and group_logic. Each aggregation entry (velocity / count / sum) carries metric, field, operator, expected_value, the computed_value the aggregate evaluated to, window, filters, matched and sample_references; deferred async aggregations have computed_value: null and deferred: true. A nested condition group is one conditions[] entry with operator any_of or all_of, field null, and its members' results under its own conditions. * * @param metadata the value * @return this builder @@ -4379,234 +4379,6 @@ public boolean isKnown() { } } - /** - * Travel Rule transfer or check. Populated when travel_rule_details was submitted, and also when Travel Rule is enabled and the transaction was created with transaction_category: travelRule (Didit initiates the exchange itself, even without travel_rule_details). null when no transfer or check exists. - */ - public static final class TravelRule extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "status", "protocol", "required", "obligations_count", "originator_data", "beneficiary_data", "metadata"); - - private TravelRule(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code TravelRule} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static TravelRule fromJson(JsonNode json) { - return new TravelRule(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The uuid field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional uuid() { - return Json.optional(json, "uuid", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.STRING); - } - - /** - * The protocol field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional protocol() { - return Json.optional(json, "protocol", Conv.STRING); - } - - /** - * The required field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional required() { - return Json.optional(json, "required", Conv.BOOLEAN); - } - - /** - * The obligations_count field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional obligationsCount() { - return Json.optional(json, "obligations_count", Conv.LONG); - } - - /** - * The originator_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> originatorData() { - return Json.optional(json, "originator_data", Conv.OBJECT); - } - - /** - * The beneficiary_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> beneficiaryData() { - return Json.optional(json, "beneficiary_data", Conv.OBJECT); - } - - /** - * The metadata field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> metadata() { - return Json.optional(json, "metadata", Conv.OBJECT); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link TravelRule}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(TravelRule from) { - super(from); - } - - /** - * The uuid field. - * - * @param uuid the value - * @return this builder - */ - public Builder uuid(String uuid) { - return set("uuid", Conv.STRING, uuid); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(String status) { - return set("status", Conv.STRING, status); - } - - /** - * The protocol field. - * - * @param protocol the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder protocol(@Nullable String protocol) { - return setNullable("protocol", Conv.STRING, protocol); - } - - /** - * The required field. - * - * @param required the value - * @return this builder - */ - public Builder required(boolean required) { - return set("required", Conv.BOOLEAN, required); - } - - /** - * The obligations_count field. - * - * @param obligationsCount the value - * @return this builder - */ - public Builder obligationsCount(long obligationsCount) { - return set("obligations_count", Conv.LONG, obligationsCount); - } - - /** - * The originator_data field. - * - * @param originatorData the value - * @return this builder - */ - public Builder originatorData(Map originatorData) { - return setAny("originator_data", originatorData); - } - - /** - * The beneficiary_data field. - * - * @param beneficiaryData the value - * @return this builder - */ - public Builder beneficiaryData(Map beneficiaryData) { - return setAny("beneficiary_data", beneficiaryData); - } - - /** - * The metadata field. - * - * @param metadata the value - * @return this builder - */ - public Builder metadata(Map metadata) { - return setAny("metadata", metadata); - } - - /** - * Builds the value. - * - * @return a new immutable {@code TravelRule} - */ - public TravelRule build() { - return new TravelRule(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - /** * Graph snapshot of related transactions/parties used by the rules engine. Either the network_snapshot submitted with the transaction or the graph Didit builds from a successful AML wallet screening (which replaces a submitted one). null when neither exists. */ @@ -4950,8 +4722,8 @@ public String url() { * @param value the value * @return the union */ - public static ActionRequired ofVerificationSession(TransactionDetail.ActionRequired.VerificationSession value) { - return new ActionRequired(Conv.model(TransactionDetail.ActionRequired.VerificationSession::fromJson).write(value)); + public static ActionRequired ofVerificationSession(TransactionVerificationSessionAction value) { + return new ActionRequired(Conv.model(TransactionVerificationSessionAction::fromJson).write(value)); } /** @@ -4970,8 +4742,8 @@ public boolean isVerificationSession() { * * @return the value, or empty when {@link #isVerificationSession()} is false */ - public Optional asVerificationSession() { - return isVerificationSession() ? Optional.of(Conv.model(TransactionDetail.ActionRequired.VerificationSession::fromJson).read(json)) : Optional.empty(); + public Optional asVerificationSession() { + return isVerificationSession() ? Optional.of(Conv.model(TransactionVerificationSessionAction::fromJson).read(json)) : Optional.empty(); } /** @@ -4980,8 +4752,8 @@ public Optional asVerifica * @param value the value * @return the union */ - public static ActionRequired ofWalletOwnership(TransactionDetail.ActionRequired.WalletOwnership value) { - return new ActionRequired(Conv.model(TransactionDetail.ActionRequired.WalletOwnership::fromJson).write(value)); + public static ActionRequired ofWalletOwnership(TransactionWalletOwnershipAction value) { + return new ActionRequired(Conv.model(TransactionWalletOwnershipAction::fromJson).write(value)); } /** @@ -5000,404 +4772,8 @@ public boolean isWalletOwnership() { * * @return the value, or empty when {@link #isWalletOwnership()} is false */ - public Optional asWalletOwnership() { - return isWalletOwnership() ? Optional.of(Conv.model(TransactionDetail.ActionRequired.WalletOwnership::fromJson).read(json)) : Optional.empty(); - } - - /** Verification session */ - public static final class VerificationSession extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "url", "session_id", "session_token", "status"); - - private VerificationSession(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code VerificationSession} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static VerificationSession fromJson(JsonNode json) { - return new VerificationSession(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The type field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public TransactionDetail.ActionRequired.VerificationSession.Type type() { - return Json.required(json, "type", Conv.enumeration(TransactionDetail.ActionRequired.VerificationSession.Type::of)); - } - - /** - * Hosted verification URL to open for the end user. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String url() { - return Json.required(json, "url", Conv.STRING); - } - - /** - * Verification session id. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sessionId() { - return Json.optional(json, "session_id", Conv.STRING); - } - - /** - * Session token, used by the mobile SDKs to launch the flow natively. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional sessionToken() { - return Json.optional(json, "session_token", Conv.STRING); - } - - /** - * Verification session status. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link VerificationSession}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(VerificationSession from) { - super(from); - } - - /** - * The type field. - * - * @param type the value - * @return this builder - */ - public Builder type(TransactionDetail.ActionRequired.VerificationSession.Type type) { - return set("type", Conv.enumeration(TransactionDetail.ActionRequired.VerificationSession.Type::of), type); - } - - /** - * Hosted verification URL to open for the end user. - * - * @param url the value - * @return this builder - */ - public Builder url(String url) { - return set("url", Conv.STRING, url); - } - - /** - * Verification session id. - * - * @param sessionId the value - * @return this builder - */ - public Builder sessionId(String sessionId) { - return set("session_id", Conv.STRING, sessionId); - } - - /** - * Session token, used by the mobile SDKs to launch the flow natively. - * - * @param sessionToken the value - * @return this builder - */ - public Builder sessionToken(String sessionToken) { - return set("session_token", Conv.STRING, sessionToken); - } - - /** - * Verification session status. - * - * @param status the value - * @return this builder - */ - public Builder status(String status) { - return set("status", Conv.STRING, status); - } - - /** - * Builds the value. - * - * @return a new immutable {@code VerificationSession} - * @throws IllegalStateException when a required field is missing - */ - public VerificationSession build() { - require("type", false); - require("url", false); - return new VerificationSession(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Type extends StringEnum { - /** The value {@code "verification_session"}. */ - public static final Type VERIFICATION_SESSION = new Type("verification_session"); - - private static final List KNOWN_VALUES = List.of(VERIFICATION_SESSION); - - private Type(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Type of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Type::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** Wallet ownership */ - public static final class WalletOwnership extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "url", "widget_session_id", "expires_at"); - - private WalletOwnership(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code WalletOwnership} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static WalletOwnership fromJson(JsonNode json) { - return new WalletOwnership(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The type field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public TransactionDetail.ActionRequired.WalletOwnership.Type type() { - return Json.required(json, "type", Conv.enumeration(TransactionDetail.ActionRequired.WalletOwnership.Type::of)); - } - - /** - * Hosted wallet-ownership widget URL. Open it in a real browser surface - on mobile use SFSafariViewController or a Chrome Custom Tab, never an embedded webview. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String url() { - return Json.required(json, "url", Conv.STRING); - } - - /** - * Wallet-ownership widget session id. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional widgetSessionId() { - return Json.optional(json, "widget_session_id", Conv.STRING); - } - - /** - * When the widget session expires. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional expiresAt() { - return Json.optional(json, "expires_at", Conv.DATE_TIME); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link WalletOwnership}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(WalletOwnership from) { - super(from); - } - - /** - * The type field. - * - * @param type the value - * @return this builder - */ - public Builder type(TransactionDetail.ActionRequired.WalletOwnership.Type type) { - return set("type", Conv.enumeration(TransactionDetail.ActionRequired.WalletOwnership.Type::of), type); - } - - /** - * Hosted wallet-ownership widget URL. Open it in a real browser surface - on mobile use SFSafariViewController or a Chrome Custom Tab, never an embedded webview. - * - * @param url the value - * @return this builder - */ - public Builder url(String url) { - return set("url", Conv.STRING, url); - } - - /** - * Wallet-ownership widget session id. - * - * @param widgetSessionId the value - * @return this builder - */ - public Builder widgetSessionId(String widgetSessionId) { - return set("widget_session_id", Conv.STRING, widgetSessionId); - } - - /** - * When the widget session expires. - * - * @param expiresAt the value - * @return this builder - */ - public Builder expiresAt(OffsetDateTime expiresAt) { - return set("expires_at", Conv.DATE_TIME, expiresAt); - } - - /** - * Builds the value. - * - * @return a new immutable {@code WalletOwnership} - * @throws IllegalStateException when a required field is missing - */ - public WalletOwnership build() { - require("type", false); - require("url", false); - return new WalletOwnership(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Type extends StringEnum { - /** The value {@code "wallet_ownership"}. */ - public static final Type WALLET_OWNERSHIP = new Type("wallet_ownership"); - - private static final List KNOWN_VALUES = List.of(WALLET_OWNERSHIP); - - private Type(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Type of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Type::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } + public Optional asWalletOwnership() { + return isWalletOwnership() ? Optional.of(Conv.model(TransactionWalletOwnershipAction::fromJson).read(json)) : Optional.empty(); } } } diff --git a/src/main/java/me/didit/types/TransactionNote.java b/src/main/java/me/didit/types/TransactionNote.java new file mode 100644 index 0000000..e362180 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionNote.java @@ -0,0 +1,618 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** One note on a monitored transaction's review timeline. */ +public final class TransactionNote extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "note", "actor_email", "actor_name", "metadata", "created_at"); + + private TransactionNote(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionNote} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionNote fromJson(JsonNode json) { + return new TransactionNote(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Note identifier. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * Plain-text note body. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String note() { + return Json.required(json, "note", Conv.STRING); + } + + /** + * Email of the Console user who wrote the note or changed the status. null for notes created with an API key and for notes Didit adds automatically. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actorEmail() { + return Json.optional(json, "actor_email", Conv.STRING); + } + + /** + * Display name of the Console user (their email when they have no name). Rule Engine on notes Didit adds automatically. null for notes created with an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actorName() { + return Json.optional(json, "actor_name", Conv.STRING); + } + + /** + * Tells the note types apart. Notes written through the API or the Console carry mentioned_emails. A status change made in the Console carries type: status_change with previous_status, new_status, reason_code and reason_label. Notes Didit adds carry source: rule_action (with rule_uuid) for a rule's add_note action, or remediation_session for a problem creating the user-action verification session. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionNote.Metadata metadata() { + return Json.required(json, "metadata", Conv.model(TransactionNote.Metadata::fromJson)); + } + + /** + * When the note was stored. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionNote}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionNote from) { + super(from); + } + + /** + * Note identifier. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Plain-text note body. + * + * @param note the value + * @return this builder + */ + public Builder note(String note) { + return set("note", Conv.STRING, note); + } + + /** + * Email of the Console user who wrote the note or changed the status. null for notes created with an API key and for notes Didit adds automatically. + * + * @param actorEmail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actorEmail(@Nullable String actorEmail) { + return setNullable("actor_email", Conv.STRING, actorEmail); + } + + /** + * Display name of the Console user (their email when they have no name). Rule Engine on notes Didit adds automatically. null for notes created with an API key. + * + * @param actorName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actorName(@Nullable String actorName) { + return setNullable("actor_name", Conv.STRING, actorName); + } + + /** + * Tells the note types apart. Notes written through the API or the Console carry mentioned_emails. A status change made in the Console carries type: status_change with previous_status, new_status, reason_code and reason_label. Notes Didit adds carry source: rule_action (with rule_uuid) for a rule's add_note action, or remediation_session for a problem creating the user-action verification session. + * + * @param metadata the value + * @return this builder + */ + public Builder metadata(TransactionNote.Metadata metadata) { + return set("metadata", Conv.model(TransactionNote.Metadata::fromJson), metadata); + } + + /** + * When the note was stored. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionNote} + * @throws IllegalStateException when a required field is missing + */ + public TransactionNote build() { + require("uuid", false); + require("note", false); + require("actor_email", true); + require("actor_name", true); + require("metadata", false); + require("created_at", false); + return new TransactionNote(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Tells the note types apart. Notes written through the API or the Console carry mentioned_emails. A status change made in the Console carries type: status_change with previous_status, new_status, reason_code and reason_label. Notes Didit adds carry source: rule_action (with rule_uuid) for a rule's add_note action, or remediation_session for a problem creating the user-action verification session. + */ + public static final class Metadata extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("mentioned_emails", "type", "previous_status", "new_status", "reason_code", "reason_label", "source", "rule_uuid"); + + private Metadata(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Metadata} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Metadata fromJson(JsonNode json) { + return new Metadata(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Mentioned organization members, lower-cased and de-duplicated. Empty when nobody was mentioned. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> mentionedEmails() { + return Json.optional(json, "mentioned_emails", Conv.list(Conv.STRING)); + } + + /** + * Present on notes recording a status change made in the Console. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional type() { + return Json.optional(json, "type", Conv.enumeration(TransactionNote.Metadata.Type::of)); + } + + /** + * Status before the change. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previousStatus() { + return Json.optional(json, "previous_status", Conv.enumeration(TransactionNote.Metadata.PreviousStatus::of)); + } + + /** + * Status after the change. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional newStatus() { + return Json.optional(json, "new_status", Conv.enumeration(TransactionNote.Metadata.NewStatus::of)); + } + + /** + * Reason code given with the change. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reasonCode() { + return Json.optional(json, "reason_code", Conv.STRING); + } + + /** + * Reason label given with the change. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reasonLabel() { + return Json.optional(json, "reason_label", Conv.STRING); + } + + /** + * Present on notes Didit adds automatically. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(TransactionNote.Metadata.Source::of)); + } + + /** + * Rule whose add_note action wrote the note (with source: rule_action). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ruleUuid() { + return Json.optional(json, "rule_uuid", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Metadata}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Metadata from) { + super(from); + } + + /** + * Mentioned organization members, lower-cased and de-duplicated. Empty when nobody was mentioned. + * + * @param mentionedEmails the value + * @return this builder + */ + public Builder mentionedEmails(List mentionedEmails) { + return set("mentioned_emails", Conv.list(Conv.STRING), mentionedEmails); + } + + /** + * Present on notes recording a status change made in the Console. + * + * @param type the value + * @return this builder + */ + public Builder type(TransactionNote.Metadata.Type type) { + return set("type", Conv.enumeration(TransactionNote.Metadata.Type::of), type); + } + + /** + * Status before the change. + * + * @param previousStatus the value + * @return this builder + */ + public Builder previousStatus(TransactionNote.Metadata.PreviousStatus previousStatus) { + return set("previous_status", Conv.enumeration(TransactionNote.Metadata.PreviousStatus::of), previousStatus); + } + + /** + * Status after the change. + * + * @param newStatus the value + * @return this builder + */ + public Builder newStatus(TransactionNote.Metadata.NewStatus newStatus) { + return set("new_status", Conv.enumeration(TransactionNote.Metadata.NewStatus::of), newStatus); + } + + /** + * Reason code given with the change. + * + * @param reasonCode the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reasonCode(@Nullable String reasonCode) { + return setNullable("reason_code", Conv.STRING, reasonCode); + } + + /** + * Reason label given with the change. + * + * @param reasonLabel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reasonLabel(@Nullable String reasonLabel) { + return setNullable("reason_label", Conv.STRING, reasonLabel); + } + + /** + * Present on notes Didit adds automatically. + * + * @param source the value + * @return this builder + */ + public Builder source(TransactionNote.Metadata.Source source) { + return set("source", Conv.enumeration(TransactionNote.Metadata.Source::of), source); + } + + /** + * Rule whose add_note action wrote the note (with source: rule_action). + * + * @param ruleUuid the value + * @return this builder + */ + public Builder ruleUuid(String ruleUuid) { + return set("rule_uuid", Conv.STRING, ruleUuid); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Metadata} + */ + public Metadata build() { + return new Metadata(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Present on notes recording a status change made in the Console. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "status_change"}. */ + public static final Type STATUS_CHANGE = new Type("status_change"); + + private static final List KNOWN_VALUES = List.of(STATUS_CHANGE); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Status before the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class PreviousStatus extends StringEnum { + /** The value {@code "APPROVED"}. */ + public static final PreviousStatus APPROVED = new PreviousStatus("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final PreviousStatus IN_REVIEW = new PreviousStatus("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final PreviousStatus DECLINED = new PreviousStatus("DECLINED"); + + /** The value {@code "AWAITING_USER"}. */ + public static final PreviousStatus AWAITING_USER = new PreviousStatus("AWAITING_USER"); + + private static final List KNOWN_VALUES = List.of(APPROVED, IN_REVIEW, DECLINED, AWAITING_USER); + + private PreviousStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static PreviousStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, PreviousStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Status after the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NewStatus extends StringEnum { + /** The value {@code "APPROVED"}. */ + public static final NewStatus APPROVED = new NewStatus("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final NewStatus IN_REVIEW = new NewStatus("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final NewStatus DECLINED = new NewStatus("DECLINED"); + + /** The value {@code "AWAITING_USER"}. */ + public static final NewStatus AWAITING_USER = new NewStatus("AWAITING_USER"); + + private static final List KNOWN_VALUES = List.of(APPROVED, IN_REVIEW, DECLINED, AWAITING_USER); + + private NewStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NewStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NewStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Present on notes Didit adds automatically. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "rule_action"}. */ + public static final Source RULE_ACTION = new Source("rule_action"); + + /** The value {@code "remediation_session"}. */ + public static final Source REMEDIATION_SESSION = new Source("remediation_session"); + + private static final List KNOWN_VALUES = List.of(RULE_ACTION, REMEDIATION_SESSION); + + private Source(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/TransactionRuleAction.java b/src/main/java/me/didit/types/TransactionRuleAction.java index 11329dc..818c8f5 100644 --- a/src/main/java/me/didit/types/TransactionRuleAction.java +++ b/src/main/java/me/didit/types/TransactionRuleAction.java @@ -2,17 +2,10 @@ package me.didit.types; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import me.didit.core.Conv; -import me.didit.core.FileUpload; import me.didit.core.Json; -import me.didit.core.JsonModel; import me.didit.core.JsonUnion; -import me.didit.core.StringEnum; /** * One rule action. A rule can carry several; all are applied together when it matches. Rules in TEST mode record what they would have done without applying any action. @@ -41,8 +34,8 @@ public static TransactionRuleAction fromJson(JsonNode json) { * @param value the value * @return the union */ - public static TransactionRuleAction ofAddScore(TransactionRuleAction.AddScore value) { - return new TransactionRuleAction(Conv.model(TransactionRuleAction.AddScore::fromJson).write(value)); + public static TransactionRuleAction ofAddScore(TransactionRuleActionAddScore value) { + return new TransactionRuleAction(Conv.model(TransactionRuleActionAddScore::fromJson).write(value)); } /** @@ -61,8 +54,8 @@ public boolean isAddScore() { * * @return the value, or empty when {@link #isAddScore()} is false */ - public Optional asAddScore() { - return isAddScore() ? Optional.of(Conv.model(TransactionRuleAction.AddScore::fromJson).read(json)) : Optional.empty(); + public Optional asAddScore() { + return isAddScore() ? Optional.of(Conv.model(TransactionRuleActionAddScore::fromJson).read(json)) : Optional.empty(); } /** @@ -71,8 +64,8 @@ public Optional asAddScore() { * @param value the value * @return the union */ - public static TransactionRuleAction ofChangeStatus(TransactionRuleAction.ChangeStatus value) { - return new TransactionRuleAction(Conv.model(TransactionRuleAction.ChangeStatus::fromJson).write(value)); + public static TransactionRuleAction ofChangeStatus(TransactionRuleActionChangeStatus value) { + return new TransactionRuleAction(Conv.model(TransactionRuleActionChangeStatus::fromJson).write(value)); } /** @@ -91,8 +84,8 @@ public boolean isChangeStatus() { * * @return the value, or empty when {@link #isChangeStatus()} is false */ - public Optional asChangeStatus() { - return isChangeStatus() ? Optional.of(Conv.model(TransactionRuleAction.ChangeStatus::fromJson).read(json)) : Optional.empty(); + public Optional asChangeStatus() { + return isChangeStatus() ? Optional.of(Conv.model(TransactionRuleActionChangeStatus::fromJson).read(json)) : Optional.empty(); } /** @@ -101,8 +94,8 @@ public Optional asChangeStatus() { * @param value the value * @return the union */ - public static TransactionRuleAction ofAddTags(TransactionRuleAction.AddTags value) { - return new TransactionRuleAction(Conv.model(TransactionRuleAction.AddTags::fromJson).write(value)); + public static TransactionRuleAction ofAddTags(TransactionRuleActionAddTags value) { + return new TransactionRuleAction(Conv.model(TransactionRuleActionAddTags::fromJson).write(value)); } /** @@ -121,8 +114,8 @@ public boolean isAddTags() { * * @return the value, or empty when {@link #isAddTags()} is false */ - public Optional asAddTags() { - return isAddTags() ? Optional.of(Conv.model(TransactionRuleAction.AddTags::fromJson).read(json)) : Optional.empty(); + public Optional asAddTags() { + return isAddTags() ? Optional.of(Conv.model(TransactionRuleActionAddTags::fromJson).read(json)) : Optional.empty(); } /** @@ -131,8 +124,8 @@ public Optional asAddTags() { * @param value the value * @return the union */ - public static TransactionRuleAction ofAddNote(TransactionRuleAction.AddNote value) { - return new TransactionRuleAction(Conv.model(TransactionRuleAction.AddNote::fromJson).write(value)); + public static TransactionRuleAction ofAddNote(TransactionRuleActionAddNote value) { + return new TransactionRuleAction(Conv.model(TransactionRuleActionAddNote::fromJson).write(value)); } /** @@ -151,8 +144,8 @@ public boolean isAddNote() { * * @return the value, or empty when {@link #isAddNote()} is false */ - public Optional asAddNote() { - return isAddNote() ? Optional.of(Conv.model(TransactionRuleAction.AddNote::fromJson).read(json)) : Optional.empty(); + public Optional asAddNote() { + return isAddNote() ? Optional.of(Conv.model(TransactionRuleActionAddNote::fromJson).read(json)) : Optional.empty(); } /** @@ -161,8 +154,8 @@ public Optional asAddNote() { * @param value the value * @return the union */ - public static TransactionRuleAction ofAddToList(TransactionRuleAction.AddToList value) { - return new TransactionRuleAction(Conv.model(TransactionRuleAction.AddToList::fromJson).write(value)); + public static TransactionRuleAction ofAddToList(TransactionRuleActionAddToList value) { + return new TransactionRuleAction(Conv.model(TransactionRuleActionAddToList::fromJson).write(value)); } /** @@ -181,8 +174,8 @@ public boolean isAddToList() { * * @return the value, or empty when {@link #isAddToList()} is false */ - public Optional asAddToList() { - return isAddToList() ? Optional.of(Conv.model(TransactionRuleAction.AddToList::fromJson).read(json)) : Optional.empty(); + public Optional asAddToList() { + return isAddToList() ? Optional.of(Conv.model(TransactionRuleActionAddToList::fromJson).read(json)) : Optional.empty(); } /** @@ -191,8 +184,8 @@ public Optional asAddToList() { * @param value the value * @return the union */ - public static TransactionRuleAction ofOpenCase(TransactionRuleAction.OpenCase value) { - return new TransactionRuleAction(Conv.model(TransactionRuleAction.OpenCase::fromJson).write(value)); + public static TransactionRuleAction ofOpenCase(TransactionRuleActionOpenCase value) { + return new TransactionRuleAction(Conv.model(TransactionRuleActionOpenCase::fromJson).write(value)); } /** @@ -211,1089 +204,7 @@ public boolean isOpenCase() { * * @return the value, or empty when {@link #isOpenCase()} is false */ - public Optional asOpenCase() { - return isOpenCase() ? Optional.of(Conv.model(TransactionRuleAction.OpenCase::fromJson).read(json)) : Optional.empty(); - } - - /** - * Add points to the transaction's cumulative risk score; the total decides the status via the application's review/decline thresholds. - */ - public static final class AddScore extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "value"); - - private AddScore(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code AddScore} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static AddScore fromJson(JsonNode json) { - return new AddScore(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The type field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public TransactionRuleAction.AddScore.Type type() { - return Json.required(json, "type", Conv.enumeration(TransactionRuleAction.AddScore.Type::of)); - } - - /** - * Points added to the transaction's risk score. May be negative. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long value() { - return Json.required(json, "value", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link AddScore}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(AddScore from) { - super(from); - } - - /** - * The type field. - * - * @param type the value - * @return this builder - */ - public Builder type(TransactionRuleAction.AddScore.Type type) { - return set("type", Conv.enumeration(TransactionRuleAction.AddScore.Type::of), type); - } - - /** - * Points added to the transaction's risk score. May be negative. - * - * @param value the value - * @return this builder - */ - public Builder value(long value) { - return set("value", Conv.LONG, value); - } - - /** - * Builds the value. - * - * @return a new immutable {@code AddScore} - * @throws IllegalStateException when a required field is missing - */ - public AddScore build() { - require("type", false); - require("value", false); - return new AddScore(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Type extends StringEnum { - /** The value {@code "add_score"}. */ - public static final Type ADD_SCORE = new Type("add_score"); - - private static final List KNOWN_VALUES = List.of(ADD_SCORE); - - private Type(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Type of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Type::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** Directly set the transaction status. DECLINED always wins across matched rules. */ - public static final class ChangeStatus extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "value", "workflow_id"); - - private ChangeStatus(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code ChangeStatus} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static ChangeStatus fromJson(JsonNode json) { - return new ChangeStatus(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The type field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public TransactionRuleAction.ChangeStatus.Type type() { - return Json.required(json, "type", Conv.enumeration(TransactionRuleAction.ChangeStatus.Type::of)); - } - - /** - * Status to set immediately, regardless of score. Alias key: status. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public TransactionRuleAction.ChangeStatus.Value value() { - return Json.required(json, "value", Conv.enumeration(TransactionRuleAction.ChangeStatus.Value::of)); - } - - /** - * REQUIRED when value is AWAITING_USER: the verification workflow the subject is asked to complete. Alias key: remediation_workflow_id. Didit creates a hosted remediation session for the subject when the rule fires. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional workflowId() { - return Json.optional(json, "workflow_id", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link ChangeStatus}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(ChangeStatus from) { - super(from); - } - - /** - * The type field. - * - * @param type the value - * @return this builder - */ - public Builder type(TransactionRuleAction.ChangeStatus.Type type) { - return set("type", Conv.enumeration(TransactionRuleAction.ChangeStatus.Type::of), type); - } - - /** - * Status to set immediately, regardless of score. Alias key: status. - * - * @param value the value - * @return this builder - */ - public Builder value(TransactionRuleAction.ChangeStatus.Value value) { - return set("value", Conv.enumeration(TransactionRuleAction.ChangeStatus.Value::of), value); - } - - /** - * REQUIRED when value is AWAITING_USER: the verification workflow the subject is asked to complete. Alias key: remediation_workflow_id. Didit creates a hosted remediation session for the subject when the rule fires. - * - * @param workflowId the value - * @return this builder - */ - public Builder workflowId(String workflowId) { - return set("workflow_id", Conv.STRING, workflowId); - } - - /** - * Builds the value. - * - * @return a new immutable {@code ChangeStatus} - * @throws IllegalStateException when a required field is missing - */ - public ChangeStatus build() { - require("type", false); - require("value", false); - return new ChangeStatus(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Type extends StringEnum { - /** The value {@code "change_status"}. */ - public static final Type CHANGE_STATUS = new Type("change_status"); - - private static final List KNOWN_VALUES = List.of(CHANGE_STATUS); - - private Type(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Type of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Type::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Status to set immediately, regardless of score. Alias key: status. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Value extends StringEnum { - /** The value {@code "APPROVED"}. */ - public static final Value APPROVED = new Value("APPROVED"); - - /** The value {@code "IN_REVIEW"}. */ - public static final Value IN_REVIEW = new Value("IN_REVIEW"); - - /** The value {@code "DECLINED"}. */ - public static final Value DECLINED = new Value("DECLINED"); - - /** The value {@code "AWAITING_USER"}. */ - public static final Value AWAITING_USER = new Value("AWAITING_USER"); - - private static final List KNOWN_VALUES = List.of(APPROVED, IN_REVIEW, DECLINED, AWAITING_USER); - - private Value(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Value of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Value::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** Attach an application tag to the matched transaction. */ - public static final class AddTags extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "tag_uuid", "tag_name", "tag_color"); - - private AddTags(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code AddTags} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static AddTags fromJson(JsonNode json) { - return new AddTags(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The type field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public TransactionRuleAction.AddTags.Type type() { - return Json.required(json, "type", Conv.enumeration(TransactionRuleAction.AddTags.Type::of)); - } - - /** - * Existing application tag to attach. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional tagUuid() { - return Json.optional(json, "tag_uuid", Conv.STRING); - } - - /** - * Tag name to attach; created on first use (stored uppercased). Provide tag_uuid or tag_name. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional tagName() { - return Json.optional(json, "tag_name", Conv.STRING); - } - - /** - * Hex color used when tag_name creates a new tag, e.g. #007AFE. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional tagColor() { - return Json.optional(json, "tag_color", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link AddTags}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(AddTags from) { - super(from); - } - - /** - * The type field. - * - * @param type the value - * @return this builder - */ - public Builder type(TransactionRuleAction.AddTags.Type type) { - return set("type", Conv.enumeration(TransactionRuleAction.AddTags.Type::of), type); - } - - /** - * Existing application tag to attach. - * - * @param tagUuid the value - * @return this builder - */ - public Builder tagUuid(String tagUuid) { - return set("tag_uuid", Conv.STRING, tagUuid); - } - - /** - * Tag name to attach; created on first use (stored uppercased). Provide tag_uuid or tag_name. - * - * @param tagName the value - * @return this builder - */ - public Builder tagName(String tagName) { - return set("tag_name", Conv.STRING, tagName); - } - - /** - * Hex color used when tag_name creates a new tag, e.g. #007AFE. - * - * @param tagColor the value - * @return this builder - */ - public Builder tagColor(String tagColor) { - return set("tag_color", Conv.STRING, tagColor); - } - - /** - * Builds the value. - * - * @return a new immutable {@code AddTags} - * @throws IllegalStateException when a required field is missing - */ - public AddTags build() { - require("type", false); - return new AddTags(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Type extends StringEnum { - /** The value {@code "add_tags"}. */ - public static final Type ADD_TAGS = new Type("add_tags"); - - private static final List KNOWN_VALUES = List.of(ADD_TAGS); - - private Type(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Type of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Type::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** Attach an automated analyst note to the matched transaction. */ - public static final class AddNote extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "note"); - - private AddNote(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code AddNote} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static AddNote fromJson(JsonNode json) { - return new AddNote(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The type field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public TransactionRuleAction.AddNote.Type type() { - return Json.required(json, "type", Conv.enumeration(TransactionRuleAction.AddNote.Type::of)); - } - - /** - * Note text attached to the transaction (author: Rule Engine). Alias key: value. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String note() { - return Json.required(json, "note", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link AddNote}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(AddNote from) { - super(from); - } - - /** - * The type field. - * - * @param type the value - * @return this builder - */ - public Builder type(TransactionRuleAction.AddNote.Type type) { - return set("type", Conv.enumeration(TransactionRuleAction.AddNote.Type::of), type); - } - - /** - * Note text attached to the transaction (author: Rule Engine). Alias key: value. - * - * @param note the value - * @return this builder - */ - public Builder note(String note) { - return set("note", Conv.STRING, note); - } - - /** - * Builds the value. - * - * @return a new immutable {@code AddNote} - * @throws IllegalStateException when a required field is missing - */ - public AddNote build() { - require("type", false); - require("note", false); - return new AddNote(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Type extends StringEnum { - /** The value {@code "add_note"}. */ - public static final Type ADD_NOTE = new Type("add_note"); - - private static final List KNOWN_VALUES = List.of(ADD_NOTE); - - private Type(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Type of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Type::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** Add the subject's vendor_data to a blocklist, allowlist or custom list. */ - public static final class AddToList extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "list_id"); - - private AddToList(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code AddToList} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static AddToList fromJson(JsonNode json) { - return new AddToList(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The type field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public TransactionRuleAction.AddToList.Type type() { - return Json.required(json, "type", Conv.enumeration(TransactionRuleAction.AddToList.Type::of)); - } - - /** - * List to add the subject's vendor_data to. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String listId() { - return Json.required(json, "list_id", Conv.STRING); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link AddToList}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(AddToList from) { - super(from); - } - - /** - * The type field. - * - * @param type the value - * @return this builder - */ - public Builder type(TransactionRuleAction.AddToList.Type type) { - return set("type", Conv.enumeration(TransactionRuleAction.AddToList.Type::of), type); - } - - /** - * List to add the subject's vendor_data to. - * - * @param listId the value - * @return this builder - */ - public Builder listId(String listId) { - return set("list_id", Conv.STRING, listId); - } - - /** - * Builds the value. - * - * @return a new immutable {@code AddToList} - * @throws IllegalStateException when a required field is missing - */ - public AddToList build() { - require("type", false); - require("list_id", false); - return new AddToList(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Type extends StringEnum { - /** The value {@code "add_to_list"}. */ - public static final Type ADD_TO_LIST = new Type("add_to_list"); - - private static final List KNOWN_VALUES = List.of(ADD_TO_LIST); - - private Type(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Type of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Type::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** - * Open (or add to) an investigation case. Only the first open_case action on a rule is applied. - */ - public static final class OpenCase extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("type", "blueprint", "grouping", "attach_matched_transaction"); - - private OpenCase(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code OpenCase} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static OpenCase fromJson(JsonNode json) { - return new OpenCase(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The type field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public TransactionRuleAction.OpenCase.Type type() { - return Json.required(json, "type", Conv.enumeration(TransactionRuleAction.OpenCase.Type::of)); - } - - /** - * Case blueprint UUID; defaults to the application's default blueprint. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional blueprint() { - return Json.optional(json, "blueprint", Conv.STRING); - } - - /** - * Whether repeat matches join one case per applicant or one per rule+applicant. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional grouping() { - return Json.optional(json, "grouping", Conv.enumeration(TransactionRuleAction.OpenCase.Grouping::of)); - } - - /** - * Attach the matched transaction to the case. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional attachMatchedTransaction() { - return Json.optional(json, "attach_matched_transaction", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link OpenCase}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(OpenCase from) { - super(from); - } - - /** - * The type field. - * - * @param type the value - * @return this builder - */ - public Builder type(TransactionRuleAction.OpenCase.Type type) { - return set("type", Conv.enumeration(TransactionRuleAction.OpenCase.Type::of), type); - } - - /** - * Case blueprint UUID; defaults to the application's default blueprint. - * - * @param blueprint the value - * @return this builder - */ - public Builder blueprint(String blueprint) { - return set("blueprint", Conv.STRING, blueprint); - } - - /** - * Whether repeat matches join one case per applicant or one per rule+applicant. - * - * @param grouping the value - * @return this builder - */ - public Builder grouping(TransactionRuleAction.OpenCase.Grouping grouping) { - return set("grouping", Conv.enumeration(TransactionRuleAction.OpenCase.Grouping::of), grouping); - } - - /** - * Attach the matched transaction to the case. - * - * @param attachMatchedTransaction the value - * @return this builder - */ - public Builder attachMatchedTransaction(boolean attachMatchedTransaction) { - return set("attach_matched_transaction", Conv.BOOLEAN, attachMatchedTransaction); - } - - /** - * Builds the value. - * - * @return a new immutable {@code OpenCase} - * @throws IllegalStateException when a required field is missing - */ - public OpenCase build() { - require("type", false); - return new OpenCase(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Type extends StringEnum { - /** The value {@code "open_case"}. */ - public static final Type OPEN_CASE = new Type("open_case"); - - private static final List KNOWN_VALUES = List.of(OPEN_CASE); - - private Type(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Type of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Type::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Whether repeat matches join one case per applicant or one per rule+applicant. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Grouping extends StringEnum { - /** The value {@code "by_applicant"}. */ - public static final Grouping BY_APPLICANT = new Grouping("by_applicant"); - - /** The value {@code "by_rule_and_applicant"}. */ - public static final Grouping BY_RULE_AND_APPLICANT = new Grouping("by_rule_and_applicant"); - - private static final List KNOWN_VALUES = List.of(BY_APPLICANT, BY_RULE_AND_APPLICANT); - - private Grouping(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Grouping of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Grouping::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } + public Optional asOpenCase() { + return isOpenCase() ? Optional.of(Conv.model(TransactionRuleActionOpenCase::fromJson).read(json)) : Optional.empty(); } } diff --git a/src/main/java/me/didit/types/TransactionRuleActionAddNote.java b/src/main/java/me/didit/types/TransactionRuleActionAddNote.java new file mode 100644 index 0000000..617a976 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionRuleActionAddNote.java @@ -0,0 +1,161 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Attach an automated analyst note to the matched transaction. */ +public final class TransactionRuleActionAddNote extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "note"); + + private TransactionRuleActionAddNote(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionRuleActionAddNote} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionRuleActionAddNote fromJson(JsonNode json) { + return new TransactionRuleActionAddNote(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionRuleActionAddNote.Type type() { + return Json.required(json, "type", Conv.enumeration(TransactionRuleActionAddNote.Type::of)); + } + + /** + * Note text attached to the transaction (author: Rule Engine). Alias key: value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String note() { + return Json.required(json, "note", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionRuleActionAddNote}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionRuleActionAddNote from) { + super(from); + } + + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(TransactionRuleActionAddNote.Type type) { + return set("type", Conv.enumeration(TransactionRuleActionAddNote.Type::of), type); + } + + /** + * Note text attached to the transaction (author: Rule Engine). Alias key: value. + * + * @param note the value + * @return this builder + */ + public Builder note(String note) { + return set("note", Conv.STRING, note); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionRuleActionAddNote} + * @throws IllegalStateException when a required field is missing + */ + public TransactionRuleActionAddNote build() { + require("type", false); + require("note", false); + return new TransactionRuleActionAddNote(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "add_note"}. */ + public static final Type ADD_NOTE = new Type("add_note"); + + private static final List KNOWN_VALUES = List.of(ADD_NOTE); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionRuleActionAddScore.java b/src/main/java/me/didit/types/TransactionRuleActionAddScore.java new file mode 100644 index 0000000..ad23fe6 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionRuleActionAddScore.java @@ -0,0 +1,163 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * Add points to the transaction's cumulative risk score; the total decides the status via the application's review/decline thresholds. + */ +public final class TransactionRuleActionAddScore extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "value"); + + private TransactionRuleActionAddScore(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionRuleActionAddScore} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionRuleActionAddScore fromJson(JsonNode json) { + return new TransactionRuleActionAddScore(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionRuleActionAddScore.Type type() { + return Json.required(json, "type", Conv.enumeration(TransactionRuleActionAddScore.Type::of)); + } + + /** + * Points added to the transaction's risk score. May be negative. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long value() { + return Json.required(json, "value", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionRuleActionAddScore}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionRuleActionAddScore from) { + super(from); + } + + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(TransactionRuleActionAddScore.Type type) { + return set("type", Conv.enumeration(TransactionRuleActionAddScore.Type::of), type); + } + + /** + * Points added to the transaction's risk score. May be negative. + * + * @param value the value + * @return this builder + */ + public Builder value(long value) { + return set("value", Conv.LONG, value); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionRuleActionAddScore} + * @throws IllegalStateException when a required field is missing + */ + public TransactionRuleActionAddScore build() { + require("type", false); + require("value", false); + return new TransactionRuleActionAddScore(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "add_score"}. */ + public static final Type ADD_SCORE = new Type("add_score"); + + private static final List KNOWN_VALUES = List.of(ADD_SCORE); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionRuleActionAddTags.java b/src/main/java/me/didit/types/TransactionRuleActionAddTags.java new file mode 100644 index 0000000..1d6ba19 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionRuleActionAddTags.java @@ -0,0 +1,201 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Attach an application tag to the matched transaction. */ +public final class TransactionRuleActionAddTags extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "tag_uuid", "tag_name", "tag_color"); + + private TransactionRuleActionAddTags(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionRuleActionAddTags} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionRuleActionAddTags fromJson(JsonNode json) { + return new TransactionRuleActionAddTags(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionRuleActionAddTags.Type type() { + return Json.required(json, "type", Conv.enumeration(TransactionRuleActionAddTags.Type::of)); + } + + /** + * Existing application tag to attach. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional tagUuid() { + return Json.optional(json, "tag_uuid", Conv.STRING); + } + + /** + * Tag name to attach; created on first use (stored uppercased). Provide tag_uuid or tag_name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional tagName() { + return Json.optional(json, "tag_name", Conv.STRING); + } + + /** + * Hex color used when tag_name creates a new tag, e.g. #007AFE. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional tagColor() { + return Json.optional(json, "tag_color", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionRuleActionAddTags}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionRuleActionAddTags from) { + super(from); + } + + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(TransactionRuleActionAddTags.Type type) { + return set("type", Conv.enumeration(TransactionRuleActionAddTags.Type::of), type); + } + + /** + * Existing application tag to attach. + * + * @param tagUuid the value + * @return this builder + */ + public Builder tagUuid(String tagUuid) { + return set("tag_uuid", Conv.STRING, tagUuid); + } + + /** + * Tag name to attach; created on first use (stored uppercased). Provide tag_uuid or tag_name. + * + * @param tagName the value + * @return this builder + */ + public Builder tagName(String tagName) { + return set("tag_name", Conv.STRING, tagName); + } + + /** + * Hex color used when tag_name creates a new tag, e.g. #007AFE. + * + * @param tagColor the value + * @return this builder + */ + public Builder tagColor(String tagColor) { + return set("tag_color", Conv.STRING, tagColor); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionRuleActionAddTags} + * @throws IllegalStateException when a required field is missing + */ + public TransactionRuleActionAddTags build() { + require("type", false); + return new TransactionRuleActionAddTags(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "add_tags"}. */ + public static final Type ADD_TAGS = new Type("add_tags"); + + private static final List KNOWN_VALUES = List.of(ADD_TAGS); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionRuleActionAddToList.java b/src/main/java/me/didit/types/TransactionRuleActionAddToList.java new file mode 100644 index 0000000..99adb0b --- /dev/null +++ b/src/main/java/me/didit/types/TransactionRuleActionAddToList.java @@ -0,0 +1,161 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Add the subject's vendor_data to a blocklist, allowlist or custom list. */ +public final class TransactionRuleActionAddToList extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "list_id"); + + private TransactionRuleActionAddToList(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionRuleActionAddToList} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionRuleActionAddToList fromJson(JsonNode json) { + return new TransactionRuleActionAddToList(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionRuleActionAddToList.Type type() { + return Json.required(json, "type", Conv.enumeration(TransactionRuleActionAddToList.Type::of)); + } + + /** + * List to add the subject's vendor_data to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String listId() { + return Json.required(json, "list_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionRuleActionAddToList}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionRuleActionAddToList from) { + super(from); + } + + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(TransactionRuleActionAddToList.Type type) { + return set("type", Conv.enumeration(TransactionRuleActionAddToList.Type::of), type); + } + + /** + * List to add the subject's vendor_data to. + * + * @param listId the value + * @return this builder + */ + public Builder listId(String listId) { + return set("list_id", Conv.STRING, listId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionRuleActionAddToList} + * @throws IllegalStateException when a required field is missing + */ + public TransactionRuleActionAddToList build() { + require("type", false); + require("list_id", false); + return new TransactionRuleActionAddToList(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "add_to_list"}. */ + public static final Type ADD_TO_LIST = new Type("add_to_list"); + + private static final List KNOWN_VALUES = List.of(ADD_TO_LIST); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionRuleActionChangeStatus.java b/src/main/java/me/didit/types/TransactionRuleActionChangeStatus.java new file mode 100644 index 0000000..a93040c --- /dev/null +++ b/src/main/java/me/didit/types/TransactionRuleActionChangeStatus.java @@ -0,0 +1,231 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Directly set the transaction status. DECLINED always wins across matched rules. */ +public final class TransactionRuleActionChangeStatus extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "value", "workflow_id"); + + private TransactionRuleActionChangeStatus(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionRuleActionChangeStatus} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionRuleActionChangeStatus fromJson(JsonNode json) { + return new TransactionRuleActionChangeStatus(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionRuleActionChangeStatus.Type type() { + return Json.required(json, "type", Conv.enumeration(TransactionRuleActionChangeStatus.Type::of)); + } + + /** + * Status to set immediately, regardless of score. Alias key: status. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionRuleActionChangeStatus.Value value() { + return Json.required(json, "value", Conv.enumeration(TransactionRuleActionChangeStatus.Value::of)); + } + + /** + * REQUIRED when value is AWAITING_USER: the verification workflow the subject is asked to complete. Alias key: remediation_workflow_id. Didit creates a hosted remediation session for the subject when the rule fires. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowId() { + return Json.optional(json, "workflow_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionRuleActionChangeStatus}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionRuleActionChangeStatus from) { + super(from); + } + + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(TransactionRuleActionChangeStatus.Type type) { + return set("type", Conv.enumeration(TransactionRuleActionChangeStatus.Type::of), type); + } + + /** + * Status to set immediately, regardless of score. Alias key: status. + * + * @param value the value + * @return this builder + */ + public Builder value(TransactionRuleActionChangeStatus.Value value) { + return set("value", Conv.enumeration(TransactionRuleActionChangeStatus.Value::of), value); + } + + /** + * REQUIRED when value is AWAITING_USER: the verification workflow the subject is asked to complete. Alias key: remediation_workflow_id. Didit creates a hosted remediation session for the subject when the rule fires. + * + * @param workflowId the value + * @return this builder + */ + public Builder workflowId(String workflowId) { + return set("workflow_id", Conv.STRING, workflowId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionRuleActionChangeStatus} + * @throws IllegalStateException when a required field is missing + */ + public TransactionRuleActionChangeStatus build() { + require("type", false); + require("value", false); + return new TransactionRuleActionChangeStatus(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "change_status"}. */ + public static final Type CHANGE_STATUS = new Type("change_status"); + + private static final List KNOWN_VALUES = List.of(CHANGE_STATUS); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Status to set immediately, regardless of score. Alias key: status. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Value extends StringEnum { + /** The value {@code "APPROVED"}. */ + public static final Value APPROVED = new Value("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Value IN_REVIEW = new Value("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Value DECLINED = new Value("DECLINED"); + + /** The value {@code "AWAITING_USER"}. */ + public static final Value AWAITING_USER = new Value("AWAITING_USER"); + + private static final List KNOWN_VALUES = List.of(APPROVED, IN_REVIEW, DECLINED, AWAITING_USER); + + private Value(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Value of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Value::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionRuleActionOpenCase.java b/src/main/java/me/didit/types/TransactionRuleActionOpenCase.java new file mode 100644 index 0000000..11e0a0e --- /dev/null +++ b/src/main/java/me/didit/types/TransactionRuleActionOpenCase.java @@ -0,0 +1,246 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * Open (or add to) an investigation case. Only the first open_case action on a rule is applied. + */ +public final class TransactionRuleActionOpenCase extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "blueprint", "grouping", "attach_matched_transaction"); + + private TransactionRuleActionOpenCase(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionRuleActionOpenCase} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionRuleActionOpenCase fromJson(JsonNode json) { + return new TransactionRuleActionOpenCase(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionRuleActionOpenCase.Type type() { + return Json.required(json, "type", Conv.enumeration(TransactionRuleActionOpenCase.Type::of)); + } + + /** + * Case blueprint UUID; defaults to the application's default blueprint. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional blueprint() { + return Json.optional(json, "blueprint", Conv.STRING); + } + + /** + * Whether repeat matches join one case per applicant or one per rule+applicant. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional grouping() { + return Json.optional(json, "grouping", Conv.enumeration(TransactionRuleActionOpenCase.Grouping::of)); + } + + /** + * Attach the matched transaction to the case. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional attachMatchedTransaction() { + return Json.optional(json, "attach_matched_transaction", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionRuleActionOpenCase}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionRuleActionOpenCase from) { + super(from); + } + + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(TransactionRuleActionOpenCase.Type type) { + return set("type", Conv.enumeration(TransactionRuleActionOpenCase.Type::of), type); + } + + /** + * Case blueprint UUID; defaults to the application's default blueprint. + * + * @param blueprint the value + * @return this builder + */ + public Builder blueprint(String blueprint) { + return set("blueprint", Conv.STRING, blueprint); + } + + /** + * Whether repeat matches join one case per applicant or one per rule+applicant. + * + * @param grouping the value + * @return this builder + */ + public Builder grouping(TransactionRuleActionOpenCase.Grouping grouping) { + return set("grouping", Conv.enumeration(TransactionRuleActionOpenCase.Grouping::of), grouping); + } + + /** + * Attach the matched transaction to the case. + * + * @param attachMatchedTransaction the value + * @return this builder + */ + public Builder attachMatchedTransaction(boolean attachMatchedTransaction) { + return set("attach_matched_transaction", Conv.BOOLEAN, attachMatchedTransaction); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionRuleActionOpenCase} + * @throws IllegalStateException when a required field is missing + */ + public TransactionRuleActionOpenCase build() { + require("type", false); + return new TransactionRuleActionOpenCase(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "open_case"}. */ + public static final Type OPEN_CASE = new Type("open_case"); + + private static final List KNOWN_VALUES = List.of(OPEN_CASE); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Whether repeat matches join one case per applicant or one per rule+applicant. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Grouping extends StringEnum { + /** The value {@code "by_applicant"}. */ + public static final Grouping BY_APPLICANT = new Grouping("by_applicant"); + + /** The value {@code "by_rule_and_applicant"}. */ + public static final Grouping BY_RULE_AND_APPLICANT = new Grouping("by_rule_and_applicant"); + + private static final List KNOWN_VALUES = List.of(BY_APPLICANT, BY_RULE_AND_APPLICANT); + + private Grouping(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Grouping of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Grouping::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionRuleCondition.java b/src/main/java/me/didit/types/TransactionRuleCondition.java index 1d11963..edb6a30 100644 --- a/src/main/java/me/didit/types/TransactionRuleCondition.java +++ b/src/main/java/me/didit/types/TransactionRuleCondition.java @@ -54,7 +54,7 @@ public Builder toBuilder() { } /** - * Transaction field path to evaluate. Core fields: amount, currency, direction, action_type, score, severity, transaction_type, payment_details, preferred_currency_amount (falls back to amount_in_default_currency), default_currency_code, subject_country, counterparty_country, subject_vendor_data, counterparty_vendor_data, subject_device_fingerprint, counterparty_device_fingerprint, subject_browser_family, subject_browser_version, subject_platform, subject_accept_language, subject_session_age_ms, subject_ip_country, counterparty_ip_country, subject_ip_address, counterparty_ip_address, subject_payment_method_type, subject_payment_method_country, subject_payment_method_fingerprint, subject_payment_method_account_id (and the counterparty_payment_method_* equivalents), travel_rule_status, travel_rule_required, travel_rule_obligations_count, subject_days_since_previous_transaction, tags, and custom_values.<key> for any key submitted in custom_properties on transaction create. Country-code fields accept ISO-2 or ISO-3; both sides are normalized to ISO-3 before comparison. + * Transaction field path to evaluate. Core fields: amount, currency, direction, action_type, score, severity, transaction_type, payment_details, preferred_currency_amount (falls back to amount_in_default_currency), default_currency_code, subject_country, counterparty_country, subject_vendor_data, counterparty_vendor_data, subject_device_fingerprint, counterparty_device_fingerprint, subject_browser_family, subject_browser_version, subject_platform, subject_accept_language, subject_session_age_ms, subject_ip_country, counterparty_ip_country, subject_ip_address, counterparty_ip_address, subject_payment_method_type, subject_payment_method_country, subject_payment_method_fingerprint, subject_payment_method_account_id (and the counterparty_payment_method_* equivalents), travel_rule_status, travel_rule_required, travel_rule_obligations_count, counterparty_dd_status (your decision on the Travel Rule counterparty: APPROVED, RESTRICTED or BLOCKED, an entity following its brand's), counterparty_risk_level (LOW, MEDIUM or HIGH; HIGH while your own counterparty screening is flagged), counterparty_jurisdiction (the counterparty VASP's country), counterparty_responsiveness (RESPONSIVE, UNRESPONSIVE or UNKNOWN), subject_days_since_previous_transaction, tags, and custom_values.<key> for any key submitted in custom_properties on transaction create. Country-code fields accept ISO-2 or ISO-3; both sides are normalized to ISO-3 before comparison. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -147,7 +147,7 @@ private Builder(TransactionRuleCondition from) { } /** - * Transaction field path to evaluate. Core fields: amount, currency, direction, action_type, score, severity, transaction_type, payment_details, preferred_currency_amount (falls back to amount_in_default_currency), default_currency_code, subject_country, counterparty_country, subject_vendor_data, counterparty_vendor_data, subject_device_fingerprint, counterparty_device_fingerprint, subject_browser_family, subject_browser_version, subject_platform, subject_accept_language, subject_session_age_ms, subject_ip_country, counterparty_ip_country, subject_ip_address, counterparty_ip_address, subject_payment_method_type, subject_payment_method_country, subject_payment_method_fingerprint, subject_payment_method_account_id (and the counterparty_payment_method_* equivalents), travel_rule_status, travel_rule_required, travel_rule_obligations_count, subject_days_since_previous_transaction, tags, and custom_values.<key> for any key submitted in custom_properties on transaction create. Country-code fields accept ISO-2 or ISO-3; both sides are normalized to ISO-3 before comparison. + * Transaction field path to evaluate. Core fields: amount, currency, direction, action_type, score, severity, transaction_type, payment_details, preferred_currency_amount (falls back to amount_in_default_currency), default_currency_code, subject_country, counterparty_country, subject_vendor_data, counterparty_vendor_data, subject_device_fingerprint, counterparty_device_fingerprint, subject_browser_family, subject_browser_version, subject_platform, subject_accept_language, subject_session_age_ms, subject_ip_country, counterparty_ip_country, subject_ip_address, counterparty_ip_address, subject_payment_method_type, subject_payment_method_country, subject_payment_method_fingerprint, subject_payment_method_account_id (and the counterparty_payment_method_* equivalents), travel_rule_status, travel_rule_required, travel_rule_obligations_count, counterparty_dd_status (your decision on the Travel Rule counterparty: APPROVED, RESTRICTED or BLOCKED, an entity following its brand's), counterparty_risk_level (LOW, MEDIUM or HIGH; HIGH while your own counterparty screening is flagged), counterparty_jurisdiction (the counterparty VASP's country), counterparty_responsiveness (RESPONSIVE, UNRESPONSIVE or UNKNOWN), subject_days_since_previous_transaction, tags, and custom_values.<key> for any key submitted in custom_properties on transaction create. Country-code fields accept ISO-2 or ISO-3; both sides are normalized to ISO-3 before comparison. * * @param field the value * @return this builder diff --git a/src/main/java/me/didit/types/TransactionStatusUpdatedEvent.java b/src/main/java/me/didit/types/TransactionStatusUpdatedEvent.java new file mode 100644 index 0000000..5bf0367 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionStatusUpdatedEvent.java @@ -0,0 +1,686 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.JsonUnion; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * A transaction changed status, from a rule, a review or an end-user action. Sent for live applications only. + */ +public final class TransactionStatusUpdatedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "webhook_type", "transaction_id", "txn_id", "status", "score", "severity", "amount", "currency", "direction", "action_required"); + + private TransactionStatusUpdatedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionStatusUpdatedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionStatusUpdatedEvent fromJson(JsonNode json) { + return new TransactionStatusUpdatedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * Always transaction.status.updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionStatusUpdatedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(TransactionStatusUpdatedEvent.WebhookType::of)); + } + + /** + * Didit's identifier of the transaction. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String transactionId() { + return Json.required(json, "transaction_id", Conv.STRING); + } + + /** + * The transaction_id you supplied at create time, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txnId() { + return Json.optional(json, "txn_id", Conv.STRING); + } + + /** + * Monitoring status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionStatusUpdatedEvent.Status status() { + return Json.required(json, "status", Conv.enumeration(TransactionStatusUpdatedEvent.Status::of)); + } + + /** + * Risk score accumulated by rules. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long score() { + return Json.required(json, "score", Conv.LONG); + } + + /** + * Risk severity, or null until a rule or provider sets one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional severity() { + return Json.optional(json, "severity", Conv.enumeration(TransactionStatusUpdatedEvent.Severity::of)); + } + + /** + * Transaction amount as a decimal string. Unlike the API it is not normalized, so it can carry trailing zeros (up to 18 decimal places). + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String amount() { + return Json.required(json, "amount", Conv.STRING); + } + + /** + * Currency code or crypto asset symbol of amount. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String currency() { + return Json.required(json, "currency", Conv.STRING); + } + + /** + * Direction relative to the subject. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionStatusUpdatedEvent.Direction direction() { + return Json.required(json, "direction", Conv.enumeration(TransactionStatusUpdatedEvent.Direction::of)); + } + + /** + * Pending end-user action, or null. Same object as action_required on the transaction; session_token is a credential, so never log it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actionRequired() { + return Json.optional(json, "action_required", Conv.union(TransactionStatusUpdatedEvent.ActionRequired::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionStatusUpdatedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionStatusUpdatedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * Always transaction.status.updated. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(TransactionStatusUpdatedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(TransactionStatusUpdatedEvent.WebhookType::of), webhookType); + } + + /** + * Didit's identifier of the transaction. + * + * @param transactionId the value + * @return this builder + */ + public Builder transactionId(String transactionId) { + return set("transaction_id", Conv.STRING, transactionId); + } + + /** + * The transaction_id you supplied at create time, or null. + * + * @param txnId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder txnId(@Nullable String txnId) { + return setNullable("txn_id", Conv.STRING, txnId); + } + + /** + * Monitoring status after the change. + * + * @param status the value + * @return this builder + */ + public Builder status(TransactionStatusUpdatedEvent.Status status) { + return set("status", Conv.enumeration(TransactionStatusUpdatedEvent.Status::of), status); + } + + /** + * Risk score accumulated by rules. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * Risk severity, or null until a rule or provider sets one. + * + * @param severity the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder severity(TransactionStatusUpdatedEvent.@Nullable Severity severity) { + return setNullable("severity", Conv.enumeration(TransactionStatusUpdatedEvent.Severity::of), severity); + } + + /** + * Transaction amount as a decimal string. Unlike the API it is not normalized, so it can carry trailing zeros (up to 18 decimal places). + * + * @param amount the value + * @return this builder + */ + public Builder amount(String amount) { + return set("amount", Conv.STRING, amount); + } + + /** + * Currency code or crypto asset symbol of amount. + * + * @param currency the value + * @return this builder + */ + public Builder currency(String currency) { + return set("currency", Conv.STRING, currency); + } + + /** + * Direction relative to the subject. + * + * @param direction the value + * @return this builder + */ + public Builder direction(TransactionStatusUpdatedEvent.Direction direction) { + return set("direction", Conv.enumeration(TransactionStatusUpdatedEvent.Direction::of), direction); + } + + /** + * Pending end-user action, or null. Same object as action_required on the transaction; session_token is a credential, so never log it. + * + * @param actionRequired the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder actionRequired(TransactionStatusUpdatedEvent.@Nullable ActionRequired actionRequired) { + return setNullable("action_required", Conv.union(TransactionStatusUpdatedEvent.ActionRequired::fromJson), actionRequired); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionStatusUpdatedEvent} + * @throws IllegalStateException when a required field is missing + */ + public TransactionStatusUpdatedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("webhook_type", false); + require("transaction_id", false); + require("txn_id", true); + require("status", false); + require("score", false); + require("severity", true); + require("amount", false); + require("currency", false); + require("direction", false); + require("action_required", true); + return new TransactionStatusUpdatedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always transaction.status.updated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "transaction.status.updated"}. */ + public static final WebhookType TRANSACTION_STATUS_UPDATED = new WebhookType("transaction.status.updated"); + + private static final List KNOWN_VALUES = List.of(TRANSACTION_STATUS_UPDATED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Monitoring status after the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "APPROVED"}. */ + public static final Status APPROVED = new Status("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Status DECLINED = new Status("DECLINED"); + + /** The value {@code "AWAITING_USER"}. */ + public static final Status AWAITING_USER = new Status("AWAITING_USER"); + + private static final List KNOWN_VALUES = List.of(APPROVED, IN_REVIEW, DECLINED, AWAITING_USER); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Risk severity, or null until a rule or provider sets one. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Severity extends StringEnum { + /** The value {@code "UNKNOWN"}. */ + public static final Severity UNKNOWN = new Severity("UNKNOWN"); + + /** The value {@code "LOW"}. */ + public static final Severity LOW = new Severity("LOW"); + + /** The value {@code "MEDIUM"}. */ + public static final Severity MEDIUM = new Severity("MEDIUM"); + + /** The value {@code "HIGH"}. */ + public static final Severity HIGH = new Severity("HIGH"); + + /** The value {@code "CRITICAL"}. */ + public static final Severity CRITICAL = new Severity("CRITICAL"); + + private static final List KNOWN_VALUES = List.of(UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL); + + private Severity(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Severity of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Severity::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Direction relative to the subject. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Direction extends StringEnum { + /** The value {@code "INBOUND"}. */ + public static final Direction INBOUND = new Direction("INBOUND"); + + /** The value {@code "OUTBOUND"}. */ + public static final Direction OUTBOUND = new Direction("OUTBOUND"); + + private static final List KNOWN_VALUES = List.of(INBOUND, OUTBOUND); + + private Direction(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Direction of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Direction::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Pending end-user action, or null. Same object as action_required on the transaction; session_token is a credential, so never log it. + * + *

One of: {@code VerificationSession}, {@code WalletOwnership}. Check with the {@code is...} + * methods and read with the {@code as...} methods; the fields every variant has are readable directly. + */ + public static final class ActionRequired extends JsonUnion { + private ActionRequired(JsonNode json) { + super(json); + } + + /** + * Reads a {@code ActionRequired} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static ActionRequired fromJson(JsonNode json) { + return new ActionRequired(json.deepCopy()); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String type() { + return Json.required(json, "type", Conv.STRING); + } + + /** + * Hosted verification URL to open for the end user. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String url() { + return Json.required(json, "url", Conv.STRING); + } + + /** + * Wraps a {@code VerificationSession} value. + * + * @param value the value + * @return the union + */ + public static ActionRequired ofVerificationSession(TransactionVerificationSessionAction value) { + return new ActionRequired(Conv.model(TransactionVerificationSessionAction::fromJson).write(value)); + } + + /** + * Whether this value is a {@code VerificationSession}. + * + * Verification session + * + * @return true when it matches + */ + public boolean isVerificationSession() { + return Json.hasValue(json, "type", "verification_session"); + } + + /** + * Reads this value as a {@code VerificationSession}. + * + * @return the value, or empty when {@link #isVerificationSession()} is false + */ + public Optional asVerificationSession() { + return isVerificationSession() ? Optional.of(Conv.model(TransactionVerificationSessionAction::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code WalletOwnership} value. + * + * @param value the value + * @return the union + */ + public static ActionRequired ofWalletOwnership(TransactionWalletOwnershipAction value) { + return new ActionRequired(Conv.model(TransactionWalletOwnershipAction::fromJson).write(value)); + } + + /** + * Whether this value is a {@code WalletOwnership}. + * + * Wallet ownership + * + * @return true when it matches + */ + public boolean isWalletOwnership() { + return Json.hasValue(json, "type", "wallet_ownership"); + } + + /** + * Reads this value as a {@code WalletOwnership}. + * + * @return the value, or empty when {@link #isWalletOwnership()} is false + */ + public Optional asWalletOwnership() { + return isWalletOwnership() ? Optional.of(Conv.model(TransactionWalletOwnershipAction::fromJson).read(json)) : Optional.empty(); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionSummary.java b/src/main/java/me/didit/types/TransactionSummary.java new file mode 100644 index 0000000..6bc809d --- /dev/null +++ b/src/main/java/me/didit/types/TransactionSummary.java @@ -0,0 +1,1222 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Summary of a monitored transaction, the shape GET /v3/transactions/ lists. Call GET /v3/transactions/{transaction_id}/ with uuid for parties, alerts and rule runs. + */ +public final class TransactionSummary extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "transaction_number", "txn_id", "transaction_type", "action_type", "status", "source", "direction", "amount", "currency", "currency_type", "preferred_currency_amount", "preferred_currency_code", "payment_details", "txn_date", "created_at", "score", "severity", "decision_reason_code", "decision_reason_label", "tags", "subject_name", "subject_vendor_data", "subject_type", "subject_uuid", "subject_country", "counterparty_name", "counterparty_vendor_data", "counterparty_country", "assigned_to_name", "travel_rule_status", "application_id", "application_name", "organization_id", "organization_name"); + + private TransactionSummary(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionSummary} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionSummary fromJson(JsonNode json) { + return new TransactionSummary(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Didit transaction UUID. Use it as {transaction_id} on the transaction endpoints. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * Application-scoped sequential number shown in the Console. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionNumber() { + return Json.optional(json, "transaction_number", Conv.LONG); + } + + /** + * Your own identifier from the create call. Unique per application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txnId() { + return Json.optional(json, "txn_id", Conv.STRING); + } + + /** + * Category as stored: finance, kyc, travelRule, userPlatformEvent, gamblingBet, gamblingLimitChange, gamblingBonusChange or auditTrailEvent. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionType() { + return Json.optional(json, "transaction_type", Conv.STRING); + } + + /** + * Sub-type within the category, for example withdrawal. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actionType() { + return Json.optional(json, "action_type", Conv.STRING); + } + + /** + * Current monitoring verdict. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(TransactionSummary.Status::of)); + } + + /** + * LIVE for transactions submitted for monitoring, IMPORTED for historical imports. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(TransactionSummary.Source::of)); + } + + /** + * Direction relative to the subject. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional direction() { + return Json.optional(json, "direction", Conv.enumeration(TransactionSummary.Direction::of)); + } + + /** + * Amount as a decimal string with trailing zeros removed (up to 18 decimal places). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional amount() { + return Json.optional(json, "amount", Conv.STRING); + } + + /** + * Currency code or crypto asset symbol of amount. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currency() { + return Json.optional(json, "currency", Conv.STRING); + } + + /** + * fiat or crypto when known. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currencyType() { + return Json.optional(json, "currency_type", Conv.STRING); + } + + /** + * amount converted to the application's preferred currency, when a rate was available. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional preferredCurrencyAmount() { + return Json.optional(json, "preferred_currency_amount", Conv.STRING); + } + + /** + * Currency of preferred_currency_amount. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional preferredCurrencyCode() { + return Json.optional(json, "preferred_currency_code", Conv.STRING); + } + + /** + * Free-text payment reference. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional paymentDetails() { + return Json.optional(json, "payment_details", Conv.STRING); + } + + /** + * When the transaction occurred. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txnDate() { + return Json.optional(json, "txn_date", Conv.DATE_TIME); + } + + /** + * When Didit recorded it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.DATE_TIME); + } + + /** + * Risk score. Higher is riskier. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.LONG); + } + + /** + * Risk severity, null until something sets it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional severity() { + return Json.optional(json, "severity", Conv.enumeration(TransactionSummary.Severity::of)); + } + + /** + * Machine-readable reason for the current status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decisionReasonCode() { + return Json.optional(json, "decision_reason_code", Conv.STRING); + } + + /** + * Human-readable label for decision_reason_code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decisionReasonLabel() { + return Json.optional(json, "decision_reason_label", Conv.STRING); + } + + /** + * Tags on the transaction. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> tags() { + return Json.optional(json, "tags", Conv.list(Conv.model(TransactionSummary.Tags::fromJson))); + } + + /** + * Full name of the APPLICANT party. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectName() { + return Json.optional(json, "subject_name", Conv.STRING); + } + + /** + * Your vendor_data for the applicant. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectVendorData() { + return Json.optional(json, "subject_vendor_data", Conv.STRING); + } + + /** + * Whether the applicant is linked to a Didit user or business; null when it is linked to neither. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectType() { + return Json.optional(json, "subject_type", Conv.enumeration(TransactionSummary.SubjectType::of)); + } + + /** + * UUID of the Didit user or business the applicant is linked to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectUuid() { + return Json.optional(json, "subject_uuid", Conv.STRING); + } + + /** + * Country code of the applicant. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional subjectCountry() { + return Json.optional(json, "subject_country", Conv.STRING); + } + + /** + * Full name of the COUNTERPARTY party, if any. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyName() { + return Json.optional(json, "counterparty_name", Conv.STRING); + } + + /** + * Your vendor_data for the counterparty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyVendorData() { + return Json.optional(json, "counterparty_vendor_data", Conv.STRING); + } + + /** + * Country code of the counterparty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyCountry() { + return Json.optional(json, "counterparty_country", Conv.STRING); + } + + /** + * Name of the Console reviewer assigned to the transaction. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional assignedToName() { + return Json.optional(json, "assigned_to_name", Conv.STRING); + } + + /** + * Status of the transaction's Travel Rule transfer, or null when it has none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional travelRuleStatus() { + return Json.optional(json, "travel_rule_status", Conv.STRING); + } + + /** + * Application that owns the transaction. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional applicationId() { + return Json.optional(json, "application_id", Conv.STRING); + } + + /** + * Name of that application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional applicationName() { + return Json.optional(json, "application_name", Conv.STRING); + } + + /** + * Organization that owns the application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional organizationId() { + return Json.optional(json, "organization_id", Conv.STRING); + } + + /** + * Name of that organization. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional organizationName() { + return Json.optional(json, "organization_name", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionSummary}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionSummary from) { + super(from); + } + + /** + * Didit transaction UUID. Use it as {transaction_id} on the transaction endpoints. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Application-scoped sequential number shown in the Console. + * + * @param transactionNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder transactionNumber(@Nullable Long transactionNumber) { + return setNullable("transaction_number", Conv.LONG, transactionNumber); + } + + /** + * Your own identifier from the create call. Unique per application. + * + * @param txnId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder txnId(@Nullable String txnId) { + return setNullable("txn_id", Conv.STRING, txnId); + } + + /** + * Category as stored: finance, kyc, travelRule, userPlatformEvent, gamblingBet, gamblingLimitChange, gamblingBonusChange or auditTrailEvent. + * + * @param transactionType the value + * @return this builder + */ + public Builder transactionType(String transactionType) { + return set("transaction_type", Conv.STRING, transactionType); + } + + /** + * Sub-type within the category, for example withdrawal. + * + * @param actionType the value + * @return this builder + */ + public Builder actionType(String actionType) { + return set("action_type", Conv.STRING, actionType); + } + + /** + * Current monitoring verdict. + * + * @param status the value + * @return this builder + */ + public Builder status(TransactionSummary.Status status) { + return set("status", Conv.enumeration(TransactionSummary.Status::of), status); + } + + /** + * LIVE for transactions submitted for monitoring, IMPORTED for historical imports. + * + * @param source the value + * @return this builder + */ + public Builder source(TransactionSummary.Source source) { + return set("source", Conv.enumeration(TransactionSummary.Source::of), source); + } + + /** + * Direction relative to the subject. + * + * @param direction the value + * @return this builder + */ + public Builder direction(TransactionSummary.Direction direction) { + return set("direction", Conv.enumeration(TransactionSummary.Direction::of), direction); + } + + /** + * Amount as a decimal string with trailing zeros removed (up to 18 decimal places). + * + * @param amount the value + * @return this builder + */ + public Builder amount(String amount) { + return set("amount", Conv.STRING, amount); + } + + /** + * Currency code or crypto asset symbol of amount. + * + * @param currency the value + * @return this builder + */ + public Builder currency(String currency) { + return set("currency", Conv.STRING, currency); + } + + /** + * fiat or crypto when known. + * + * @param currencyType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder currencyType(@Nullable String currencyType) { + return setNullable("currency_type", Conv.STRING, currencyType); + } + + /** + * amount converted to the application's preferred currency, when a rate was available. + * + * @param preferredCurrencyAmount the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder preferredCurrencyAmount(@Nullable String preferredCurrencyAmount) { + return setNullable("preferred_currency_amount", Conv.STRING, preferredCurrencyAmount); + } + + /** + * Currency of preferred_currency_amount. + * + * @param preferredCurrencyCode the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder preferredCurrencyCode(@Nullable String preferredCurrencyCode) { + return setNullable("preferred_currency_code", Conv.STRING, preferredCurrencyCode); + } + + /** + * Free-text payment reference. + * + * @param paymentDetails the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder paymentDetails(@Nullable String paymentDetails) { + return setNullable("payment_details", Conv.STRING, paymentDetails); + } + + /** + * When the transaction occurred. + * + * @param txnDate the value + * @return this builder + */ + public Builder txnDate(OffsetDateTime txnDate) { + return set("txn_date", Conv.DATE_TIME, txnDate); + } + + /** + * When Didit recorded it. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Risk score. Higher is riskier. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * Risk severity, null until something sets it. + * + * @param severity the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder severity(TransactionSummary.@Nullable Severity severity) { + return setNullable("severity", Conv.enumeration(TransactionSummary.Severity::of), severity); + } + + /** + * Machine-readable reason for the current status. + * + * @param decisionReasonCode the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder decisionReasonCode(@Nullable String decisionReasonCode) { + return setNullable("decision_reason_code", Conv.STRING, decisionReasonCode); + } + + /** + * Human-readable label for decision_reason_code. + * + * @param decisionReasonLabel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder decisionReasonLabel(@Nullable String decisionReasonLabel) { + return setNullable("decision_reason_label", Conv.STRING, decisionReasonLabel); + } + + /** + * Tags on the transaction. + * + * @param tags the value + * @return this builder + */ + public Builder tags(List tags) { + return set("tags", Conv.list(Conv.model(TransactionSummary.Tags::fromJson)), tags); + } + + /** + * Full name of the APPLICANT party. + * + * @param subjectName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder subjectName(@Nullable String subjectName) { + return setNullable("subject_name", Conv.STRING, subjectName); + } + + /** + * Your vendor_data for the applicant. + * + * @param subjectVendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder subjectVendorData(@Nullable String subjectVendorData) { + return setNullable("subject_vendor_data", Conv.STRING, subjectVendorData); + } + + /** + * Whether the applicant is linked to a Didit user or business; null when it is linked to neither. + * + * @param subjectType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder subjectType(TransactionSummary.@Nullable SubjectType subjectType) { + return setNullable("subject_type", Conv.enumeration(TransactionSummary.SubjectType::of), subjectType); + } + + /** + * UUID of the Didit user or business the applicant is linked to. + * + * @param subjectUuid the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder subjectUuid(@Nullable String subjectUuid) { + return setNullable("subject_uuid", Conv.STRING, subjectUuid); + } + + /** + * Country code of the applicant. + * + * @param subjectCountry the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder subjectCountry(@Nullable String subjectCountry) { + return setNullable("subject_country", Conv.STRING, subjectCountry); + } + + /** + * Full name of the COUNTERPARTY party, if any. + * + * @param counterpartyName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder counterpartyName(@Nullable String counterpartyName) { + return setNullable("counterparty_name", Conv.STRING, counterpartyName); + } + + /** + * Your vendor_data for the counterparty. + * + * @param counterpartyVendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder counterpartyVendorData(@Nullable String counterpartyVendorData) { + return setNullable("counterparty_vendor_data", Conv.STRING, counterpartyVendorData); + } + + /** + * Country code of the counterparty. + * + * @param counterpartyCountry the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder counterpartyCountry(@Nullable String counterpartyCountry) { + return setNullable("counterparty_country", Conv.STRING, counterpartyCountry); + } + + /** + * Name of the Console reviewer assigned to the transaction. + * + * @param assignedToName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder assignedToName(@Nullable String assignedToName) { + return setNullable("assigned_to_name", Conv.STRING, assignedToName); + } + + /** + * Status of the transaction's Travel Rule transfer, or null when it has none. + * + * @param travelRuleStatus the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder travelRuleStatus(@Nullable String travelRuleStatus) { + return setNullable("travel_rule_status", Conv.STRING, travelRuleStatus); + } + + /** + * Application that owns the transaction. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * Name of that application. + * + * @param applicationName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder applicationName(@Nullable String applicationName) { + return setNullable("application_name", Conv.STRING, applicationName); + } + + /** + * Organization that owns the application. + * + * @param organizationId the value + * @return this builder + */ + public Builder organizationId(String organizationId) { + return set("organization_id", Conv.STRING, organizationId); + } + + /** + * Name of that organization. + * + * @param organizationName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder organizationName(@Nullable String organizationName) { + return setNullable("organization_name", Conv.STRING, organizationName); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionSummary} + */ + public TransactionSummary build() { + return new TransactionSummary(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Current monitoring verdict. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "APPROVED"}. */ + public static final Status APPROVED = new Status("APPROVED"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "DECLINED"}. */ + public static final Status DECLINED = new Status("DECLINED"); + + /** The value {@code "AWAITING_USER"}. */ + public static final Status AWAITING_USER = new Status("AWAITING_USER"); + + private static final List KNOWN_VALUES = List.of(APPROVED, IN_REVIEW, DECLINED, AWAITING_USER); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * LIVE for transactions submitted for monitoring, IMPORTED for historical imports. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "LIVE"}. */ + public static final Source LIVE = new Source("LIVE"); + + /** The value {@code "IMPORTED"}. */ + public static final Source IMPORTED = new Source("IMPORTED"); + + private static final List KNOWN_VALUES = List.of(LIVE, IMPORTED); + + private Source(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Direction relative to the subject. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Direction extends StringEnum { + /** The value {@code "INBOUND"}. */ + public static final Direction INBOUND = new Direction("INBOUND"); + + /** The value {@code "OUTBOUND"}. */ + public static final Direction OUTBOUND = new Direction("OUTBOUND"); + + private static final List KNOWN_VALUES = List.of(INBOUND, OUTBOUND); + + private Direction(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Direction of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Direction::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Risk severity, null until something sets it. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Severity extends StringEnum { + /** The value {@code "UNKNOWN"}. */ + public static final Severity UNKNOWN = new Severity("UNKNOWN"); + + /** The value {@code "LOW"}. */ + public static final Severity LOW = new Severity("LOW"); + + /** The value {@code "MEDIUM"}. */ + public static final Severity MEDIUM = new Severity("MEDIUM"); + + /** The value {@code "HIGH"}. */ + public static final Severity HIGH = new Severity("HIGH"); + + /** The value {@code "CRITICAL"}. */ + public static final Severity CRITICAL = new Severity("CRITICAL"); + + private static final List KNOWN_VALUES = List.of(UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL); + + private Severity(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Severity of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Severity::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class Tags extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "name", "color", "description", "source"); + + private Tags(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Tags} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Tags fromJson(JsonNode json) { + return new Tags(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * Hex color, for example #F59E0B. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional color() { + return Json.optional(json, "color", Conv.STRING); + } + + /** + * The description field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional description() { + return Json.optional(json, "description", Conv.STRING); + } + + /** + * Didit preset or custom tag. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(TransactionSummary.Tags.Source::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Tags}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Tags from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Hex color, for example #F59E0B. + * + * @param color the value + * @return this builder + */ + public Builder color(String color) { + return set("color", Conv.STRING, color); + } + + /** + * The description field. + * + * @param description the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder description(@Nullable String description) { + return setNullable("description", Conv.STRING, description); + } + + /** + * Didit preset or custom tag. + * + * @param source the value + * @return this builder + */ + public Builder source(TransactionSummary.Tags.Source source) { + return set("source", Conv.enumeration(TransactionSummary.Tags.Source::of), source); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Tags} + */ + public Tags build() { + return new Tags(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Didit preset or custom tag. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "didit"}. */ + public static final Source DIDIT = new Source("didit"); + + /** The value {@code "custom"}. */ + public static final Source CUSTOM = new Source("custom"); + + private static final List KNOWN_VALUES = List.of(DIDIT, CUSTOM); + + private Source(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * Whether the applicant is linked to a Didit user or business; null when it is linked to neither. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SubjectType extends StringEnum { + /** The value {@code "user"}. */ + public static final SubjectType USER = new SubjectType("user"); + + /** The value {@code "business"}. */ + public static final SubjectType BUSINESS = new SubjectType("business"); + + private static final List KNOWN_VALUES = List.of(USER, BUSINESS); + + private SubjectType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SubjectType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SubjectType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionTravelRule.java b/src/main/java/me/didit/types/TransactionTravelRule.java new file mode 100644 index 0000000..fd543a2 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionTravelRule.java @@ -0,0 +1,858 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * A transaction's Travel Rule transfer, as GET /v3/transactions/{transaction_id}/, the 201 of POST /v3/transactions/ and each row of GET /v3/transactions/ return it. direction, rail, counterparty_vasp, delivery, discovery, reason and next_actions have the same shape as on PATCH /v3/transactions/{transaction_id}/travel-rule/, so one read of a transaction says why its transfer is stuck and what moves it on. + */ +public final class TransactionTravelRule extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "status", "protocol", "required", "obligations_count", "originator_data", "beneficiary_data", "name_match", "data_match", "mismatched_fields", "data_unmasked", "data_masked", "metadata", "direction", "rail", "counterparty_vasp", "delivery", "discovery", "reason", "next_actions", "chain_details"); + + private TransactionTravelRule(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionTravelRule} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionTravelRule fromJson(JsonNode json) { + return new TransactionTravelRule(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * The protocol field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional protocol() { + return Json.optional(json, "protocol", Conv.STRING); + } + + /** + * The required field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional required() { + return Json.optional(json, "required", Conv.BOOLEAN); + } + + /** + * The obligations_count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional obligationsCount() { + return Json.optional(json, "obligations_count", Conv.LONG); + } + + /** + * Originator party. For an inbound transfer the counterparty's values are masked until ownership is confirmed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> originatorData() { + return Json.optional(json, "originator_data", Conv.OBJECT); + } + + /** + * Beneficiary party. For an inbound transfer the counterparty's values are masked until ownership is confirmed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> beneficiaryData() { + return Json.optional(json, "beneficiary_data", Conv.OBJECT); + } + + /** + * The name comparison that decided the transfer (score and strictness). Null until one ran. The compared names are masked together with the parties. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> nameMatch() { + return Json.optional(json, "name_match", Conv.OBJECT); + } + + /** + * Field-by-field comparison of the transfer's party data with what the other side knows about the wallet holder. It names fields and results, never values, so it stays readable while the counterparty's data is masked. Null until a comparison ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataMatch() { + return Json.optional(json, "data_match", Conv.model(TravelRuleDataMatch::fromJson)); + } + + /** + * Shortcut for data_match.mismatched_fields. Empty when nothing differs. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> mismatchedFields() { + return Json.optional(json, "mismatched_fields", Conv.list(Conv.STRING)); + } + + /** + * Audit record of the moment the counterparty's data was unmasked. Null while the data is still masked, and for outbound transfers. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataUnmasked() { + return Json.optional(json, "data_unmasked", Conv.model(TravelRuleDataUnmasked::fromJson)); + } + + /** + * True while the counterparty's personal data is masked in this response. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataMasked() { + return Json.optional(json, "data_masked", Conv.BOOLEAN); + } + + /** + * The metadata field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> metadata() { + return Json.optional(json, "metadata", Conv.OBJECT); + } + + /** + * Which side of the exchange this transfer is. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional direction() { + return Json.optional(json, "direction", Conv.enumeration(TransactionTravelRule.Direction::of)); + } + + /** + * The rail the transfer went over: INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA or EMAIL (TEST_VASP on a sandbox application, when the Didit Test VASP answered), or null until a rail is chosen. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rail() { + return Json.optional(json, "rail", Conv.STRING); + } + + /** + * The counterparty's directory entry, or null when the transfer links none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyVasp() { + return Json.optional(json, "counterparty_vasp", Conv.model(TransactionTravelRule.CounterpartyVasp::fromJson)); + } + + /** + * The newest message the transfer owes another party. null when it has owed none, for example below threshold_amount or for a declared self-hosted wallet. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional delivery() { + return Json.optional(json, "delivery", Conv.model(TravelRuleDelivery::fromJson)); + } + + /** + * How Didit looked for the counterparty, one entry per step that ran. Empty when no discovery ran, for example on an inbound transfer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> discovery() { + return Json.optional(json, "discovery", Conv.list(Conv.model(TravelRuleDiscoveryStep::fromJson))); + } + + /** + * Why a COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE transfer is stuck; "" for any other status. A failed delivery decides it first, on COUNTERPARTY_VASP_NOT_REACHABLE only: email_undeliverable (the mail provider refused the compliance email and no address is left to try: when it was sent, on a reminder, or as a permanent bounce reported later) or delivery_failed (the rail that was delivering failed for good: a network rail refused the message or ran out of retries, a TRP identity host did not answer, or the counterparty's TRP host did not accept the inquiry). Otherwise the discovery trace decides it: awaiting_end_user (no step named a counterparty Didit can reach), directory_match_no_route (a step named a directory VASP that has no route), picked_member_without_proven_wallet (the end user picked a Didit customer that has not proven control of the wallet), or no_directory_match when the trace gives no other reason. A resend moves the transfer on, so the reason clears. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * What moves the transfer on; [] for any status but COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE. email_undeliverable offers update_compliance_email, then resend. delivery_failed offers resend. A discovery reason offers select_counterparty_vasp, declare_self_hosted, add_vasp_hint and resend. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> nextActions() { + return Json.optional(json, "next_actions", Conv.list(Conv.model(TravelRuleNextAction::fromJson))); + } + + /** + * How the transfer's chain names its two wallets, when that is more than one address paying another. Empty ({}) for an account-chain transfer (EVM, Solana, Tron), where the two addresses are the wallets. Matching, routing and the address book read the wallets from here first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional chainDetails() { + return Json.optional(json, "chain_details", Conv.model(TravelRuleChainDetails::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionTravelRule}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionTravelRule from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * The protocol field. + * + * @param protocol the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder protocol(@Nullable String protocol) { + return setNullable("protocol", Conv.STRING, protocol); + } + + /** + * The required field. + * + * @param required the value + * @return this builder + */ + public Builder required(boolean required) { + return set("required", Conv.BOOLEAN, required); + } + + /** + * The obligations_count field. + * + * @param obligationsCount the value + * @return this builder + */ + public Builder obligationsCount(long obligationsCount) { + return set("obligations_count", Conv.LONG, obligationsCount); + } + + /** + * Originator party. For an inbound transfer the counterparty's values are masked until ownership is confirmed. + * + * @param originatorData the value + * @return this builder + */ + public Builder originatorData(Map originatorData) { + return setAny("originator_data", originatorData); + } + + /** + * Beneficiary party. For an inbound transfer the counterparty's values are masked until ownership is confirmed. + * + * @param beneficiaryData the value + * @return this builder + */ + public Builder beneficiaryData(Map beneficiaryData) { + return setAny("beneficiary_data", beneficiaryData); + } + + /** + * The name comparison that decided the transfer (score and strictness). Null until one ran. The compared names are masked together with the parties. + * + * @param nameMatch the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nameMatch(@Nullable Map nameMatch) { + return setAny("name_match", nameMatch); + } + + /** + * Field-by-field comparison of the transfer's party data with what the other side knows about the wallet holder. It names fields and results, never values, so it stays readable while the counterparty's data is masked. Null until a comparison ran. + * + * @param dataMatch the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dataMatch(@Nullable TravelRuleDataMatch dataMatch) { + return setNullable("data_match", Conv.model(TravelRuleDataMatch::fromJson), dataMatch); + } + + /** + * Shortcut for data_match.mismatched_fields. Empty when nothing differs. + * + * @param mismatchedFields the value + * @return this builder + */ + public Builder mismatchedFields(List mismatchedFields) { + return set("mismatched_fields", Conv.list(Conv.STRING), mismatchedFields); + } + + /** + * Audit record of the moment the counterparty's data was unmasked. Null while the data is still masked, and for outbound transfers. + * + * @param dataUnmasked the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dataUnmasked(@Nullable TravelRuleDataUnmasked dataUnmasked) { + return setNullable("data_unmasked", Conv.model(TravelRuleDataUnmasked::fromJson), dataUnmasked); + } + + /** + * True while the counterparty's personal data is masked in this response. + * + * @param dataMasked the value + * @return this builder + */ + public Builder dataMasked(boolean dataMasked) { + return set("data_masked", Conv.BOOLEAN, dataMasked); + } + + /** + * The metadata field. + * + * @param metadata the value + * @return this builder + */ + public Builder metadata(Map metadata) { + return setAny("metadata", metadata); + } + + /** + * Which side of the exchange this transfer is. + * + * @param direction the value + * @return this builder + */ + public Builder direction(TransactionTravelRule.Direction direction) { + return set("direction", Conv.enumeration(TransactionTravelRule.Direction::of), direction); + } + + /** + * The rail the transfer went over: INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA or EMAIL (TEST_VASP on a sandbox application, when the Didit Test VASP answered), or null until a rail is chosen. + * + * @param rail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder rail(@Nullable String rail) { + return setNullable("rail", Conv.STRING, rail); + } + + /** + * The counterparty's directory entry, or null when the transfer links none. + * + * @param counterpartyVasp the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder counterpartyVasp(TransactionTravelRule.@Nullable CounterpartyVasp counterpartyVasp) { + return setNullable("counterparty_vasp", Conv.model(TransactionTravelRule.CounterpartyVasp::fromJson), counterpartyVasp); + } + + /** + * The newest message the transfer owes another party. null when it has owed none, for example below threshold_amount or for a declared self-hosted wallet. + * + * @param delivery the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder delivery(@Nullable TravelRuleDelivery delivery) { + return setNullable("delivery", Conv.model(TravelRuleDelivery::fromJson), delivery); + } + + /** + * How Didit looked for the counterparty, one entry per step that ran. Empty when no discovery ran, for example on an inbound transfer. + * + * @param discovery the value + * @return this builder + */ + public Builder discovery(List discovery) { + return set("discovery", Conv.list(Conv.model(TravelRuleDiscoveryStep::fromJson)), discovery); + } + + /** + * Why a COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE transfer is stuck; "" for any other status. A failed delivery decides it first, on COUNTERPARTY_VASP_NOT_REACHABLE only: email_undeliverable (the mail provider refused the compliance email and no address is left to try: when it was sent, on a reminder, or as a permanent bounce reported later) or delivery_failed (the rail that was delivering failed for good: a network rail refused the message or ran out of retries, a TRP identity host did not answer, or the counterparty's TRP host did not accept the inquiry). Otherwise the discovery trace decides it: awaiting_end_user (no step named a counterparty Didit can reach), directory_match_no_route (a step named a directory VASP that has no route), picked_member_without_proven_wallet (the end user picked a Didit customer that has not proven control of the wallet), or no_directory_match when the trace gives no other reason. A resend moves the transfer on, so the reason clears. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck). + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * What moves the transfer on; [] for any status but COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE. email_undeliverable offers update_compliance_email, then resend. delivery_failed offers resend. A discovery reason offers select_counterparty_vasp, declare_self_hosted, add_vasp_hint and resend. + * + * @param nextActions the value + * @return this builder + */ + public Builder nextActions(List nextActions) { + return set("next_actions", Conv.list(Conv.model(TravelRuleNextAction::fromJson)), nextActions); + } + + /** + * How the transfer's chain names its two wallets, when that is more than one address paying another. Empty ({}) for an account-chain transfer (EVM, Solana, Tron), where the two addresses are the wallets. Matching, routing and the address book read the wallets from here first. + * + * @param chainDetails the value + * @return this builder + */ + public Builder chainDetails(TravelRuleChainDetails chainDetails) { + return set("chain_details", Conv.model(TravelRuleChainDetails::fromJson), chainDetails); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionTravelRule} + */ + public TransactionTravelRule build() { + return new TransactionTravelRule(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Which side of the exchange this transfer is. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Direction extends StringEnum { + /** The value {@code "INBOUND"}. */ + public static final Direction INBOUND = new Direction("INBOUND"); + + /** The value {@code "OUTBOUND"}. */ + public static final Direction OUTBOUND = new Direction("OUTBOUND"); + + private static final List KNOWN_VALUES = List.of(INBOUND, OUTBOUND); + + private Direction(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Direction of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Direction::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** The counterparty's directory entry, or null when the transfer links none. */ + public static final class CounterpartyVasp extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "lei", "dd_score", "dd_score_breakdown", "dd_assessed_at"); + + private CounterpartyVasp(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code CounterpartyVasp} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static CounterpartyVasp fromJson(JsonNode json) { + return new CounterpartyVasp(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * The lei field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lei() { + return Json.optional(json, "lei", Conv.STRING); + } + + /** + * Due-diligence score from 0 to 100, where higher is better. Same scale as in the VASP directory. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ddScore() { + return Json.optional(json, "dd_score", Conv.LONG); + } + + /** + * Per-component contributions to dd_score, one object per component as in the VASP directory (jurisdiction, verification, protocol, lei, history, essentials, responsiveness), each {points, max, reason, detail}. {} until the counterparty has been assessed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> ddScoreBreakdown() { + return Json.optional(json, "dd_score_breakdown", Conv.map(Conv.model(TransactionTravelRule.CounterpartyVasp.DdScoreBreakdownValue::fromJson))); + } + + /** + * When the due-diligence assessment last ran. Null until the counterparty has been assessed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ddAssessedAt() { + return Json.optional(json, "dd_assessed_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link CounterpartyVasp}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(CounterpartyVasp from) { + super(from); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * The lei field. + * + * @param lei the value + * @return this builder + */ + public Builder lei(String lei) { + return set("lei", Conv.STRING, lei); + } + + /** + * Due-diligence score from 0 to 100, where higher is better. Same scale as in the VASP directory. + * + * @param ddScore the value + * @return this builder + */ + public Builder ddScore(long ddScore) { + return set("dd_score", Conv.LONG, ddScore); + } + + /** + * Per-component contributions to dd_score, one object per component as in the VASP directory (jurisdiction, verification, protocol, lei, history, essentials, responsiveness), each {points, max, reason, detail}. {} until the counterparty has been assessed. + * + * @param ddScoreBreakdown the value + * @return this builder + */ + public Builder ddScoreBreakdown(Map ddScoreBreakdown) { + return set("dd_score_breakdown", Conv.map(Conv.model(TransactionTravelRule.CounterpartyVasp.DdScoreBreakdownValue::fromJson)), ddScoreBreakdown); + } + + /** + * When the due-diligence assessment last ran. Null until the counterparty has been assessed. + * + * @param ddAssessedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ddAssessedAt(@Nullable OffsetDateTime ddAssessedAt) { + return setNullable("dd_assessed_at", Conv.DATE_TIME, ddAssessedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code CounterpartyVasp} + */ + public CounterpartyVasp build() { + return new CounterpartyVasp(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class DdScoreBreakdownValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("points", "max", "reason", "detail"); + + private DdScoreBreakdownValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code DdScoreBreakdownValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DdScoreBreakdownValue fromJson(JsonNode json) { + return new DdScoreBreakdownValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Points awarded for this component. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional points() { + return Json.optional(json, "points", Conv.DOUBLE); + } + + /** + * Maximum points this component can award. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional max() { + return Json.optional(json, "max", Conv.LONG); + } + + /** + * Machine-readable code of the component's verdict. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * Human-readable reason for the awarded points. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional detail() { + return Json.optional(json, "detail", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link DdScoreBreakdownValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(DdScoreBreakdownValue from) { + super(from); + } + + /** + * Points awarded for this component. + * + * @param points the value + * @return this builder + */ + public Builder points(double points) { + return set("points", Conv.DOUBLE, points); + } + + /** + * Maximum points this component can award. + * + * @param max the value + * @return this builder + */ + public Builder max(long max) { + return set("max", Conv.LONG, max); + } + + /** + * Machine-readable code of the component's verdict. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * Human-readable reason for the awarded points. + * + * @param detail the value + * @return this builder + */ + public Builder detail(String detail) { + return set("detail", Conv.STRING, detail); + } + + /** + * Builds the value. + * + * @return a new immutable {@code DdScoreBreakdownValue} + */ + public DdScoreBreakdownValue build() { + return new DdScoreBreakdownValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } +} diff --git a/src/main/java/me/didit/types/TransactionVerificationSessionAction.java b/src/main/java/me/didit/types/TransactionVerificationSessionAction.java new file mode 100644 index 0000000..3cb7814 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionVerificationSessionAction.java @@ -0,0 +1,222 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Verification session */ +public final class TransactionVerificationSessionAction extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "url", "session_id", "session_token", "status"); + + private TransactionVerificationSessionAction(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionVerificationSessionAction} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionVerificationSessionAction fromJson(JsonNode json) { + return new TransactionVerificationSessionAction(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionVerificationSessionAction.Type type() { + return Json.required(json, "type", Conv.enumeration(TransactionVerificationSessionAction.Type::of)); + } + + /** + * Hosted verification URL to open for the end user. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String url() { + return Json.required(json, "url", Conv.STRING); + } + + /** + * Verification session id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * Session token, used by the mobile SDKs to launch the flow natively. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionToken() { + return Json.optional(json, "session_token", Conv.STRING); + } + + /** + * Verification session status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionVerificationSessionAction}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionVerificationSessionAction from) { + super(from); + } + + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(TransactionVerificationSessionAction.Type type) { + return set("type", Conv.enumeration(TransactionVerificationSessionAction.Type::of), type); + } + + /** + * Hosted verification URL to open for the end user. + * + * @param url the value + * @return this builder + */ + public Builder url(String url) { + return set("url", Conv.STRING, url); + } + + /** + * Verification session id. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Session token, used by the mobile SDKs to launch the flow natively. + * + * @param sessionToken the value + * @return this builder + */ + public Builder sessionToken(String sessionToken) { + return set("session_token", Conv.STRING, sessionToken); + } + + /** + * Verification session status. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionVerificationSessionAction} + * @throws IllegalStateException when a required field is missing + */ + public TransactionVerificationSessionAction build() { + require("type", false); + require("url", false); + return new TransactionVerificationSessionAction(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "verification_session"}. */ + public static final Type VERIFICATION_SESSION = new Type("verification_session"); + + private static final List KNOWN_VALUES = List.of(VERIFICATION_SESSION); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionWalletOwnershipAction.java b/src/main/java/me/didit/types/TransactionWalletOwnershipAction.java new file mode 100644 index 0000000..0fbf68f --- /dev/null +++ b/src/main/java/me/didit/types/TransactionWalletOwnershipAction.java @@ -0,0 +1,203 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** Wallet ownership */ +public final class TransactionWalletOwnershipAction extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("type", "url", "widget_session_id", "expires_at"); + + private TransactionWalletOwnershipAction(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionWalletOwnershipAction} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionWalletOwnershipAction fromJson(JsonNode json) { + return new TransactionWalletOwnershipAction(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The type field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TransactionWalletOwnershipAction.Type type() { + return Json.required(json, "type", Conv.enumeration(TransactionWalletOwnershipAction.Type::of)); + } + + /** + * Hosted wallet-ownership widget URL. Open it in a real browser surface - on mobile use SFSafariViewController or a Chrome Custom Tab, never an embedded webview. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String url() { + return Json.required(json, "url", Conv.STRING); + } + + /** + * Wallet-ownership widget session id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional widgetSessionId() { + return Json.optional(json, "widget_session_id", Conv.STRING); + } + + /** + * When the widget session expires. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expiresAt() { + return Json.optional(json, "expires_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionWalletOwnershipAction}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionWalletOwnershipAction from) { + super(from); + } + + /** + * The type field. + * + * @param type the value + * @return this builder + */ + public Builder type(TransactionWalletOwnershipAction.Type type) { + return set("type", Conv.enumeration(TransactionWalletOwnershipAction.Type::of), type); + } + + /** + * Hosted wallet-ownership widget URL. Open it in a real browser surface - on mobile use SFSafariViewController or a Chrome Custom Tab, never an embedded webview. + * + * @param url the value + * @return this builder + */ + public Builder url(String url) { + return set("url", Conv.STRING, url); + } + + /** + * Wallet-ownership widget session id. + * + * @param widgetSessionId the value + * @return this builder + */ + public Builder widgetSessionId(String widgetSessionId) { + return set("widget_session_id", Conv.STRING, widgetSessionId); + } + + /** + * When the widget session expires. + * + * @param expiresAt the value + * @return this builder + */ + public Builder expiresAt(OffsetDateTime expiresAt) { + return set("expires_at", Conv.DATE_TIME, expiresAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionWalletOwnershipAction} + * @throws IllegalStateException when a required field is missing + */ + public TransactionWalletOwnershipAction build() { + require("type", false); + require("url", false); + return new TransactionWalletOwnershipAction(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Type extends StringEnum { + /** The value {@code "wallet_ownership"}. */ + public static final Type WALLET_OWNERSHIP = new Type("wallet_ownership"); + + private static final List KNOWN_VALUES = List.of(WALLET_OWNERSHIP); + + private Type(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Type of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Type::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TransactionsCreateParams.java b/src/main/java/me/didit/types/TransactionsCreateParams.java index c827d46..a08b48d 100644 --- a/src/main/java/me/didit/types/TransactionsCreateParams.java +++ b/src/main/java/me/didit/types/TransactionsCreateParams.java @@ -134,7 +134,7 @@ public Optional> customProperties() { } /** - * Travel Rule compliance metadata. + * Travel Rule data. When Travel Rule is enabled and transaction_category is travel_rule, Didit runs the exchange itself: it ignores status, protocol, required and obligations_count, and uses originator_data, beneficiary_data and metadata. Otherwise Didit stores these values as a passive Travel Rule record. While Travel Rule is on, an outbound transfer at or above your threshold must carry what its regime requires (EU TFR or FATF Recommendation 16), checked on the sides you send: a missing field answers 400 under travel_rule_details.originator_data or travel_rule_details.beneficiary_data, for example "EU TFR requires the originator name." * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -257,7 +257,7 @@ public Builder customProperties(Map customProperties) { } /** - * Travel Rule compliance metadata. + * Travel Rule data. When Travel Rule is enabled and transaction_category is travel_rule, Didit runs the exchange itself: it ignores status, protocol, required and obligations_count, and uses originator_data, beneficiary_data and metadata. Otherwise Didit stores these values as a passive Travel Rule record. While Travel Rule is on, an outbound transfer at or above your threshold must carry what its regime requires (EU TFR or FATF Recommendation 16), checked on the sides you send: a missing field answers 400 under travel_rule_details.originator_data or travel_rule_details.beneficiary_data, for example "EU TFR requires the originator name." * * @param travelRuleDetails the value * @return this builder @@ -728,7 +728,7 @@ public Builder toBuilder() { } /** - * Actual network used for screening. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism) and ARB (Arbitrum One). Always specify this for multi-network tokens. Connected-provider coverage varies; LIGHTNING requires a supported connected provider. + * Actual network used for screening. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism) and ARB (Arbitrum One). Always specify this for multi-network tokens. When it is missing for USDT, USDC or ETH sent with a 0x transaction hash, Didit looks the hash up on Ethereum, BNB Chain, Polygon, Base, Arbitrum and Optimism and screens it on the network where it is found; if no network has it, the transaction is not screened and custom_values.aml_screening_status is FAILED with the reason in custom_values.aml_screening_error. When it is missing for USDT or USDC screened by wallet (no transaction hash), a Tron address is screened on Tron, a Solana address on Solana, and a 0x address on the first of those six networks where it has sent a transaction or holds the token, or on Ethereum when it is active on none of them. Connected-provider coverage varies; LIGHTNING requires a supported connected provider. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -751,7 +751,7 @@ private Builder(CryptoParams from) { } /** - * Actual network used for screening. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism) and ARB (Arbitrum One). Always specify this for multi-network tokens. Connected-provider coverage varies; LIGHTNING requires a supported connected provider. + * Actual network used for screening. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism) and ARB (Arbitrum One). Always specify this for multi-network tokens. When it is missing for USDT, USDC or ETH sent with a 0x transaction hash, Didit looks the hash up on Ethereum, BNB Chain, Polygon, Base, Arbitrum and Optimism and screens it on the network where it is found; if no network has it, the transaction is not screened and custom_values.aml_screening_status is FAILED with the reason in custom_values.aml_screening_error. When it is missing for USDT or USDC screened by wallet (no transaction hash), a Tron address is screened on Tron, a Solana address on Solana, and a 0x address on the first of those six networks where it has sent a transaction or holds the token, or on Ethereum when it is active on none of them. Connected-provider coverage varies; LIGHTNING requires a supported connected provider. * * @param cryptoChain the value * @return this builder @@ -1795,7 +1795,7 @@ protected Builder self() { /** Payment method used by the subject. */ public static final class PaymentMethod extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("method_type", "account_id", "issuing_country"); + private static final Set KNOWN_FIELDS = Set.of("method_type", "account_id", "memo", "issuing_country"); private PaymentMethod(ObjectNode json, Map> files) { super(json, files); @@ -1841,7 +1841,7 @@ public Optional methodType() { } /** - * Account identifier (e.g. IBAN, wallet address). For crypto screening, this should contain the wallet address for this participant. Inbound transaction-hash screening requires the service/customer deposit address on the subject. Outbound screening uses the destination wallet on the counterparty. + * Account identifier (e.g. IBAN, wallet address). For crypto screening, this should contain the wallet address for this participant. Inbound transaction-hash screening requires the service/customer deposit address on the subject. Outbound screening uses the destination wallet on the counterparty. On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account ("" when it names none) and what the request says on the Travel Rule transfer's chain_details.lightning. At most 255 characters once stored. On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account ("" when it names none) and what the request says on the Travel Rule transfer's chain_details.lightning. At most 255 characters once stored. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -1850,6 +1850,16 @@ public Optional accountId() { return Json.optional(json, "account_id", Conv.STRING); } + /** + * The destination tag or memo that is part of the wallet on a memo network (XRP, XLM, EOS, ATOM, HBAR). On one shared exchange address each memo is its own payment method. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. Alias: destinationTag or destination_tag. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional memo() { + return Json.optional(json, "memo", Conv.STRING); + } + /** * ISO 3166-1 alpha-3 country code of the issuer. * @@ -1884,7 +1894,7 @@ public Builder methodType(String methodType) { } /** - * Account identifier (e.g. IBAN, wallet address). For crypto screening, this should contain the wallet address for this participant. Inbound transaction-hash screening requires the service/customer deposit address on the subject. Outbound screening uses the destination wallet on the counterparty. + * Account identifier (e.g. IBAN, wallet address). For crypto screening, this should contain the wallet address for this participant. Inbound transaction-hash screening requires the service/customer deposit address on the subject. Outbound screening uses the destination wallet on the counterparty. On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account ("" when it names none) and what the request says on the Travel Rule transfer's chain_details.lightning. At most 255 characters once stored. On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account ("" when it names none) and what the request says on the Travel Rule transfer's chain_details.lightning. At most 255 characters once stored. * * @param accountId the value * @return this builder @@ -1893,6 +1903,16 @@ public Builder accountId(String accountId) { return set("account_id", Conv.STRING, accountId); } + /** + * The destination tag or memo that is part of the wallet on a memo network (XRP, XLM, EOS, ATOM, HBAR). On one shared exchange address each memo is its own payment method. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. Alias: destinationTag or destination_tag. + * + * @param memo the value + * @return this builder + */ + public Builder memo(String memo) { + return set("memo", Conv.STRING, memo); + } + /** * ISO 3166-1 alpha-3 country code of the issuer. * @@ -2645,7 +2665,7 @@ protected Builder self() { * Payment method used by the counterparty. On travelRule transactions, method_type: "unhosted_wallet" declares the counterparty wallet as self-hosted: the transfer skips counterparty-VASP routing, is created directly in UNCONFIRMED_OWNERSHIP, and (when auto_wallet_verification is enabled) a wallet-ownership widget session is auto-minted and returned in the transaction's action_required block. */ public static final class PaymentMethod extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("method_type", "account_id", "issuing_country"); + private static final Set KNOWN_FIELDS = Set.of("method_type", "account_id", "memo", "issuing_country"); private PaymentMethod(ObjectNode json, Map> files) { super(json, files); @@ -2691,7 +2711,7 @@ public Optional methodType() { } /** - * Account identifier (e.g. IBAN or wallet address). + * Account identifier (e.g. IBAN or wallet address). On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account ("" when it names none) and what the request says on the Travel Rule transfer's chain_details.lightning. At most 255 characters once stored. On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account ("" when it names none) and what the request says on the Travel Rule transfer's chain_details.lightning. At most 255 characters once stored. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -2700,6 +2720,16 @@ public Optional accountId() { return Json.optional(json, "account_id", Conv.STRING); } + /** + * The destination tag or memo that is part of the wallet on a memo network (XRP, XLM, EOS, ATOM, HBAR). On one shared exchange address each memo is its own payment method. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. Alias: destinationTag or destination_tag. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional memo() { + return Json.optional(json, "memo", Conv.STRING); + } + /** * The issuing_country field. * @@ -2734,7 +2764,7 @@ public Builder methodType(String methodType) { } /** - * Account identifier (e.g. IBAN or wallet address). + * Account identifier (e.g. IBAN or wallet address). On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account ("" when it names none) and what the request says on the Travel Rule transfer's chain_details.lightning. At most 255 characters once stored. On a crypto transfer on bitcoin or Lightning it may be a Lightning payment request (BOLT11 invoice, BOLT12 offer, LNURL) or a Lightning Address: Didit stores the payee it names as the account ("" when it names none) and what the request says on the Travel Rule transfer's chain_details.lightning. At most 255 characters once stored. * * @param accountId the value * @return this builder @@ -2743,6 +2773,16 @@ public Builder accountId(String accountId) { return set("account_id", Conv.STRING, accountId); } + /** + * The destination tag or memo that is part of the wallet on a memo network (XRP, XLM, EOS, ATOM, HBAR). On one shared exchange address each memo is its own payment method. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. Alias: destinationTag or destination_tag. + * + * @param memo the value + * @return this builder + */ + public Builder memo(String memo) { + return set("memo", Conv.STRING, memo); + } + /** * The issuing_country field. * @@ -2770,9 +2810,11 @@ protected Builder self() { } } - /** Travel Rule compliance metadata. */ + /** + * Travel Rule data. When Travel Rule is enabled and transaction_category is travel_rule, Didit runs the exchange itself: it ignores status, protocol, required and obligations_count, and uses originator_data, beneficiary_data and metadata. Otherwise Didit stores these values as a passive Travel Rule record. While Travel Rule is on, an outbound transfer at or above your threshold must carry what its regime requires (EU TFR or FATF Recommendation 16), checked on the sides you send: a missing field answers 400 under travel_rule_details.originator_data or travel_rule_details.beneficiary_data, for example "EU TFR requires the originator name." + */ public static final class TravelRuleDetails extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("status", "protocol", "required", "obligations_count", "originator_data", "beneficiary_data", "metadata"); + private static final Set KNOWN_FIELDS = Set.of("status", "protocol", "required", "obligations_count", "originator_data", "beneficiary_data", "metadata", "utxo", "originator", "beneficiary", "ivms101"); private TravelRuleDetails(ObjectNode json, Map> files) { super(json, files); @@ -2808,7 +2850,7 @@ public Builder toBuilder() { } /** - * The status field. + * Required whenever you send travel_rule_details. One of the Travel Rule transfer statuses (for example AWAITING_COUNTERPARTY, COMPLETED, NOT_APPLICABLE); lowercase and camelCase spellings and the legacy values UNKNOWN, COMPLIANT, PENDING_ACTION, PENDING_COUNTERPARTY, FAILED and EXEMPT are also accepted and mapped. Any other value returns 400. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -2848,7 +2890,7 @@ public Optional obligationsCount() { } /** - * The originator_data field. + * Originator party, as flat fields or one IVMS 101 person. For your user with an approved KYC verification, Didit replaces the fields the verification holds with the verified values (see metadata.originator_provenance). It must be an object: anything else is a 400. On a memo network a memo key names the originator wallet's memo or tag. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -2858,7 +2900,7 @@ public Optional> originatorData() { } /** - * The beneficiary_data field. + * Beneficiary party, as flat fields or one IVMS 101 person. An empty wallet_address is filled from counterparty.payment_method.account_id. It must be an object: anything else is a 400. On a memo network memo names the beneficiary wallet's memo or tag; wallet_address and memo win over the counterparty's payment method. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -2877,6 +2919,46 @@ public Optional> metadata() { return Json.optional(json, "metadata", Conv.OBJECT); } + /** + * Optional, UTXO chains only (BTC, LTC, BCH, DOGE). The transaction's inputs and outputs, from which Didit infers the originator wallet and the beneficiary output when you do not declare them. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional utxo() { + return Json.optional(json, "utxo", Conv.model(TravelRuleUtxo::fromJson)); + } + + /** + * An IVMS 101 originator block: originatorPersons (one naturalPerson or legalPerson; a legal person makes the party a company) and accountNumber (wallet addresses). Used for originator_data when that object is empty. A malformed block answers 400 keyed by its path, for example originator.originatorPersons. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> originator() { + return Json.optional(json, "originator", Conv.OBJECT); + } + + /** + * An IVMS 101 beneficiary block: beneficiaryPersons (one naturalPerson or legalPerson; a legal person makes the party a company) and accountNumber (wallet addresses). Used for beneficiary_data when that object is empty. A malformed block answers 400 keyed by its path, for example beneficiary.beneficiaryPersons. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> beneficiary() { + return Json.optional(json, "beneficiary", Conv.OBJECT); + } + + /** + * The originator and beneficiary blocks wrapped in one IVMS 101 object (IVMS101 is accepted too). Top-level blocks win over the wrapper's. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> ivms101() { + return Json.optional(json, "ivms101", Conv.OBJECT); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -2891,7 +2973,7 @@ private Builder(TravelRuleDetails from) { } /** - * The status field. + * Required whenever you send travel_rule_details. One of the Travel Rule transfer statuses (for example AWAITING_COUNTERPARTY, COMPLETED, NOT_APPLICABLE); lowercase and camelCase spellings and the legacy values UNKNOWN, COMPLIANT, PENDING_ACTION, PENDING_COUNTERPARTY, FAILED and EXEMPT are also accepted and mapped. Any other value returns 400. * * @param status the value * @return this builder @@ -2931,7 +3013,7 @@ public Builder obligationsCount(long obligationsCount) { } /** - * The originator_data field. + * Originator party, as flat fields or one IVMS 101 person. For your user with an approved KYC verification, Didit replaces the fields the verification holds with the verified values (see metadata.originator_provenance). It must be an object: anything else is a 400. On a memo network a memo key names the originator wallet's memo or tag. * * @param originatorData the value * @return this builder @@ -2941,7 +3023,7 @@ public Builder originatorData(Map originatorData) { } /** - * The beneficiary_data field. + * Beneficiary party, as flat fields or one IVMS 101 person. An empty wallet_address is filled from counterparty.payment_method.account_id. It must be an object: anything else is a 400. On a memo network memo names the beneficiary wallet's memo or tag; wallet_address and memo win over the counterparty's payment method. * * @param beneficiaryData the value * @return this builder @@ -2960,6 +3042,46 @@ public Builder metadata(Map metadata) { return setAny("metadata", metadata); } + /** + * Optional, UTXO chains only (BTC, LTC, BCH, DOGE). The transaction's inputs and outputs, from which Didit infers the originator wallet and the beneficiary output when you do not declare them. + * + * @param utxo the value + * @return this builder + */ + public Builder utxo(TravelRuleUtxo utxo) { + return set("utxo", Conv.model(TravelRuleUtxo::fromJson), utxo); + } + + /** + * An IVMS 101 originator block: originatorPersons (one naturalPerson or legalPerson; a legal person makes the party a company) and accountNumber (wallet addresses). Used for originator_data when that object is empty. A malformed block answers 400 keyed by its path, for example originator.originatorPersons. + * + * @param originator the value + * @return this builder + */ + public Builder originator(Map originator) { + return setAny("originator", originator); + } + + /** + * An IVMS 101 beneficiary block: beneficiaryPersons (one naturalPerson or legalPerson; a legal person makes the party a company) and accountNumber (wallet addresses). Used for beneficiary_data when that object is empty. A malformed block answers 400 keyed by its path, for example beneficiary.beneficiaryPersons. + * + * @param beneficiary the value + * @return this builder + */ + public Builder beneficiary(Map beneficiary) { + return setAny("beneficiary", beneficiary); + } + + /** + * The originator and beneficiary blocks wrapped in one IVMS 101 object (IVMS101 is accepted too). Top-level blocks win over the wrapper's. + * + * @param ivms101 the value + * @return this builder + */ + public Builder ivms101(Map ivms101) { + return setAny("ivms101", ivms101); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/TransactionsListParams.java b/src/main/java/me/didit/types/TransactionsListParams.java index 934f078..63cf683 100644 --- a/src/main/java/me/didit/types/TransactionsListParams.java +++ b/src/main/java/me/didit/types/TransactionsListParams.java @@ -14,7 +14,7 @@ /** Parameters for {@code transactions.list}. */ public final class TransactionsListParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("limit", "offset"); + private static final Set KNOWN_FIELDS = Set.of("limit", "offset", "travel_rule_status"); private TransactionsListParams(ObjectNode json, Map> files) { super(json, files); @@ -69,6 +69,16 @@ public Optional offset() { return Json.optional(json, "offset", Conv.LONG); } + /** + * Return only transactions whose Travel Rule transfer is in this exact status (for example ON_HOLD or AWAITING_COUNTERPARTY). count follows this filter; total_transactions still counts every transaction in the application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional travelRuleStatus() { + return Json.optional(json, "travel_rule_status", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -102,6 +112,16 @@ public Builder offset(long offset) { return set("offset", Conv.LONG, offset); } + /** + * Return only transactions whose Travel Rule transfer is in this exact status (for example ON_HOLD or AWAITING_COUNTERPARTY). count follows this filter; total_transactions still counts every transaction in the application. + * + * @param travelRuleStatus the value + * @return this builder + */ + public Builder travelRuleStatus(String travelRuleStatus) { + return set("travel_rule_status", Conv.STRING, travelRuleStatus); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/TransactionsListPartyNetworksResponse.java b/src/main/java/me/didit/types/TransactionsListPartyNetworksResponse.java deleted file mode 100644 index b7a5a7b..0000000 --- a/src/main/java/me/didit/types/TransactionsListPartyNetworksResponse.java +++ /dev/null @@ -1,98 +0,0 @@ -// Code generated by codegen from openapi/*.json. DO NOT EDIT. -package me.didit.types; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; - -public final class TransactionsListPartyNetworksResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("results"); - - private TransactionsListPartyNetworksResponse(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code TransactionsListPartyNetworksResponse} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static TransactionsListPartyNetworksResponse fromJson(JsonNode json) { - return new TransactionsListPartyNetworksResponse(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The results field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(EntityNetworkMembership::fromJson))); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link TransactionsListPartyNetworksResponse}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(TransactionsListPartyNetworksResponse from) { - super(from); - } - - /** - * The results field. - * - * @param results the value - * @return this builder - */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(EntityNetworkMembership::fromJson)), results); - } - - /** - * Builds the value. - * - * @return a new immutable {@code TransactionsListPartyNetworksResponse} - */ - public TransactionsListPartyNetworksResponse build() { - return new TransactionsListPartyNetworksResponse(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } -} diff --git a/src/main/java/me/didit/types/TransactionsListRelatedParams.java b/src/main/java/me/didit/types/TransactionsListRelatedParams.java new file mode 100644 index 0000000..0815dea --- /dev/null +++ b/src/main/java/me/didit/types/TransactionsListRelatedParams.java @@ -0,0 +1,119 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code transactions.list_related}. */ +public final class TransactionsListRelatedParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("limit", "offset"); + + private TransactionsListRelatedParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionsListRelatedParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionsListRelatedParams fromJson(JsonNode json) { + return new TransactionsListRelatedParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Number of transactions per page. Defaults to 50. A value that is not a positive integer falls back to 50; a value above 1000000 returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Zero-based position of the first transaction to return. Prefer following next. A value that is not a whole number falls back to 0; a value above 1000000 returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionsListRelatedParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionsListRelatedParams from) { + super(from); + } + + /** + * Number of transactions per page. Defaults to 50. A value that is not a positive integer falls back to 50; a value above 1000000 returns 400. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Zero-based position of the first transaction to return. Prefer following next. A value that is not a whole number falls back to 0; a value above 1000000 returns 400. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionsListRelatedParams} + */ + public TransactionsListRelatedParams build() { + return new TransactionsListRelatedParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TransactionsListRelatedResponse.java b/src/main/java/me/didit/types/TransactionsListRelatedResponse.java new file mode 100644 index 0000000..7c49436 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionsListRelatedResponse.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class TransactionsListRelatedResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private TransactionsListRelatedResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionsListRelatedResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionsListRelatedResponse fromJson(JsonNode json) { + return new TransactionsListRelatedResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Number of related transactions: exact up to 100, capped at 100 above that. Follow next instead of computing pages from it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(TransactionSummary::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionsListRelatedResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionsListRelatedResponse from) { + super(from); + } + + /** + * Number of related transactions: exact up to 100, capped at 100 above that. Follow next instead of computing pages from it. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(TransactionSummary::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionsListRelatedResponse} + * @throws IllegalStateException when a required field is missing + */ + public TransactionsListRelatedResponse build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new TransactionsListRelatedResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TransactionsListResponse.java b/src/main/java/me/didit/types/TransactionsListResponse.java index 716e746..f889b26 100644 --- a/src/main/java/me/didit/types/TransactionsListResponse.java +++ b/src/main/java/me/didit/types/TransactionsListResponse.java @@ -180,7 +180,7 @@ protected Builder self() { } public static final class Results extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "transaction_number", "txn_id", "transaction_type", "action_type", "status", "direction", "amount", "currency", "preferred_currency_amount", "preferred_currency_code", "payment_details", "score", "severity", "subject_name", "subject_vendor_data", "subject_country", "counterparty_name", "counterparty_vendor_data", "counterparty_country", "tags", "txn_date", "created_at", "decision_reason_code", "decision_reason_label", "assigned_to_name"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "transaction_number", "txn_id", "transaction_type", "action_type", "status", "direction", "amount", "currency", "preferred_currency_amount", "preferred_currency_code", "payment_details", "score", "severity", "subject_name", "subject_vendor_data", "subject_country", "counterparty_name", "counterparty_vendor_data", "counterparty_country", "tags", "txn_date", "created_at", "decision_reason_code", "decision_reason_label", "assigned_to_name", "travel_rule_status", "travel_rule"); private Results(ObjectNode json, Map> files) { super(json, files); @@ -475,6 +475,26 @@ public Optional assignedToName() { return Json.optional(json, "assigned_to_name", Conv.STRING); } + /** + * Status of the transaction's Travel Rule transfer (for example COMPLETED), or null when it has none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional travelRuleStatus() { + return Json.optional(json, "travel_rule_status", Conv.STRING); + } + + /** + * The transaction's Travel Rule transfer, the same block as travel_rule on GET /v3/transactions/{transaction_id}/, or null when the transaction has none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional travelRule() { + return Json.optional(json, "travel_rule", Conv.model(TransactionTravelRule::fromJson)); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -748,6 +768,26 @@ public Builder assignedToName(@Nullable String assignedToName) { return setNullable("assigned_to_name", Conv.STRING, assignedToName); } + /** + * Status of the transaction's Travel Rule transfer (for example COMPLETED), or null when it has none. + * + * @param travelRuleStatus the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder travelRuleStatus(@Nullable String travelRuleStatus) { + return setNullable("travel_rule_status", Conv.STRING, travelRuleStatus); + } + + /** + * The transaction's Travel Rule transfer, the same block as travel_rule on GET /v3/transactions/{transaction_id}/, or null when the transaction has none. + * + * @param travelRule the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder travelRule(@Nullable TransactionTravelRule travelRule) { + return setNullable("travel_rule", Conv.model(TransactionTravelRule::fromJson), travelRule); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/TransactionsNotesCreateParams.java b/src/main/java/me/didit/types/TransactionsNotesCreateParams.java new file mode 100644 index 0000000..2a26b2f --- /dev/null +++ b/src/main/java/me/didit/types/TransactionsNotesCreateParams.java @@ -0,0 +1,121 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code transactions.notes.create}. */ +public final class TransactionsNotesCreateParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("note", "mentioned_emails"); + + private TransactionsNotesCreateParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionsNotesCreateParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionsNotesCreateParams fromJson(JsonNode json) { + return new TransactionsNotesCreateParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Plain-text note. Leading and trailing whitespace is trimmed and a blank note is rejected. No length limit. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String note() { + return Json.required(json, "note", Conv.STRING); + } + + /** + * Emails of members of your organization to mention. Each must be a valid email of an organization member. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> mentionedEmails() { + return Json.optional(json, "mentioned_emails", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionsNotesCreateParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionsNotesCreateParams from) { + super(from); + } + + /** + * Plain-text note. Leading and trailing whitespace is trimmed and a blank note is rejected. No length limit. + * + * @param note the value + * @return this builder + */ + public Builder note(String note) { + return set("note", Conv.STRING, note); + } + + /** + * Emails of members of your organization to mention. Each must be a valid email of an organization member. + * + * @param mentionedEmails the value + * @return this builder + */ + public Builder mentionedEmails(List mentionedEmails) { + return set("mentioned_emails", Conv.list(Conv.STRING), mentionedEmails); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionsNotesCreateParams} + * @throws IllegalStateException when a required field is missing + */ + public TransactionsNotesCreateParams build() { + require("note", false); + return new TransactionsNotesCreateParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TransactionsNotesListParams.java b/src/main/java/me/didit/types/TransactionsNotesListParams.java new file mode 100644 index 0000000..4925f77 --- /dev/null +++ b/src/main/java/me/didit/types/TransactionsNotesListParams.java @@ -0,0 +1,119 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code transactions.notes.list}. */ +public final class TransactionsNotesListParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("limit", "offset"); + + private TransactionsNotesListParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionsNotesListParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionsNotesListParams fromJson(JsonNode json) { + return new TransactionsNotesListParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Number of notes per page. Defaults to 50. A value that is not a positive integer falls back to 50; a value above 1000000 returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Zero-based position of the first note to return. Prefer following next. A value that is not a whole number falls back to 0; a value above 1000000 returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionsNotesListParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionsNotesListParams from) { + super(from); + } + + /** + * Number of notes per page. Defaults to 50. A value that is not a positive integer falls back to 50; a value above 1000000 returns 400. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Zero-based position of the first note to return. Prefer following next. A value that is not a whole number falls back to 0; a value above 1000000 returns 400. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionsNotesListParams} + */ + public TransactionsNotesListParams build() { + return new TransactionsNotesListParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TransactionsNotesListResponse.java b/src/main/java/me/didit/types/TransactionsNotesListResponse.java new file mode 100644 index 0000000..c15ac9f --- /dev/null +++ b/src/main/java/me/didit/types/TransactionsNotesListResponse.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class TransactionsNotesListResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private TransactionsNotesListResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TransactionsNotesListResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TransactionsNotesListResponse fromJson(JsonNode json) { + return new TransactionsNotesListResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Number of notes on the transaction: exact up to 100, capped at 100 above that. Follow next instead of computing pages from it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(TransactionNote::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TransactionsNotesListResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TransactionsNotesListResponse from) { + super(from); + } + + /** + * Number of notes on the transaction: exact up to 100, capped at 100 above that. Follow next instead of computing pages from it. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(TransactionNote::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TransactionsNotesListResponse} + * @throws IllegalStateException when a required field is missing + */ + public TransactionsNotesListResponse build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new TransactionsNotesListResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleChainDetails.java b/src/main/java/me/didit/types/TravelRuleChainDetails.java new file mode 100644 index 0000000..9a37a1a --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleChainDetails.java @@ -0,0 +1,530 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * How the transfer's chain names its two wallets, when that is more than one address paying another. Empty ({}) for an account-chain transfer (EVM, Solana, Tron), where the two addresses are the wallets. Matching, routing and the address book read the wallets from here first. + */ +public final class TravelRuleChainDetails extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("chain", "shape", "originator", "beneficiary", "utxo", "lightning", "warnings", "not_applicable_reason"); + + private TravelRuleChainDetails(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleChainDetails} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleChainDetails fromJson(JsonNode json) { + return new TravelRuleChainDetails(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Canonical chain, e.g. BTC, XRP, or LIGHTNING for a Lightning transfer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional chain() { + return Json.optional(json, "chain", Conv.STRING); + } + + /** + * The shape field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional shape() { + return Json.optional(json, "shape", Conv.enumeration(TravelRuleChainDetails.Shape::of)); + } + + /** + * One side's wallet as Didit resolved it from the chain data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originator() { + return Json.optional(json, "originator", Conv.model(TravelRuleChainSide::fromJson)); + } + + /** + * One side's wallet as Didit resolved it from the chain data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional beneficiary() { + return Json.optional(json, "beneficiary", Conv.model(TravelRuleChainSide::fromJson)); + } + + /** + * On a UTXO transfer: the inputs and outputs as stored (amounts as decimal strings) and which outputs Didit read as what. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional utxo() { + return Json.optional(json, "utxo", Conv.model(TravelRuleChainDetails.Utxo::fromJson)); + } + + /** + * On a Lightning transfer: what the payment request says. Always kind (bolt11, bolt12_offer, bolt12_invoice, bolt12_invoice_request, lnurl, lightning_address, node_id or unknown); a BOLT11 invoice adds network, payee_node_id, payment_hash, amount_msat, created_at and expires_at; a BOLT12 string blinded_paths and payee_node_id; an LNURL or a Lightning Address its domain. Nothing is resolved over the network: the request is decoded only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> lightning() { + return Json.optional(json, "lightning", Conv.OBJECT); + } + + /** + * UTXO inconsistencies worth a look: the declared originator is not an input, the declared beneficiary is not an output, no single beneficiary output could be told apart, or the beneficiary output pays a different amount than the transaction states. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> warnings() { + return Json.optional(json, "warnings", Conv.list(Conv.enumeration(TravelRuleChainDetails.Warnings::of))); + } + + /** + * On an outbound Lightning transfer whose payment request names no payee: why the transfer is NOT_APPLICABLE. LIGHTNING_NO_PAYEE_IDENTIFIER (the request names no payee node, or could not be decoded), LIGHTNING_BOLT12_BLINDED_PAYEE (the offer hides its payee behind blinded paths), LIGHTNING_LNURL_NAMES_NO_PAYEE (an LNURL names a service, not a payee). The same code opens the transfer's last timeline entry. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional notApplicableReason() { + return Json.optional(json, "not_applicable_reason", Conv.enumeration(TravelRuleChainDetails.NotApplicableReason::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleChainDetails}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleChainDetails from) { + super(from); + } + + /** + * Canonical chain, e.g. BTC, XRP, or LIGHTNING for a Lightning transfer. + * + * @param chain the value + * @return this builder + */ + public Builder chain(String chain) { + return set("chain", Conv.STRING, chain); + } + + /** + * The shape field. + * + * @param shape the value + * @return this builder + */ + public Builder shape(TravelRuleChainDetails.Shape shape) { + return set("shape", Conv.enumeration(TravelRuleChainDetails.Shape::of), shape); + } + + /** + * One side's wallet as Didit resolved it from the chain data. + * + * @param originator the value + * @return this builder + */ + public Builder originator(TravelRuleChainSide originator) { + return set("originator", Conv.model(TravelRuleChainSide::fromJson), originator); + } + + /** + * One side's wallet as Didit resolved it from the chain data. + * + * @param beneficiary the value + * @return this builder + */ + public Builder beneficiary(TravelRuleChainSide beneficiary) { + return set("beneficiary", Conv.model(TravelRuleChainSide::fromJson), beneficiary); + } + + /** + * On a UTXO transfer: the inputs and outputs as stored (amounts as decimal strings) and which outputs Didit read as what. + * + * @param utxo the value + * @return this builder + */ + public Builder utxo(TravelRuleChainDetails.Utxo utxo) { + return set("utxo", Conv.model(TravelRuleChainDetails.Utxo::fromJson), utxo); + } + + /** + * On a Lightning transfer: what the payment request says. Always kind (bolt11, bolt12_offer, bolt12_invoice, bolt12_invoice_request, lnurl, lightning_address, node_id or unknown); a BOLT11 invoice adds network, payee_node_id, payment_hash, amount_msat, created_at and expires_at; a BOLT12 string blinded_paths and payee_node_id; an LNURL or a Lightning Address its domain. Nothing is resolved over the network: the request is decoded only. + * + * @param lightning the value + * @return this builder + */ + public Builder lightning(Map lightning) { + return setAny("lightning", lightning); + } + + /** + * UTXO inconsistencies worth a look: the declared originator is not an input, the declared beneficiary is not an output, no single beneficiary output could be told apart, or the beneficiary output pays a different amount than the transaction states. + * + * @param warnings the value + * @return this builder + */ + public Builder warnings(List warnings) { + return set("warnings", Conv.list(Conv.enumeration(TravelRuleChainDetails.Warnings::of)), warnings); + } + + /** + * On an outbound Lightning transfer whose payment request names no payee: why the transfer is NOT_APPLICABLE. LIGHTNING_NO_PAYEE_IDENTIFIER (the request names no payee node, or could not be decoded), LIGHTNING_BOLT12_BLINDED_PAYEE (the offer hides its payee behind blinded paths), LIGHTNING_LNURL_NAMES_NO_PAYEE (an LNURL names a service, not a payee). The same code opens the transfer's last timeline entry. + * + * @param notApplicableReason the value + * @return this builder + */ + public Builder notApplicableReason(TravelRuleChainDetails.NotApplicableReason notApplicableReason) { + return set("not_applicable_reason", Conv.enumeration(TravelRuleChainDetails.NotApplicableReason::of), notApplicableReason); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleChainDetails} + */ + public TravelRuleChainDetails build() { + return new TravelRuleChainDetails(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Shape extends StringEnum { + /** The value {@code "utxo"}. */ + public static final Shape UTXO = new Shape("utxo"); + + /** The value {@code "memo"}. */ + public static final Shape MEMO = new Shape("memo"); + + /** The value {@code "lightning"}. */ + public static final Shape LIGHTNING = new Shape("lightning"); + + private static final List KNOWN_VALUES = List.of(UTXO, MEMO, LIGHTNING); + + private Shape(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Shape of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Shape::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * On a UTXO transfer: the inputs and outputs as stored (amounts as decimal strings) and which outputs Didit read as what. + */ + public static final class Utxo extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("inputs", "outputs", "change_output_indexes", "beneficiary_output_index"); + + private Utxo(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Utxo} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Utxo fromJson(JsonNode json) { + return new Utxo(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The inputs field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> inputs() { + return Json.optional(json, "inputs", Conv.list(Conv.model(TravelRuleUtxoEntry::fromJson))); + } + + /** + * The outputs field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> outputs() { + return Json.optional(json, "outputs", Conv.list(Conv.model(TravelRuleUtxoEntry::fromJson))); + } + + /** + * Outputs that pay an input address back. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> changeOutputIndexes() { + return Json.optional(json, "change_output_indexes", Conv.list(Conv.LONG)); + } + + /** + * The output that pays the beneficiary; null when none could be told apart. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional beneficiaryOutputIndex() { + return Json.optional(json, "beneficiary_output_index", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Utxo}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Utxo from) { + super(from); + } + + /** + * The inputs field. + * + * @param inputs the value + * @return this builder + */ + public Builder inputs(List inputs) { + return set("inputs", Conv.list(Conv.model(TravelRuleUtxoEntry::fromJson)), inputs); + } + + /** + * The outputs field. + * + * @param outputs the value + * @return this builder + */ + public Builder outputs(List outputs) { + return set("outputs", Conv.list(Conv.model(TravelRuleUtxoEntry::fromJson)), outputs); + } + + /** + * Outputs that pay an input address back. + * + * @param changeOutputIndexes the value + * @return this builder + */ + public Builder changeOutputIndexes(List changeOutputIndexes) { + return set("change_output_indexes", Conv.list(Conv.LONG), changeOutputIndexes); + } + + /** + * The output that pays the beneficiary; null when none could be told apart. + * + * @param beneficiaryOutputIndex the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder beneficiaryOutputIndex(@Nullable Long beneficiaryOutputIndex) { + return setNullable("beneficiary_output_index", Conv.LONG, beneficiaryOutputIndex); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Utxo} + */ + public Utxo build() { + return new Utxo(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Warnings extends StringEnum { + /** The value {@code "declared_originator_not_an_input"}. */ + public static final Warnings DECLARED_ORIGINATOR_NOT_AN_INPUT = new Warnings("declared_originator_not_an_input"); + + /** The value {@code "declared_beneficiary_not_an_output"}. */ + public static final Warnings DECLARED_BENEFICIARY_NOT_AN_OUTPUT = new Warnings("declared_beneficiary_not_an_output"); + + /** The value {@code "beneficiary_output_ambiguous"}. */ + public static final Warnings BENEFICIARY_OUTPUT_AMBIGUOUS = new Warnings("beneficiary_output_ambiguous"); + + /** The value {@code "beneficiary_output_amount_differs"}. */ + public static final Warnings BENEFICIARY_OUTPUT_AMOUNT_DIFFERS = new Warnings("beneficiary_output_amount_differs"); + + private static final List KNOWN_VALUES = List.of(DECLARED_ORIGINATOR_NOT_AN_INPUT, DECLARED_BENEFICIARY_NOT_AN_OUTPUT, BENEFICIARY_OUTPUT_AMBIGUOUS, BENEFICIARY_OUTPUT_AMOUNT_DIFFERS); + + private Warnings(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Warnings of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Warnings::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * On an outbound Lightning transfer whose payment request names no payee: why the transfer is NOT_APPLICABLE. LIGHTNING_NO_PAYEE_IDENTIFIER (the request names no payee node, or could not be decoded), LIGHTNING_BOLT12_BLINDED_PAYEE (the offer hides its payee behind blinded paths), LIGHTNING_LNURL_NAMES_NO_PAYEE (an LNURL names a service, not a payee). The same code opens the transfer's last timeline entry. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NotApplicableReason extends StringEnum { + /** The value {@code "LIGHTNING_NO_PAYEE_IDENTIFIER"}. */ + public static final NotApplicableReason LIGHTNING_NO_PAYEE_IDENTIFIER = new NotApplicableReason("LIGHTNING_NO_PAYEE_IDENTIFIER"); + + /** The value {@code "LIGHTNING_BOLT12_BLINDED_PAYEE"}. */ + public static final NotApplicableReason LIGHTNING_BOLT_12_BLINDED_PAYEE = new NotApplicableReason("LIGHTNING_BOLT12_BLINDED_PAYEE"); + + /** The value {@code "LIGHTNING_LNURL_NAMES_NO_PAYEE"}. */ + public static final NotApplicableReason LIGHTNING_LNURL_NAMES_NO_PAYEE = new NotApplicableReason("LIGHTNING_LNURL_NAMES_NO_PAYEE"); + + private static final List KNOWN_VALUES = List.of(LIGHTNING_NO_PAYEE_IDENTIFIER, LIGHTNING_BOLT_12_BLINDED_PAYEE, LIGHTNING_LNURL_NAMES_NO_PAYEE); + + private NotApplicableReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NotApplicableReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NotApplicableReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleChainSide.java b/src/main/java/me/didit/types/TravelRuleChainSide.java new file mode 100644 index 0000000..031737b --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleChainSide.java @@ -0,0 +1,198 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** One side's wallet as Didit resolved it from the chain data. */ +public final class TravelRuleChainSide extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("address", "memo", "source"); + + private TravelRuleChainSide(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleChainSide} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleChainSide fromJson(JsonNode json) { + return new TravelRuleChainSide(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The wallet. On a memo network the classic address (an XRP X-address is split into its classic address and its tag); on Lightning the payee node id or Lightning Address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional address() { + return Json.optional(json, "address", Conv.STRING); + } + + /** + * The destination tag or memo that is part of the wallet; "" for none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional memo() { + return Json.optional(json, "memo", Conv.STRING); + } + + /** + * Where the wallet came from: declared (your request), x_address (split from an XRP X-address), largest_input (the UTXO input address that contributed the most), only_non_change_output (the only UTXO output that is not change), amount_match (the one UTXO output paying the transfer amount), invoice_payee (the payee a Lightning payment request names), updated (set later with action: update_originator_wallet). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(TravelRuleChainSide.Source::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleChainSide}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleChainSide from) { + super(from); + } + + /** + * The wallet. On a memo network the classic address (an XRP X-address is split into its classic address and its tag); on Lightning the payee node id or Lightning Address. + * + * @param address the value + * @return this builder + */ + public Builder address(String address) { + return set("address", Conv.STRING, address); + } + + /** + * The destination tag or memo that is part of the wallet; "" for none. + * + * @param memo the value + * @return this builder + */ + public Builder memo(String memo) { + return set("memo", Conv.STRING, memo); + } + + /** + * Where the wallet came from: declared (your request), x_address (split from an XRP X-address), largest_input (the UTXO input address that contributed the most), only_non_change_output (the only UTXO output that is not change), amount_match (the one UTXO output paying the transfer amount), invoice_payee (the payee a Lightning payment request names), updated (set later with action: update_originator_wallet). + * + * @param source the value + * @return this builder + */ + public Builder source(TravelRuleChainSide.Source source) { + return set("source", Conv.enumeration(TravelRuleChainSide.Source::of), source); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleChainSide} + */ + public TravelRuleChainSide build() { + return new TravelRuleChainSide(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Where the wallet came from: declared (your request), x_address (split from an XRP X-address), largest_input (the UTXO input address that contributed the most), only_non_change_output (the only UTXO output that is not change), amount_match (the one UTXO output paying the transfer amount), invoice_payee (the payee a Lightning payment request names), updated (set later with action: update_originator_wallet). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "declared"}. */ + public static final Source DECLARED = new Source("declared"); + + /** The value {@code "x_address"}. */ + public static final Source X_ADDRESS = new Source("x_address"); + + /** The value {@code "largest_input"}. */ + public static final Source LARGEST_INPUT = new Source("largest_input"); + + /** The value {@code "only_non_change_output"}. */ + public static final Source ONLY_NON_CHANGE_OUTPUT = new Source("only_non_change_output"); + + /** The value {@code "amount_match"}. */ + public static final Source AMOUNT_MATCH = new Source("amount_match"); + + /** The value {@code "invoice_payee"}. */ + public static final Source INVOICE_PAYEE = new Source("invoice_payee"); + + /** The value {@code "updated"}. */ + public static final Source UPDATED = new Source("updated"); + + private static final List KNOWN_VALUES = List.of(DECLARED, X_ADDRESS, LARGEST_INPUT, ONLY_NON_CHANGE_OUTPUT, AMOUNT_MATCH, INVOICE_PAYEE, UPDATED); + + private Source(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyContact.java b/src/main/java/me/didit/types/TravelRuleCounterpartyContact.java new file mode 100644 index 0000000..aecca8a --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyContact.java @@ -0,0 +1,502 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** A compliance address the EMAIL rail writes to for one directory entry. */ +public final class TravelRuleCounterpartyContact extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "counterparty", "counterparty_name", "email", "name", "source", "scope", "created_by", "verified_at", "is_invalid", "invalid_reason", "invalid_detail", "invalid_at", "created_at"); + + private TravelRuleCounterpartyContact(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyContact} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyContact fromJson(JsonNode json) { + return new TravelRuleCounterpartyContact(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * The directory entry's id, as GET /v3/travel-rule/vasps/ shows it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterparty() { + return Json.optional(json, "counterparty", Conv.STRING); + } + + /** + * The counterparty_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyName() { + return Json.optional(json, "counterparty_name", Conv.STRING); + } + + /** + * The email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + /** + * Optional label, for example the team name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * staff: curated by Didit. learned: an address at one of the VASP's own domains that answered a request through its response link. application: added by your application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(TravelRuleCounterpartyContact.Source::of)); + } + + /** + * platform: curated by Didit or learned from a verified answer, used for every Didit customer, read-only for you. application: private to your application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(TravelRuleCounterpartyContact.Scope::of)); + } + + /** + * Empty for platform contacts and for contacts an API key added. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdBy() { + return Json.optional(json, "created_by", Conv.STRING); + } + + /** + * When this address last answered a request through its response link. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verifiedAt() { + return Json.optional(json, "verified_at", Conv.DATE_TIME); + } + + /** + * true once the address bounced or complained; the EMAIL rail skips it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isInvalid() { + return Json.optional(json, "is_invalid", Conv.BOOLEAN); + } + + /** + * The invalid_reason field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional invalidReason() { + return Json.optional(json, "invalid_reason", Conv.enumeration(TravelRuleCounterpartyContact.InvalidReason::of)); + } + + /** + * The receiving mail server's reason, when it gave one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional invalidDetail() { + return Json.optional(json, "invalid_detail", Conv.STRING); + } + + /** + * The invalid_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional invalidAt() { + return Json.optional(json, "invalid_at", Conv.DATE_TIME); + } + + /** + * The created_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyContact}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyContact from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The directory entry's id, as GET /v3/travel-rule/vasps/ shows it. + * + * @param counterparty the value + * @return this builder + */ + public Builder counterparty(String counterparty) { + return set("counterparty", Conv.STRING, counterparty); + } + + /** + * The counterparty_name field. + * + * @param counterpartyName the value + * @return this builder + */ + public Builder counterpartyName(String counterpartyName) { + return set("counterparty_name", Conv.STRING, counterpartyName); + } + + /** + * The email field. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * Optional label, for example the team name. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * staff: curated by Didit. learned: an address at one of the VASP's own domains that answered a request through its response link. application: added by your application. + * + * @param source the value + * @return this builder + */ + public Builder source(TravelRuleCounterpartyContact.Source source) { + return set("source", Conv.enumeration(TravelRuleCounterpartyContact.Source::of), source); + } + + /** + * platform: curated by Didit or learned from a verified answer, used for every Didit customer, read-only for you. application: private to your application. + * + * @param scope the value + * @return this builder + */ + public Builder scope(TravelRuleCounterpartyContact.Scope scope) { + return set("scope", Conv.enumeration(TravelRuleCounterpartyContact.Scope::of), scope); + } + + /** + * Empty for platform contacts and for contacts an API key added. + * + * @param createdBy the value + * @return this builder + */ + public Builder createdBy(String createdBy) { + return set("created_by", Conv.STRING, createdBy); + } + + /** + * When this address last answered a request through its response link. + * + * @param verifiedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verifiedAt(@Nullable OffsetDateTime verifiedAt) { + return setNullable("verified_at", Conv.DATE_TIME, verifiedAt); + } + + /** + * true once the address bounced or complained; the EMAIL rail skips it. + * + * @param isInvalid the value + * @return this builder + */ + public Builder isInvalid(boolean isInvalid) { + return set("is_invalid", Conv.BOOLEAN, isInvalid); + } + + /** + * The invalid_reason field. + * + * @param invalidReason the value + * @return this builder + */ + public Builder invalidReason(TravelRuleCounterpartyContact.InvalidReason invalidReason) { + return set("invalid_reason", Conv.enumeration(TravelRuleCounterpartyContact.InvalidReason::of), invalidReason); + } + + /** + * The receiving mail server's reason, when it gave one. + * + * @param invalidDetail the value + * @return this builder + */ + public Builder invalidDetail(String invalidDetail) { + return set("invalid_detail", Conv.STRING, invalidDetail); + } + + /** + * The invalid_at field. + * + * @param invalidAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder invalidAt(@Nullable OffsetDateTime invalidAt) { + return setNullable("invalid_at", Conv.DATE_TIME, invalidAt); + } + + /** + * The created_at field. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyContact} + */ + public TravelRuleCounterpartyContact build() { + return new TravelRuleCounterpartyContact(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * staff: curated by Didit. learned: an address at one of the VASP's own domains that answered a request through its response link. application: added by your application. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "staff"}. */ + public static final Source STAFF = new Source("staff"); + + /** The value {@code "application"}. */ + public static final Source APPLICATION = new Source("application"); + + /** The value {@code "learned"}. */ + public static final Source LEARNED = new Source("learned"); + + private static final List KNOWN_VALUES = List.of(STAFF, APPLICATION, LEARNED); + + private Source(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * platform: curated by Didit or learned from a verified answer, used for every Didit customer, read-only for you. application: private to your application. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "platform"}. */ + public static final Scope PLATFORM = new Scope("platform"); + + /** The value {@code "application"}. */ + public static final Scope APPLICATION = new Scope("application"); + + private static final List KNOWN_VALUES = List.of(PLATFORM, APPLICATION); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class InvalidReason extends StringEnum { + /** The value {@code ""}. */ + public static final InvalidReason EMPTY = new InvalidReason(""); + + /** The value {@code "hard_bounce"}. */ + public static final InvalidReason HARD_BOUNCE = new InvalidReason("hard_bounce"); + + /** The value {@code "soft_bounce"}. */ + public static final InvalidReason SOFT_BOUNCE = new InvalidReason("soft_bounce"); + + /** The value {@code "spam_complaint"}. */ + public static final InvalidReason SPAM_COMPLAINT = new InvalidReason("spam_complaint"); + + /** The value {@code "suppressed"}. */ + public static final InvalidReason SUPPRESSED = new InvalidReason("suppressed"); + + private static final List KNOWN_VALUES = List.of(EMPTY, HARD_BOUNCE, SOFT_BOUNCE, SPAM_COMPLAINT, SUPPRESSED); + + private InvalidReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static InvalidReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, InvalidReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyContactCreate.java b/src/main/java/me/didit/types/TravelRuleCounterpartyContactCreate.java new file mode 100644 index 0000000..c6e5a2b --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyContactCreate.java @@ -0,0 +1,141 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleCounterpartyContactCreate extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("counterparty", "email", "name"); + + private TravelRuleCounterpartyContactCreate(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyContactCreate} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyContactCreate fromJson(JsonNode json) { + return new TravelRuleCounterpartyContactCreate(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The directory entry's id. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String counterparty() { + return Json.required(json, "counterparty", Conv.STRING); + } + + /** + * The email field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String email() { + return Json.required(json, "email", Conv.STRING); + } + + /** + * Optional label. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyContactCreate}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyContactCreate from) { + super(from); + } + + /** + * The directory entry's id. + * + * @param counterparty the value + * @return this builder + */ + public Builder counterparty(String counterparty) { + return set("counterparty", Conv.STRING, counterparty); + } + + /** + * The email field. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * Optional label. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyContactCreate} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleCounterpartyContactCreate build() { + require("counterparty", false); + require("email", false); + return new TravelRuleCounterpartyContactCreate(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyContactsListParams.java b/src/main/java/me/didit/types/TravelRuleCounterpartyContactsListParams.java new file mode 100644 index 0000000..b0202cc --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyContactsListParams.java @@ -0,0 +1,139 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code travel_rule.counterparty_contacts.list}. */ +public final class TravelRuleCounterpartyContactsListParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("counterparty", "limit", "offset"); + + private TravelRuleCounterpartyContactsListParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyContactsListParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyContactsListParams fromJson(JsonNode json) { + return new TravelRuleCounterpartyContactsListParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Only the contacts of this directory entry. Must be a UUID; an empty value means no filter. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterparty() { + return Json.optional(json, "counterparty", Conv.STRING); + } + + /** + * Page size, default 50. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Rows to skip. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyContactsListParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyContactsListParams from) { + super(from); + } + + /** + * Only the contacts of this directory entry. Must be a UUID; an empty value means no filter. + * + * @param counterparty the value + * @return this builder + */ + public Builder counterparty(String counterparty) { + return set("counterparty", Conv.STRING, counterparty); + } + + /** + * Page size, default 50. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Rows to skip. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyContactsListParams} + */ + public TravelRuleCounterpartyContactsListParams build() { + return new TravelRuleCounterpartyContactsListParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyContactsListResponse.java b/src/main/java/me/didit/types/TravelRuleCounterpartyContactsListResponse.java new file mode 100644 index 0000000..568565f --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyContactsListResponse.java @@ -0,0 +1,159 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class TravelRuleCounterpartyContactsListResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private TravelRuleCounterpartyContactsListResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyContactsListResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyContactsListResponse fromJson(JsonNode json) { + return new TravelRuleCounterpartyContactsListResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The count field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional count() { + return Json.optional(json, "count", Conv.LONG); + } + + /** + * The next field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * The previous field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> results() { + return Json.optional(json, "results", Conv.list(Conv.model(TravelRuleCounterpartyContact::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyContactsListResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyContactsListResponse from) { + super(from); + } + + /** + * The count field. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * The next field. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * The previous field. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(TravelRuleCounterpartyContact::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyContactsListResponse} + */ + public TravelRuleCounterpartyContactsListResponse build() { + return new TravelRuleCounterpartyContactsListResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyDecision.java b/src/main/java/me/didit/types/TravelRuleCounterpartyDecision.java new file mode 100644 index 0000000..5275fdd --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyDecision.java @@ -0,0 +1,338 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Your application's decision on a counterparty VASP. Private to your application: it never changes the shared directory entry and another customer never sees it. It acts only through your transaction rules (the counterparty_dd_status rule field and the travel-rule-counterparty-blocked and travel-rule-counterparty-restricted presets); it never changes how a transfer is routed. + */ +public final class TravelRuleCounterpartyDecision extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("decision", "reason", "decided_by", "updated_at", "inherited_from"); + + private TravelRuleCounterpartyDecision(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyDecision} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyDecision fromJson(JsonNode json) { + return new TravelRuleCounterpartyDecision(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * APPROVED: your team cleared the counterparty. RESTRICTED: transfers to it may go ahead after review. BLOCKED: your team refused it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decision() { + return Json.optional(json, "decision", Conv.enumeration(TravelRuleCounterpartyDecision.Decision::of)); + } + + /** + * Free-text reason you gave, empty when none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * The console user who made or last changed the decision. Empty when an API key made it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decidedBy() { + return Json.optional(json, "decided_by", Conv.STRING); + } + + /** + * The updated_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional updatedAt() { + return Json.optional(json, "updated_at", Conv.DATE_TIME); + } + + /** + * The brand whose decision this entity follows because you made none on the entity itself. Null when the decision is the counterparty's own. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional inheritedFrom() { + return Json.optional(json, "inherited_from", Conv.model(TravelRuleCounterpartyDecision.InheritedFrom::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyDecision}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyDecision from) { + super(from); + } + + /** + * APPROVED: your team cleared the counterparty. RESTRICTED: transfers to it may go ahead after review. BLOCKED: your team refused it. + * + * @param decision the value + * @return this builder + */ + public Builder decision(TravelRuleCounterpartyDecision.Decision decision) { + return set("decision", Conv.enumeration(TravelRuleCounterpartyDecision.Decision::of), decision); + } + + /** + * Free-text reason you gave, empty when none. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * The console user who made or last changed the decision. Empty when an API key made it. + * + * @param decidedBy the value + * @return this builder + */ + public Builder decidedBy(String decidedBy) { + return set("decided_by", Conv.STRING, decidedBy); + } + + /** + * The updated_at field. + * + * @param updatedAt the value + * @return this builder + */ + public Builder updatedAt(OffsetDateTime updatedAt) { + return set("updated_at", Conv.DATE_TIME, updatedAt); + } + + /** + * The brand whose decision this entity follows because you made none on the entity itself. Null when the decision is the counterparty's own. + * + * @param inheritedFrom the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder inheritedFrom(TravelRuleCounterpartyDecision.@Nullable InheritedFrom inheritedFrom) { + return setNullable("inherited_from", Conv.model(TravelRuleCounterpartyDecision.InheritedFrom::fromJson), inheritedFrom); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyDecision} + */ + public TravelRuleCounterpartyDecision build() { + return new TravelRuleCounterpartyDecision(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * APPROVED: your team cleared the counterparty. RESTRICTED: transfers to it may go ahead after review. BLOCKED: your team refused it. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Decision extends StringEnum { + /** The value {@code "APPROVED"}. */ + public static final Decision APPROVED = new Decision("APPROVED"); + + /** The value {@code "RESTRICTED"}. */ + public static final Decision RESTRICTED = new Decision("RESTRICTED"); + + /** The value {@code "BLOCKED"}. */ + public static final Decision BLOCKED = new Decision("BLOCKED"); + + private static final List KNOWN_VALUES = List.of(APPROVED, RESTRICTED, BLOCKED); + + private Decision(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Decision of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Decision::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The brand whose decision this entity follows because you made none on the entity itself. Null when the decision is the counterparty's own. + */ + public static final class InheritedFrom extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "name"); + + private InheritedFrom(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code InheritedFrom} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static InheritedFrom fromJson(JsonNode json) { + return new InheritedFrom(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link InheritedFrom}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(InheritedFrom from) { + super(from); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Builds the value. + * + * @return a new immutable {@code InheritedFrom} + */ + public InheritedFrom build() { + return new InheritedFrom(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyDecisionRequest.java b/src/main/java/me/didit/types/TravelRuleCounterpartyDecisionRequest.java new file mode 100644 index 0000000..296e661 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyDecisionRequest.java @@ -0,0 +1,166 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class TravelRuleCounterpartyDecisionRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("decision", "reason"); + + private TravelRuleCounterpartyDecisionRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyDecisionRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyDecisionRequest fromJson(JsonNode json) { + return new TravelRuleCounterpartyDecisionRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The decision field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TravelRuleCounterpartyDecisionRequest.Decision decision() { + return Json.required(json, "decision", Conv.enumeration(TravelRuleCounterpartyDecisionRequest.Decision::of)); + } + + /** + * Optional. Replaces the previous reason; omit it to clear it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyDecisionRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyDecisionRequest from) { + super(from); + } + + /** + * The decision field. + * + * @param decision the value + * @return this builder + */ + public Builder decision(TravelRuleCounterpartyDecisionRequest.Decision decision) { + return set("decision", Conv.enumeration(TravelRuleCounterpartyDecisionRequest.Decision::of), decision); + } + + /** + * Optional. Replaces the previous reason; omit it to clear it. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyDecisionRequest} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleCounterpartyDecisionRequest build() { + require("decision", false); + return new TravelRuleCounterpartyDecisionRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Decision extends StringEnum { + /** The value {@code "APPROVED"}. */ + public static final Decision APPROVED = new Decision("APPROVED"); + + /** The value {@code "RESTRICTED"}. */ + public static final Decision RESTRICTED = new Decision("RESTRICTED"); + + /** The value {@code "BLOCKED"}. */ + public static final Decision BLOCKED = new Decision("BLOCKED"); + + private static final List KNOWN_VALUES = List.of(APPROVED, RESTRICTED, BLOCKED); + + private Decision(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Decision of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Decision::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyDecisionResponse.java b/src/main/java/me/didit/types/TravelRuleCounterpartyDecisionResponse.java new file mode 100644 index 0000000..2bf945c --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyDecisionResponse.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class TravelRuleCounterpartyDecisionResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("decision"); + + private TravelRuleCounterpartyDecisionResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyDecisionResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyDecisionResponse fromJson(JsonNode json) { + return new TravelRuleCounterpartyDecisionResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The decision that applies to the counterparty for your application: its own, else (for an entity of a brand) the brand's. Null when there is none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decision() { + return Json.optional(json, "decision", Conv.model(TravelRuleCounterpartyDecision::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyDecisionResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyDecisionResponse from) { + super(from); + } + + /** + * The decision that applies to the counterparty for your application: its own, else (for an entity of a brand) the brand's. Null when there is none. + * + * @param decision the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder decision(@Nullable TravelRuleCounterpartyDecision decision) { + return setNullable("decision", Conv.model(TravelRuleCounterpartyDecision::fromJson), decision); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyDecisionResponse} + */ + public TravelRuleCounterpartyDecisionResponse build() { + return new TravelRuleCounterpartyDecisionResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyQuestionnaire.java b/src/main/java/me/didit/types/TravelRuleCounterpartyQuestionnaire.java new file mode 100644 index 0000000..951ad5c --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyQuestionnaire.java @@ -0,0 +1,330 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One due-diligence questionnaire your application sent the counterparty: Didit's standard questionnaire, emailed to a compliance address on record with a single-use answer link. Private to your application. + */ +public final class TravelRuleCounterpartyQuestionnaire extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "status", "form_version", "sent_to", "sent_at", "expires_at", "answered_at", "valid_until", "current", "answers"); + + private TravelRuleCounterpartyQuestionnaire(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyQuestionnaire} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyQuestionnaire fromJson(JsonNode json) { + return new TravelRuleCounterpartyQuestionnaire(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * SENT: waiting for answers. ANSWERED: the counterparty answered every question. EXPIRED: the answer link closed unanswered (its deadline passed, or a newer questionnaire replaced it), or answers passed valid_until. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(TravelRuleCounterpartyQuestionnaire.Status::of)); + } + + /** + * Version of Didit's standard questionnaire the answers belong to. Currently 1. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional formVersion() { + return Json.optional(json, "form_version", Conv.STRING); + } + + /** + * The sent_to field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sentTo() { + return Json.optional(json, "sent_to", Conv.STRING); + } + + /** + * The sent_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sentAt() { + return Json.optional(json, "sent_at", Conv.DATE_TIME); + } + + /** + * The answer-by deadline, 30 days after sending. The link stops working after it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expiresAt() { + return Json.optional(json, "expires_at", Conv.DATE_TIME); + } + + /** + * The answered_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional answeredAt() { + return Json.optional(json, "answered_at", Conv.DATE_TIME); + } + + /** + * How long the answers stay current: 365 days after answering. Null until answered. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional validUntil() { + return Json.optional(json, "valid_until", Conv.DATE_TIME); + } + + /** + * true while the questionnaire is ANSWERED and valid_until has not passed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional current() { + return Json.optional(json, "current", Conv.BOOLEAN); + } + + /** + * The answers keyed by question id: legal_name, licensing, jurisdictions, aml_program, compliance_officer, sanctions_screening, transaction_monitoring, travel_rule, custody, ownership, record_keeping, data_protection. Each answer is up to 2,000 characters. Empty until answered. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> answers() { + return Json.optional(json, "answers", Conv.map(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyQuestionnaire}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyQuestionnaire from) { + super(from); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * SENT: waiting for answers. ANSWERED: the counterparty answered every question. EXPIRED: the answer link closed unanswered (its deadline passed, or a newer questionnaire replaced it), or answers passed valid_until. + * + * @param status the value + * @return this builder + */ + public Builder status(TravelRuleCounterpartyQuestionnaire.Status status) { + return set("status", Conv.enumeration(TravelRuleCounterpartyQuestionnaire.Status::of), status); + } + + /** + * Version of Didit's standard questionnaire the answers belong to. Currently 1. + * + * @param formVersion the value + * @return this builder + */ + public Builder formVersion(String formVersion) { + return set("form_version", Conv.STRING, formVersion); + } + + /** + * The sent_to field. + * + * @param sentTo the value + * @return this builder + */ + public Builder sentTo(String sentTo) { + return set("sent_to", Conv.STRING, sentTo); + } + + /** + * The sent_at field. + * + * @param sentAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sentAt(@Nullable OffsetDateTime sentAt) { + return setNullable("sent_at", Conv.DATE_TIME, sentAt); + } + + /** + * The answer-by deadline, 30 days after sending. The link stops working after it. + * + * @param expiresAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expiresAt(@Nullable OffsetDateTime expiresAt) { + return setNullable("expires_at", Conv.DATE_TIME, expiresAt); + } + + /** + * The answered_at field. + * + * @param answeredAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder answeredAt(@Nullable OffsetDateTime answeredAt) { + return setNullable("answered_at", Conv.DATE_TIME, answeredAt); + } + + /** + * How long the answers stay current: 365 days after answering. Null until answered. + * + * @param validUntil the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder validUntil(@Nullable OffsetDateTime validUntil) { + return setNullable("valid_until", Conv.DATE_TIME, validUntil); + } + + /** + * true while the questionnaire is ANSWERED and valid_until has not passed. + * + * @param current the value + * @return this builder + */ + public Builder current(boolean current) { + return set("current", Conv.BOOLEAN, current); + } + + /** + * The answers keyed by question id: legal_name, licensing, jurisdictions, aml_program, compliance_officer, sanctions_screening, transaction_monitoring, travel_rule, custody, ownership, record_keeping, data_protection. Each answer is up to 2,000 characters. Empty until answered. + * + * @param answers the value + * @return this builder + */ + public Builder answers(Map answers) { + return set("answers", Conv.map(Conv.STRING), answers); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyQuestionnaire} + */ + public TravelRuleCounterpartyQuestionnaire build() { + return new TravelRuleCounterpartyQuestionnaire(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * SENT: waiting for answers. ANSWERED: the counterparty answered every question. EXPIRED: the answer link closed unanswered (its deadline passed, or a newer questionnaire replaced it), or answers passed valid_until. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "SENT"}. */ + public static final Status SENT = new Status("SENT"); + + /** The value {@code "ANSWERED"}. */ + public static final Status ANSWERED = new Status("ANSWERED"); + + /** The value {@code "EXPIRED"}. */ + public static final Status EXPIRED = new Status("EXPIRED"); + + private static final List KNOWN_VALUES = List.of(SENT, ANSWERED, EXPIRED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyQuestionnaireList.java b/src/main/java/me/didit/types/TravelRuleCounterpartyQuestionnaireList.java new file mode 100644 index 0000000..8ee6624 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyQuestionnaireList.java @@ -0,0 +1,98 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleCounterpartyQuestionnaireList extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("questionnaires"); + + private TravelRuleCounterpartyQuestionnaireList(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyQuestionnaireList} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyQuestionnaireList fromJson(JsonNode json) { + return new TravelRuleCounterpartyQuestionnaireList(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Newest first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> questionnaires() { + return Json.optional(json, "questionnaires", Conv.list(Conv.model(TravelRuleCounterpartyQuestionnaire::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyQuestionnaireList}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyQuestionnaireList from) { + super(from); + } + + /** + * Newest first. + * + * @param questionnaires the value + * @return this builder + */ + public Builder questionnaires(List questionnaires) { + return set("questionnaires", Conv.list(Conv.model(TravelRuleCounterpartyQuestionnaire::fromJson)), questionnaires); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyQuestionnaireList} + */ + public TravelRuleCounterpartyQuestionnaireList build() { + return new TravelRuleCounterpartyQuestionnaireList(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyQuestionnaireRequest.java b/src/main/java/me/didit/types/TravelRuleCounterpartyQuestionnaireRequest.java new file mode 100644 index 0000000..3f1926b --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyQuestionnaireRequest.java @@ -0,0 +1,98 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleCounterpartyQuestionnaireRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("sent_to"); + + private TravelRuleCounterpartyQuestionnaireRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyQuestionnaireRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyQuestionnaireRequest fromJson(JsonNode json) { + return new TravelRuleCounterpartyQuestionnaireRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Optional. A compliance address on record for the counterparty: for a directory entry its compliance address or a contact your application sees, for a Didit network member its confirmed compliance email. Default: the address a Travel Rule email to the counterparty goes to (your own contact first, then a platform contact, then the entry's compliance address). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sentTo() { + return Json.optional(json, "sent_to", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyQuestionnaireRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyQuestionnaireRequest from) { + super(from); + } + + /** + * Optional. A compliance address on record for the counterparty: for a directory entry its compliance address or a contact your application sees, for a Didit network member its confirmed compliance email. Default: the address a Travel Rule email to the counterparty goes to (your own contact first, then a platform contact, then the entry's compliance address). + * + * @param sentTo the value + * @return this builder + */ + public Builder sentTo(String sentTo) { + return set("sent_to", Conv.STRING, sentTo); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyQuestionnaireRequest} + */ + public TravelRuleCounterpartyQuestionnaireRequest build() { + return new TravelRuleCounterpartyQuestionnaireRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyScreening.java b/src/main/java/me/didit/types/TravelRuleCounterpartyScreening.java new file mode 100644 index 0000000..2523918 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyScreening.java @@ -0,0 +1,270 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One sanctions and adverse-media screening of the counterparty entity, run for and billed to your application. Private to your application: another customer never sees it. + */ +public final class TravelRuleCounterpartyScreening extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "status", "total_hits", "score", "screened_at", "fresh", "flagged"); + + private TravelRuleCounterpartyScreening(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyScreening} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyScreening fromJson(JsonNode json) { + return new TravelRuleCounterpartyScreening(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * Approved: no possible match needs attention. In Review or Declined: a possible sanctions or adverse-media match. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(TravelRuleCounterpartyScreening.Status::of)); + } + + /** + * How many possible matches the screening returned. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalHits() { + return Json.optional(json, "total_hits", Conv.LONG); + } + + /** + * The highest risk score among the possible matches. Null when nothing matched. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.DOUBLE); + } + + /** + * The screened_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional screenedAt() { + return Json.optional(json, "screened_at", Conv.DATE_TIME); + } + + /** + * Whether the screening is younger than fresh_days (30 days). A fresh screening is reused instead of billed again. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fresh() { + return Json.optional(json, "fresh", Conv.BOOLEAN); + } + + /** + * true while the screening is fresh and its status is In Review or Declined. While your latest screening is flagged and counterparty screening stays on, counterparty_risk_level is HIGH in your rules. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional flagged() { + return Json.optional(json, "flagged", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyScreening}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyScreening from) { + super(from); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * Approved: no possible match needs attention. In Review or Declined: a possible sanctions or adverse-media match. + * + * @param status the value + * @return this builder + */ + public Builder status(TravelRuleCounterpartyScreening.Status status) { + return set("status", Conv.enumeration(TravelRuleCounterpartyScreening.Status::of), status); + } + + /** + * How many possible matches the screening returned. + * + * @param totalHits the value + * @return this builder + */ + public Builder totalHits(long totalHits) { + return set("total_hits", Conv.LONG, totalHits); + } + + /** + * The highest risk score among the possible matches. Null when nothing matched. + * + * @param score the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder score(@Nullable Double score) { + return setNullable("score", Conv.DOUBLE, score); + } + + /** + * The screened_at field. + * + * @param screenedAt the value + * @return this builder + */ + public Builder screenedAt(OffsetDateTime screenedAt) { + return set("screened_at", Conv.DATE_TIME, screenedAt); + } + + /** + * Whether the screening is younger than fresh_days (30 days). A fresh screening is reused instead of billed again. + * + * @param fresh the value + * @return this builder + */ + public Builder fresh(boolean fresh) { + return set("fresh", Conv.BOOLEAN, fresh); + } + + /** + * true while the screening is fresh and its status is In Review or Declined. While your latest screening is flagged and counterparty screening stays on, counterparty_risk_level is HIGH in your rules. + * + * @param flagged the value + * @return this builder + */ + public Builder flagged(boolean flagged) { + return set("flagged", Conv.BOOLEAN, flagged); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyScreening} + */ + public TravelRuleCounterpartyScreening build() { + return new TravelRuleCounterpartyScreening(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Approved: no possible match needs attention. In Review or Declined: a possible sanctions or adverse-media match. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + private static final List KNOWN_VALUES = List.of(APPROVED, IN_REVIEW, DECLINED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyScreeningRequest.java b/src/main/java/me/didit/types/TravelRuleCounterpartyScreeningRequest.java new file mode 100644 index 0000000..ae6027a --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyScreeningRequest.java @@ -0,0 +1,98 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleCounterpartyScreeningRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("refresh"); + + private TravelRuleCounterpartyScreeningRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyScreeningRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyScreeningRequest fromJson(JsonNode json) { + return new TravelRuleCounterpartyScreeningRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * true screens again (and bills again) even when a fresh screening exists. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional refresh() { + return Json.optional(json, "refresh", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyScreeningRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyScreeningRequest from) { + super(from); + } + + /** + * true screens again (and bills again) even when a fresh screening exists. + * + * @param refresh the value + * @return this builder + */ + public Builder refresh(boolean refresh) { + return set("refresh", Conv.BOOLEAN, refresh); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyScreeningRequest} + */ + public TravelRuleCounterpartyScreeningRequest build() { + return new TravelRuleCounterpartyScreeningRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyScreeningResult.java b/src/main/java/me/didit/types/TravelRuleCounterpartyScreeningResult.java new file mode 100644 index 0000000..61a6bdc --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyScreeningResult.java @@ -0,0 +1,162 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class TravelRuleCounterpartyScreeningResult extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("outcome", "screening"); + + private TravelRuleCounterpartyScreeningResult(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyScreeningResult} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyScreeningResult fromJson(JsonNode json) { + return new TravelRuleCounterpartyScreeningResult(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * screened: a new screening ran and was billed (201). reused: a fresh screening was returned and nothing was billed (200). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional outcome() { + return Json.optional(json, "outcome", Conv.enumeration(TravelRuleCounterpartyScreeningResult.Outcome::of)); + } + + /** + * One sanctions and adverse-media screening of the counterparty entity, run for and billed to your application. Private to your application: another customer never sees it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional screening() { + return Json.optional(json, "screening", Conv.model(TravelRuleCounterpartyScreening::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyScreeningResult}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyScreeningResult from) { + super(from); + } + + /** + * screened: a new screening ran and was billed (201). reused: a fresh screening was returned and nothing was billed (200). + * + * @param outcome the value + * @return this builder + */ + public Builder outcome(TravelRuleCounterpartyScreeningResult.Outcome outcome) { + return set("outcome", Conv.enumeration(TravelRuleCounterpartyScreeningResult.Outcome::of), outcome); + } + + /** + * One sanctions and adverse-media screening of the counterparty entity, run for and billed to your application. Private to your application: another customer never sees it. + * + * @param screening the value + * @return this builder + */ + public Builder screening(TravelRuleCounterpartyScreening screening) { + return set("screening", Conv.model(TravelRuleCounterpartyScreening::fromJson), screening); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyScreeningResult} + */ + public TravelRuleCounterpartyScreeningResult build() { + return new TravelRuleCounterpartyScreeningResult(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * screened: a new screening ran and was billed (201). reused: a fresh screening was returned and nothing was billed (200). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Outcome extends StringEnum { + /** The value {@code "screened"}. */ + public static final Outcome SCREENED = new Outcome("screened"); + + /** The value {@code "reused"}. */ + public static final Outcome REUSED = new Outcome("reused"); + + private static final List KNOWN_VALUES = List.of(SCREENED, REUSED); + + private Outcome(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Outcome of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleCounterpartyScreeningStatus.java b/src/main/java/me/didit/types/TravelRuleCounterpartyScreeningStatus.java new file mode 100644 index 0000000..4ddf712 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleCounterpartyScreeningStatus.java @@ -0,0 +1,159 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class TravelRuleCounterpartyScreeningStatus extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("enabled", "price", "fresh_days", "screening"); + + private TravelRuleCounterpartyScreeningStatus(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleCounterpartyScreeningStatus} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleCounterpartyScreeningStatus fromJson(JsonNode json) { + return new TravelRuleCounterpartyScreeningStatus(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Your counterparty_screening_enabled Travel Rule setting. Off by default; nothing is screened or billed while it is off. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional enabled() { + return Json.optional(json, "enabled", Conv.BOOLEAN); + } + + /** + * Decimal string, in USD: what one screening costs your organization, the Company AML Screening price. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional price() { + return Json.optional(json, "price", Conv.STRING); + } + + /** + * How many days a screening stays fresh and is reused. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional freshDays() { + return Json.optional(json, "fresh_days", Conv.LONG); + } + + /** + * Your latest screening of the counterparty. Null when you never screened it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional screening() { + return Json.optional(json, "screening", Conv.model(TravelRuleCounterpartyScreening::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleCounterpartyScreeningStatus}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleCounterpartyScreeningStatus from) { + super(from); + } + + /** + * Your counterparty_screening_enabled Travel Rule setting. Off by default; nothing is screened or billed while it is off. + * + * @param enabled the value + * @return this builder + */ + public Builder enabled(boolean enabled) { + return set("enabled", Conv.BOOLEAN, enabled); + } + + /** + * Decimal string, in USD: what one screening costs your organization, the Company AML Screening price. + * + * @param price the value + * @return this builder + */ + public Builder price(String price) { + return set("price", Conv.STRING, price); + } + + /** + * How many days a screening stays fresh and is reused. + * + * @param freshDays the value + * @return this builder + */ + public Builder freshDays(long freshDays) { + return set("fresh_days", Conv.LONG, freshDays); + } + + /** + * Your latest screening of the counterparty. Null when you never screened it. + * + * @param screening the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder screening(@Nullable TravelRuleCounterpartyScreening screening) { + return setNullable("screening", Conv.model(TravelRuleCounterpartyScreening::fromJson), screening); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleCounterpartyScreeningStatus} + */ + public TravelRuleCounterpartyScreeningStatus build() { + return new TravelRuleCounterpartyScreeningStatus(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleDataMatch.java b/src/main/java/me/didit/types/TravelRuleDataMatch.java new file mode 100644 index 0000000..998223e --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleDataMatch.java @@ -0,0 +1,392 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * Field-by-field comparison of the transfer's party data with what the other side knows about the wallet holder. It names fields and results, never values, so it stays readable while the counterparty's data is masked. Null until a comparison ran. + */ +public final class TravelRuleDataMatch extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("matched", "mismatched_fields", "fields"); + + private TravelRuleDataMatch(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleDataMatch} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleDataMatch fromJson(JsonNode json) { + return new TravelRuleDataMatch(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * True when no compared field differs. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matched() { + return Json.optional(json, "matched", Conv.BOOLEAN); + } + + /** + * The fields that differ. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> mismatchedFields() { + return Json.optional(json, "mismatched_fields", Conv.list(Conv.enumeration(TravelRuleDataMatch.MismatchedFields::of))); + } + + /** + * One entry per field compared for the party's entity type (name, date_of_birth, address, account; a company has no date_of_birth). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> fields() { + return Json.optional(json, "fields", Conv.map(Conv.model(TravelRuleDataMatch.FieldsValue::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleDataMatch}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleDataMatch from) { + super(from); + } + + /** + * True when no compared field differs. + * + * @param matched the value + * @return this builder + */ + public Builder matched(boolean matched) { + return set("matched", Conv.BOOLEAN, matched); + } + + /** + * The fields that differ. + * + * @param mismatchedFields the value + * @return this builder + */ + public Builder mismatchedFields(List mismatchedFields) { + return set("mismatched_fields", Conv.list(Conv.enumeration(TravelRuleDataMatch.MismatchedFields::of)), mismatchedFields); + } + + /** + * One entry per field compared for the party's entity type (name, date_of_birth, address, account; a company has no date_of_birth). + * + * @param fields the value + * @return this builder + */ + public Builder fields(Map fields) { + return set("fields", Conv.map(Conv.model(TravelRuleDataMatch.FieldsValue::fromJson)), fields); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleDataMatch} + */ + public TravelRuleDataMatch build() { + return new TravelRuleDataMatch(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class MismatchedFields extends StringEnum { + /** The value {@code "name"}. */ + public static final MismatchedFields NAME = new MismatchedFields("name"); + + /** The value {@code "date_of_birth"}. */ + public static final MismatchedFields DATE_OF_BIRTH = new MismatchedFields("date_of_birth"); + + /** The value {@code "address"}. */ + public static final MismatchedFields ADDRESS = new MismatchedFields("address"); + + /** The value {@code "account"}. */ + public static final MismatchedFields ACCOUNT = new MismatchedFields("account"); + + private static final List KNOWN_VALUES = List.of(NAME, DATE_OF_BIRTH, ADDRESS, ACCOUNT); + + private MismatchedFields(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static MismatchedFields of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, MismatchedFields::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class FieldsValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("result", "strictness"); + + private FieldsValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code FieldsValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static FieldsValue fromJson(JsonNode json) { + return new FieldsValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * not_compared when the rule is NONE or either side has no value for the field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional result() { + return Json.optional(json, "result", Conv.enumeration(TravelRuleDataMatch.FieldsValue.Result::of)); + } + + /** + * The strictness field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional strictness() { + return Json.optional(json, "strictness", Conv.enumeration(TravelRuleDataMatch.FieldsValue.Strictness::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link FieldsValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(FieldsValue from) { + super(from); + } + + /** + * not_compared when the rule is NONE or either side has no value for the field. + * + * @param result the value + * @return this builder + */ + public Builder result(TravelRuleDataMatch.FieldsValue.Result result) { + return set("result", Conv.enumeration(TravelRuleDataMatch.FieldsValue.Result::of), result); + } + + /** + * The strictness field. + * + * @param strictness the value + * @return this builder + */ + public Builder strictness(TravelRuleDataMatch.FieldsValue.Strictness strictness) { + return set("strictness", Conv.enumeration(TravelRuleDataMatch.FieldsValue.Strictness::of), strictness); + } + + /** + * Builds the value. + * + * @return a new immutable {@code FieldsValue} + */ + public FieldsValue build() { + return new FieldsValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * not_compared when the rule is NONE or either side has no value for the field. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Result extends StringEnum { + /** The value {@code "matched"}. */ + public static final Result MATCHED = new Result("matched"); + + /** The value {@code "mismatched"}. */ + public static final Result MISMATCHED = new Result("mismatched"); + + /** The value {@code "not_compared"}. */ + public static final Result NOT_COMPARED = new Result("not_compared"); + + private static final List KNOWN_VALUES = List.of(MATCHED, MISMATCHED, NOT_COMPARED); + + private Result(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Result of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Result::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Strictness extends StringEnum { + /** The value {@code "NONE"}. */ + public static final Strictness NONE = new Strictness("NONE"); + + /** The value {@code "STRICT"}. */ + public static final Strictness STRICT = new Strictness("STRICT"); + + /** The value {@code "CONTAINS"}. */ + public static final Strictness CONTAINS = new Strictness("CONTAINS"); + + /** The value {@code "DEFAULT"}. */ + public static final Strictness DEFAULT = new Strictness("DEFAULT"); + + /** The value {@code "FUZZY"}. */ + public static final Strictness FUZZY = new Strictness("FUZZY"); + + private static final List KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); + + private Strictness(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Strictness of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Strictness::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleDataSharingFields.java b/src/main/java/me/didit/types/TravelRuleDataSharingFields.java new file mode 100644 index 0000000..16c15e9 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleDataSharingFields.java @@ -0,0 +1,230 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * The fields Didit sends to the counterparty VASP about your own users, per entity type. The name and the wallet_address are always sent, whatever the list says. An entity type you never chose shares every field. An empty list is a choice too: it shares the name and the wallet only. + */ +public final class TravelRuleDataSharingFields extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("individual", "company"); + + private TravelRuleDataSharingFields(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleDataSharingFields} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleDataSharingFields fromJson(JsonNode json) { + return new TravelRuleDataSharingFields(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Fields shared about a natural person. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> individual() { + return Json.optional(json, "individual", Conv.list(Conv.enumeration(TravelRuleDataSharingFields.Individual::of))); + } + + /** + * Fields shared about a legal person. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> company() { + return Json.optional(json, "company", Conv.list(Conv.enumeration(TravelRuleDataSharingFields.Company::of))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleDataSharingFields}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleDataSharingFields from) { + super(from); + } + + /** + * Fields shared about a natural person. + * + * @param individual the value + * @return this builder + */ + public Builder individual(List individual) { + return set("individual", Conv.list(Conv.enumeration(TravelRuleDataSharingFields.Individual::of)), individual); + } + + /** + * Fields shared about a legal person. + * + * @param company the value + * @return this builder + */ + public Builder company(List company) { + return set("company", Conv.list(Conv.enumeration(TravelRuleDataSharingFields.Company::of)), company); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleDataSharingFields} + */ + public TravelRuleDataSharingFields build() { + return new TravelRuleDataSharingFields(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Individual extends StringEnum { + /** The value {@code "name"}. */ + public static final Individual NAME = new Individual("name"); + + /** The value {@code "wallet_address"}. */ + public static final Individual WALLET_ADDRESS = new Individual("wallet_address"); + + /** The value {@code "address"}. */ + public static final Individual ADDRESS = new Individual("address"); + + /** The value {@code "national_id"}. */ + public static final Individual NATIONAL_ID = new Individual("national_id"); + + /** The value {@code "customer_identification"}. */ + public static final Individual CUSTOMER_IDENTIFICATION = new Individual("customer_identification"); + + /** The value {@code "date_of_birth"}. */ + public static final Individual DATE_OF_BIRTH = new Individual("date_of_birth"); + + /** The value {@code "place_of_birth"}. */ + public static final Individual PLACE_OF_BIRTH = new Individual("place_of_birth"); + + private static final List KNOWN_VALUES = List.of(NAME, WALLET_ADDRESS, ADDRESS, NATIONAL_ID, CUSTOMER_IDENTIFICATION, DATE_OF_BIRTH, PLACE_OF_BIRTH); + + private Individual(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Individual of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Individual::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Company extends StringEnum { + /** The value {@code "name"}. */ + public static final Company NAME = new Company("name"); + + /** The value {@code "wallet_address"}. */ + public static final Company WALLET_ADDRESS = new Company("wallet_address"); + + /** The value {@code "address"}. */ + public static final Company ADDRESS = new Company("address"); + + /** The value {@code "national_id"}. */ + public static final Company NATIONAL_ID = new Company("national_id"); + + /** The value {@code "customer_identification"}. */ + public static final Company CUSTOMER_IDENTIFICATION = new Company("customer_identification"); + + private static final List KNOWN_VALUES = List.of(NAME, WALLET_ADDRESS, ADDRESS, NATIONAL_ID, CUSTOMER_IDENTIFICATION); + + private Company(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Company of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Company::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleDataUnmasked.java b/src/main/java/me/didit/types/TravelRuleDataUnmasked.java new file mode 100644 index 0000000..ad44142 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleDataUnmasked.java @@ -0,0 +1,201 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * Audit record of the moment the counterparty's data was unmasked. Null while the data is still masked, and for outbound transfers. + */ +public final class TravelRuleDataUnmasked extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("at", "reason", "actor"); + + private TravelRuleDataUnmasked(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleDataUnmasked} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleDataUnmasked fromJson(JsonNode json) { + return new TravelRuleDataUnmasked(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional at() { + return Json.optional(json, "at", Conv.DATE_TIME); + } + + /** + * What confirmed ownership: ownership_confirmed (your confirmation through the console or the API), wallet_proof (a wallet proof in the widget, or an INTERNAL inbound transfer, set when it is created), address_book_match (a verified address-book wallet), own_verified_wallet (your user's own verified wallet), inquiry_approved (your approval of an inbound TRP inquiry or a GTR or Sygna message), known_payment_method (a wallet your user already paid with) or travel_address (the per-wallet travel address named the wallet). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.enumeration(TravelRuleDataUnmasked.Reason::of)); + } + + /** + * Who confirmed it: the console user's email or the API key's label. Blank for an automatic decision. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional actor() { + return Json.optional(json, "actor", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleDataUnmasked}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleDataUnmasked from) { + super(from); + } + + /** + * The at field. + * + * @param at the value + * @return this builder + */ + public Builder at(OffsetDateTime at) { + return set("at", Conv.DATE_TIME, at); + } + + /** + * What confirmed ownership: ownership_confirmed (your confirmation through the console or the API), wallet_proof (a wallet proof in the widget, or an INTERNAL inbound transfer, set when it is created), address_book_match (a verified address-book wallet), own_verified_wallet (your user's own verified wallet), inquiry_approved (your approval of an inbound TRP inquiry or a GTR or Sygna message), known_payment_method (a wallet your user already paid with) or travel_address (the per-wallet travel address named the wallet). + * + * @param reason the value + * @return this builder + */ + public Builder reason(TravelRuleDataUnmasked.Reason reason) { + return set("reason", Conv.enumeration(TravelRuleDataUnmasked.Reason::of), reason); + } + + /** + * Who confirmed it: the console user's email or the API key's label. Blank for an automatic decision. + * + * @param actor the value + * @return this builder + */ + public Builder actor(String actor) { + return set("actor", Conv.STRING, actor); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleDataUnmasked} + */ + public TravelRuleDataUnmasked build() { + return new TravelRuleDataUnmasked(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * What confirmed ownership: ownership_confirmed (your confirmation through the console or the API), wallet_proof (a wallet proof in the widget, or an INTERNAL inbound transfer, set when it is created), address_book_match (a verified address-book wallet), own_verified_wallet (your user's own verified wallet), inquiry_approved (your approval of an inbound TRP inquiry or a GTR or Sygna message), known_payment_method (a wallet your user already paid with) or travel_address (the per-wallet travel address named the wallet). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Reason extends StringEnum { + /** The value {@code "ownership_confirmed"}. */ + public static final Reason OWNERSHIP_CONFIRMED = new Reason("ownership_confirmed"); + + /** The value {@code "wallet_proof"}. */ + public static final Reason WALLET_PROOF = new Reason("wallet_proof"); + + /** The value {@code "address_book_match"}. */ + public static final Reason ADDRESS_BOOK_MATCH = new Reason("address_book_match"); + + /** The value {@code "own_verified_wallet"}. */ + public static final Reason OWN_VERIFIED_WALLET = new Reason("own_verified_wallet"); + + /** The value {@code "inquiry_approved"}. */ + public static final Reason INQUIRY_APPROVED = new Reason("inquiry_approved"); + + /** The value {@code "known_payment_method"}. */ + public static final Reason KNOWN_PAYMENT_METHOD = new Reason("known_payment_method"); + + /** The value {@code "travel_address"}. */ + public static final Reason TRAVEL_ADDRESS = new Reason("travel_address"); + + private static final List KNOWN_VALUES = List.of(OWNERSHIP_CONFIRMED, WALLET_PROOF, ADDRESS_BOOK_MATCH, OWN_VERIFIED_WALLET, INQUIRY_APPROVED, KNOWN_PAYMENT_METHOD, TRAVEL_ADDRESS); + + private Reason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Reason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Reason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleDelivery.java b/src/main/java/me/didit/types/TravelRuleDelivery.java new file mode 100644 index 0000000..31b29ce --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleDelivery.java @@ -0,0 +1,399 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Where the latest message the transfer owes the other side stands. Didit contacts the counterparty after the request that created or changed the transfer has returned, so this object says whether that message got through: queued, being sent, waiting to be retried after an error, delivered, or failed. A failure the network may still recover from (no answer in time, a failed connection, HTTP 408, 429 or 5xx) is tried again after 30 seconds, 2 minutes, 10 minutes, 30 minutes and 1 hour. When a first message (kind: SEND) fails for good, because the network refused it or the sixth attempt failed too, the transfer moves to COUNTERPARTY_VASP_NOT_REACHABLE with last_error in its timeline; any other message that fails for good is noted in the timeline. The transfer's status stays the source of truth for the exchange itself. + */ +public final class TravelRuleDelivery extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("kind", "status", "rail", "attempts", "last_error", "next_attempt_at", "delivered_at"); + + private TravelRuleDelivery(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleDelivery} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleDelivery fromJson(JsonNode json) { + return new TravelRuleDelivery(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * What the message carries. SEND: the transfer's first message to the counterparty (outbound; the rail is chosen when it is sent). ANSWER: your accept or decline, owed to the network that asked (inbound). TXID: the on-chain transaction hash, after you finish the transfer. CANCEL: the cancellation, after you cancel it. POLL: a status query to a network that offers one, repeated every 5 minutes while the transfer waits on the counterparty. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TravelRuleDelivery.Kind kind() { + return Json.required(json, "kind", Conv.enumeration(TravelRuleDelivery.Kind::of)); + } + + /** + * QUEUED: waiting to be sent. SENDING: an attempt is running. RETRYING: an attempt failed and the next one runs at next_attempt_at. DELIVERED: the other side took the message. FAILED: the other side refused it, or the last attempt failed. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TravelRuleDelivery.Status status() { + return Json.required(json, "status", Conv.enumeration(TravelRuleDelivery.Status::of)); + } + + /** + * The rail that carries the message. Empty until Didit has chosen one for a first message. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TravelRuleDelivery.Rail rail() { + return Json.required(json, "rail", Conv.enumeration(TravelRuleDelivery.Rail::of)); + } + + /** + * Attempts made so far for this message. A message that is not a status poll is attempted at most 6 times. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long attempts() { + return Json.required(json, "attempts", Conv.LONG); + } + + /** + * The kind of failure the last attempt hit, "" when none did: for example email undeliverable, trp identity endpoint unreachable or trp transfer inquiry delivery failed. It never carries the counterparty's own answer, which stays on the transfer's timeline. + * When GTR answers with an error, its own status code and message are included. Response bodies are never included. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String lastError() { + return Json.required(json, "last_error", Conv.STRING); + } + + /** + * When the next attempt runs while the message is QUEUED or RETRYING. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nextAttemptAt() { + return Json.optional(json, "next_attempt_at", Conv.DATE_TIME); + } + + /** + * When the other side took the message. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deliveredAt() { + return Json.optional(json, "delivered_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleDelivery}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleDelivery from) { + super(from); + } + + /** + * What the message carries. SEND: the transfer's first message to the counterparty (outbound; the rail is chosen when it is sent). ANSWER: your accept or decline, owed to the network that asked (inbound). TXID: the on-chain transaction hash, after you finish the transfer. CANCEL: the cancellation, after you cancel it. POLL: a status query to a network that offers one, repeated every 5 minutes while the transfer waits on the counterparty. + * + * @param kind the value + * @return this builder + */ + public Builder kind(TravelRuleDelivery.Kind kind) { + return set("kind", Conv.enumeration(TravelRuleDelivery.Kind::of), kind); + } + + /** + * QUEUED: waiting to be sent. SENDING: an attempt is running. RETRYING: an attempt failed and the next one runs at next_attempt_at. DELIVERED: the other side took the message. FAILED: the other side refused it, or the last attempt failed. + * + * @param status the value + * @return this builder + */ + public Builder status(TravelRuleDelivery.Status status) { + return set("status", Conv.enumeration(TravelRuleDelivery.Status::of), status); + } + + /** + * The rail that carries the message. Empty until Didit has chosen one for a first message. + * + * @param rail the value + * @return this builder + */ + public Builder rail(TravelRuleDelivery.Rail rail) { + return set("rail", Conv.enumeration(TravelRuleDelivery.Rail::of), rail); + } + + /** + * Attempts made so far for this message. A message that is not a status poll is attempted at most 6 times. + * + * @param attempts the value + * @return this builder + */ + public Builder attempts(long attempts) { + return set("attempts", Conv.LONG, attempts); + } + + /** + * The kind of failure the last attempt hit, "" when none did: for example email undeliverable, trp identity endpoint unreachable or trp transfer inquiry delivery failed. It never carries the counterparty's own answer, which stays on the transfer's timeline. + * When GTR answers with an error, its own status code and message are included. Response bodies are never included. + * + * @param lastError the value + * @return this builder + */ + public Builder lastError(String lastError) { + return set("last_error", Conv.STRING, lastError); + } + + /** + * When the next attempt runs while the message is QUEUED or RETRYING. + * + * @param nextAttemptAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nextAttemptAt(@Nullable OffsetDateTime nextAttemptAt) { + return setNullable("next_attempt_at", Conv.DATE_TIME, nextAttemptAt); + } + + /** + * When the other side took the message. + * + * @param deliveredAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder deliveredAt(@Nullable OffsetDateTime deliveredAt) { + return setNullable("delivered_at", Conv.DATE_TIME, deliveredAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleDelivery} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleDelivery build() { + require("kind", false); + require("status", false); + require("rail", false); + require("attempts", false); + require("last_error", false); + require("next_attempt_at", true); + require("delivered_at", true); + return new TravelRuleDelivery(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * What the message carries. SEND: the transfer's first message to the counterparty (outbound; the rail is chosen when it is sent). ANSWER: your accept or decline, owed to the network that asked (inbound). TXID: the on-chain transaction hash, after you finish the transfer. CANCEL: the cancellation, after you cancel it. POLL: a status query to a network that offers one, repeated every 5 minutes while the transfer waits on the counterparty. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "SEND"}. */ + public static final Kind SEND = new Kind("SEND"); + + /** The value {@code "ANSWER"}. */ + public static final Kind ANSWER = new Kind("ANSWER"); + + /** The value {@code "TXID"}. */ + public static final Kind TXID = new Kind("TXID"); + + /** The value {@code "CANCEL"}. */ + public static final Kind CANCEL = new Kind("CANCEL"); + + /** The value {@code "POLL"}. */ + public static final Kind POLL = new Kind("POLL"); + + private static final List KNOWN_VALUES = List.of(SEND, ANSWER, TXID, CANCEL, POLL); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * QUEUED: waiting to be sent. SENDING: an attempt is running. RETRYING: an attempt failed and the next one runs at next_attempt_at. DELIVERED: the other side took the message. FAILED: the other side refused it, or the last attempt failed. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "QUEUED"}. */ + public static final Status QUEUED = new Status("QUEUED"); + + /** The value {@code "SENDING"}. */ + public static final Status SENDING = new Status("SENDING"); + + /** The value {@code "RETRYING"}. */ + public static final Status RETRYING = new Status("RETRYING"); + + /** The value {@code "DELIVERED"}. */ + public static final Status DELIVERED = new Status("DELIVERED"); + + /** The value {@code "FAILED"}. */ + public static final Status FAILED = new Status("FAILED"); + + private static final List KNOWN_VALUES = List.of(QUEUED, SENDING, RETRYING, DELIVERED, FAILED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The rail that carries the message. Empty until Didit has chosen one for a first message. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Rail extends StringEnum { + /** The value {@code "INTERNAL"}. */ + public static final Rail INTERNAL = new Rail("INTERNAL"); + + /** The value {@code "TRP"}. */ + public static final Rail TRP = new Rail("TRP"); + + /** The value {@code "GTR"}. */ + public static final Rail GTR = new Rail("GTR"); + + /** The value {@code "TRUST"}. */ + public static final Rail TRUST = new Rail("TRUST"); + + /** The value {@code "VERIFYVASP"}. */ + public static final Rail VERIFYVASP = new Rail("VERIFYVASP"); + + /** The value {@code "SYGNA"}. */ + public static final Rail SYGNA = new Rail("SYGNA"); + + /** The value {@code "EMAIL"}. */ + public static final Rail EMAIL = new Rail("EMAIL"); + + /** The value {@code ""}. */ + public static final Rail EMPTY = new Rail(""); + + private static final List KNOWN_VALUES = List.of(INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL, EMPTY); + + private Rail(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Rail of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Rail::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleDiscoveryStep.java b/src/main/java/me/didit/types/TravelRuleDiscoveryStep.java new file mode 100644 index 0000000..37ce5ff --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleDiscoveryStep.java @@ -0,0 +1,302 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One step of the counterparty discovery Didit ran for an outbound transfer, in the order it ran them. The first step that names a VASP with a route ends the chain. + */ +public final class TravelRuleDiscoveryStep extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("step", "outcome", "reason", "vasp", "rail", "at"); + + private TravelRuleDiscoveryStep(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleDiscoveryStep} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleDiscoveryStep fromJson(JsonNode json) { + return new TravelRuleDiscoveryStep(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * internal_address_book: another Didit customer that proved control of the destination wallet. wallet_attribution: the wallet's blockchain analytics attribution. network_lookup: a network that can say which VASP holds the address. customer_hint: the VASP name, LEI or jurisdiction you sent in beneficiary_data. end_user: the counterparty the end user (or you) picked, or the request to pick one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional step() { + return Json.optional(json, "step", Conv.enumeration(TravelRuleDiscoveryStep.Step::of)); + } + + /** + * match: the step named a counterparty. miss: it ran and found none. skipped: it could not run, and reason says why. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional outcome() { + return Json.optional(json, "outcome", Conv.enumeration(TravelRuleDiscoveryStep.Outcome::of)); + } + + /** + * Machine-readable reason, "" on a match: no_proven_internal_wallet, attribution_disabled, already_attributed, no_attributed_entity, directory_match_no_route, gtr_address_lookup_runs_on_dispatch, no_membership, network_switched_off, no_network_address_lookup, no_directory_match, awaiting_end_user or picked_member_without_proven_wallet. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * The directory VASP the step named, when it named one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vasp() { + return Json.optional(json, "vasp", Conv.STRING); + } + + /** + * The rail that reaches that counterparty, when the step found one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rail() { + return Json.optional(json, "rail", Conv.STRING); + } + + /** + * The at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional at() { + return Json.optional(json, "at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleDiscoveryStep}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleDiscoveryStep from) { + super(from); + } + + /** + * internal_address_book: another Didit customer that proved control of the destination wallet. wallet_attribution: the wallet's blockchain analytics attribution. network_lookup: a network that can say which VASP holds the address. customer_hint: the VASP name, LEI or jurisdiction you sent in beneficiary_data. end_user: the counterparty the end user (or you) picked, or the request to pick one. + * + * @param step the value + * @return this builder + */ + public Builder step(TravelRuleDiscoveryStep.Step step) { + return set("step", Conv.enumeration(TravelRuleDiscoveryStep.Step::of), step); + } + + /** + * match: the step named a counterparty. miss: it ran and found none. skipped: it could not run, and reason says why. + * + * @param outcome the value + * @return this builder + */ + public Builder outcome(TravelRuleDiscoveryStep.Outcome outcome) { + return set("outcome", Conv.enumeration(TravelRuleDiscoveryStep.Outcome::of), outcome); + } + + /** + * Machine-readable reason, "" on a match: no_proven_internal_wallet, attribution_disabled, already_attributed, no_attributed_entity, directory_match_no_route, gtr_address_lookup_runs_on_dispatch, no_membership, network_switched_off, no_network_address_lookup, no_directory_match, awaiting_end_user or picked_member_without_proven_wallet. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * The directory VASP the step named, when it named one. + * + * @param vasp the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vasp(@Nullable String vasp) { + return setNullable("vasp", Conv.STRING, vasp); + } + + /** + * The rail that reaches that counterparty, when the step found one. + * + * @param rail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder rail(@Nullable String rail) { + return setNullable("rail", Conv.STRING, rail); + } + + /** + * The at field. + * + * @param at the value + * @return this builder + */ + public Builder at(OffsetDateTime at) { + return set("at", Conv.DATE_TIME, at); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleDiscoveryStep} + */ + public TravelRuleDiscoveryStep build() { + return new TravelRuleDiscoveryStep(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * internal_address_book: another Didit customer that proved control of the destination wallet. wallet_attribution: the wallet's blockchain analytics attribution. network_lookup: a network that can say which VASP holds the address. customer_hint: the VASP name, LEI or jurisdiction you sent in beneficiary_data. end_user: the counterparty the end user (or you) picked, or the request to pick one. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Step extends StringEnum { + /** The value {@code "internal_address_book"}. */ + public static final Step INTERNAL_ADDRESS_BOOK = new Step("internal_address_book"); + + /** The value {@code "wallet_attribution"}. */ + public static final Step WALLET_ATTRIBUTION = new Step("wallet_attribution"); + + /** The value {@code "network_lookup"}. */ + public static final Step NETWORK_LOOKUP = new Step("network_lookup"); + + /** The value {@code "customer_hint"}. */ + public static final Step CUSTOMER_HINT = new Step("customer_hint"); + + /** The value {@code "end_user"}. */ + public static final Step END_USER = new Step("end_user"); + + private static final List KNOWN_VALUES = List.of(INTERNAL_ADDRESS_BOOK, WALLET_ATTRIBUTION, NETWORK_LOOKUP, CUSTOMER_HINT, END_USER); + + private Step(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Step of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Step::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * match: the step named a counterparty. miss: it ran and found none. skipped: it could not run, and reason says why. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Outcome extends StringEnum { + /** The value {@code "match"}. */ + public static final Outcome MATCH = new Outcome("match"); + + /** The value {@code "miss"}. */ + public static final Outcome MISS = new Outcome("miss"); + + /** The value {@code "skipped"}. */ + public static final Outcome SKIPPED = new Outcome("skipped"); + + private static final List KNOWN_VALUES = List.of(MATCH, MISS, SKIPPED); + + private Outcome(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Outcome of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleInboundRequest.java b/src/main/java/me/didit/types/TravelRuleInboundRequest.java index dd21f0f..9067696 100644 --- a/src/main/java/me/didit/types/TravelRuleInboundRequest.java +++ b/src/main/java/me/didit/types/TravelRuleInboundRequest.java @@ -13,7 +13,7 @@ import me.didit.core.JsonModel; public final class TravelRuleInboundRequest extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("chain", "txid", "wallet_address", "amount", "currency", "originator_data", "beneficiary_data", "originating_vasp"); + private static final Set KNOWN_FIELDS = Set.of("chain", "txid", "wallet_address", "amount", "currency", "originator_data", "beneficiary_data", "originating_vasp", "memo", "utxo"); private TravelRuleInboundRequest(ObjectNode json, Map> files) { super(json, files); @@ -49,7 +49,7 @@ public Builder toBuilder() { } /** - * Chain the deposit settled on, e.g. "ethereum". + * Chain the deposit settled on, for example ETH. Stored lowercase and compared case-insensitively with your wallet address book entries, so use the same chain token there. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -69,7 +69,7 @@ public String txid() { } /** - * The destination wallet you received the deposit on. + * The destination wallet you received the deposit on. An XRP X-address is split into its classic address and tag. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -79,7 +79,7 @@ public String walletAddress() { } /** - * Deposit amount as a decimal string. + * Deposit amount as a decimal string, zero or greater, with up to 18 decimal places. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -99,7 +99,7 @@ public String currency() { } /** - * IVMS-101 originator payload from the sending VASP, if known. + * Originator details from the sending VASP, if known, as a flat object such as {"name": "..."}. Didit reads name (or full_name), first_name, last_name and vendor_data from it. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -109,7 +109,7 @@ public Optional> originatorData() { } /** - * IVMS-101 beneficiary payload (your customer). + * Beneficiary details (your customer), as a flat object such as {"name": "..."}. Didit matches name against the holder_name of the matched address book entry, and sets wallet_address and chain from the request. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -119,7 +119,7 @@ public Optional> beneficiaryData() { } /** - * Optional description of the sending VASP. + * Optional description of the sending VASP. It stays on this transfer only (metadata.originating_vasp, and the transfer's originator VASP in GET /v3/travel-rule/transfers/); it never creates, edits or links a shared VASP directory entry. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -128,6 +128,26 @@ public Optional originatingVasp() { return Json.optional(json, "originating_vasp", Conv.model(TravelRuleInboundRequest.OriginatingVasp::fromJson)); } + /** + * Optional. The deposit's destination tag or memo on a memo network: on a shared deposit address it is what tells your customers apart, and the address-book entry for that memo is matched. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. Alias: destinationTag or destination_tag. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional memo() { + return Json.optional(json, "memo", Conv.STRING); + } + + /** + * Optional, UTXO chains only. The deposit transaction's inputs and outputs: the sender's wallet is the declared originator_data.wallet_address, else the largest input. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional utxo() { + return Json.optional(json, "utxo", Conv.model(TravelRuleUtxo::fromJson)); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -142,7 +162,7 @@ private Builder(TravelRuleInboundRequest from) { } /** - * Chain the deposit settled on, e.g. "ethereum". + * Chain the deposit settled on, for example ETH. Stored lowercase and compared case-insensitively with your wallet address book entries, so use the same chain token there. * * @param chain the value * @return this builder @@ -162,7 +182,7 @@ public Builder txid(String txid) { } /** - * The destination wallet you received the deposit on. + * The destination wallet you received the deposit on. An XRP X-address is split into its classic address and tag. * * @param walletAddress the value * @return this builder @@ -172,7 +192,7 @@ public Builder walletAddress(String walletAddress) { } /** - * Deposit amount as a decimal string. + * Deposit amount as a decimal string, zero or greater, with up to 18 decimal places. * * @param amount the value * @return this builder @@ -192,7 +212,7 @@ public Builder currency(String currency) { } /** - * IVMS-101 originator payload from the sending VASP, if known. + * Originator details from the sending VASP, if known, as a flat object such as {"name": "..."}. Didit reads name (or full_name), first_name, last_name and vendor_data from it. * * @param originatorData the value * @return this builder @@ -202,7 +222,7 @@ public Builder originatorData(Map originatorData) { } /** - * IVMS-101 beneficiary payload (your customer). + * Beneficiary details (your customer), as a flat object such as {"name": "..."}. Didit matches name against the holder_name of the matched address book entry, and sets wallet_address and chain from the request. * * @param beneficiaryData the value * @return this builder @@ -212,7 +232,7 @@ public Builder beneficiaryData(Map beneficiaryData) { } /** - * Optional description of the sending VASP. + * Optional description of the sending VASP. It stays on this transfer only (metadata.originating_vasp, and the transfer's originator VASP in GET /v3/travel-rule/transfers/); it never creates, edits or links a shared VASP directory entry. * * @param originatingVasp the value * @return this builder @@ -221,6 +241,26 @@ public Builder originatingVasp(TravelRuleInboundRequest.OriginatingVasp originat return set("originating_vasp", Conv.model(TravelRuleInboundRequest.OriginatingVasp::fromJson), originatingVasp); } + /** + * Optional. The deposit's destination tag or memo on a memo network: on a shared deposit address it is what tells your customers apart, and the address-book entry for that memo is matched. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. Alias: destinationTag or destination_tag. + * + * @param memo the value + * @return this builder + */ + public Builder memo(String memo) { + return set("memo", Conv.STRING, memo); + } + + /** + * Optional, UTXO chains only. The deposit transaction's inputs and outputs: the sender's wallet is the declared originator_data.wallet_address, else the largest input. + * + * @param utxo the value + * @return this builder + */ + public Builder utxo(TravelRuleUtxo utxo) { + return set("utxo", Conv.model(TravelRuleUtxo::fromJson), utxo); + } + /** * Builds the value. * @@ -242,7 +282,9 @@ protected Builder self() { } } - /** Optional description of the sending VASP. */ + /** + * Optional description of the sending VASP. It stays on this transfer only (metadata.originating_vasp, and the transfer's originator VASP in GET /v3/travel-rule/transfers/); it never creates, edits or links a shared VASP directory entry. + */ public static final class OriginatingVasp extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("name", "lei", "travel_address"); @@ -290,7 +332,7 @@ public Optional name() { } /** - * The lei field. + * Optional. A valid 20-character ISO 17442 LEI, or empty. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -300,7 +342,7 @@ public Optional lei() { } /** - * Travel address that lets Didit reach the sender on the TRP rail. + * The sender's travel address, recorded on this transfer. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -333,7 +375,7 @@ public Builder name(String name) { } /** - * The lei field. + * Optional. A valid 20-character ISO 17442 LEI, or empty. * * @param lei the value * @return this builder @@ -343,7 +385,7 @@ public Builder lei(String lei) { } /** - * Travel address that lets Didit reach the sender on the TRP rail. + * The sender's travel address, recorded on this transfer. * * @param travelAddress the value * @return this builder diff --git a/src/main/java/me/didit/types/TravelRuleInboundResponse.java b/src/main/java/me/didit/types/TravelRuleInboundResponse.java index 238e4b9..590f52b 100644 --- a/src/main/java/me/didit/types/TravelRuleInboundResponse.java +++ b/src/main/java/me/didit/types/TravelRuleInboundResponse.java @@ -59,7 +59,7 @@ public Optional transactionId() { } /** - * Didit-generated txn_id for the sunrise transaction. + * txn_id of the inbound transaction: Didit-generated when Didit created the transaction, or your own when an unclaimed deposit was reused. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -69,7 +69,7 @@ public Optional txnId() { } /** - * true when a new transaction was minted (HTTP 201); false when an existing unclaimed deposit was reused (HTTP 200). + * true when this call registered the transfer (HTTP 201); false when a transfer for the same txid and wallet_address already existed (HTTP 200). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -79,7 +79,7 @@ public Optional created() { } /** - * Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. originator_data/beneficiary_data are omitted while PII is masked (see description). + * Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. While an inbound transfer's wallet ownership is unconfirmed, the counterparty's originator_data and beneficiary_data come back masked (the first letter of each word is kept; the wallet, chain and VASP routing stay readable) and data_masked is true. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -112,7 +112,7 @@ public Builder transactionId(String transactionId) { } /** - * Didit-generated txn_id for the sunrise transaction. + * txn_id of the inbound transaction: Didit-generated when Didit created the transaction, or your own when an unclaimed deposit was reused. * * @param txnId the value * @return this builder @@ -122,7 +122,7 @@ public Builder txnId(String txnId) { } /** - * true when a new transaction was minted (HTTP 201); false when an existing unclaimed deposit was reused (HTTP 200). + * true when this call registered the transfer (HTTP 201); false when a transfer for the same txid and wallet_address already existed (HTTP 200). * * @param created the value * @return this builder @@ -132,7 +132,7 @@ public Builder created(boolean created) { } /** - * Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. originator_data/beneficiary_data are omitted while PII is masked (see description). + * Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. While an inbound transfer's wallet ownership is unconfirmed, the counterparty's originator_data and beneficiary_data come back masked (the first letter of each word is kept; the wallet, chain and VASP routing stay readable) and data_masked is true. * * @param travelRule the value * @return this builder diff --git a/src/main/java/me/didit/types/TravelRuleJoinCredentials.java b/src/main/java/me/didit/types/TravelRuleJoinCredentials.java new file mode 100644 index 0000000..19ab6c7 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleJoinCredentials.java @@ -0,0 +1,329 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** The new free organization and the credentials its application calls the API with. */ +public final class TravelRuleJoinCredentials extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("organization", "application", "client_id", "api_key"); + + private TravelRuleJoinCredentials(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleJoinCredentials} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleJoinCredentials fromJson(JsonNode json) { + return new TravelRuleJoinCredentials(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The organization field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional organization() { + return Json.optional(json, "organization", Conv.model(TravelRuleJoinCredentials.Organization::fromJson)); + } + + /** + * The application field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional application() { + return Json.optional(json, "application", Conv.model(TravelRuleJoinCredentials.Application::fromJson)); + } + + /** + * The client_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional clientId() { + return Json.optional(json, "client_id", Conv.STRING); + } + + /** + * Send as the x-api-key header on the workspace endpoints. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional apiKey() { + return Json.optional(json, "api_key", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleJoinCredentials}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleJoinCredentials from) { + super(from); + } + + /** + * The organization field. + * + * @param organization the value + * @return this builder + */ + public Builder organization(TravelRuleJoinCredentials.Organization organization) { + return set("organization", Conv.model(TravelRuleJoinCredentials.Organization::fromJson), organization); + } + + /** + * The application field. + * + * @param application the value + * @return this builder + */ + public Builder application(TravelRuleJoinCredentials.Application application) { + return set("application", Conv.model(TravelRuleJoinCredentials.Application::fromJson), application); + } + + /** + * The client_id field. + * + * @param clientId the value + * @return this builder + */ + public Builder clientId(String clientId) { + return set("client_id", Conv.STRING, clientId); + } + + /** + * Send as the x-api-key header on the workspace endpoints. + * + * @param apiKey the value + * @return this builder + */ + public Builder apiKey(String apiKey) { + return set("api_key", Conv.STRING, apiKey); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleJoinCredentials} + */ + public TravelRuleJoinCredentials build() { + return new TravelRuleJoinCredentials(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Organization extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid"); + + private Organization(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Organization} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Organization fromJson(JsonNode json) { + return new Organization(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Organization}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Organization from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Organization} + */ + public Organization build() { + return new Organization(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Application extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid"); + + private Application(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Application} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Application fromJson(JsonNode json) { + return new Application(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Application}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Application from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Application} + */ + public Application build() { + return new Application(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleJoinInvitation.java b/src/main/java/me/didit/types/TravelRuleJoinInvitation.java new file mode 100644 index 0000000..93227d9 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleJoinInvitation.java @@ -0,0 +1,346 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** What the join page shows before anything is typed. */ +public final class TravelRuleJoinInvitation extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("email", "vasp_name", "source", "expires_at", "prefill"); + + private TravelRuleJoinInvitation(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleJoinInvitation} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleJoinInvitation fromJson(JsonNode json) { + return new TravelRuleJoinInvitation(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The address the invitation went to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + /** + * The vasp_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vaspName() { + return Json.optional(json, "vasp_name", Conv.STRING); + } + + /** + * Where the invitation came from, for example pickup. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.STRING); + } + + /** + * The expires_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional expiresAt() { + return Json.optional(json, "expires_at", Conv.DATE_TIME); + } + + /** + * The prefill field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional prefill() { + return Json.optional(json, "prefill", Conv.model(TravelRuleJoinInvitation.Prefill::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleJoinInvitation}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleJoinInvitation from) { + super(from); + } + + /** + * The address the invitation went to. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * The vasp_name field. + * + * @param vaspName the value + * @return this builder + */ + public Builder vaspName(String vaspName) { + return set("vasp_name", Conv.STRING, vaspName); + } + + /** + * Where the invitation came from, for example pickup. + * + * @param source the value + * @return this builder + */ + public Builder source(String source) { + return set("source", Conv.STRING, source); + } + + /** + * The expires_at field. + * + * @param expiresAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder expiresAt(@Nullable OffsetDateTime expiresAt) { + return setNullable("expires_at", Conv.DATE_TIME, expiresAt); + } + + /** + * The prefill field. + * + * @param prefill the value + * @return this builder + */ + public Builder prefill(TravelRuleJoinInvitation.Prefill prefill) { + return set("prefill", Conv.model(TravelRuleJoinInvitation.Prefill::fromJson), prefill); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleJoinInvitation} + */ + public TravelRuleJoinInvitation build() { + return new TravelRuleJoinInvitation(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Prefill extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("vasp_id", "legal_name", "lei", "jurisdiction", "domains"); + + private Prefill(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Prefill} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Prefill fromJson(JsonNode json) { + return new Prefill(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The vasp_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vaspId() { + return Json.optional(json, "vasp_id", Conv.STRING); + } + + /** + * The legal_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional legalName() { + return Json.optional(json, "legal_name", Conv.STRING); + } + + /** + * The lei field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lei() { + return Json.optional(json, "lei", Conv.STRING); + } + + /** + * The jurisdiction field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional jurisdiction() { + return Json.optional(json, "jurisdiction", Conv.STRING); + } + + /** + * The domains field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> domains() { + return Json.optional(json, "domains", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Prefill}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Prefill from) { + super(from); + } + + /** + * The vasp_id field. + * + * @param vaspId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vaspId(@Nullable String vaspId) { + return setNullable("vasp_id", Conv.STRING, vaspId); + } + + /** + * The legal_name field. + * + * @param legalName the value + * @return this builder + */ + public Builder legalName(String legalName) { + return set("legal_name", Conv.STRING, legalName); + } + + /** + * The lei field. + * + * @param lei the value + * @return this builder + */ + public Builder lei(String lei) { + return set("lei", Conv.STRING, lei); + } + + /** + * The jurisdiction field. + * + * @param jurisdiction the value + * @return this builder + */ + public Builder jurisdiction(String jurisdiction) { + return set("jurisdiction", Conv.STRING, jurisdiction); + } + + /** + * The domains field. + * + * @param domains the value + * @return this builder + */ + public Builder domains(List domains) { + return set("domains", Conv.list(Conv.STRING), domains); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Prefill} + */ + public Prefill build() { + return new Prefill(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleJoinRegisterRequest.java b/src/main/java/me/didit/types/TravelRuleJoinRegisterRequest.java new file mode 100644 index 0000000..69d95f7 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleJoinRegisterRequest.java @@ -0,0 +1,120 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleJoinRegisterRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("email", "password"); + + private TravelRuleJoinRegisterRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleJoinRegisterRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleJoinRegisterRequest fromJson(JsonNode json) { + return new TravelRuleJoinRegisterRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Must be the address the invitation went to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String email() { + return Json.required(json, "email", Conv.STRING); + } + + /** + * The password field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String password() { + return Json.required(json, "password", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleJoinRegisterRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleJoinRegisterRequest from) { + super(from); + } + + /** + * Must be the address the invitation went to. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * The password field. + * + * @param password the value + * @return this builder + */ + public Builder password(String password) { + return set("password", Conv.STRING, password); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleJoinRegisterRequest} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleJoinRegisterRequest build() { + require("email", false); + require("password", false); + return new TravelRuleJoinRegisterRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleJoinVerifyRequest.java b/src/main/java/me/didit/types/TravelRuleJoinVerifyRequest.java new file mode 100644 index 0000000..1b575c0 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleJoinVerifyRequest.java @@ -0,0 +1,120 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleJoinVerifyRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("email", "code"); + + private TravelRuleJoinVerifyRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleJoinVerifyRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleJoinVerifyRequest fromJson(JsonNode json) { + return new TravelRuleJoinVerifyRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The email field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String email() { + return Json.required(json, "email", Conv.STRING); + } + + /** + * The 6-character code emailed after registering. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String code() { + return Json.required(json, "code", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleJoinVerifyRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleJoinVerifyRequest from) { + super(from); + } + + /** + * The email field. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * The 6-character code emailed after registering. + * + * @param code the value + * @return this builder + */ + public Builder code(String code) { + return set("code", Conv.STRING, code); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleJoinVerifyRequest} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleJoinVerifyRequest build() { + require("email", false); + require("code", false); + return new TravelRuleJoinVerifyRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleListVaspsParams.java b/src/main/java/me/didit/types/TravelRuleListVaspsParams.java index c00874c..b476464 100644 --- a/src/main/java/me/didit/types/TravelRuleListVaspsParams.java +++ b/src/main/java/me/didit/types/TravelRuleListVaspsParams.java @@ -11,10 +11,11 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; +import me.didit.core.StringEnum; /** Parameters for {@code travel_rule.list_vasps}. */ public final class TravelRuleListVaspsParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("search", "limit", "offset"); + private static final Set KNOWN_FIELDS = Set.of("search", "network", "jurisdiction", "reachable", "kind", "risk", "responsiveness", "limit", "offset"); private TravelRuleListVaspsParams(ObjectNode json, Map> files) { super(json, files); @@ -50,7 +51,7 @@ public Builder toBuilder() { } /** - * Case-insensitive substring match on VASP name. + * A name or alias fragment (case-insensitive), an exact LEI, or a domain (example.com or a URL on it). Didit network members match on their legal name, LEI or the domain of their compliance email. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -59,6 +60,66 @@ public Optional search() { return Json.optional(json, "search", Conv.STRING); } + /** + * Only directory entries a network's member directory currently lists. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional network() { + return Json.optional(json, "network", Conv.enumeration(TravelRuleListVaspsParams.Network::of)); + } + + /** + * ISO 3166 alpha-2 or alpha-3 country code. 400 for anything else. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional jurisdiction() { + return Json.optional(json, "jurisdiction", Conv.STRING); + } + + /** + * true: only VASPs your application has an available route to (Didit network members included). false: only directory entries it cannot reach today. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reachable() { + return Json.optional(json, "reachable", Conv.BOOLEAN); + } + + /** + * Only directory entries, or only Didit network members. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(TravelRuleListVaspsParams.Kind::of)); + } + + /** + * Only directory entries whose risk_level is this band. Didit network members, which are not scored, never match. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional risk() { + return Json.optional(json, "risk", Conv.enumeration(TravelRuleListVaspsParams.Risk::of)); + } + + /** + * Only directory entries whose responsiveness.status is this value. Didit network members never match. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional responsiveness() { + return Json.optional(json, "responsiveness", Conv.enumeration(TravelRuleListVaspsParams.Responsiveness::of)); + } + /** * The limit field. * @@ -93,7 +154,7 @@ private Builder(TravelRuleListVaspsParams from) { } /** - * Case-insensitive substring match on VASP name. + * A name or alias fragment (case-insensitive), an exact LEI, or a domain (example.com or a URL on it). Didit network members match on their legal name, LEI or the domain of their compliance email. * * @param search the value * @return this builder @@ -102,6 +163,66 @@ public Builder search(String search) { return set("search", Conv.STRING, search); } + /** + * Only directory entries a network's member directory currently lists. + * + * @param network the value + * @return this builder + */ + public Builder network(TravelRuleListVaspsParams.Network network) { + return set("network", Conv.enumeration(TravelRuleListVaspsParams.Network::of), network); + } + + /** + * ISO 3166 alpha-2 or alpha-3 country code. 400 for anything else. + * + * @param jurisdiction the value + * @return this builder + */ + public Builder jurisdiction(String jurisdiction) { + return set("jurisdiction", Conv.STRING, jurisdiction); + } + + /** + * true: only VASPs your application has an available route to (Didit network members included). false: only directory entries it cannot reach today. + * + * @param reachable the value + * @return this builder + */ + public Builder reachable(boolean reachable) { + return set("reachable", Conv.BOOLEAN, reachable); + } + + /** + * Only directory entries, or only Didit network members. + * + * @param kind the value + * @return this builder + */ + public Builder kind(TravelRuleListVaspsParams.Kind kind) { + return set("kind", Conv.enumeration(TravelRuleListVaspsParams.Kind::of), kind); + } + + /** + * Only directory entries whose risk_level is this band. Didit network members, which are not scored, never match. + * + * @param risk the value + * @return this builder + */ + public Builder risk(TravelRuleListVaspsParams.Risk risk) { + return set("risk", Conv.enumeration(TravelRuleListVaspsParams.Risk::of), risk); + } + + /** + * Only directory entries whose responsiveness.status is this value. Didit network members never match. + * + * @param responsiveness the value + * @return this builder + */ + public Builder responsiveness(TravelRuleListVaspsParams.Responsiveness responsiveness) { + return set("responsiveness", Conv.enumeration(TravelRuleListVaspsParams.Responsiveness::of), responsiveness); + } + /** * The limit field. * @@ -136,4 +257,187 @@ protected Builder self() { return this; } } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Network extends StringEnum { + /** The value {@code "GTR"}. */ + public static final Network GTR = new Network("GTR"); + + /** The value {@code "TRUST"}. */ + public static final Network TRUST = new Network("TRUST"); + + /** The value {@code "VERIFYVASP"}. */ + public static final Network VERIFYVASP = new Network("VERIFYVASP"); + + /** The value {@code "SYGNA"}. */ + public static final Network SYGNA = new Network("SYGNA"); + + /** The value {@code "TRISA"}. */ + public static final Network TRISA = new Network("TRISA"); + + private static final List KNOWN_VALUES = List.of(GTR, TRUST, VERIFYVASP, SYGNA, TRISA); + + private Network(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Network of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Network::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "directory"}. */ + public static final Kind DIRECTORY = new Kind("directory"); + + /** The value {@code "didit"}. */ + public static final Kind DIDIT = new Kind("didit"); + + private static final List KNOWN_VALUES = List.of(DIRECTORY, DIDIT); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Risk extends StringEnum { + /** The value {@code "LOW"}. */ + public static final Risk LOW = new Risk("LOW"); + + /** The value {@code "MEDIUM"}. */ + public static final Risk MEDIUM = new Risk("MEDIUM"); + + /** The value {@code "HIGH"}. */ + public static final Risk HIGH = new Risk("HIGH"); + + private static final List KNOWN_VALUES = List.of(LOW, MEDIUM, HIGH); + + private Risk(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Risk of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Risk::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Responsiveness extends StringEnum { + /** The value {@code "RESPONSIVE"}. */ + public static final Responsiveness RESPONSIVE = new Responsiveness("RESPONSIVE"); + + /** The value {@code "UNRESPONSIVE"}. */ + public static final Responsiveness UNRESPONSIVE = new Responsiveness("UNRESPONSIVE"); + + /** The value {@code "UNKNOWN"}. */ + public static final Responsiveness UNKNOWN = new Responsiveness("UNKNOWN"); + + private static final List KNOWN_VALUES = List.of(RESPONSIVE, UNRESPONSIVE, UNKNOWN); + + private Responsiveness(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Responsiveness of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Responsiveness::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } } diff --git a/src/main/java/me/didit/types/TravelRuleMatchingRules.java b/src/main/java/me/didit/types/TravelRuleMatchingRules.java new file mode 100644 index 0000000..cbde6d8 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleMatchingRules.java @@ -0,0 +1,758 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * How strictly each field of the counterparty's data is compared with what you hold, per entity type. A field you leave out keeps its default. NONE skips the comparison, STRICT needs exactly the same words, CONTAINS accepts one value whose words all appear in the other, DEFAULT is the standard subset match, and FUZZY accepts close spellings (and, for dates of birth, a swapped day and month). + */ +public final class TravelRuleMatchingRules extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("individual", "company"); + + private TravelRuleMatchingRules(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleMatchingRules} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleMatchingRules fromJson(JsonNode json) { + return new TravelRuleMatchingRules(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Rules for a natural person. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional individual() { + return Json.optional(json, "individual", Conv.model(TravelRuleMatchingRules.Individual::fromJson)); + } + + /** + * Rules for a legal person. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional company() { + return Json.optional(json, "company", Conv.model(TravelRuleMatchingRules.Company::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleMatchingRules}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleMatchingRules from) { + super(from); + } + + /** + * Rules for a natural person. + * + * @param individual the value + * @return this builder + */ + public Builder individual(TravelRuleMatchingRules.Individual individual) { + return set("individual", Conv.model(TravelRuleMatchingRules.Individual::fromJson), individual); + } + + /** + * Rules for a legal person. + * + * @param company the value + * @return this builder + */ + public Builder company(TravelRuleMatchingRules.Company company) { + return set("company", Conv.model(TravelRuleMatchingRules.Company::fromJson), company); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleMatchingRules} + */ + public TravelRuleMatchingRules build() { + return new TravelRuleMatchingRules(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** Rules for a natural person. */ + public static final class Individual extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "date_of_birth", "address", "account"); + + private Individual(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Individual} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Individual fromJson(JsonNode json) { + return new Individual(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Default: follows name_matching_strictness. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.enumeration(TravelRuleMatchingRules.Individual.Name::of)); + } + + /** + * Default NONE. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateOfBirth() { + return Json.optional(json, "date_of_birth", Conv.enumeration(TravelRuleMatchingRules.Individual.DateOfBirth::of)); + } + + /** + * Default NONE. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional address() { + return Json.optional(json, "address", Conv.enumeration(TravelRuleMatchingRules.Individual.Address::of)); + } + + /** + * The wallet the exchange was routed by. Default STRICT. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional account() { + return Json.optional(json, "account", Conv.enumeration(TravelRuleMatchingRules.Individual.Account::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Individual}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Individual from) { + super(from); + } + + /** + * Default: follows name_matching_strictness. + * + * @param name the value + * @return this builder + */ + public Builder name(TravelRuleMatchingRules.Individual.Name name) { + return set("name", Conv.enumeration(TravelRuleMatchingRules.Individual.Name::of), name); + } + + /** + * Default NONE. + * + * @param dateOfBirth the value + * @return this builder + */ + public Builder dateOfBirth(TravelRuleMatchingRules.Individual.DateOfBirth dateOfBirth) { + return set("date_of_birth", Conv.enumeration(TravelRuleMatchingRules.Individual.DateOfBirth::of), dateOfBirth); + } + + /** + * Default NONE. + * + * @param address the value + * @return this builder + */ + public Builder address(TravelRuleMatchingRules.Individual.Address address) { + return set("address", Conv.enumeration(TravelRuleMatchingRules.Individual.Address::of), address); + } + + /** + * The wallet the exchange was routed by. Default STRICT. + * + * @param account the value + * @return this builder + */ + public Builder account(TravelRuleMatchingRules.Individual.Account account) { + return set("account", Conv.enumeration(TravelRuleMatchingRules.Individual.Account::of), account); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Individual} + */ + public Individual build() { + return new Individual(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Default: follows name_matching_strictness. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Name extends StringEnum { + /** The value {@code "NONE"}. */ + public static final Name NONE = new Name("NONE"); + + /** The value {@code "STRICT"}. */ + public static final Name STRICT = new Name("STRICT"); + + /** The value {@code "CONTAINS"}. */ + public static final Name CONTAINS = new Name("CONTAINS"); + + /** The value {@code "DEFAULT"}. */ + public static final Name DEFAULT = new Name("DEFAULT"); + + /** The value {@code "FUZZY"}. */ + public static final Name FUZZY = new Name("FUZZY"); + + private static final List KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); + + private Name(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Name of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Name::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Default NONE. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DateOfBirth extends StringEnum { + /** The value {@code "NONE"}. */ + public static final DateOfBirth NONE = new DateOfBirth("NONE"); + + /** The value {@code "STRICT"}. */ + public static final DateOfBirth STRICT = new DateOfBirth("STRICT"); + + /** The value {@code "CONTAINS"}. */ + public static final DateOfBirth CONTAINS = new DateOfBirth("CONTAINS"); + + /** The value {@code "DEFAULT"}. */ + public static final DateOfBirth DEFAULT = new DateOfBirth("DEFAULT"); + + /** The value {@code "FUZZY"}. */ + public static final DateOfBirth FUZZY = new DateOfBirth("FUZZY"); + + private static final List KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); + + private DateOfBirth(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DateOfBirth of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DateOfBirth::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Default NONE. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Address extends StringEnum { + /** The value {@code "NONE"}. */ + public static final Address NONE = new Address("NONE"); + + /** The value {@code "STRICT"}. */ + public static final Address STRICT = new Address("STRICT"); + + /** The value {@code "CONTAINS"}. */ + public static final Address CONTAINS = new Address("CONTAINS"); + + /** The value {@code "DEFAULT"}. */ + public static final Address DEFAULT = new Address("DEFAULT"); + + /** The value {@code "FUZZY"}. */ + public static final Address FUZZY = new Address("FUZZY"); + + private static final List

KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); + + private Address(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Address of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Address::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List
knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The wallet the exchange was routed by. Default STRICT. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Account extends StringEnum { + /** The value {@code "NONE"}. */ + public static final Account NONE = new Account("NONE"); + + /** The value {@code "STRICT"}. */ + public static final Account STRICT = new Account("STRICT"); + + /** The value {@code "CONTAINS"}. */ + public static final Account CONTAINS = new Account("CONTAINS"); + + /** The value {@code "DEFAULT"}. */ + public static final Account DEFAULT = new Account("DEFAULT"); + + /** The value {@code "FUZZY"}. */ + public static final Account FUZZY = new Account("FUZZY"); + + private static final List KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); + + private Account(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Account of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Account::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** Rules for a legal person. */ + public static final class Company extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "address", "account"); + + private Company(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Company} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Company fromJson(JsonNode json) { + return new Company(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Default: follows name_matching_strictness. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.enumeration(TravelRuleMatchingRules.Company.Name::of)); + } + + /** + * Default NONE. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional address() { + return Json.optional(json, "address", Conv.enumeration(TravelRuleMatchingRules.Company.Address::of)); + } + + /** + * The wallet the exchange was routed by. Default STRICT. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional account() { + return Json.optional(json, "account", Conv.enumeration(TravelRuleMatchingRules.Company.Account::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Company}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Company from) { + super(from); + } + + /** + * Default: follows name_matching_strictness. + * + * @param name the value + * @return this builder + */ + public Builder name(TravelRuleMatchingRules.Company.Name name) { + return set("name", Conv.enumeration(TravelRuleMatchingRules.Company.Name::of), name); + } + + /** + * Default NONE. + * + * @param address the value + * @return this builder + */ + public Builder address(TravelRuleMatchingRules.Company.Address address) { + return set("address", Conv.enumeration(TravelRuleMatchingRules.Company.Address::of), address); + } + + /** + * The wallet the exchange was routed by. Default STRICT. + * + * @param account the value + * @return this builder + */ + public Builder account(TravelRuleMatchingRules.Company.Account account) { + return set("account", Conv.enumeration(TravelRuleMatchingRules.Company.Account::of), account); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Company} + */ + public Company build() { + return new Company(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Default: follows name_matching_strictness. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Name extends StringEnum { + /** The value {@code "NONE"}. */ + public static final Name NONE = new Name("NONE"); + + /** The value {@code "STRICT"}. */ + public static final Name STRICT = new Name("STRICT"); + + /** The value {@code "CONTAINS"}. */ + public static final Name CONTAINS = new Name("CONTAINS"); + + /** The value {@code "DEFAULT"}. */ + public static final Name DEFAULT = new Name("DEFAULT"); + + /** The value {@code "FUZZY"}. */ + public static final Name FUZZY = new Name("FUZZY"); + + private static final List KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); + + private Name(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Name of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Name::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Default NONE. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Address extends StringEnum { + /** The value {@code "NONE"}. */ + public static final Address NONE = new Address("NONE"); + + /** The value {@code "STRICT"}. */ + public static final Address STRICT = new Address("STRICT"); + + /** The value {@code "CONTAINS"}. */ + public static final Address CONTAINS = new Address("CONTAINS"); + + /** The value {@code "DEFAULT"}. */ + public static final Address DEFAULT = new Address("DEFAULT"); + + /** The value {@code "FUZZY"}. */ + public static final Address FUZZY = new Address("FUZZY"); + + private static final List

KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); + + private Address(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Address of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Address::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List
knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The wallet the exchange was routed by. Default STRICT. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Account extends StringEnum { + /** The value {@code "NONE"}. */ + public static final Account NONE = new Account("NONE"); + + /** The value {@code "STRICT"}. */ + public static final Account STRICT = new Account("STRICT"); + + /** The value {@code "CONTAINS"}. */ + public static final Account CONTAINS = new Account("CONTAINS"); + + /** The value {@code "DEFAULT"}. */ + public static final Account DEFAULT = new Account("DEFAULT"); + + /** The value {@code "FUZZY"}. */ + public static final Account FUZZY = new Account("FUZZY"); + + private static final List KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); + + private Account(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Account of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Account::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleMessage.java b/src/main/java/me/didit/types/TravelRuleMessage.java new file mode 100644 index 0000000..2852120 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleMessage.java @@ -0,0 +1,366 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +public final class TravelRuleMessage extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "kind", "direction", "author", "body", "requested_documents", "reply_to", "fulfilled_at", "attachment", "created_at"); + + private TravelRuleMessage(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleMessage} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleMessage fromJson(JsonNode json) { + return new TravelRuleMessage(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The kind field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(TravelRuleMessage.Kind::of)); + } + + /** + * OUTBOUND is what the reader's own side sent. In the pickup portal the direction is from the counterparty's side. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional direction() { + return Json.optional(json, "direction", Conv.enumeration(TravelRuleMessage.Direction::of)); + } + + /** + * Who wrote it on its own side: a console user's email or an API key label, or the other VASP's name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional author() { + return Json.optional(json, "author", Conv.STRING); + } + + /** + * The body field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional body() { + return Json.optional(json, "body", Conv.STRING); + } + + /** + * What a DOCUMENT_REQUEST asks for, in the requester's words. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> requestedDocuments() { + return Json.optional(json, "requested_documents", Conv.list(Conv.STRING)); + } + + /** + * The reply_to field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional replyTo() { + return Json.optional(json, "reply_to", Conv.STRING); + } + + /** + * Set on a DOCUMENT_REQUEST once a reply to it carries a document. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fulfilledAt() { + return Json.optional(json, "fulfilled_at", Conv.DATE_TIME); + } + + /** + * The document attached to a message, stored like any other transaction document. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional attachment() { + return Json.optional(json, "attachment", Conv.model(TravelRuleMessageAttachment::fromJson)); + } + + /** + * The created_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleMessage}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleMessage from) { + super(from); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The kind field. + * + * @param kind the value + * @return this builder + */ + public Builder kind(TravelRuleMessage.Kind kind) { + return set("kind", Conv.enumeration(TravelRuleMessage.Kind::of), kind); + } + + /** + * OUTBOUND is what the reader's own side sent. In the pickup portal the direction is from the counterparty's side. + * + * @param direction the value + * @return this builder + */ + public Builder direction(TravelRuleMessage.Direction direction) { + return set("direction", Conv.enumeration(TravelRuleMessage.Direction::of), direction); + } + + /** + * Who wrote it on its own side: a console user's email or an API key label, or the other VASP's name. + * + * @param author the value + * @return this builder + */ + public Builder author(String author) { + return set("author", Conv.STRING, author); + } + + /** + * The body field. + * + * @param body the value + * @return this builder + */ + public Builder body(String body) { + return set("body", Conv.STRING, body); + } + + /** + * What a DOCUMENT_REQUEST asks for, in the requester's words. + * + * @param requestedDocuments the value + * @return this builder + */ + public Builder requestedDocuments(List requestedDocuments) { + return set("requested_documents", Conv.list(Conv.STRING), requestedDocuments); + } + + /** + * The reply_to field. + * + * @param replyTo the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder replyTo(@Nullable String replyTo) { + return setNullable("reply_to", Conv.STRING, replyTo); + } + + /** + * Set on a DOCUMENT_REQUEST once a reply to it carries a document. + * + * @param fulfilledAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fulfilledAt(@Nullable OffsetDateTime fulfilledAt) { + return setNullable("fulfilled_at", Conv.DATE_TIME, fulfilledAt); + } + + /** + * The document attached to a message, stored like any other transaction document. + * + * @param attachment the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder attachment(@Nullable TravelRuleMessageAttachment attachment) { + return setNullable("attachment", Conv.model(TravelRuleMessageAttachment::fromJson), attachment); + } + + /** + * The created_at field. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleMessage} + */ + public TravelRuleMessage build() { + return new TravelRuleMessage(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "MESSAGE"}. */ + public static final Kind MESSAGE = new Kind("MESSAGE"); + + /** The value {@code "DOCUMENT_REQUEST"}. */ + public static final Kind DOCUMENT_REQUEST = new Kind("DOCUMENT_REQUEST"); + + private static final List KNOWN_VALUES = List.of(MESSAGE, DOCUMENT_REQUEST); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * OUTBOUND is what the reader's own side sent. In the pickup portal the direction is from the counterparty's side. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Direction extends StringEnum { + /** The value {@code "INBOUND"}. */ + public static final Direction INBOUND = new Direction("INBOUND"); + + /** The value {@code "OUTBOUND"}. */ + public static final Direction OUTBOUND = new Direction("OUTBOUND"); + + private static final List KNOWN_VALUES = List.of(INBOUND, OUTBOUND); + + private Direction(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Direction of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Direction::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleMessageAttachment.java b/src/main/java/me/didit/types/TravelRuleMessageAttachment.java new file mode 100644 index 0000000..c774a57 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleMessageAttachment.java @@ -0,0 +1,346 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** The document attached to a message, stored like any other transaction document. */ +public final class TravelRuleMessageAttachment extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "note", "uploaded_by", "created_at", "files"); + + private TravelRuleMessageAttachment(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleMessageAttachment} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleMessageAttachment fromJson(JsonNode json) { + return new TravelRuleMessageAttachment(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * The label field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional label() { + return Json.optional(json, "label", Conv.STRING); + } + + /** + * The note field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional note() { + return Json.optional(json, "note", Conv.STRING); + } + + /** + * Who attached it. In the pickup portal, a file you sent shows your VASP name, never the person or API key behind it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uploadedBy() { + return Json.optional(json, "uploaded_by", Conv.STRING); + } + + /** + * The created_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.DATE_TIME); + } + + /** + * The files field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> files() { + return Json.optional(json, "files", Conv.list(Conv.model(TravelRuleMessageAttachment.Files::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleMessageAttachment}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleMessageAttachment from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * The label field. + * + * @param label the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder label(@Nullable String label) { + return setNullable("label", Conv.STRING, label); + } + + /** + * The note field. + * + * @param note the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder note(@Nullable String note) { + return setNullable("note", Conv.STRING, note); + } + + /** + * Who attached it. In the pickup portal, a file you sent shows your VASP name, never the person or API key behind it. + * + * @param uploadedBy the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder uploadedBy(@Nullable String uploadedBy) { + return setNullable("uploaded_by", Conv.STRING, uploadedBy); + } + + /** + * The created_at field. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * The files field. + * + * @param files the value + * @return this builder + */ + public Builder files(List files) { + return set("files", Conv.list(Conv.model(TravelRuleMessageAttachment.Files::fromJson)), files); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleMessageAttachment} + */ + public TravelRuleMessageAttachment build() { + return new TravelRuleMessageAttachment(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Files extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "file_url", "original_filename", "file_size"); + + private Files(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Files} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Files fromJson(JsonNode json) { + return new Files(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The uuid field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * Time-limited download URL. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fileUrl() { + return Json.optional(json, "file_url", Conv.STRING); + } + + /** + * The original_filename field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originalFilename() { + return Json.optional(json, "original_filename", Conv.STRING); + } + + /** + * The file_size field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fileSize() { + return Json.optional(json, "file_size", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Files}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Files from) { + super(from); + } + + /** + * The uuid field. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Time-limited download URL. + * + * @param fileUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fileUrl(@Nullable String fileUrl) { + return setNullable("file_url", Conv.STRING, fileUrl); + } + + /** + * The original_filename field. + * + * @param originalFilename the value + * @return this builder + */ + public Builder originalFilename(String originalFilename) { + return set("original_filename", Conv.STRING, originalFilename); + } + + /** + * The file_size field. + * + * @param fileSize the value + * @return this builder + */ + public Builder fileSize(long fileSize) { + return set("file_size", Conv.LONG, fileSize); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Files} + */ + public Files build() { + return new Files(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleMessageCreateRequest.java b/src/main/java/me/didit/types/TravelRuleMessageCreateRequest.java new file mode 100644 index 0000000..e2c28a6 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleMessageCreateRequest.java @@ -0,0 +1,223 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** Send as JSON, or as multipart/form-data with the attachments in files. */ +public final class TravelRuleMessageCreateRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("kind", "body", "requested_documents", "reply_to", "files"); + + private TravelRuleMessageCreateRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleMessageCreateRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleMessageCreateRequest fromJson(JsonNode json) { + return new TravelRuleMessageCreateRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The kind field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(TravelRuleMessageCreateRequest.Kind::of)); + } + + /** + * Required for a MESSAGE unless a file is attached. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional body() { + return Json.optional(json, "body", Conv.STRING); + } + + /** + * Required (at least one) for a DOCUMENT_REQUEST, and only allowed there. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> requestedDocuments() { + return Json.optional(json, "requested_documents", Conv.list(Conv.STRING)); + } + + /** + * A message of the same transfer. A reply that carries a document fulfils the document request it answers. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional replyTo() { + return Json.optional(json, "reply_to", Conv.STRING); + } + + /** + * Multipart only. Up to 5 files of 10 MB each. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> files() { + return Json.optional(json, "files", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleMessageCreateRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleMessageCreateRequest from) { + super(from); + } + + /** + * The kind field. + * + * @param kind the value + * @return this builder + */ + public Builder kind(TravelRuleMessageCreateRequest.Kind kind) { + return set("kind", Conv.enumeration(TravelRuleMessageCreateRequest.Kind::of), kind); + } + + /** + * Required for a MESSAGE unless a file is attached. + * + * @param body the value + * @return this builder + */ + public Builder body(String body) { + return set("body", Conv.STRING, body); + } + + /** + * Required (at least one) for a DOCUMENT_REQUEST, and only allowed there. + * + * @param requestedDocuments the value + * @return this builder + */ + public Builder requestedDocuments(List requestedDocuments) { + return set("requested_documents", Conv.list(Conv.STRING), requestedDocuments); + } + + /** + * A message of the same transfer. A reply that carries a document fulfils the document request it answers. + * + * @param replyTo the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder replyTo(@Nullable String replyTo) { + return setNullable("reply_to", Conv.STRING, replyTo); + } + + /** + * Multipart only. Up to 5 files of 10 MB each. + * + * @param files the value + * @return this builder + */ + public Builder files(List files) { + return set("files", Conv.list(Conv.STRING), files); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleMessageCreateRequest} + */ + public TravelRuleMessageCreateRequest build() { + return new TravelRuleMessageCreateRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "MESSAGE"}. */ + public static final Kind MESSAGE = new Kind("MESSAGE"); + + /** The value {@code "DOCUMENT_REQUEST"}. */ + public static final Kind DOCUMENT_REQUEST = new Kind("DOCUMENT_REQUEST"); + + private static final List KNOWN_VALUES = List.of(MESSAGE, DOCUMENT_REQUEST); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleNameMatch.java b/src/main/java/me/didit/types/TravelRuleNameMatch.java new file mode 100644 index 0000000..841f704 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleNameMatch.java @@ -0,0 +1,408 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * The name comparison that decided the transfer, or null until one ran. While the parties are masked, and on the other customer's copy of an INTERNAL exchange, transfer_name, compared_name, matched_names and mismatched_names are hidden. score and matched can disagree: matched follows the strictness, score is the similarity of the two full names. + */ +public final class TravelRuleNameMatch extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("score", "matched", "method", "strictness", "compared_with", "transfer_name", "compared_name", "matched_names", "mismatched_names"); + + private TravelRuleNameMatch(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleNameMatch} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleNameMatch fromJson(JsonNode json) { + return new TravelRuleNameMatch(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Similarity of the two full names, 0 to 100. 0 when either name is blank. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.LONG); + } + + /** + * Whether the names satisfy strictness. The transfer's outcome follows data_match, which compares every field, so a transfer can be COUNTERPARTY_MISMATCHED_DATA while this is true. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matched() { + return Json.optional(json, "matched", Conv.BOOLEAN); + } + + /** + * How matched was decided: none (NONE), exact_token (STRICT), token_subset (DEFAULT), token_containment (CONTAINS) or sequence_ratio (FUZZY). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional method() { + return Json.optional(json, "method", Conv.enumeration(TravelRuleNameMatch.Method::of)); + } + + /** + * The strictness field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional strictness() { + return Json.optional(json, "strictness", Conv.enumeration(TravelRuleNameMatch.Strictness::of)); + } + + /** + * wallet_holder: the holder recorded for the wallet. counterparty_vasp: the name the counterparty answered on the email response page, or the Didit Test VASP's answer in sandbox. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional comparedWith() { + return Json.optional(json, "compared_with", Conv.enumeration(TravelRuleNameMatch.ComparedWith::of)); + } + + /** + * The name on the transfer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transferName() { + return Json.optional(json, "transfer_name", Conv.STRING); + } + + /** + * The name it was compared with. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional comparedName() { + return Json.optional(json, "compared_name", Conv.STRING); + } + + /** + * Words of transfer_name found in compared_name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> matchedNames() { + return Json.optional(json, "matched_names", Conv.list(Conv.STRING)); + } + + /** + * Words of transfer_name missing from compared_name, then words of compared_name missing from transfer_name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> mismatchedNames() { + return Json.optional(json, "mismatched_names", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleNameMatch}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleNameMatch from) { + super(from); + } + + /** + * Similarity of the two full names, 0 to 100. 0 when either name is blank. + * + * @param score the value + * @return this builder + */ + public Builder score(long score) { + return set("score", Conv.LONG, score); + } + + /** + * Whether the names satisfy strictness. The transfer's outcome follows data_match, which compares every field, so a transfer can be COUNTERPARTY_MISMATCHED_DATA while this is true. + * + * @param matched the value + * @return this builder + */ + public Builder matched(boolean matched) { + return set("matched", Conv.BOOLEAN, matched); + } + + /** + * How matched was decided: none (NONE), exact_token (STRICT), token_subset (DEFAULT), token_containment (CONTAINS) or sequence_ratio (FUZZY). + * + * @param method the value + * @return this builder + */ + public Builder method(TravelRuleNameMatch.Method method) { + return set("method", Conv.enumeration(TravelRuleNameMatch.Method::of), method); + } + + /** + * The strictness field. + * + * @param strictness the value + * @return this builder + */ + public Builder strictness(TravelRuleNameMatch.Strictness strictness) { + return set("strictness", Conv.enumeration(TravelRuleNameMatch.Strictness::of), strictness); + } + + /** + * wallet_holder: the holder recorded for the wallet. counterparty_vasp: the name the counterparty answered on the email response page, or the Didit Test VASP's answer in sandbox. + * + * @param comparedWith the value + * @return this builder + */ + public Builder comparedWith(TravelRuleNameMatch.ComparedWith comparedWith) { + return set("compared_with", Conv.enumeration(TravelRuleNameMatch.ComparedWith::of), comparedWith); + } + + /** + * The name on the transfer. + * + * @param transferName the value + * @return this builder + */ + public Builder transferName(String transferName) { + return set("transfer_name", Conv.STRING, transferName); + } + + /** + * The name it was compared with. + * + * @param comparedName the value + * @return this builder + */ + public Builder comparedName(String comparedName) { + return set("compared_name", Conv.STRING, comparedName); + } + + /** + * Words of transfer_name found in compared_name. + * + * @param matchedNames the value + * @return this builder + */ + public Builder matchedNames(List matchedNames) { + return set("matched_names", Conv.list(Conv.STRING), matchedNames); + } + + /** + * Words of transfer_name missing from compared_name, then words of compared_name missing from transfer_name. + * + * @param mismatchedNames the value + * @return this builder + */ + public Builder mismatchedNames(List mismatchedNames) { + return set("mismatched_names", Conv.list(Conv.STRING), mismatchedNames); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleNameMatch} + */ + public TravelRuleNameMatch build() { + return new TravelRuleNameMatch(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * How matched was decided: none (NONE), exact_token (STRICT), token_subset (DEFAULT), token_containment (CONTAINS) or sequence_ratio (FUZZY). + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Method extends StringEnum { + /** The value {@code "none"}. */ + public static final Method NONE = new Method("none"); + + /** The value {@code "exact_token"}. */ + public static final Method EXACT_TOKEN = new Method("exact_token"); + + /** The value {@code "token_subset"}. */ + public static final Method TOKEN_SUBSET = new Method("token_subset"); + + /** The value {@code "token_containment"}. */ + public static final Method TOKEN_CONTAINMENT = new Method("token_containment"); + + /** The value {@code "sequence_ratio"}. */ + public static final Method SEQUENCE_RATIO = new Method("sequence_ratio"); + + private static final List KNOWN_VALUES = List.of(NONE, EXACT_TOKEN, TOKEN_SUBSET, TOKEN_CONTAINMENT, SEQUENCE_RATIO); + + private Method(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Method of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Method::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Strictness extends StringEnum { + /** The value {@code "NONE"}. */ + public static final Strictness NONE = new Strictness("NONE"); + + /** The value {@code "STRICT"}. */ + public static final Strictness STRICT = new Strictness("STRICT"); + + /** The value {@code "CONTAINS"}. */ + public static final Strictness CONTAINS = new Strictness("CONTAINS"); + + /** The value {@code "DEFAULT"}. */ + public static final Strictness DEFAULT = new Strictness("DEFAULT"); + + /** The value {@code "FUZZY"}. */ + public static final Strictness FUZZY = new Strictness("FUZZY"); + + private static final List KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); + + private Strictness(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Strictness of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Strictness::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * wallet_holder: the holder recorded for the wallet. counterparty_vasp: the name the counterparty answered on the email response page, or the Didit Test VASP's answer in sandbox. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ComparedWith extends StringEnum { + /** The value {@code "wallet_holder"}. */ + public static final ComparedWith WALLET_HOLDER = new ComparedWith("wallet_holder"); + + /** The value {@code "counterparty_vasp"}. */ + public static final ComparedWith COUNTERPARTY_VASP = new ComparedWith("counterparty_vasp"); + + private static final List KNOWN_VALUES = List.of(WALLET_HOLDER, COUNTERPARTY_VASP); + + private ComparedWith(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ComparedWith of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ComparedWith::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleNextAction.java b/src/main/java/me/didit/types/TravelRuleNextAction.java new file mode 100644 index 0000000..ac85953 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleNextAction.java @@ -0,0 +1,176 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * One thing that moves a COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE transfer on. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck). + */ +public final class TravelRuleNextAction extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("action", "url"); + + private TravelRuleNextAction(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleNextAction} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleNextAction fromJson(JsonNode json) { + return new TravelRuleNextAction(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * select_counterparty_vasp: the end user picks the destination VASP in the wallet-ownership widget. declare_self_hosted: the end user declares the wallet self-hosted in the widget. add_vasp_hint: tell Didit who the counterparty is, with a VASP name, LEI or travel address (for example a travel_address on the destination wallet's address-book entry), then resend. update_compliance_email and resend are action values of PATCH /v3/transactions/{transaction_id}/travel-rule/: give the counterparty's compliance email again, and re-run routing. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TravelRuleNextAction.Action action() { + return Json.required(json, "action", Conv.enumeration(TravelRuleNextAction.Action::of)); + } + + /** + * The wallet-ownership widget link, on select_counterparty_vasp and declare_self_hosted while the transfer has an open widget session. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional url() { + return Json.optional(json, "url", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleNextAction}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleNextAction from) { + super(from); + } + + /** + * select_counterparty_vasp: the end user picks the destination VASP in the wallet-ownership widget. declare_self_hosted: the end user declares the wallet self-hosted in the widget. add_vasp_hint: tell Didit who the counterparty is, with a VASP name, LEI or travel address (for example a travel_address on the destination wallet's address-book entry), then resend. update_compliance_email and resend are action values of PATCH /v3/transactions/{transaction_id}/travel-rule/: give the counterparty's compliance email again, and re-run routing. + * + * @param action the value + * @return this builder + */ + public Builder action(TravelRuleNextAction.Action action) { + return set("action", Conv.enumeration(TravelRuleNextAction.Action::of), action); + } + + /** + * The wallet-ownership widget link, on select_counterparty_vasp and declare_self_hosted while the transfer has an open widget session. + * + * @param url the value + * @return this builder + */ + public Builder url(String url) { + return set("url", Conv.STRING, url); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleNextAction} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleNextAction build() { + require("action", false); + return new TravelRuleNextAction(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * select_counterparty_vasp: the end user picks the destination VASP in the wallet-ownership widget. declare_self_hosted: the end user declares the wallet self-hosted in the widget. add_vasp_hint: tell Didit who the counterparty is, with a VASP name, LEI or travel address (for example a travel_address on the destination wallet's address-book entry), then resend. update_compliance_email and resend are action values of PATCH /v3/transactions/{transaction_id}/travel-rule/: give the counterparty's compliance email again, and re-run routing. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Action extends StringEnum { + /** The value {@code "select_counterparty_vasp"}. */ + public static final Action SELECT_COUNTERPARTY_VASP = new Action("select_counterparty_vasp"); + + /** The value {@code "declare_self_hosted"}. */ + public static final Action DECLARE_SELF_HOSTED = new Action("declare_self_hosted"); + + /** The value {@code "add_vasp_hint"}. */ + public static final Action ADD_VASP_HINT = new Action("add_vasp_hint"); + + /** The value {@code "update_compliance_email"}. */ + public static final Action UPDATE_COMPLIANCE_EMAIL = new Action("update_compliance_email"); + + /** The value {@code "resend"}. */ + public static final Action RESEND = new Action("resend"); + + private static final List KNOWN_VALUES = List.of(SELECT_COUNTERPARTY_VASP, DECLARE_SELF_HOSTED, ADD_VASP_HINT, UPDATE_COMPLIANCE_EMAIL, RESEND); + + private Action(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Action of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Action::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleOwnershipConfirmRequest.java b/src/main/java/me/didit/types/TravelRuleOwnershipConfirmRequest.java index 3f193f1..86c2c2f 100644 --- a/src/main/java/me/didit/types/TravelRuleOwnershipConfirmRequest.java +++ b/src/main/java/me/didit/types/TravelRuleOwnershipConfirmRequest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import me.didit.core.Conv; import me.didit.core.FileUpload; @@ -12,7 +13,7 @@ import me.didit.core.JsonModel; public final class TravelRuleOwnershipConfirmRequest extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("confirmed"); + private static final Set KNOWN_FIELDS = Set.of("confirmed", "wallet_address"); private TravelRuleOwnershipConfirmRequest(ObjectNode json, Map> files) { super(json, files); @@ -48,7 +49,7 @@ public Builder toBuilder() { } /** - * The confirmed field. + * true to confirm ownership, false to deny it. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -57,6 +58,16 @@ public boolean confirmed() { return Json.required(json, "confirmed", Conv.BOOLEAN); } + /** + * Inbound TRP transfers: the payment address Didit returns to the originating VASP when you approve. Defaults to the matched address book entry's address, then beneficiary_data.wallet_address. On a declared self-hosted wallet transfer that names no counterparty wallet, the wallet whose ownership you verified; without it the request is rejected with 400. Ignored for other transfers. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional walletAddress() { + return Json.optional(json, "wallet_address", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -71,7 +82,7 @@ private Builder(TravelRuleOwnershipConfirmRequest from) { } /** - * The confirmed field. + * true to confirm ownership, false to deny it. * * @param confirmed the value * @return this builder @@ -80,6 +91,16 @@ public Builder confirmed(boolean confirmed) { return set("confirmed", Conv.BOOLEAN, confirmed); } + /** + * Inbound TRP transfers: the payment address Didit returns to the originating VASP when you approve. Defaults to the matched address book entry's address, then beneficiary_data.wallet_address. On a declared self-hosted wallet transfer that names no counterparty wallet, the wallet whose ownership you verified; without it the request is rejected with 400. Ignored for other transfers. + * + * @param walletAddress the value + * @return this builder + */ + public Builder walletAddress(String walletAddress) { + return set("wallet_address", Conv.STRING, walletAddress); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/TravelRulePickupInfo.java b/src/main/java/me/didit/types/TravelRulePickupInfo.java index c874db6..ac0ae50 100644 --- a/src/main/java/me/didit/types/TravelRulePickupInfo.java +++ b/src/main/java/me/didit/types/TravelRulePickupInfo.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; import java.util.List; import java.util.Map; import java.util.Optional; @@ -11,9 +12,13 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +/** + * The pending exchange behind an emailed pickup link, as the counterparty's response page shows it. + */ public final class TravelRulePickupInfo extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("originator_vasp_name", "amount", "currency", "beneficiary_wallet", "status"); + private static final Set KNOWN_FIELDS = Set.of("reference", "requested_at", "originator_vasp_name", "originator_vasp_lei", "recipient_vasp_name", "language", "amount", "currency", "fiat_amount", "fiat_currency", "chain", "beneficiary_wallet", "status", "ivms_version", "deadline_at", "reminders_sent", "contact", "decline_reasons", "join_invitation", "originator_data", "beneficiary_data", "ivms_request", "data_masked"); private TravelRulePickupInfo(ObjectNode json, Map> files) { super(json, files); @@ -48,6 +53,26 @@ public Builder toBuilder() { return new Builder(this); } + /** + * The transfer's uuid, as the sending VASP's console shows it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reference() { + return Json.optional(json, "reference", Conv.STRING); + } + + /** + * The requested_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional requestedAt() { + return Json.optional(json, "requested_at", Conv.DATE_TIME); + } + /** * The originator_vasp_name field. * @@ -58,6 +83,36 @@ public Optional originatorVaspName() { return Json.optional(json, "originator_vasp_name", Conv.STRING); } + /** + * The originator_vasp_lei field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originatorVaspLei() { + return Json.optional(json, "originator_vasp_lei", Conv.STRING); + } + + /** + * The counterparty VASP the request is addressed to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional recipientVaspName() { + return Json.optional(json, "recipient_vasp_name", Conv.STRING); + } + + /** + * The language of the request email and the response page, from the counterparty's jurisdiction. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional language() { + return Json.optional(json, "language", Conv.STRING); + } + /** * The amount field. * @@ -78,6 +133,36 @@ public Optional currency() { return Json.optional(json, "currency", Conv.STRING); } + /** + * The converted value the sender gave, or empty. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fiatAmount() { + return Json.optional(json, "fiat_amount", Conv.STRING); + } + + /** + * The fiat_currency field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fiatCurrency() { + return Json.optional(json, "fiat_currency", Conv.STRING); + } + + /** + * The chain field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional chain() { + return Json.optional(json, "chain", Conv.STRING); + } + /** * The beneficiary_wallet field. * @@ -98,6 +183,106 @@ public Optional status() { return Json.optional(json, "status", Conv.STRING); } + /** + * The ivms_version field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ivmsVersion() { + return Json.optional(json, "ivms_version", Conv.STRING); + } + + /** + * When the link stops answering. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deadlineAt() { + return Json.optional(json, "deadline_at", Conv.DATE_TIME); + } + + /** + * The reminders_sent field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional remindersSent() { + return Json.optional(json, "reminders_sent", Conv.LONG); + } + + /** + * The ?contact= value of the link, echoed back; empty when the link has none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional contact() { + return Json.optional(json, "contact", Conv.STRING); + } + + /** + * The values decline_reason accepts. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> declineReasons() { + return Json.optional(json, "decline_reasons", Conv.list(Conv.enumeration(TravelRulePickupInfo.DeclineReasons::of))); + } + + /** + * Whether the counterparty holds a live invitation to join Didit for free. The invitation link travels only in the email to the invited address, so url is empty here. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional joinInvitation() { + return Json.optional(json, "join_invitation", Conv.model(TravelRulePickupInfo.JoinInvitation::fromJson)); + } + + /** + * The originator fields the sending VASP shares (data_sharing_fields). The name and the wallet are always included. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> originatorData() { + return Json.optional(json, "originator_data", Conv.OBJECT); + } + + /** + * The beneficiary_data field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> beneficiaryData() { + return Json.optional(json, "beneficiary_data", Conv.OBJECT); + } + + /** + * The same request as an IVMS 101 object. Left out while data_masked is true. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> ivmsRequest() { + return Json.optional(json, "ivms_request", Conv.OBJECT); + } + + /** + * Always false on this endpoint: an email request is outbound, and the sender's own user is never masked. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataMasked() { + return Json.optional(json, "data_masked", Conv.BOOLEAN); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -111,6 +296,26 @@ private Builder(TravelRulePickupInfo from) { super(from); } + /** + * The transfer's uuid, as the sending VASP's console shows it. + * + * @param reference the value + * @return this builder + */ + public Builder reference(String reference) { + return set("reference", Conv.STRING, reference); + } + + /** + * The requested_at field. + * + * @param requestedAt the value + * @return this builder + */ + public Builder requestedAt(OffsetDateTime requestedAt) { + return set("requested_at", Conv.DATE_TIME, requestedAt); + } + /** * The originator_vasp_name field. * @@ -121,6 +326,36 @@ public Builder originatorVaspName(String originatorVaspName) { return set("originator_vasp_name", Conv.STRING, originatorVaspName); } + /** + * The originator_vasp_lei field. + * + * @param originatorVaspLei the value + * @return this builder + */ + public Builder originatorVaspLei(String originatorVaspLei) { + return set("originator_vasp_lei", Conv.STRING, originatorVaspLei); + } + + /** + * The counterparty VASP the request is addressed to. + * + * @param recipientVaspName the value + * @return this builder + */ + public Builder recipientVaspName(String recipientVaspName) { + return set("recipient_vasp_name", Conv.STRING, recipientVaspName); + } + + /** + * The language of the request email and the response page, from the counterparty's jurisdiction. + * + * @param language the value + * @return this builder + */ + public Builder language(String language) { + return set("language", Conv.STRING, language); + } + /** * The amount field. * @@ -141,6 +376,36 @@ public Builder currency(String currency) { return set("currency", Conv.STRING, currency); } + /** + * The converted value the sender gave, or empty. + * + * @param fiatAmount the value + * @return this builder + */ + public Builder fiatAmount(String fiatAmount) { + return set("fiat_amount", Conv.STRING, fiatAmount); + } + + /** + * The fiat_currency field. + * + * @param fiatCurrency the value + * @return this builder + */ + public Builder fiatCurrency(String fiatCurrency) { + return set("fiat_currency", Conv.STRING, fiatCurrency); + } + + /** + * The chain field. + * + * @param chain the value + * @return this builder + */ + public Builder chain(String chain) { + return set("chain", Conv.STRING, chain); + } + /** * The beneficiary_wallet field. * @@ -161,6 +426,106 @@ public Builder status(String status) { return set("status", Conv.STRING, status); } + /** + * The ivms_version field. + * + * @param ivmsVersion the value + * @return this builder + */ + public Builder ivmsVersion(String ivmsVersion) { + return set("ivms_version", Conv.STRING, ivmsVersion); + } + + /** + * When the link stops answering. + * + * @param deadlineAt the value + * @return this builder + */ + public Builder deadlineAt(OffsetDateTime deadlineAt) { + return set("deadline_at", Conv.DATE_TIME, deadlineAt); + } + + /** + * The reminders_sent field. + * + * @param remindersSent the value + * @return this builder + */ + public Builder remindersSent(long remindersSent) { + return set("reminders_sent", Conv.LONG, remindersSent); + } + + /** + * The ?contact= value of the link, echoed back; empty when the link has none. + * + * @param contact the value + * @return this builder + */ + public Builder contact(String contact) { + return set("contact", Conv.STRING, contact); + } + + /** + * The values decline_reason accepts. + * + * @param declineReasons the value + * @return this builder + */ + public Builder declineReasons(List declineReasons) { + return set("decline_reasons", Conv.list(Conv.enumeration(TravelRulePickupInfo.DeclineReasons::of)), declineReasons); + } + + /** + * Whether the counterparty holds a live invitation to join Didit for free. The invitation link travels only in the email to the invited address, so url is empty here. + * + * @param joinInvitation the value + * @return this builder + */ + public Builder joinInvitation(TravelRulePickupInfo.JoinInvitation joinInvitation) { + return set("join_invitation", Conv.model(TravelRulePickupInfo.JoinInvitation::fromJson), joinInvitation); + } + + /** + * The originator fields the sending VASP shares (data_sharing_fields). The name and the wallet are always included. + * + * @param originatorData the value + * @return this builder + */ + public Builder originatorData(Map originatorData) { + return setAny("originator_data", originatorData); + } + + /** + * The beneficiary_data field. + * + * @param beneficiaryData the value + * @return this builder + */ + public Builder beneficiaryData(Map beneficiaryData) { + return setAny("beneficiary_data", beneficiaryData); + } + + /** + * The same request as an IVMS 101 object. Left out while data_masked is true. + * + * @param ivmsRequest the value + * @return this builder + */ + public Builder ivmsRequest(Map ivmsRequest) { + return setAny("ivms_request", ivmsRequest); + } + + /** + * Always false on this endpoint: an email request is outbound, and the sender's own user is never masked. + * + * @param dataMasked the value + * @return this builder + */ + public Builder dataMasked(boolean dataMasked) { + return set("data_masked", Conv.BOOLEAN, dataMasked); + } + /** * Builds the value. * @@ -175,4 +540,172 @@ protected Builder self() { return this; } } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DeclineReasons extends StringEnum { + /** The value {@code "data_mismatch"}. */ + public static final DeclineReasons DATA_MISMATCH = new DeclineReasons("data_mismatch"); + + /** The value {@code "not_our_customer"}. */ + public static final DeclineReasons NOT_OUR_CUSTOMER = new DeclineReasons("not_our_customer"); + + /** The value {@code "wallet_not_hosted"}. */ + public static final DeclineReasons WALLET_NOT_HOSTED = new DeclineReasons("wallet_not_hosted"); + + /** The value {@code "sanctions"}. */ + public static final DeclineReasons SANCTIONS = new DeclineReasons("sanctions"); + + /** The value {@code "risk_appetite"}. */ + public static final DeclineReasons RISK_APPETITE = new DeclineReasons("risk_appetite"); + + /** The value {@code "jurisdiction"}. */ + public static final DeclineReasons JURISDICTION = new DeclineReasons("jurisdiction"); + + /** The value {@code "insufficient_data"}. */ + public static final DeclineReasons INSUFFICIENT_DATA = new DeclineReasons("insufficient_data"); + + /** The value {@code "other"}. */ + public static final DeclineReasons OTHER = new DeclineReasons("other"); + + private static final List KNOWN_VALUES = List.of(DATA_MISMATCH, NOT_OUR_CUSTOMER, WALLET_NOT_HOSTED, SANCTIONS, RISK_APPETITE, JURISDICTION, INSUFFICIENT_DATA, OTHER); + + private DeclineReasons(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DeclineReasons of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DeclineReasons::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Whether the counterparty holds a live invitation to join Didit for free. The invitation link travels only in the email to the invited address, so url is empty here. + */ + public static final class JoinInvitation extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("eligible", "url"); + + private JoinInvitation(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code JoinInvitation} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static JoinInvitation fromJson(JsonNode json) { + return new JoinInvitation(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The eligible field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional eligible() { + return Json.optional(json, "eligible", Conv.BOOLEAN); + } + + /** + * The url field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional url() { + return Json.optional(json, "url", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link JoinInvitation}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(JoinInvitation from) { + super(from); + } + + /** + * The eligible field. + * + * @param eligible the value + * @return this builder + */ + public Builder eligible(boolean eligible) { + return set("eligible", Conv.BOOLEAN, eligible); + } + + /** + * The url field. + * + * @param url the value + * @return this builder + */ + public Builder url(String url) { + return set("url", Conv.STRING, url); + } + + /** + * Builds the value. + * + * @return a new immutable {@code JoinInvitation} + */ + public JoinInvitation build() { + return new JoinInvitation(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } } diff --git a/src/main/java/me/didit/types/TravelRulePickupRespondRequest.java b/src/main/java/me/didit/types/TravelRulePickupRespondRequest.java index af65649..bd821b2 100644 --- a/src/main/java/me/didit/types/TravelRulePickupRespondRequest.java +++ b/src/main/java/me/didit/types/TravelRulePickupRespondRequest.java @@ -14,7 +14,7 @@ import me.didit.core.StringEnum; public final class TravelRulePickupRespondRequest extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("decision", "beneficiary_name"); + private static final Set KNOWN_FIELDS = Set.of("decision", "beneficiary_name", "decline_reason", "decline_detail", "ivms_response", "contact"); private TravelRulePickupRespondRequest(ObjectNode json, Map> files) { super(json, files); @@ -60,7 +60,7 @@ public TravelRulePickupRespondRequest.Decision decision() { } /** - * Required in practice for decision=accept; run through the originator's name_matching_strictness policy. + * The beneficiary's name as your records hold it, run through the sending VASP's name rule. ivms_response takes precedence when it names the beneficiary. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -69,6 +69,46 @@ public Optional beneficiaryName() { return Json.optional(json, "beneficiary_name", Conv.STRING); } + /** + * Why you decline. Recorded on the sending VASP's transfer timeline. Empty (the default) when you give no reason. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional declineReason() { + return Json.optional(json, "decline_reason", Conv.enumeration(TravelRulePickupRespondRequest.DeclineReason::of)); + } + + /** + * Free text; required when decision is decline and decline_reason is other. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional declineDetail() { + return Json.optional(json, "decline_detail", Conv.STRING); + } + + /** + * Your full IVMS 101 response. Its beneficiary name wins over beneficiary_name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> ivmsResponse() { + return Json.optional(json, "ivms_response", Conv.OBJECT); + } + + /** + * The contact the link was sent to, from the link's ?contact= value. When the body leaves it out, the contact query parameter is used. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional contact() { + return Json.optional(json, "contact", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -93,7 +133,7 @@ public Builder decision(TravelRulePickupRespondRequest.Decision decision) { } /** - * Required in practice for decision=accept; run through the originator's name_matching_strictness policy. + * The beneficiary's name as your records hold it, run through the sending VASP's name rule. ivms_response takes precedence when it names the beneficiary. * * @param beneficiaryName the value * @return this builder @@ -102,6 +142,46 @@ public Builder beneficiaryName(String beneficiaryName) { return set("beneficiary_name", Conv.STRING, beneficiaryName); } + /** + * Why you decline. Recorded on the sending VASP's transfer timeline. Empty (the default) when you give no reason. + * + * @param declineReason the value + * @return this builder + */ + public Builder declineReason(TravelRulePickupRespondRequest.DeclineReason declineReason) { + return set("decline_reason", Conv.enumeration(TravelRulePickupRespondRequest.DeclineReason::of), declineReason); + } + + /** + * Free text; required when decision is decline and decline_reason is other. + * + * @param declineDetail the value + * @return this builder + */ + public Builder declineDetail(String declineDetail) { + return set("decline_detail", Conv.STRING, declineDetail); + } + + /** + * Your full IVMS 101 response. Its beneficiary name wins over beneficiary_name. + * + * @param ivmsResponse the value + * @return this builder + */ + public Builder ivmsResponse(Map ivmsResponse) { + return setAny("ivms_response", ivmsResponse); + } + + /** + * The contact the link was sent to, from the link's ?contact= value. When the body leaves it out, the contact query parameter is used. + * + * @param contact the value + * @return this builder + */ + public Builder contact(String contact) { + return set("contact", Conv.STRING, contact); + } + /** * Builds the value. * @@ -160,4 +240,68 @@ public boolean isKnown() { return KNOWN_VALUES.contains(this); } } + + /** + * Why you decline. Recorded on the sending VASP's transfer timeline. Empty (the default) when you give no reason. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DeclineReason extends StringEnum { + /** The value {@code ""}. */ + public static final DeclineReason EMPTY = new DeclineReason(""); + + /** The value {@code "data_mismatch"}. */ + public static final DeclineReason DATA_MISMATCH = new DeclineReason("data_mismatch"); + + /** The value {@code "not_our_customer"}. */ + public static final DeclineReason NOT_OUR_CUSTOMER = new DeclineReason("not_our_customer"); + + /** The value {@code "wallet_not_hosted"}. */ + public static final DeclineReason WALLET_NOT_HOSTED = new DeclineReason("wallet_not_hosted"); + + /** The value {@code "sanctions"}. */ + public static final DeclineReason SANCTIONS = new DeclineReason("sanctions"); + + /** The value {@code "risk_appetite"}. */ + public static final DeclineReason RISK_APPETITE = new DeclineReason("risk_appetite"); + + /** The value {@code "jurisdiction"}. */ + public static final DeclineReason JURISDICTION = new DeclineReason("jurisdiction"); + + /** The value {@code "insufficient_data"}. */ + public static final DeclineReason INSUFFICIENT_DATA = new DeclineReason("insufficient_data"); + + /** The value {@code "other"}. */ + public static final DeclineReason OTHER = new DeclineReason("other"); + + private static final List KNOWN_VALUES = List.of(EMPTY, DATA_MISMATCH, NOT_OUR_CUSTOMER, WALLET_NOT_HOSTED, SANCTIONS, RISK_APPETITE, JURISDICTION, INSUFFICIENT_DATA, OTHER); + + private DeclineReason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DeclineReason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DeclineReason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } } diff --git a/src/main/java/me/didit/types/TravelRuleSendComplianceEmailConfirmationResponse.java b/src/main/java/me/didit/types/TravelRuleSendComplianceEmailConfirmationResponse.java new file mode 100644 index 0000000..c51a8fd --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleSendComplianceEmailConfirmationResponse.java @@ -0,0 +1,98 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleSendComplianceEmailConfirmationResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("sent_to"); + + private TravelRuleSendComplianceEmailConfirmationResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleSendComplianceEmailConfirmationResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleSendComplianceEmailConfirmationResponse fromJson(JsonNode json) { + return new TravelRuleSendComplianceEmailConfirmationResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The sent_to field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sentTo() { + return Json.optional(json, "sent_to", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleSendComplianceEmailConfirmationResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleSendComplianceEmailConfirmationResponse from) { + super(from); + } + + /** + * The sent_to field. + * + * @param sentTo the value + * @return this builder + */ + public Builder sentTo(String sentTo) { + return set("sent_to", Conv.STRING, sentTo); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleSendComplianceEmailConfirmationResponse} + */ + public TravelRuleSendComplianceEmailConfirmationResponse build() { + return new TravelRuleSendComplianceEmailConfirmationResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleSettingsDetail.java b/src/main/java/me/didit/types/TravelRuleSettingsDetail.java index 7f19f30..2f183a7 100644 --- a/src/main/java/me/didit/types/TravelRuleSettingsDetail.java +++ b/src/main/java/me/didit/types/TravelRuleSettingsDetail.java @@ -14,7 +14,7 @@ import me.didit.core.StringEnum; public final class TravelRuleSettingsDetail extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("is_enabled", "jurisdiction", "name_matching_strictness", "confirmation_timeout_hours", "timeout_outcome", "threshold_amount", "legal_name", "lei", "compliance_email", "is_discoverable", "inbound_auto_accept", "allow_self_declaration", "allow_screenshot_proof", "auto_wallet_verification", "vasp_attribution_enabled", "travel_address", "disabled_networks", "networks"); + private static final Set KNOWN_FIELDS = Set.of("is_enabled", "jurisdiction", "name_matching_strictness", "confirmation_timeout_hours", "timeout_outcome", "threshold_amount", "ownership_validation_mode", "simplified_validation_threshold", "data_sharing_fields", "matching_rules", "legal_name", "lei", "compliance_email", "is_discoverable", "inbound_auto_accept", "allow_self_declaration", "allow_screenshot_proof", "auto_wallet_verification", "vasp_attribution_enabled", "counterparty_screening_enabled", "sunrise_policy", "sunrise_jurisdictions", "non_responsive_policy", "travel_address", "disabled_networks", "networks", "didit_network"); private TravelRuleSettingsDetail(ObjectNode json, Map> files) { super(json, files); @@ -60,7 +60,7 @@ public Optional isEnabled() { } /** - * Your VASP's operating jurisdiction code. Default "EU". + * Your VASP's operating jurisdiction code. Default "EU". Didit maps it to the regime it stamps on your Travel Rule transactions as travel_rule_regime (for example EU or an EU member code becomes eu), which transaction rules can key on. Other Didit customers see it as your jurisdiction in the VASP directory once the Didit network lists your profile. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -70,7 +70,7 @@ public Optional jurisdiction() { } /** - * How strictly beneficiary names must match before a transfer can reach COMPLETED. + * How strictly beneficiary names must match before a transfer can reach COMPLETED. CONTAINS accepts one name whose words all appear in the other. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -90,7 +90,7 @@ public Optional confirmationTimeoutHours() { } /** - * What happens to the underlying transaction when a transfer expires. Default HOLD. + * What happens to the underlying transaction when a transfer expires. Default HOLD. sunrise_policy and non_responsive_policy take precedence when they apply and are set. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -100,7 +100,7 @@ public Optional timeoutOutcome() { } /** - * Decimal string. Minimum transfer amount that triggers a managed exchange; transfers below it resolve straight to NOT_APPLICABLE. Defaults to "0.00" (EU TFR applies no minimum amount). + * Decimal string, in your application's default currency. Minimum transfer amount that triggers a managed exchange; transfers below it resolve straight to NOT_APPLICABLE. Didit compares the transaction amount converted to your default currency, or the amount_in_default_currency you send; when no converted amount is available, the threshold does not apply. Defaults to "0.00" (EU TFR applies no minimum amount). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -109,6 +109,46 @@ public Optional thresholdAmount() { return Json.optional(json, "threshold_amount", Conv.STRING); } + /** + * How much proof a wallet's ownership takes. FULL always needs a proof (signed message, test deposit or reviewed screenshot), never the end user's word. SIMPLIFIED accepts a self-declaration below simplified_validation_threshold and a proof above it. AUTOMATED lets the address book and earlier proofs answer on their own, self-declarations included while allow_self_declaration is true. Default AUTOMATED. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ownershipValidationMode() { + return Json.optional(json, "ownership_validation_mode", Conv.enumeration(TravelRuleSettingsDetail.OwnershipValidationMode::of)); + } + + /** + * Decimal string, default "0.00". In SIMPLIFIED mode a transfer below this amount (in the threshold currency) accepts a self-declaration. A transfer with no amount in that currency counts as above it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional simplifiedValidationThreshold() { + return Json.optional(json, "simplified_validation_threshold", Conv.STRING); + } + + /** + * The fields Didit sends to the counterparty VASP about your own users, per entity type. The name and the wallet_address are always sent, whatever the list says. An entity type you never chose shares every field. An empty list is a choice too: it shares the name and the wallet only. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataSharingFields() { + return Json.optional(json, "data_sharing_fields", Conv.model(TravelRuleDataSharingFields::fromJson)); + } + + /** + * How strictly each field of the counterparty's data is compared with what you hold, per entity type. A field you leave out keeps its default. NONE skips the comparison, STRICT needs exactly the same words, CONTAINS accepts one value whose words all appear in the other, DEFAULT is the standard subset match, and FUZZY accepts close spellings (and, for dates of birth, a swapped day and month). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchingRules() { + return Json.optional(json, "matching_rules", Conv.model(TravelRuleMatchingRules::fromJson)); + } + /** * Your VASP's legal name. Required (non-blank) to set is_enabled to true. * @@ -130,7 +170,7 @@ public Optional lei() { } /** - * Compliance contact email for your VASP profile. + * Compliance contact email for your VASP profile. The Didit network lists your profile only once this address is confirmed through an emailed link (POST /v3/travel-rule/settings/compliance-email/confirmation/); changing it needs a new confirmation. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -140,7 +180,7 @@ public Optional complianceEmail() { } /** - * Whether your VASP profile appears in GET /v3/travel-rule/vasps/ search results and can be reached on the INTERNAL rail. Default true. + * Whether your VASP profile can be listed in the Didit network: found in GET /v3/travel-rule/vasps/ by other Didit customers and reached on the INTERNAL rail. Listing also needs a live application, Travel Rule on, a legal name and a confirmed compliance email; didit_network says which are missing. Default true. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -150,7 +190,7 @@ public Optional isDiscoverable() { } /** - * When true, inbound transfers whose beneficiary wallet is a verified address-book entry with a matching name are accepted automatically, without a manual review step. Default false. + * When true, an inbound TRP inquiry whose beneficiary wallet is a verified address-book entry with a matching name is approved automatically. When false, that inquiry waits in ON_HOLD for you to approve or deny it with the ownership endpoint. INTERNAL-rail and after-deposit transfers are not affected. Default true. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -160,7 +200,7 @@ public Optional inboundAutoAccept() { } /** - * When true, the wallet-ownership widget offers self-declaration as a fallback proof method. Default false. + * When true, the wallet-ownership widget offers self-declaration as a fallback proof method. Default true. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -200,7 +240,47 @@ public Optional vaspAttributionEnabled() { } /** - * Read-only. Your VASP's own travel address - the encoded inbound TRP inquiry endpoint other VASPs use to reach you. Populated once your VASP profile is complete. + * Opt-in sanctions and adverse-media screening of counterparty VASPs, billed per screening at the Company AML Screening price. When on, Didit screens the counterparty an outbound exchange reaches (reusing a screening younger than 30 days) and you can screen one with POST /v3/travel-rule/vasps/{vasp_id}/screening/. Default false: nothing is screened or billed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyScreeningEnabled() { + return Json.optional(json, "counterparty_screening_enabled", Conv.BOOLEAN); + } + + /** + * What an expired transfer does to its transaction when the counterparty is a directory entry in a jurisdiction on sunrise_jurisdictions, which does not enforce the Travel Rule yet: HOLD moves it to review, REJECT declines it, PROCEED leaves its status untouched. Empty (the default) follows timeout_outcome. Applies before non_responsive_policy. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sunrisePolicy() { + return Json.optional(json, "sunrise_policy", Conv.enumeration(TravelRuleSettingsDetail.SunrisePolicy::of)); + } + + /** + * Read-only. The countries (ISO 3166 alpha-2) Didit treats as in a Travel Rule sunrise period, where sunrise_policy applies. Currently CO, KH and VN. Any other jurisdiction, and an empty or unknown one, follows timeout_outcome. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> sunriseJurisdictions() { + return Json.optional(json, "sunrise_jurisdictions", Conv.list(Conv.STRING)); + } + + /** + * What an expired transfer does to its transaction when the counterparty is a directory entry whose responsiveness is UNRESPONSIVE: HOLD, REJECT or PROCEED, as for timeout_outcome. Empty (the default) follows timeout_outcome. sunrise_policy applies first when both fit. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nonResponsivePolicy() { + return Json.optional(json, "non_responsive_policy", Conv.enumeration(TravelRuleSettingsDetail.NonResponsivePolicy::of)); + } + + /** + * Read-only. Your VASP's own travel address - the encoded inbound TRP inquiry endpoint other VASPs use to reach you. Empty while is_enabled is false. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -229,6 +309,16 @@ public Optional networks() { return Json.optional(json, "networks", Conv.model(TravelRuleSettingsDetail.Networks::fromJson)); } + /** + * Whether the Didit network lists your VASP profile, so other Didit customers find it in the directory and reach it on the INTERNAL rail, and each condition it still misses. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional diditNetwork() { + return Json.optional(json, "didit_network", Conv.model(TravelRuleSettingsDetail.DiditNetwork::fromJson)); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -253,7 +343,7 @@ public Builder isEnabled(boolean isEnabled) { } /** - * Your VASP's operating jurisdiction code. Default "EU". + * Your VASP's operating jurisdiction code. Default "EU". Didit maps it to the regime it stamps on your Travel Rule transactions as travel_rule_regime (for example EU or an EU member code becomes eu), which transaction rules can key on. Other Didit customers see it as your jurisdiction in the VASP directory once the Didit network lists your profile. * * @param jurisdiction the value * @return this builder @@ -263,7 +353,7 @@ public Builder jurisdiction(String jurisdiction) { } /** - * How strictly beneficiary names must match before a transfer can reach COMPLETED. + * How strictly beneficiary names must match before a transfer can reach COMPLETED. CONTAINS accepts one name whose words all appear in the other. * * @param nameMatchingStrictness the value * @return this builder @@ -283,7 +373,7 @@ public Builder confirmationTimeoutHours(long confirmationTimeoutHours) { } /** - * What happens to the underlying transaction when a transfer expires. Default HOLD. + * What happens to the underlying transaction when a transfer expires. Default HOLD. sunrise_policy and non_responsive_policy take precedence when they apply and are set. * * @param timeoutOutcome the value * @return this builder @@ -293,7 +383,7 @@ public Builder timeoutOutcome(TravelRuleSettingsDetail.TimeoutOutcome timeoutOut } /** - * Decimal string. Minimum transfer amount that triggers a managed exchange; transfers below it resolve straight to NOT_APPLICABLE. Defaults to "0.00" (EU TFR applies no minimum amount). + * Decimal string, in your application's default currency. Minimum transfer amount that triggers a managed exchange; transfers below it resolve straight to NOT_APPLICABLE. Didit compares the transaction amount converted to your default currency, or the amount_in_default_currency you send; when no converted amount is available, the threshold does not apply. Defaults to "0.00" (EU TFR applies no minimum amount). * * @param thresholdAmount the value * @return this builder @@ -302,6 +392,46 @@ public Builder thresholdAmount(String thresholdAmount) { return set("threshold_amount", Conv.STRING, thresholdAmount); } + /** + * How much proof a wallet's ownership takes. FULL always needs a proof (signed message, test deposit or reviewed screenshot), never the end user's word. SIMPLIFIED accepts a self-declaration below simplified_validation_threshold and a proof above it. AUTOMATED lets the address book and earlier proofs answer on their own, self-declarations included while allow_self_declaration is true. Default AUTOMATED. + * + * @param ownershipValidationMode the value + * @return this builder + */ + public Builder ownershipValidationMode(TravelRuleSettingsDetail.OwnershipValidationMode ownershipValidationMode) { + return set("ownership_validation_mode", Conv.enumeration(TravelRuleSettingsDetail.OwnershipValidationMode::of), ownershipValidationMode); + } + + /** + * Decimal string, default "0.00". In SIMPLIFIED mode a transfer below this amount (in the threshold currency) accepts a self-declaration. A transfer with no amount in that currency counts as above it. + * + * @param simplifiedValidationThreshold the value + * @return this builder + */ + public Builder simplifiedValidationThreshold(String simplifiedValidationThreshold) { + return set("simplified_validation_threshold", Conv.STRING, simplifiedValidationThreshold); + } + + /** + * The fields Didit sends to the counterparty VASP about your own users, per entity type. The name and the wallet_address are always sent, whatever the list says. An entity type you never chose shares every field. An empty list is a choice too: it shares the name and the wallet only. + * + * @param dataSharingFields the value + * @return this builder + */ + public Builder dataSharingFields(TravelRuleDataSharingFields dataSharingFields) { + return set("data_sharing_fields", Conv.model(TravelRuleDataSharingFields::fromJson), dataSharingFields); + } + + /** + * How strictly each field of the counterparty's data is compared with what you hold, per entity type. A field you leave out keeps its default. NONE skips the comparison, STRICT needs exactly the same words, CONTAINS accepts one value whose words all appear in the other, DEFAULT is the standard subset match, and FUZZY accepts close spellings (and, for dates of birth, a swapped day and month). + * + * @param matchingRules the value + * @return this builder + */ + public Builder matchingRules(TravelRuleMatchingRules matchingRules) { + return set("matching_rules", Conv.model(TravelRuleMatchingRules::fromJson), matchingRules); + } + /** * Your VASP's legal name. Required (non-blank) to set is_enabled to true. * @@ -323,7 +453,7 @@ public Builder lei(String lei) { } /** - * Compliance contact email for your VASP profile. + * Compliance contact email for your VASP profile. The Didit network lists your profile only once this address is confirmed through an emailed link (POST /v3/travel-rule/settings/compliance-email/confirmation/); changing it needs a new confirmation. * * @param complianceEmail the value * @return this builder @@ -333,7 +463,7 @@ public Builder complianceEmail(String complianceEmail) { } /** - * Whether your VASP profile appears in GET /v3/travel-rule/vasps/ search results and can be reached on the INTERNAL rail. Default true. + * Whether your VASP profile can be listed in the Didit network: found in GET /v3/travel-rule/vasps/ by other Didit customers and reached on the INTERNAL rail. Listing also needs a live application, Travel Rule on, a legal name and a confirmed compliance email; didit_network says which are missing. Default true. * * @param isDiscoverable the value * @return this builder @@ -343,7 +473,7 @@ public Builder isDiscoverable(boolean isDiscoverable) { } /** - * When true, inbound transfers whose beneficiary wallet is a verified address-book entry with a matching name are accepted automatically, without a manual review step. Default false. + * When true, an inbound TRP inquiry whose beneficiary wallet is a verified address-book entry with a matching name is approved automatically. When false, that inquiry waits in ON_HOLD for you to approve or deny it with the ownership endpoint. INTERNAL-rail and after-deposit transfers are not affected. Default true. * * @param inboundAutoAccept the value * @return this builder @@ -353,7 +483,7 @@ public Builder inboundAutoAccept(boolean inboundAutoAccept) { } /** - * When true, the wallet-ownership widget offers self-declaration as a fallback proof method. Default false. + * When true, the wallet-ownership widget offers self-declaration as a fallback proof method. Default true. * * @param allowSelfDeclaration the value * @return this builder @@ -393,7 +523,47 @@ public Builder vaspAttributionEnabled(boolean vaspAttributionEnabled) { } /** - * Read-only. Your VASP's own travel address - the encoded inbound TRP inquiry endpoint other VASPs use to reach you. Populated once your VASP profile is complete. + * Opt-in sanctions and adverse-media screening of counterparty VASPs, billed per screening at the Company AML Screening price. When on, Didit screens the counterparty an outbound exchange reaches (reusing a screening younger than 30 days) and you can screen one with POST /v3/travel-rule/vasps/{vasp_id}/screening/. Default false: nothing is screened or billed. + * + * @param counterpartyScreeningEnabled the value + * @return this builder + */ + public Builder counterpartyScreeningEnabled(boolean counterpartyScreeningEnabled) { + return set("counterparty_screening_enabled", Conv.BOOLEAN, counterpartyScreeningEnabled); + } + + /** + * What an expired transfer does to its transaction when the counterparty is a directory entry in a jurisdiction on sunrise_jurisdictions, which does not enforce the Travel Rule yet: HOLD moves it to review, REJECT declines it, PROCEED leaves its status untouched. Empty (the default) follows timeout_outcome. Applies before non_responsive_policy. + * + * @param sunrisePolicy the value + * @return this builder + */ + public Builder sunrisePolicy(TravelRuleSettingsDetail.SunrisePolicy sunrisePolicy) { + return set("sunrise_policy", Conv.enumeration(TravelRuleSettingsDetail.SunrisePolicy::of), sunrisePolicy); + } + + /** + * Read-only. The countries (ISO 3166 alpha-2) Didit treats as in a Travel Rule sunrise period, where sunrise_policy applies. Currently CO, KH and VN. Any other jurisdiction, and an empty or unknown one, follows timeout_outcome. + * + * @param sunriseJurisdictions the value + * @return this builder + */ + public Builder sunriseJurisdictions(List sunriseJurisdictions) { + return set("sunrise_jurisdictions", Conv.list(Conv.STRING), sunriseJurisdictions); + } + + /** + * What an expired transfer does to its transaction when the counterparty is a directory entry whose responsiveness is UNRESPONSIVE: HOLD, REJECT or PROCEED, as for timeout_outcome. Empty (the default) follows timeout_outcome. sunrise_policy applies first when both fit. + * + * @param nonResponsivePolicy the value + * @return this builder + */ + public Builder nonResponsivePolicy(TravelRuleSettingsDetail.NonResponsivePolicy nonResponsivePolicy) { + return set("non_responsive_policy", Conv.enumeration(TravelRuleSettingsDetail.NonResponsivePolicy::of), nonResponsivePolicy); + } + + /** + * Read-only. Your VASP's own travel address - the encoded inbound TRP inquiry endpoint other VASPs use to reach you. Empty while is_enabled is false. * * @param travelAddress the value * @return this builder @@ -422,6 +592,16 @@ public Builder networks(TravelRuleSettingsDetail.Networks networks) { return set("networks", Conv.model(TravelRuleSettingsDetail.Networks::fromJson), networks); } + /** + * Whether the Didit network lists your VASP profile, so other Didit customers find it in the directory and reach it on the INTERNAL rail, and each condition it still misses. + * + * @param diditNetwork the value + * @return this builder + */ + public Builder diditNetwork(TravelRuleSettingsDetail.DiditNetwork diditNetwork) { + return set("didit_network", Conv.model(TravelRuleSettingsDetail.DiditNetwork::fromJson), diditNetwork); + } + /** * Builds the value. * @@ -438,7 +618,7 @@ protected Builder self() { } /** - * How strictly beneficiary names must match before a transfer can reach COMPLETED. + * How strictly beneficiary names must match before a transfer can reach COMPLETED. CONTAINS accepts one name whose words all appear in the other. * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -449,13 +629,16 @@ public static final class NameMatchingStrictness extends StringEnum { /** The value {@code "STRICT"}. */ public static final NameMatchingStrictness STRICT = new NameMatchingStrictness("STRICT"); + /** The value {@code "CONTAINS"}. */ + public static final NameMatchingStrictness CONTAINS = new NameMatchingStrictness("CONTAINS"); + /** The value {@code "DEFAULT"}. */ public static final NameMatchingStrictness DEFAULT = new NameMatchingStrictness("DEFAULT"); /** The value {@code "FUZZY"}. */ public static final NameMatchingStrictness FUZZY = new NameMatchingStrictness("FUZZY"); - private static final List KNOWN_VALUES = List.of(NONE, STRICT, DEFAULT, FUZZY); + private static final List KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); private NameMatchingStrictness(String value) { super(value); @@ -487,7 +670,7 @@ public boolean isKnown() { } /** - * What happens to the underlying transaction when a transfer expires. Default HOLD. + * What happens to the underlying transaction when a transfer expires. Default HOLD. sunrise_policy and non_responsive_policy take precedence when they apply and are set. * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -532,6 +715,150 @@ public boolean isKnown() { } } + /** + * How much proof a wallet's ownership takes. FULL always needs a proof (signed message, test deposit or reviewed screenshot), never the end user's word. SIMPLIFIED accepts a self-declaration below simplified_validation_threshold and a proof above it. AUTOMATED lets the address book and earlier proofs answer on their own, self-declarations included while allow_self_declaration is true. Default AUTOMATED. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class OwnershipValidationMode extends StringEnum { + /** The value {@code "FULL"}. */ + public static final OwnershipValidationMode FULL = new OwnershipValidationMode("FULL"); + + /** The value {@code "SIMPLIFIED"}. */ + public static final OwnershipValidationMode SIMPLIFIED = new OwnershipValidationMode("SIMPLIFIED"); + + /** The value {@code "AUTOMATED"}. */ + public static final OwnershipValidationMode AUTOMATED = new OwnershipValidationMode("AUTOMATED"); + + private static final List KNOWN_VALUES = List.of(FULL, SIMPLIFIED, AUTOMATED); + + private OwnershipValidationMode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static OwnershipValidationMode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, OwnershipValidationMode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * What an expired transfer does to its transaction when the counterparty is a directory entry in a jurisdiction on sunrise_jurisdictions, which does not enforce the Travel Rule yet: HOLD moves it to review, REJECT declines it, PROCEED leaves its status untouched. Empty (the default) follows timeout_outcome. Applies before non_responsive_policy. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SunrisePolicy extends StringEnum { + /** The value {@code ""}. */ + public static final SunrisePolicy EMPTY = new SunrisePolicy(""); + + /** The value {@code "HOLD"}. */ + public static final SunrisePolicy HOLD = new SunrisePolicy("HOLD"); + + /** The value {@code "REJECT"}. */ + public static final SunrisePolicy REJECT = new SunrisePolicy("REJECT"); + + /** The value {@code "PROCEED"}. */ + public static final SunrisePolicy PROCEED = new SunrisePolicy("PROCEED"); + + private static final List KNOWN_VALUES = List.of(EMPTY, HOLD, REJECT, PROCEED); + + private SunrisePolicy(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SunrisePolicy of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SunrisePolicy::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * What an expired transfer does to its transaction when the counterparty is a directory entry whose responsiveness is UNRESPONSIVE: HOLD, REJECT or PROCEED, as for timeout_outcome. Empty (the default) follows timeout_outcome. sunrise_policy applies first when both fit. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NonResponsivePolicy extends StringEnum { + /** The value {@code ""}. */ + public static final NonResponsivePolicy EMPTY = new NonResponsivePolicy(""); + + /** The value {@code "HOLD"}. */ + public static final NonResponsivePolicy HOLD = new NonResponsivePolicy("HOLD"); + + /** The value {@code "REJECT"}. */ + public static final NonResponsivePolicy REJECT = new NonResponsivePolicy("REJECT"); + + /** The value {@code "PROCEED"}. */ + public static final NonResponsivePolicy PROCEED = new NonResponsivePolicy("PROCEED"); + + private static final List KNOWN_VALUES = List.of(EMPTY, HOLD, REJECT, PROCEED); + + private NonResponsivePolicy(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NonResponsivePolicy of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NonResponsivePolicy::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + /** * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. @@ -1659,4 +1986,192 @@ public boolean isKnown() { } } } + + /** + * Whether the Didit network lists your VASP profile, so other Didit customers find it in the directory and reach it on the INTERNAL rail, and each condition it still misses. + */ + public static final class DiditNetwork extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("listed", "missing", "compliance_email_confirmed"); + + private DiditNetwork(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code DiditNetwork} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DiditNetwork fromJson(JsonNode json) { + return new DiditNetwork(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The listed field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional listed() { + return Json.optional(json, "listed", Conv.BOOLEAN); + } + + /** + * Empty when listed. compliance_email_unconfirmed: send the confirmation with POST /v3/travel-rule/settings/compliance-email/confirmation/ and open the emailed link. excluded_by_staff: Didit marked the application as a test, demo or internal one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> missing() { + return Json.optional(json, "missing", Conv.list(Conv.enumeration(TravelRuleSettingsDetail.DiditNetwork.Missing::of))); + } + + /** + * Whether the current compliance_email was confirmed through the emailed link. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional complianceEmailConfirmed() { + return Json.optional(json, "compliance_email_confirmed", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link DiditNetwork}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(DiditNetwork from) { + super(from); + } + + /** + * The listed field. + * + * @param listed the value + * @return this builder + */ + public Builder listed(boolean listed) { + return set("listed", Conv.BOOLEAN, listed); + } + + /** + * Empty when listed. compliance_email_unconfirmed: send the confirmation with POST /v3/travel-rule/settings/compliance-email/confirmation/ and open the emailed link. excluded_by_staff: Didit marked the application as a test, demo or internal one. + * + * @param missing the value + * @return this builder + */ + public Builder missing(List missing) { + return set("missing", Conv.list(Conv.enumeration(TravelRuleSettingsDetail.DiditNetwork.Missing::of)), missing); + } + + /** + * Whether the current compliance_email was confirmed through the emailed link. + * + * @param complianceEmailConfirmed the value + * @return this builder + */ + public Builder complianceEmailConfirmed(boolean complianceEmailConfirmed) { + return set("compliance_email_confirmed", Conv.BOOLEAN, complianceEmailConfirmed); + } + + /** + * Builds the value. + * + * @return a new immutable {@code DiditNetwork} + */ + public DiditNetwork build() { + return new DiditNetwork(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Missing extends StringEnum { + /** The value {@code "travel_rule_off"}. */ + public static final Missing TRAVEL_RULE_OFF = new Missing("travel_rule_off"); + + /** The value {@code "sandbox_application"}. */ + public static final Missing SANDBOX_APPLICATION = new Missing("sandbox_application"); + + /** The value {@code "not_discoverable"}. */ + public static final Missing NOT_DISCOVERABLE = new Missing("not_discoverable"); + + /** The value {@code "legal_name_missing"}. */ + public static final Missing LEGAL_NAME_MISSING = new Missing("legal_name_missing"); + + /** The value {@code "compliance_email_missing"}. */ + public static final Missing COMPLIANCE_EMAIL_MISSING = new Missing("compliance_email_missing"); + + /** The value {@code "compliance_email_unconfirmed"}. */ + public static final Missing COMPLIANCE_EMAIL_UNCONFIRMED = new Missing("compliance_email_unconfirmed"); + + /** The value {@code "excluded_by_staff"}. */ + public static final Missing EXCLUDED_BY_STAFF = new Missing("excluded_by_staff"); + + /** The value {@code "internal_organization"}. */ + public static final Missing INTERNAL_ORGANIZATION = new Missing("internal_organization"); + + private static final List KNOWN_VALUES = List.of(TRAVEL_RULE_OFF, SANDBOX_APPLICATION, NOT_DISCOVERABLE, LEGAL_NAME_MISSING, COMPLIANCE_EMAIL_MISSING, COMPLIANCE_EMAIL_UNCONFIRMED, EXCLUDED_BY_STAFF, INTERNAL_ORGANIZATION); + + private Missing(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Missing of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Missing::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } } diff --git a/src/main/java/me/didit/types/TravelRuleSettingsUpdate.java b/src/main/java/me/didit/types/TravelRuleSettingsUpdate.java index cdf708d..22bcd61 100644 --- a/src/main/java/me/didit/types/TravelRuleSettingsUpdate.java +++ b/src/main/java/me/didit/types/TravelRuleSettingsUpdate.java @@ -15,7 +15,7 @@ /** All fields optional; PUT applies a partial update. */ public final class TravelRuleSettingsUpdate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("is_enabled", "legal_name", "lei", "jurisdiction", "name_matching_strictness", "confirmation_timeout_hours", "timeout_outcome", "compliance_email", "is_discoverable", "threshold_amount", "inbound_auto_accept", "allow_self_declaration", "allow_screenshot_proof", "auto_wallet_verification", "vasp_attribution_enabled", "disabled_networks"); + private static final Set KNOWN_FIELDS = Set.of("is_enabled", "legal_name", "lei", "jurisdiction", "name_matching_strictness", "confirmation_timeout_hours", "timeout_outcome", "compliance_email", "is_discoverable", "threshold_amount", "ownership_validation_mode", "simplified_validation_threshold", "data_sharing_fields", "matching_rules", "inbound_auto_accept", "allow_self_declaration", "allow_screenshot_proof", "auto_wallet_verification", "vasp_attribution_enabled", "counterparty_screening_enabled", "sunrise_policy", "non_responsive_policy", "disabled_networks"); private TravelRuleSettingsUpdate(ObjectNode json, Map> files) { super(json, files); @@ -91,7 +91,7 @@ public Optional jurisdiction() { } /** - * The name_matching_strictness field. + * CONTAINS accepts one name whose words all appear in the other. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -121,7 +121,7 @@ public Optional timeoutOutcome() { } /** - * The compliance_email field. + * Compliance contact email for your VASP profile. The Didit network lists your profile only once this address is confirmed through an emailed link (POST /v3/travel-rule/settings/compliance-email/confirmation/); changing it needs a new confirmation. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -131,7 +131,7 @@ public Optional complianceEmail() { } /** - * The is_discoverable field. + * Whether your VASP profile can be listed in the Didit network: found in GET /v3/travel-rule/vasps/ by other Didit customers and reached on the INTERNAL rail. Listing also needs a live application, Travel Rule on, a legal name and a confirmed compliance email; didit_network says which are missing. Default true. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -141,7 +141,7 @@ public Optional isDiscoverable() { } /** - * Decimal string. Minimum transfer amount that triggers a managed exchange. + * Decimal string, zero or greater, in your application's default currency. Minimum transfer amount that triggers a managed exchange. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -150,6 +150,46 @@ public Optional thresholdAmount() { return Json.optional(json, "threshold_amount", Conv.STRING); } + /** + * See the response field of the same name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ownershipValidationMode() { + return Json.optional(json, "ownership_validation_mode", Conv.enumeration(TravelRuleSettingsUpdate.OwnershipValidationMode::of)); + } + + /** + * Decimal string, 0 or more. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional simplifiedValidationThreshold() { + return Json.optional(json, "simplified_validation_threshold", Conv.STRING); + } + + /** + * Merged per entity type: an entity type you leave out keeps its current choice. A choice that cannot meet the originator obligations of your jurisdiction regime (EU TFR or FATF Recommendation 16), an empty list included, is refused with 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataSharingFields() { + return Json.optional(json, "data_sharing_fields", Conv.model(TravelRuleDataSharingFields::fromJson)); + } + + /** + * Merged per entity type, and per field inside it. A field that entity type does not compare (for example date_of_birth for company) is refused with 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchingRules() { + return Json.optional(json, "matching_rules", Conv.model(TravelRuleMatchingRules::fromJson)); + } + /** * The inbound_auto_accept field. * @@ -200,6 +240,36 @@ public Optional vaspAttributionEnabled() { return Json.optional(json, "vasp_attribution_enabled", Conv.BOOLEAN); } + /** + * Opt-in sanctions and adverse-media screening of counterparty VASPs, billed per screening at the Company AML Screening price. When on, Didit screens the counterparty an outbound exchange reaches (reusing a screening younger than 30 days) and you can screen one with POST /v3/travel-rule/vasps/{vasp_id}/screening/. Default false: nothing is screened or billed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterpartyScreeningEnabled() { + return Json.optional(json, "counterparty_screening_enabled", Conv.BOOLEAN); + } + + /** + * HOLD, REJECT, PROCEED, or empty to follow timeout_outcome. See the response field of the same name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sunrisePolicy() { + return Json.optional(json, "sunrise_policy", Conv.enumeration(TravelRuleSettingsUpdate.SunrisePolicy::of)); + } + + /** + * HOLD, REJECT, PROCEED, or empty to follow timeout_outcome. See the response field of the same name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nonResponsivePolicy() { + return Json.optional(json, "non_responsive_policy", Conv.enumeration(TravelRuleSettingsUpdate.NonResponsivePolicy::of)); + } + /** * Networks opted out of platform membership. Does not disable your connected own memberships or provision unavailable networks. * @@ -264,7 +334,7 @@ public Builder jurisdiction(String jurisdiction) { } /** - * The name_matching_strictness field. + * CONTAINS accepts one name whose words all appear in the other. * * @param nameMatchingStrictness the value * @return this builder @@ -294,7 +364,7 @@ public Builder timeoutOutcome(TravelRuleSettingsUpdate.TimeoutOutcome timeoutOut } /** - * The compliance_email field. + * Compliance contact email for your VASP profile. The Didit network lists your profile only once this address is confirmed through an emailed link (POST /v3/travel-rule/settings/compliance-email/confirmation/); changing it needs a new confirmation. * * @param complianceEmail the value * @return this builder @@ -304,7 +374,7 @@ public Builder complianceEmail(String complianceEmail) { } /** - * The is_discoverable field. + * Whether your VASP profile can be listed in the Didit network: found in GET /v3/travel-rule/vasps/ by other Didit customers and reached on the INTERNAL rail. Listing also needs a live application, Travel Rule on, a legal name and a confirmed compliance email; didit_network says which are missing. Default true. * * @param isDiscoverable the value * @return this builder @@ -314,7 +384,7 @@ public Builder isDiscoverable(boolean isDiscoverable) { } /** - * Decimal string. Minimum transfer amount that triggers a managed exchange. + * Decimal string, zero or greater, in your application's default currency. Minimum transfer amount that triggers a managed exchange. * * @param thresholdAmount the value * @return this builder @@ -323,6 +393,46 @@ public Builder thresholdAmount(String thresholdAmount) { return set("threshold_amount", Conv.STRING, thresholdAmount); } + /** + * See the response field of the same name. + * + * @param ownershipValidationMode the value + * @return this builder + */ + public Builder ownershipValidationMode(TravelRuleSettingsUpdate.OwnershipValidationMode ownershipValidationMode) { + return set("ownership_validation_mode", Conv.enumeration(TravelRuleSettingsUpdate.OwnershipValidationMode::of), ownershipValidationMode); + } + + /** + * Decimal string, 0 or more. + * + * @param simplifiedValidationThreshold the value + * @return this builder + */ + public Builder simplifiedValidationThreshold(String simplifiedValidationThreshold) { + return set("simplified_validation_threshold", Conv.STRING, simplifiedValidationThreshold); + } + + /** + * Merged per entity type: an entity type you leave out keeps its current choice. A choice that cannot meet the originator obligations of your jurisdiction regime (EU TFR or FATF Recommendation 16), an empty list included, is refused with 400. + * + * @param dataSharingFields the value + * @return this builder + */ + public Builder dataSharingFields(TravelRuleDataSharingFields dataSharingFields) { + return set("data_sharing_fields", Conv.model(TravelRuleDataSharingFields::fromJson), dataSharingFields); + } + + /** + * Merged per entity type, and per field inside it. A field that entity type does not compare (for example date_of_birth for company) is refused with 400. + * + * @param matchingRules the value + * @return this builder + */ + public Builder matchingRules(TravelRuleMatchingRules matchingRules) { + return set("matching_rules", Conv.model(TravelRuleMatchingRules::fromJson), matchingRules); + } + /** * The inbound_auto_accept field. * @@ -373,6 +483,36 @@ public Builder vaspAttributionEnabled(boolean vaspAttributionEnabled) { return set("vasp_attribution_enabled", Conv.BOOLEAN, vaspAttributionEnabled); } + /** + * Opt-in sanctions and adverse-media screening of counterparty VASPs, billed per screening at the Company AML Screening price. When on, Didit screens the counterparty an outbound exchange reaches (reusing a screening younger than 30 days) and you can screen one with POST /v3/travel-rule/vasps/{vasp_id}/screening/. Default false: nothing is screened or billed. + * + * @param counterpartyScreeningEnabled the value + * @return this builder + */ + public Builder counterpartyScreeningEnabled(boolean counterpartyScreeningEnabled) { + return set("counterparty_screening_enabled", Conv.BOOLEAN, counterpartyScreeningEnabled); + } + + /** + * HOLD, REJECT, PROCEED, or empty to follow timeout_outcome. See the response field of the same name. + * + * @param sunrisePolicy the value + * @return this builder + */ + public Builder sunrisePolicy(TravelRuleSettingsUpdate.SunrisePolicy sunrisePolicy) { + return set("sunrise_policy", Conv.enumeration(TravelRuleSettingsUpdate.SunrisePolicy::of), sunrisePolicy); + } + + /** + * HOLD, REJECT, PROCEED, or empty to follow timeout_outcome. See the response field of the same name. + * + * @param nonResponsivePolicy the value + * @return this builder + */ + public Builder nonResponsivePolicy(TravelRuleSettingsUpdate.NonResponsivePolicy nonResponsivePolicy) { + return set("non_responsive_policy", Conv.enumeration(TravelRuleSettingsUpdate.NonResponsivePolicy::of), nonResponsivePolicy); + } + /** * Networks opted out of platform membership. Does not disable your connected own memberships or provision unavailable networks. * @@ -399,6 +539,7 @@ protected Builder self() { } /** + * CONTAINS accepts one name whose words all appear in the other. * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -409,13 +550,16 @@ public static final class NameMatchingStrictness extends StringEnum { /** The value {@code "STRICT"}. */ public static final NameMatchingStrictness STRICT = new NameMatchingStrictness("STRICT"); + /** The value {@code "CONTAINS"}. */ + public static final NameMatchingStrictness CONTAINS = new NameMatchingStrictness("CONTAINS"); + /** The value {@code "DEFAULT"}. */ public static final NameMatchingStrictness DEFAULT = new NameMatchingStrictness("DEFAULT"); /** The value {@code "FUZZY"}. */ public static final NameMatchingStrictness FUZZY = new NameMatchingStrictness("FUZZY"); - private static final List KNOWN_VALUES = List.of(NONE, STRICT, DEFAULT, FUZZY); + private static final List KNOWN_VALUES = List.of(NONE, STRICT, CONTAINS, DEFAULT, FUZZY); private NameMatchingStrictness(String value) { super(value); @@ -491,6 +635,150 @@ public boolean isKnown() { } } + /** + * See the response field of the same name. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class OwnershipValidationMode extends StringEnum { + /** The value {@code "FULL"}. */ + public static final OwnershipValidationMode FULL = new OwnershipValidationMode("FULL"); + + /** The value {@code "SIMPLIFIED"}. */ + public static final OwnershipValidationMode SIMPLIFIED = new OwnershipValidationMode("SIMPLIFIED"); + + /** The value {@code "AUTOMATED"}. */ + public static final OwnershipValidationMode AUTOMATED = new OwnershipValidationMode("AUTOMATED"); + + private static final List KNOWN_VALUES = List.of(FULL, SIMPLIFIED, AUTOMATED); + + private OwnershipValidationMode(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static OwnershipValidationMode of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, OwnershipValidationMode::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * HOLD, REJECT, PROCEED, or empty to follow timeout_outcome. See the response field of the same name. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SunrisePolicy extends StringEnum { + /** The value {@code ""}. */ + public static final SunrisePolicy EMPTY = new SunrisePolicy(""); + + /** The value {@code "HOLD"}. */ + public static final SunrisePolicy HOLD = new SunrisePolicy("HOLD"); + + /** The value {@code "REJECT"}. */ + public static final SunrisePolicy REJECT = new SunrisePolicy("REJECT"); + + /** The value {@code "PROCEED"}. */ + public static final SunrisePolicy PROCEED = new SunrisePolicy("PROCEED"); + + private static final List KNOWN_VALUES = List.of(EMPTY, HOLD, REJECT, PROCEED); + + private SunrisePolicy(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SunrisePolicy of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SunrisePolicy::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * HOLD, REJECT, PROCEED, or empty to follow timeout_outcome. See the response field of the same name. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class NonResponsivePolicy extends StringEnum { + /** The value {@code ""}. */ + public static final NonResponsivePolicy EMPTY = new NonResponsivePolicy(""); + + /** The value {@code "HOLD"}. */ + public static final NonResponsivePolicy HOLD = new NonResponsivePolicy("HOLD"); + + /** The value {@code "REJECT"}. */ + public static final NonResponsivePolicy REJECT = new NonResponsivePolicy("REJECT"); + + /** The value {@code "PROCEED"}. */ + public static final NonResponsivePolicy PROCEED = new NonResponsivePolicy("PROCEED"); + + private static final List KNOWN_VALUES = List.of(EMPTY, HOLD, REJECT, PROCEED); + + private NonResponsivePolicy(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static NonResponsivePolicy of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, NonResponsivePolicy::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + /** * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. diff --git a/src/main/java/me/didit/types/TravelRuleStatusUpdatedEvent.java b/src/main/java/me/didit/types/TravelRuleStatusUpdatedEvent.java new file mode 100644 index 0000000..eaa6e2d --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleStatusUpdatedEvent.java @@ -0,0 +1,535 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * A Travel Rule exchange changed status. Sent on every transition up to and including FINISHED, CANCELLED or EXPIRED, never after. Sent for live applications only. There is no top-level status. + */ +public final class TravelRuleStatusUpdatedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "webhook_type", "transaction_id", "txn_id", "travel_rule_status", "rail", "direction"); + + private TravelRuleStatusUpdatedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleStatusUpdatedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleStatusUpdatedEvent fromJson(JsonNode json) { + return new TravelRuleStatusUpdatedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * Always travel_rule.status.updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TravelRuleStatusUpdatedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(TravelRuleStatusUpdatedEvent.WebhookType::of)); + } + + /** + * The transaction the exchange belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String transactionId() { + return Json.required(json, "transaction_id", Conv.STRING); + } + + /** + * The transaction_id you supplied at create time, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txnId() { + return Json.optional(json, "txn_id", Conv.STRING); + } + + /** + * Exchange status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TravelRuleStatusUpdatedEvent.TravelRuleStatus travelRuleStatus() { + return Json.required(json, "travel_rule_status", Conv.enumeration(TravelRuleStatusUpdatedEvent.TravelRuleStatus::of)); + } + + /** + * Network the exchange runs on, or an empty string when none could be resolved. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TravelRuleStatusUpdatedEvent.Rail rail() { + return Json.required(json, "rail", Conv.enumeration(TravelRuleStatusUpdatedEvent.Rail::of)); + } + + /** + * Direction of the transfer. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public TravelRuleStatusUpdatedEvent.Direction direction() { + return Json.required(json, "direction", Conv.enumeration(TravelRuleStatusUpdatedEvent.Direction::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleStatusUpdatedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleStatusUpdatedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * Always travel_rule.status.updated. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(TravelRuleStatusUpdatedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(TravelRuleStatusUpdatedEvent.WebhookType::of), webhookType); + } + + /** + * The transaction the exchange belongs to. + * + * @param transactionId the value + * @return this builder + */ + public Builder transactionId(String transactionId) { + return set("transaction_id", Conv.STRING, transactionId); + } + + /** + * The transaction_id you supplied at create time, or null. + * + * @param txnId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder txnId(@Nullable String txnId) { + return setNullable("txn_id", Conv.STRING, txnId); + } + + /** + * Exchange status after the change. + * + * @param travelRuleStatus the value + * @return this builder + */ + public Builder travelRuleStatus(TravelRuleStatusUpdatedEvent.TravelRuleStatus travelRuleStatus) { + return set("travel_rule_status", Conv.enumeration(TravelRuleStatusUpdatedEvent.TravelRuleStatus::of), travelRuleStatus); + } + + /** + * Network the exchange runs on, or an empty string when none could be resolved. + * + * @param rail the value + * @return this builder + */ + public Builder rail(TravelRuleStatusUpdatedEvent.Rail rail) { + return set("rail", Conv.enumeration(TravelRuleStatusUpdatedEvent.Rail::of), rail); + } + + /** + * Direction of the transfer. + * + * @param direction the value + * @return this builder + */ + public Builder direction(TravelRuleStatusUpdatedEvent.Direction direction) { + return set("direction", Conv.enumeration(TravelRuleStatusUpdatedEvent.Direction::of), direction); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleStatusUpdatedEvent} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleStatusUpdatedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("webhook_type", false); + require("transaction_id", false); + require("txn_id", true); + require("travel_rule_status", false); + require("rail", false); + require("direction", false); + return new TravelRuleStatusUpdatedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always travel_rule.status.updated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "travel_rule.status.updated"}. */ + public static final WebhookType TRAVEL_RULE_STATUS_UPDATED = new WebhookType("travel_rule.status.updated"); + + private static final List KNOWN_VALUES = List.of(TRAVEL_RULE_STATUS_UPDATED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Exchange status after the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class TravelRuleStatus extends StringEnum { + /** The value {@code "AWAITING_COUNTERPARTY"}. */ + public static final TravelRuleStatus AWAITING_COUNTERPARTY = new TravelRuleStatus("AWAITING_COUNTERPARTY"); + + /** The value {@code "ON_HOLD"}. */ + public static final TravelRuleStatus ON_HOLD = new TravelRuleStatus("ON_HOLD"); + + /** The value {@code "COMPLETED"}. */ + public static final TravelRuleStatus COMPLETED = new TravelRuleStatus("COMPLETED"); + + /** The value {@code "FINISHED"}. */ + public static final TravelRuleStatus FINISHED = new TravelRuleStatus("FINISHED"); + + /** The value {@code "CANCELLED"}. */ + public static final TravelRuleStatus CANCELLED = new TravelRuleStatus("CANCELLED"); + + /** The value {@code "EXPIRED"}. */ + public static final TravelRuleStatus EXPIRED = new TravelRuleStatus("EXPIRED"); + + /** The value {@code "COUNTERPARTY_MISMATCHED_DATA"}. */ + public static final TravelRuleStatus COUNTERPARTY_MISMATCHED_DATA = new TravelRuleStatus("COUNTERPARTY_MISMATCHED_DATA"); + + /** The value {@code "COUNTERPARTY_VASP_NOT_FOUND"}. */ + public static final TravelRuleStatus COUNTERPARTY_VASP_NOT_FOUND = new TravelRuleStatus("COUNTERPARTY_VASP_NOT_FOUND"); + + /** The value {@code "COUNTERPARTY_VASP_NOT_REACHABLE"}. */ + public static final TravelRuleStatus COUNTERPARTY_VASP_NOT_REACHABLE = new TravelRuleStatus("COUNTERPARTY_VASP_NOT_REACHABLE"); + + /** The value {@code "UNCONFIRMED_OWNERSHIP"}. */ + public static final TravelRuleStatus UNCONFIRMED_OWNERSHIP = new TravelRuleStatus("UNCONFIRMED_OWNERSHIP"); + + /** The value {@code "COUNTERPARTY_VASP_GENERAL_DECLINE"}. */ + public static final TravelRuleStatus COUNTERPARTY_VASP_GENERAL_DECLINE = new TravelRuleStatus("COUNTERPARTY_VASP_GENERAL_DECLINE"); + + /** The value {@code "NOT_ENOUGH_COUNTERPARTY_DATA"}. */ + public static final TravelRuleStatus NOT_ENOUGH_COUNTERPARTY_DATA = new TravelRuleStatus("NOT_ENOUGH_COUNTERPARTY_DATA"); + + /** The value {@code "NOT_APPLICABLE"}. */ + public static final TravelRuleStatus NOT_APPLICABLE = new TravelRuleStatus("NOT_APPLICABLE"); + + private static final List KNOWN_VALUES = List.of(AWAITING_COUNTERPARTY, ON_HOLD, COMPLETED, FINISHED, CANCELLED, EXPIRED, COUNTERPARTY_MISMATCHED_DATA, COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, UNCONFIRMED_OWNERSHIP, COUNTERPARTY_VASP_GENERAL_DECLINE, NOT_ENOUGH_COUNTERPARTY_DATA, NOT_APPLICABLE); + + private TravelRuleStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static TravelRuleStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, TravelRuleStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Network the exchange runs on, or an empty string when none could be resolved. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Rail extends StringEnum { + /** The value {@code ""}. */ + public static final Rail EMPTY = new Rail(""); + + /** The value {@code "INTERNAL"}. */ + public static final Rail INTERNAL = new Rail("INTERNAL"); + + /** The value {@code "TRP"}. */ + public static final Rail TRP = new Rail("TRP"); + + /** The value {@code "GTR"}. */ + public static final Rail GTR = new Rail("GTR"); + + /** The value {@code "TRUST"}. */ + public static final Rail TRUST = new Rail("TRUST"); + + /** The value {@code "VERIFYVASP"}. */ + public static final Rail VERIFYVASP = new Rail("VERIFYVASP"); + + /** The value {@code "SYGNA"}. */ + public static final Rail SYGNA = new Rail("SYGNA"); + + /** The value {@code "EMAIL"}. */ + public static final Rail EMAIL = new Rail("EMAIL"); + + private static final List KNOWN_VALUES = List.of(EMPTY, INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL); + + private Rail(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Rail of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Rail::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Direction of the transfer. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Direction extends StringEnum { + /** The value {@code "INBOUND"}. */ + public static final Direction INBOUND = new Direction("INBOUND"); + + /** The value {@code "OUTBOUND"}. */ + public static final Direction OUTBOUND = new Direction("OUTBOUND"); + + private static final List KNOWN_VALUES = List.of(INBOUND, OUTBOUND); + + private Direction(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Direction of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Direction::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleTestVaspInboundRequest.java b/src/main/java/me/didit/types/TravelRuleTestVaspInboundRequest.java new file mode 100644 index 0000000..afa00fe --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleTestVaspInboundRequest.java @@ -0,0 +1,244 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class TravelRuleTestVaspInboundRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("beneficiary_wallet_address", "scenario", "chain", "amount", "currency", "reference"); + + private TravelRuleTestVaspInboundRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleTestVaspInboundRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleTestVaspInboundRequest fromJson(JsonNode json) { + return new TravelRuleTestVaspInboundRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The destination wallet address. The Didit Test VASP sends to it, matched against your wallet address book on the chain of the asset. A wallet with no matching entry is accepted and the inbound transaction is booked ON_HOLD. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String beneficiaryWalletAddress() { + return Json.required(json, "beneficiary_wallet_address", Conv.STRING); + } + + /** + * What the Didit Test VASP declares the beneficiary to be. accept declares the holder's own name; name_mismatch declares a name no holder agrees with. The decision itself is always yours. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scenario() { + return Json.optional(json, "scenario", Conv.enumeration(TravelRuleTestVaspInboundRequest.Scenario::of)); + } + + /** + * Chain of the asset. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional chain() { + return Json.optional(json, "chain", Conv.STRING); + } + + /** + * Amount as a decimal string. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional amount() { + return Json.optional(json, "amount", Conv.STRING); + } + + /** + * Asset code. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currency() { + return Json.optional(json, "currency", Conv.STRING); + } + + /** + * Optional idempotency key. A repeat with the same value returns the transfer the first call booked. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reference() { + return Json.optional(json, "reference", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleTestVaspInboundRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleTestVaspInboundRequest from) { + super(from); + } + + /** + * The destination wallet address. The Didit Test VASP sends to it, matched against your wallet address book on the chain of the asset. A wallet with no matching entry is accepted and the inbound transaction is booked ON_HOLD. + * + * @param beneficiaryWalletAddress the value + * @return this builder + */ + public Builder beneficiaryWalletAddress(String beneficiaryWalletAddress) { + return set("beneficiary_wallet_address", Conv.STRING, beneficiaryWalletAddress); + } + + /** + * What the Didit Test VASP declares the beneficiary to be. accept declares the holder's own name; name_mismatch declares a name no holder agrees with. The decision itself is always yours. + * + * @param scenario the value + * @return this builder + */ + public Builder scenario(TravelRuleTestVaspInboundRequest.Scenario scenario) { + return set("scenario", Conv.enumeration(TravelRuleTestVaspInboundRequest.Scenario::of), scenario); + } + + /** + * Chain of the asset. + * + * @param chain the value + * @return this builder + */ + public Builder chain(String chain) { + return set("chain", Conv.STRING, chain); + } + + /** + * Amount as a decimal string. + * + * @param amount the value + * @return this builder + */ + public Builder amount(String amount) { + return set("amount", Conv.STRING, amount); + } + + /** + * Asset code. + * + * @param currency the value + * @return this builder + */ + public Builder currency(String currency) { + return set("currency", Conv.STRING, currency); + } + + /** + * Optional idempotency key. A repeat with the same value returns the transfer the first call booked. + * + * @param reference the value + * @return this builder + */ + public Builder reference(String reference) { + return set("reference", Conv.STRING, reference); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleTestVaspInboundRequest} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleTestVaspInboundRequest build() { + require("beneficiary_wallet_address", false); + return new TravelRuleTestVaspInboundRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * What the Didit Test VASP declares the beneficiary to be. accept declares the holder's own name; name_mismatch declares a name no holder agrees with. The decision itself is always yours. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scenario extends StringEnum { + /** The value {@code "accept"}. */ + public static final Scenario ACCEPT = new Scenario("accept"); + + /** The value {@code "name_mismatch"}. */ + public static final Scenario NAME_MISMATCH = new Scenario("name_mismatch"); + + private static final List KNOWN_VALUES = List.of(ACCEPT, NAME_MISMATCH); + + private Scenario(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scenario of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scenario::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleTestVaspInboundResponse.java b/src/main/java/me/didit/types/TravelRuleTestVaspInboundResponse.java new file mode 100644 index 0000000..80b5381 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleTestVaspInboundResponse.java @@ -0,0 +1,218 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleTestVaspInboundResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("transaction_id", "txn_id", "scenario", "travel_rule_status", "detail", "test_vasp", "travel_rule"); + + private TravelRuleTestVaspInboundResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleTestVaspInboundResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleTestVaspInboundResponse fromJson(JsonNode json) { + return new TravelRuleTestVaspInboundResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Didit transaction UUID of the inbound transfer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionId() { + return Json.optional(json, "transaction_id", Conv.STRING); + } + + /** + * Didit-generated txn_id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txnId() { + return Json.optional(json, "txn_id", Conv.STRING); + } + + /** + * The scenario that ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scenario() { + return Json.optional(json, "scenario", Conv.STRING); + } + + /** + * The status your application's own decision resolved to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional travelRuleStatus() { + return Json.optional(json, "travel_rule_status", Conv.STRING); + } + + /** + * Why the transfer resolved to that status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional detail() { + return Json.optional(json, "detail", Conv.STRING); + } + + /** + * Always true: the sender was the Didit Test VASP. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional testVasp() { + return Json.optional(json, "test_vasp", Conv.BOOLEAN); + } + + /** + * Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. While an inbound transfer's wallet ownership is unconfirmed, the counterparty's originator_data and beneficiary_data come back masked (the first letter of each word is kept; the wallet, chain and VASP routing stay readable) and data_masked is true. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional travelRule() { + return Json.optional(json, "travel_rule", Conv.model(TravelRuleTransferDetail::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleTestVaspInboundResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleTestVaspInboundResponse from) { + super(from); + } + + /** + * Didit transaction UUID of the inbound transfer. + * + * @param transactionId the value + * @return this builder + */ + public Builder transactionId(String transactionId) { + return set("transaction_id", Conv.STRING, transactionId); + } + + /** + * Didit-generated txn_id. + * + * @param txnId the value + * @return this builder + */ + public Builder txnId(String txnId) { + return set("txn_id", Conv.STRING, txnId); + } + + /** + * The scenario that ran. + * + * @param scenario the value + * @return this builder + */ + public Builder scenario(String scenario) { + return set("scenario", Conv.STRING, scenario); + } + + /** + * The status your application's own decision resolved to. + * + * @param travelRuleStatus the value + * @return this builder + */ + public Builder travelRuleStatus(String travelRuleStatus) { + return set("travel_rule_status", Conv.STRING, travelRuleStatus); + } + + /** + * Why the transfer resolved to that status. + * + * @param detail the value + * @return this builder + */ + public Builder detail(String detail) { + return set("detail", Conv.STRING, detail); + } + + /** + * Always true: the sender was the Didit Test VASP. + * + * @param testVasp the value + * @return this builder + */ + public Builder testVasp(boolean testVasp) { + return set("test_vasp", Conv.BOOLEAN, testVasp); + } + + /** + * Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. While an inbound transfer's wallet ownership is unconfirmed, the counterparty's originator_data and beneficiary_data come back masked (the first letter of each word is kept; the wallet, chain and VASP routing stay readable) and data_masked is true. + * + * @param travelRule the value + * @return this builder + */ + public Builder travelRule(TravelRuleTransferDetail travelRule) { + return set("travel_rule", Conv.model(TravelRuleTransferDetail::fromJson), travelRule); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleTestVaspInboundResponse} + */ + public TravelRuleTestVaspInboundResponse build() { + return new TravelRuleTestVaspInboundResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleTransferDetail.java b/src/main/java/me/didit/types/TravelRuleTransferDetail.java index 1235e6b..f7839ec 100644 --- a/src/main/java/me/didit/types/TravelRuleTransferDetail.java +++ b/src/main/java/me/didit/types/TravelRuleTransferDetail.java @@ -16,10 +16,10 @@ import org.jspecify.annotations.Nullable; /** - * Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. originator_data/beneficiary_data are omitted while PII is masked (see description). + * Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. While an inbound transfer's wallet ownership is unconfirmed, the counterparty's originator_data and beneficiary_data come back masked (the first letter of each word is kept; the wallet, chain and VASP routing stay readable) and data_masked is true. */ public final class TravelRuleTransferDetail extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "status", "direction", "rail", "protocol", "ivms_version", "required", "ownership_confirmed", "deadline_at", "timeline", "counterparty_vasp", "originator_data", "beneficiary_data", "created_at"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "status", "direction", "rail", "protocol", "ivms_version", "required", "ownership_confirmed", "deadline_at", "timeline", "counterparty_vasp", "originator_data", "beneficiary_data", "name_match", "data_match", "mismatched_fields", "data_unmasked", "data_masked", "created_at", "delivery", "discovery", "reason", "next_actions", "chain_details"); private TravelRuleTransferDetail(ObjectNode json, Map> files) { super(json, files); @@ -85,7 +85,7 @@ public Optional direction() { } /** - * The rail field. + * The rail the transfer was routed over, or null when no rail was selected (for example below the threshold, a self-hosted counterparty wallet, an after-deposit registration, or no reachable counterparty). INTERNAL means both sides are Didit applications; EMAIL means the counterparty was reached by an emailed pickup link. TEST_VASP means the Didit Test VASP answered a sandbox application's transfer. Over GTR, VERIFYVASP and SYGNA, Didit reads the counterparty's answer from the network's response or by polling it every 5 minutes. TRUST only sends, so a TRUST transfer stays AWAITING_COUNTERPARTY until it expires. A network rail is used only when you have connected your own membership for that network. It is chosen after the transaction create returns, so it is null in that response. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -165,7 +165,7 @@ public Optional counterpartyVasp() { } /** - * The originator_data field. + * Originator party. For an inbound transfer the counterparty's values are masked until ownership is confirmed. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -175,7 +175,7 @@ public Optional> originatorData() { } /** - * The beneficiary_data field. + * Beneficiary party. For an inbound transfer the counterparty's values are masked until ownership is confirmed. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -184,6 +184,56 @@ public Optional> beneficiaryData() { return Json.optional(json, "beneficiary_data", Conv.OBJECT); } + /** + * The name comparison that decided the transfer, or null until one ran. While the parties are masked, and on the other customer's copy of an INTERNAL exchange, transfer_name, compared_name, matched_names and mismatched_names are hidden. score and matched can disagree: matched follows the strictness, score is the similarity of the two full names. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameMatch() { + return Json.optional(json, "name_match", Conv.model(TravelRuleNameMatch::fromJson)); + } + + /** + * Field-by-field comparison of the transfer's party data with what the other side knows about the wallet holder. It names fields and results, never values, so it stays readable while the counterparty's data is masked. Null until a comparison ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataMatch() { + return Json.optional(json, "data_match", Conv.model(TravelRuleDataMatch::fromJson)); + } + + /** + * Shortcut for data_match.mismatched_fields. Empty when nothing differs. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> mismatchedFields() { + return Json.optional(json, "mismatched_fields", Conv.list(Conv.STRING)); + } + + /** + * Audit record of the moment the counterparty's data was unmasked. Null while the data is still masked, and for outbound transfers. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataUnmasked() { + return Json.optional(json, "data_unmasked", Conv.model(TravelRuleDataUnmasked::fromJson)); + } + + /** + * True while the counterparty's personal data is masked in this response. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataMasked() { + return Json.optional(json, "data_masked", Conv.BOOLEAN); + } + /** * The created_at field. * @@ -194,6 +244,56 @@ public Optional createdAt() { return Json.optional(json, "created_at", Conv.DATE_TIME); } + /** + * Where the latest message this transfer owes the other side stands. Null when the transfer never owed one (for example below threshold_amount, a declared self-hosted wallet, or a sunrise registration). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional delivery() { + return Json.optional(json, "delivery", Conv.model(TravelRuleDelivery::fromJson)); + } + + /** + * How Didit looked for the counterparty, one entry per step that ran. Empty when no discovery ran, for example on an inbound transfer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> discovery() { + return Json.optional(json, "discovery", Conv.list(Conv.model(TravelRuleDiscoveryStep::fromJson))); + } + + /** + * Why a COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE transfer is stuck; "" for any other status. A failed delivery decides it first, on COUNTERPARTY_VASP_NOT_REACHABLE only: email_undeliverable (the mail provider refused the compliance email and no address is left to try: when it was sent, on a reminder, or as a permanent bounce reported later) or delivery_failed (the rail that was delivering failed for good: a network rail refused the message or ran out of retries, a TRP identity host did not answer, or the counterparty's TRP host did not accept the inquiry). Otherwise the discovery trace decides it: awaiting_end_user (no step named a counterparty Didit can reach), directory_match_no_route (a step named a directory VASP that has no route), picked_member_without_proven_wallet (the end user picked a Didit customer that has not proven control of the wallet), or no_directory_match when the trace gives no other reason. A resend moves the transfer on, so the reason clears. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * What moves the transfer on; [] for any status but COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE. email_undeliverable offers update_compliance_email, then resend. delivery_failed offers resend. A discovery reason offers select_counterparty_vasp, declare_self_hosted, add_vasp_hint and resend. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> nextActions() { + return Json.optional(json, "next_actions", Conv.list(Conv.model(TravelRuleNextAction::fromJson))); + } + + /** + * How the transfer's chain names its two wallets, when that is more than one address paying another. Empty ({}) for an account-chain transfer (EVM, Solana, Tron), where the two addresses are the wallets. Matching, routing and the address book read the wallets from here first. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional chainDetails() { + return Json.optional(json, "chain_details", Conv.model(TravelRuleChainDetails::fromJson)); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -238,7 +338,7 @@ public Builder direction(TravelRuleTransferDetail.Direction direction) { } /** - * The rail field. + * The rail the transfer was routed over, or null when no rail was selected (for example below the threshold, a self-hosted counterparty wallet, an after-deposit registration, or no reachable counterparty). INTERNAL means both sides are Didit applications; EMAIL means the counterparty was reached by an emailed pickup link. TEST_VASP means the Didit Test VASP answered a sandbox application's transfer. Over GTR, VERIFYVASP and SYGNA, Didit reads the counterparty's answer from the network's response or by polling it every 5 minutes. TRUST only sends, so a TRUST transfer stays AWAITING_COUNTERPARTY until it expires. A network rail is used only when you have connected your own membership for that network. It is chosen after the transaction create returns, so it is null in that response. * * @param rail the value, or {@code null} to send JSON null * @return this builder @@ -318,7 +418,7 @@ public Builder counterpartyVasp(TravelRuleTransferDetail.@Nullable CounterpartyV } /** - * The originator_data field. + * Originator party. For an inbound transfer the counterparty's values are masked until ownership is confirmed. * * @param originatorData the value * @return this builder @@ -328,7 +428,7 @@ public Builder originatorData(Map originatorData) { } /** - * The beneficiary_data field. + * Beneficiary party. For an inbound transfer the counterparty's values are masked until ownership is confirmed. * * @param beneficiaryData the value * @return this builder @@ -337,6 +437,56 @@ public Builder beneficiaryData(Map beneficiaryData) { return setAny("beneficiary_data", beneficiaryData); } + /** + * The name comparison that decided the transfer, or null until one ran. While the parties are masked, and on the other customer's copy of an INTERNAL exchange, transfer_name, compared_name, matched_names and mismatched_names are hidden. score and matched can disagree: matched follows the strictness, score is the similarity of the two full names. + * + * @param nameMatch the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nameMatch(@Nullable TravelRuleNameMatch nameMatch) { + return setNullable("name_match", Conv.model(TravelRuleNameMatch::fromJson), nameMatch); + } + + /** + * Field-by-field comparison of the transfer's party data with what the other side knows about the wallet holder. It names fields and results, never values, so it stays readable while the counterparty's data is masked. Null until a comparison ran. + * + * @param dataMatch the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dataMatch(@Nullable TravelRuleDataMatch dataMatch) { + return setNullable("data_match", Conv.model(TravelRuleDataMatch::fromJson), dataMatch); + } + + /** + * Shortcut for data_match.mismatched_fields. Empty when nothing differs. + * + * @param mismatchedFields the value + * @return this builder + */ + public Builder mismatchedFields(List mismatchedFields) { + return set("mismatched_fields", Conv.list(Conv.STRING), mismatchedFields); + } + + /** + * Audit record of the moment the counterparty's data was unmasked. Null while the data is still masked, and for outbound transfers. + * + * @param dataUnmasked the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dataUnmasked(@Nullable TravelRuleDataUnmasked dataUnmasked) { + return setNullable("data_unmasked", Conv.model(TravelRuleDataUnmasked::fromJson), dataUnmasked); + } + + /** + * True while the counterparty's personal data is masked in this response. + * + * @param dataMasked the value + * @return this builder + */ + public Builder dataMasked(boolean dataMasked) { + return set("data_masked", Conv.BOOLEAN, dataMasked); + } + /** * The created_at field. * @@ -347,6 +497,56 @@ public Builder createdAt(OffsetDateTime createdAt) { return set("created_at", Conv.DATE_TIME, createdAt); } + /** + * Where the latest message this transfer owes the other side stands. Null when the transfer never owed one (for example below threshold_amount, a declared self-hosted wallet, or a sunrise registration). + * + * @param delivery the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder delivery(@Nullable TravelRuleDelivery delivery) { + return setNullable("delivery", Conv.model(TravelRuleDelivery::fromJson), delivery); + } + + /** + * How Didit looked for the counterparty, one entry per step that ran. Empty when no discovery ran, for example on an inbound transfer. + * + * @param discovery the value + * @return this builder + */ + public Builder discovery(List discovery) { + return set("discovery", Conv.list(Conv.model(TravelRuleDiscoveryStep::fromJson)), discovery); + } + + /** + * Why a COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE transfer is stuck; "" for any other status. A failed delivery decides it first, on COUNTERPARTY_VASP_NOT_REACHABLE only: email_undeliverable (the mail provider refused the compliance email and no address is left to try: when it was sent, on a reminder, or as a permanent bounce reported later) or delivery_failed (the rail that was delivering failed for good: a network rail refused the message or ran out of retries, a TRP identity host did not answer, or the counterparty's TRP host did not accept the inquiry). Otherwise the discovery trace decides it: awaiting_end_user (no step named a counterparty Didit can reach), directory_match_no_route (a step named a directory VASP that has no route), picked_member_without_proven_wallet (the end user picked a Didit customer that has not proven control of the wallet), or no_directory_match when the trace gives no other reason. A resend moves the transfer on, so the reason clears. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck). + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * What moves the transfer on; [] for any status but COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE. email_undeliverable offers update_compliance_email, then resend. delivery_failed offers resend. A discovery reason offers select_counterparty_vasp, declare_self_hosted, add_vasp_hint and resend. + * + * @param nextActions the value + * @return this builder + */ + public Builder nextActions(List nextActions) { + return set("next_actions", Conv.list(Conv.model(TravelRuleNextAction::fromJson)), nextActions); + } + + /** + * How the transfer's chain names its two wallets, when that is more than one address paying another. Empty ({}) for an account-chain transfer (EVM, Solana, Tron), where the two addresses are the wallets. Matching, routing and the address book read the wallets from here first. + * + * @param chainDetails the value + * @return this builder + */ + public Builder chainDetails(TravelRuleChainDetails chainDetails) { + return set("chain_details", Conv.model(TravelRuleChainDetails::fromJson), chainDetails); + } + /** * Builds the value. * @@ -480,6 +680,7 @@ public boolean isKnown() { } /** + * The rail the transfer was routed over, or null when no rail was selected (for example below the threshold, a self-hosted counterparty wallet, an after-deposit registration, or no reachable counterparty). INTERNAL means both sides are Didit applications; EMAIL means the counterparty was reached by an emailed pickup link. TEST_VASP means the Didit Test VASP answered a sandbox application's transfer. Over GTR, VERIFYVASP and SYGNA, Didit reads the counterparty's answer from the network's response or by polling it every 5 minutes. TRUST only sends, so a TRUST transfer stays AWAITING_COUNTERPARTY until it expires. A network rail is used only when you have connected your own membership for that network. It is chosen after the transaction create returns, so it is null in that response. * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -493,10 +694,22 @@ public static final class Rail extends StringEnum { /** The value {@code "GTR"}. */ public static final Rail GTR = new Rail("GTR"); + /** The value {@code "TRUST"}. */ + public static final Rail TRUST = new Rail("TRUST"); + + /** The value {@code "VERIFYVASP"}. */ + public static final Rail VERIFYVASP = new Rail("VERIFYVASP"); + + /** The value {@code "SYGNA"}. */ + public static final Rail SYGNA = new Rail("SYGNA"); + /** The value {@code "EMAIL"}. */ public static final Rail EMAIL = new Rail("EMAIL"); - private static final List KNOWN_VALUES = List.of(INTERNAL, TRP, GTR, EMAIL); + /** The value {@code "TEST_VASP"}. */ + public static final Rail TEST_VASP = new Rail("TEST_VASP"); + + private static final List KNOWN_VALUES = List.of(INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL, TEST_VASP); private Rail(String value) { super(value); @@ -528,7 +741,7 @@ public boolean isKnown() { } public static final class Timeline extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("status", "at", "detail"); + private static final Set KNOWN_FIELDS = Set.of("status", "at", "detail", "name_match", "reason"); private Timeline(ObjectNode json, Map> files) { super(json, files); @@ -593,6 +806,26 @@ public Optional detail() { return Json.optional(json, "detail", Conv.STRING); } + /** + * The name comparison this entry decided, next to detail. Absent on entries that compared no name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameMatch() { + return Json.optional(json, "name_match", Conv.model(TravelRuleNameMatch::fromJson)); + } + + /** + * Present only on the entry of a transition a failed delivery caused: email_undeliverable or delivery_failed, the value the transfer's reason reports. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -636,6 +869,26 @@ public Builder detail(String detail) { return set("detail", Conv.STRING, detail); } + /** + * The name comparison this entry decided, next to detail. Absent on entries that compared no name. + * + * @param nameMatch the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder nameMatch(@Nullable TravelRuleNameMatch nameMatch) { + return setNullable("name_match", Conv.model(TravelRuleNameMatch::fromJson), nameMatch); + } + + /** + * Present only on the entry of a transition a failed delivery caused: email_undeliverable or delivery_failed, the value the transfer's reason reports. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + /** * Builds the value. * @@ -653,7 +906,7 @@ protected Builder self() { } public static final class CounterpartyVasp extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("name", "lei", "dd_score"); + private static final Set KNOWN_FIELDS = Set.of("name", "lei", "dd_score", "dd_score_breakdown", "dd_assessed_at"); private CounterpartyVasp(ObjectNode json, Map> files) { super(json, files); @@ -709,7 +962,7 @@ public Optional lei() { } /** - * The dd_score field. + * Due-diligence score from 0 to 100, where higher is better. Same scale as in the VASP directory. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -718,6 +971,26 @@ public Optional ddScore() { return Json.optional(json, "dd_score", Conv.LONG); } + /** + * Per-component contributions to dd_score, one object per component as in the VASP directory (jurisdiction, verification, protocol, lei, history, essentials, responsiveness), each {points, max, reason, detail}. {} until the counterparty has been assessed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> ddScoreBreakdown() { + return Json.optional(json, "dd_score_breakdown", Conv.map(Conv.model(TravelRuleTransferDetail.CounterpartyVasp.DdScoreBreakdownValue::fromJson))); + } + + /** + * When the due-diligence assessment last ran. Null until the counterparty has been assessed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ddAssessedAt() { + return Json.optional(json, "dd_assessed_at", Conv.DATE_TIME); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -752,7 +1025,7 @@ public Builder lei(String lei) { } /** - * The dd_score field. + * Due-diligence score from 0 to 100, where higher is better. Same scale as in the VASP directory. * * @param ddScore the value * @return this builder @@ -761,6 +1034,26 @@ public Builder ddScore(long ddScore) { return set("dd_score", Conv.LONG, ddScore); } + /** + * Per-component contributions to dd_score, one object per component as in the VASP directory (jurisdiction, verification, protocol, lei, history, essentials, responsiveness), each {points, max, reason, detail}. {} until the counterparty has been assessed. + * + * @param ddScoreBreakdown the value + * @return this builder + */ + public Builder ddScoreBreakdown(Map ddScoreBreakdown) { + return set("dd_score_breakdown", Conv.map(Conv.model(TravelRuleTransferDetail.CounterpartyVasp.DdScoreBreakdownValue::fromJson)), ddScoreBreakdown); + } + + /** + * When the due-diligence assessment last ran. Null until the counterparty has been assessed. + * + * @param ddAssessedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder ddAssessedAt(@Nullable OffsetDateTime ddAssessedAt) { + return setNullable("dd_assessed_at", Conv.DATE_TIME, ddAssessedAt); + } + /** * Builds the value. * @@ -775,5 +1068,150 @@ protected Builder self() { return this; } } + + public static final class DdScoreBreakdownValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("points", "max", "reason", "detail"); + + private DdScoreBreakdownValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code DdScoreBreakdownValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DdScoreBreakdownValue fromJson(JsonNode json) { + return new DdScoreBreakdownValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Points awarded for this component. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional points() { + return Json.optional(json, "points", Conv.DOUBLE); + } + + /** + * Maximum points this component can award. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional max() { + return Json.optional(json, "max", Conv.LONG); + } + + /** + * Machine-readable code of the component's verdict. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * Human-readable reason for the awarded points. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional detail() { + return Json.optional(json, "detail", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link DdScoreBreakdownValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(DdScoreBreakdownValue from) { + super(from); + } + + /** + * Points awarded for this component. + * + * @param points the value + * @return this builder + */ + public Builder points(double points) { + return set("points", Conv.DOUBLE, points); + } + + /** + * Maximum points this component can award. + * + * @param max the value + * @return this builder + */ + public Builder max(long max) { + return set("max", Conv.LONG, max); + } + + /** + * Machine-readable code of the component's verdict. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * Human-readable reason for the awarded points. + * + * @param detail the value + * @return this builder + */ + public Builder detail(String detail) { + return set("detail", Conv.STRING, detail); + } + + /** + * Builds the value. + * + * @return a new immutable {@code DdScoreBreakdownValue} + */ + public DdScoreBreakdownValue build() { + return new DdScoreBreakdownValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } } } diff --git a/src/main/java/me/didit/types/TravelRuleTransferPatchRequest.java b/src/main/java/me/didit/types/TravelRuleTransferPatchRequest.java index 3817bc2..68781ab 100644 --- a/src/main/java/me/didit/types/TravelRuleTransferPatchRequest.java +++ b/src/main/java/me/didit/types/TravelRuleTransferPatchRequest.java @@ -13,9 +13,11 @@ import me.didit.core.JsonModel; import me.didit.core.StringEnum; -/** Provide exactly one of: payment_txn_id (finish), action=cancel, or action=resend. */ +/** + * Provide exactly one of: payment_txn_id (finish), action=cancel, action=resend, action=update_compliance_email with compliance_email, or action=update_originator_wallet with wallet_address. + */ public final class TravelRuleTransferPatchRequest extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("payment_txn_id", "action"); + private static final Set KNOWN_FIELDS = Set.of("payment_txn_id", "action", "compliance_email", "wallet_address", "memo"); private TravelRuleTransferPatchRequest(ObjectNode json, Map> files) { super(json, files); @@ -61,7 +63,7 @@ public Optional paymentTxnId() { } /** - * "cancel" cancels a non-terminal transfer. "resend" re-runs routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA. + * "cancel" cancels a non-terminal transfer. "resend" re-runs routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA. "update_compliance_email" replaces the compliance email the request goes to, on an outbound transfer in one of those three statuses; send "resend" afterwards. "update_originator_wallet" records the wallet the funds actually left from (wallet_address, memo). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -70,6 +72,36 @@ public Optional action() { return Json.optional(json, "action", Conv.enumeration(TravelRuleTransferPatchRequest.Action::of)); } + /** + * Required with action: update_compliance_email. Kept as this transfer's beneficiary_data.counterparty_vasp_email: it belongs to this transfer only and joins no directory entry and no contact list. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional complianceEmail() { + return Json.optional(json, "compliance_email", Conv.STRING); + } + + /** + * With action: update_originator_wallet: the wallet the funds left from. When the transfer's UTXO inputs are known it must be one of them. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional walletAddress() { + return Json.optional(json, "wallet_address", Conv.STRING); + } + + /** + * With action: update_originator_wallet: the wallet's memo or tag on a memo network. Optional. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional memo() { + return Json.optional(json, "memo", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -94,7 +126,7 @@ public Builder paymentTxnId(String paymentTxnId) { } /** - * "cancel" cancels a non-terminal transfer. "resend" re-runs routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA. + * "cancel" cancels a non-terminal transfer. "resend" re-runs routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA. "update_compliance_email" replaces the compliance email the request goes to, on an outbound transfer in one of those three statuses; send "resend" afterwards. "update_originator_wallet" records the wallet the funds actually left from (wallet_address, memo). * * @param action the value * @return this builder @@ -103,6 +135,36 @@ public Builder action(TravelRuleTransferPatchRequest.Action action) { return set("action", Conv.enumeration(TravelRuleTransferPatchRequest.Action::of), action); } + /** + * Required with action: update_compliance_email. Kept as this transfer's beneficiary_data.counterparty_vasp_email: it belongs to this transfer only and joins no directory entry and no contact list. + * + * @param complianceEmail the value + * @return this builder + */ + public Builder complianceEmail(String complianceEmail) { + return set("compliance_email", Conv.STRING, complianceEmail); + } + + /** + * With action: update_originator_wallet: the wallet the funds left from. When the transfer's UTXO inputs are known it must be one of them. + * + * @param walletAddress the value + * @return this builder + */ + public Builder walletAddress(String walletAddress) { + return set("wallet_address", Conv.STRING, walletAddress); + } + + /** + * With action: update_originator_wallet: the wallet's memo or tag on a memo network. Optional. + * + * @param memo the value + * @return this builder + */ + public Builder memo(String memo) { + return set("memo", Conv.STRING, memo); + } + /** * Builds the value. * @@ -119,7 +181,7 @@ protected Builder self() { } /** - * "cancel" cancels a non-terminal transfer. "resend" re-runs routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA. + * "cancel" cancels a non-terminal transfer. "resend" re-runs routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA. "update_compliance_email" replaces the compliance email the request goes to, on an outbound transfer in one of those three statuses; send "resend" afterwards. "update_originator_wallet" records the wallet the funds actually left from (wallet_address, memo). * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -130,7 +192,13 @@ public static final class Action extends StringEnum { /** The value {@code "resend"}. */ public static final Action RESEND = new Action("resend"); - private static final List KNOWN_VALUES = List.of(CANCEL, RESEND); + /** The value {@code "update_compliance_email"}. */ + public static final Action UPDATE_COMPLIANCE_EMAIL = new Action("update_compliance_email"); + + /** The value {@code "update_originator_wallet"}. */ + public static final Action UPDATE_ORIGINATOR_WALLET = new Action("update_originator_wallet"); + + private static final List KNOWN_VALUES = List.of(CANCEL, RESEND, UPDATE_COMPLIANCE_EMAIL, UPDATE_ORIGINATOR_WALLET); private Action(String value) { super(value); diff --git a/src/main/java/me/didit/types/TravelRuleTransferRow.java b/src/main/java/me/didit/types/TravelRuleTransferRow.java new file mode 100644 index 0000000..d271740 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleTransferRow.java @@ -0,0 +1,838 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * One Travel Rule transfer as the transfers list shows it: VASP names, wallets and memos only, never the originator or beneficiary person data. + */ +public final class TravelRuleTransferRow extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "transaction_id", "txn_id", "txn_date", "kind", "direction", "status", "reason", "rail", "asset", "amount", "originator_vasp", "beneficiary_vasp", "originator_wallet", "beneficiary_wallet", "originator_memo", "beneficiary_memo", "originator_wallet_type", "beneficiary_wallet_type", "matched_transaction_id", "deadline_at", "created_at"); + + private TravelRuleTransferRow(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleTransferRow} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleTransferRow fromJson(JsonNode json) { + return new TravelRuleTransferRow(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The transfer's id. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The Didit transaction the transfer belongs to. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional transactionId() { + return Json.optional(json, "transaction_id", Conv.STRING); + } + + /** + * Your own id for the transaction. Transfers Didit books for you, such as a Didit Test VASP inbound transfer, carry a generated one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txnId() { + return Json.optional(json, "txn_id", Conv.STRING); + } + + /** + * The txn_date field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txnDate() { + return Json.optional(json, "txn_date", Conv.DATE_TIME); + } + + /** + * withdrawal: outbound. message: an inbound transfer a rail created from a counterparty's message (a TRP inquiry, the INTERNAL inbound leg, or a GTR, VerifyVASP or Sygna Bridge message). deposit: any other inbound transfer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(TravelRuleTransferRow.Kind::of)); + } + + /** + * The direction field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional direction() { + return Json.optional(json, "direction", Conv.enumeration(TravelRuleTransferRow.Direction::of)); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(TravelRuleTransferRow.Status::of)); + } + + /** + * Why a COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE transfer is stuck, the same value as the transfer's own reason, so the list tells a bounced compliance email from a counterparty with no route without opening each transfer. "" for any other status. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * The rail field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rail() { + return Json.optional(json, "rail", Conv.enumeration(TravelRuleTransferRow.Rail::of)); + } + + /** + * The transaction's currency. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional asset() { + return Json.optional(json, "asset", Conv.STRING); + } + + /** + * The amount as a plain decimal, for example "1.25". + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional amount() { + return Json.optional(json, "amount", Conv.STRING); + } + + /** + * Outbound: your VASP's legal name. Inbound: the counterparty's name, from its directory entry, the Didit network member, the TRP sender, the Didit Test VASP, or the name the transfer declared. Empty when none is known. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originatorVasp() { + return Json.optional(json, "originator_vasp", Conv.STRING); + } + + /** + * Outbound: the counterparty's name, worked out the same way. Inbound: your VASP's legal name. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional beneficiaryVasp() { + return Json.optional(json, "beneficiary_vasp", Conv.STRING); + } + + /** + * The originator_wallet field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originatorWallet() { + return Json.optional(json, "originator_wallet", Conv.STRING); + } + + /** + * The beneficiary_wallet field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional beneficiaryWallet() { + return Json.optional(json, "beneficiary_wallet", Conv.STRING); + } + + /** + * Memo or destination tag, empty on chains without one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originatorMemo() { + return Json.optional(json, "originator_memo", Conv.STRING); + } + + /** + * The beneficiary_memo field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional beneficiaryMemo() { + return Json.optional(json, "beneficiary_memo", Conv.STRING); + } + + /** + * vasp, self_hosted (declared unhosted) or own (a wallet your address book records as yours). Your own side is always vasp. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional originatorWalletType() { + return Json.optional(json, "originator_wallet_type", Conv.enumeration(TravelRuleTransferRow.OriginatorWalletType::of)); + } + + /** + * The beneficiary_wallet_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional beneficiaryWalletType() { + return Json.optional(json, "beneficiary_wallet_type", Conv.enumeration(TravelRuleTransferRow.BeneficiaryWalletType::of)); + } + + /** + * The other side of a deposit you matched to an announced transfer by hand. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchedTransactionId() { + return Json.optional(json, "matched_transaction_id", Conv.STRING); + } + + /** + * When the transfer expires if it is still waiting on its counterparty. Set on every transfer you create with POST /v3/transactions/ that needs an exchange, outbound or inbound. Null below the threshold, on a Lightning payment the Travel Rule does not apply to, and on inbound transfers Didit creates when a counterparty reaches you (an inbound TRP inquiry, a network message or the INTERNAL inbound leg). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional deadlineAt() { + return Json.optional(json, "deadline_at", Conv.DATE_TIME); + } + + /** + * The created_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleTransferRow}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleTransferRow from) { + super(from); + } + + /** + * The transfer's id. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The Didit transaction the transfer belongs to. + * + * @param transactionId the value + * @return this builder + */ + public Builder transactionId(String transactionId) { + return set("transaction_id", Conv.STRING, transactionId); + } + + /** + * Your own id for the transaction. Transfers Didit books for you, such as a Didit Test VASP inbound transfer, carry a generated one. + * + * @param txnId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder txnId(@Nullable String txnId) { + return setNullable("txn_id", Conv.STRING, txnId); + } + + /** + * The txn_date field. + * + * @param txnDate the value + * @return this builder + */ + public Builder txnDate(OffsetDateTime txnDate) { + return set("txn_date", Conv.DATE_TIME, txnDate); + } + + /** + * withdrawal: outbound. message: an inbound transfer a rail created from a counterparty's message (a TRP inquiry, the INTERNAL inbound leg, or a GTR, VerifyVASP or Sygna Bridge message). deposit: any other inbound transfer. + * + * @param kind the value + * @return this builder + */ + public Builder kind(TravelRuleTransferRow.Kind kind) { + return set("kind", Conv.enumeration(TravelRuleTransferRow.Kind::of), kind); + } + + /** + * The direction field. + * + * @param direction the value + * @return this builder + */ + public Builder direction(TravelRuleTransferRow.Direction direction) { + return set("direction", Conv.enumeration(TravelRuleTransferRow.Direction::of), direction); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(TravelRuleTransferRow.Status status) { + return set("status", Conv.enumeration(TravelRuleTransferRow.Status::of), status); + } + + /** + * Why a COUNTERPARTY_VASP_NOT_FOUND or COUNTERPARTY_VASP_NOT_REACHABLE transfer is stuck, the same value as the transfer's own reason, so the list tells a bounced compliance email from a counterparty with no route without opening each transfer. "" for any other status. See [Why a transfer is stuck](/transaction-monitoring/travel-rule#why-a-transfer-is-stuck). + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * The rail field. + * + * @param rail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder rail(TravelRuleTransferRow.@Nullable Rail rail) { + return setNullable("rail", Conv.enumeration(TravelRuleTransferRow.Rail::of), rail); + } + + /** + * The transaction's currency. + * + * @param asset the value + * @return this builder + */ + public Builder asset(String asset) { + return set("asset", Conv.STRING, asset); + } + + /** + * The amount as a plain decimal, for example "1.25". + * + * @param amount the value + * @return this builder + */ + public Builder amount(String amount) { + return set("amount", Conv.STRING, amount); + } + + /** + * Outbound: your VASP's legal name. Inbound: the counterparty's name, from its directory entry, the Didit network member, the TRP sender, the Didit Test VASP, or the name the transfer declared. Empty when none is known. + * + * @param originatorVasp the value + * @return this builder + */ + public Builder originatorVasp(String originatorVasp) { + return set("originator_vasp", Conv.STRING, originatorVasp); + } + + /** + * Outbound: the counterparty's name, worked out the same way. Inbound: your VASP's legal name. + * + * @param beneficiaryVasp the value + * @return this builder + */ + public Builder beneficiaryVasp(String beneficiaryVasp) { + return set("beneficiary_vasp", Conv.STRING, beneficiaryVasp); + } + + /** + * The originator_wallet field. + * + * @param originatorWallet the value + * @return this builder + */ + public Builder originatorWallet(String originatorWallet) { + return set("originator_wallet", Conv.STRING, originatorWallet); + } + + /** + * The beneficiary_wallet field. + * + * @param beneficiaryWallet the value + * @return this builder + */ + public Builder beneficiaryWallet(String beneficiaryWallet) { + return set("beneficiary_wallet", Conv.STRING, beneficiaryWallet); + } + + /** + * Memo or destination tag, empty on chains without one. + * + * @param originatorMemo the value + * @return this builder + */ + public Builder originatorMemo(String originatorMemo) { + return set("originator_memo", Conv.STRING, originatorMemo); + } + + /** + * The beneficiary_memo field. + * + * @param beneficiaryMemo the value + * @return this builder + */ + public Builder beneficiaryMemo(String beneficiaryMemo) { + return set("beneficiary_memo", Conv.STRING, beneficiaryMemo); + } + + /** + * vasp, self_hosted (declared unhosted) or own (a wallet your address book records as yours). Your own side is always vasp. + * + * @param originatorWalletType the value + * @return this builder + */ + public Builder originatorWalletType(TravelRuleTransferRow.OriginatorWalletType originatorWalletType) { + return set("originator_wallet_type", Conv.enumeration(TravelRuleTransferRow.OriginatorWalletType::of), originatorWalletType); + } + + /** + * The beneficiary_wallet_type field. + * + * @param beneficiaryWalletType the value + * @return this builder + */ + public Builder beneficiaryWalletType(TravelRuleTransferRow.BeneficiaryWalletType beneficiaryWalletType) { + return set("beneficiary_wallet_type", Conv.enumeration(TravelRuleTransferRow.BeneficiaryWalletType::of), beneficiaryWalletType); + } + + /** + * The other side of a deposit you matched to an announced transfer by hand. + * + * @param matchedTransactionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder matchedTransactionId(@Nullable String matchedTransactionId) { + return setNullable("matched_transaction_id", Conv.STRING, matchedTransactionId); + } + + /** + * When the transfer expires if it is still waiting on its counterparty. Set on every transfer you create with POST /v3/transactions/ that needs an exchange, outbound or inbound. Null below the threshold, on a Lightning payment the Travel Rule does not apply to, and on inbound transfers Didit creates when a counterparty reaches you (an inbound TRP inquiry, a network message or the INTERNAL inbound leg). + * + * @param deadlineAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder deadlineAt(@Nullable OffsetDateTime deadlineAt) { + return setNullable("deadline_at", Conv.DATE_TIME, deadlineAt); + } + + /** + * The created_at field. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleTransferRow} + */ + public TravelRuleTransferRow build() { + return new TravelRuleTransferRow(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * withdrawal: outbound. message: an inbound transfer a rail created from a counterparty's message (a TRP inquiry, the INTERNAL inbound leg, or a GTR, VerifyVASP or Sygna Bridge message). deposit: any other inbound transfer. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "withdrawal"}. */ + public static final Kind WITHDRAWAL = new Kind("withdrawal"); + + /** The value {@code "deposit"}. */ + public static final Kind DEPOSIT = new Kind("deposit"); + + /** The value {@code "message"}. */ + public static final Kind MESSAGE = new Kind("message"); + + private static final List KNOWN_VALUES = List.of(WITHDRAWAL, DEPOSIT, MESSAGE); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Direction extends StringEnum { + /** The value {@code "INBOUND"}. */ + public static final Direction INBOUND = new Direction("INBOUND"); + + /** The value {@code "OUTBOUND"}. */ + public static final Direction OUTBOUND = new Direction("OUTBOUND"); + + private static final List KNOWN_VALUES = List.of(INBOUND, OUTBOUND); + + private Direction(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Direction of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Direction::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "AWAITING_COUNTERPARTY"}. */ + public static final Status AWAITING_COUNTERPARTY = new Status("AWAITING_COUNTERPARTY"); + + /** The value {@code "ON_HOLD"}. */ + public static final Status ON_HOLD = new Status("ON_HOLD"); + + /** The value {@code "COMPLETED"}. */ + public static final Status COMPLETED = new Status("COMPLETED"); + + /** The value {@code "FINISHED"}. */ + public static final Status FINISHED = new Status("FINISHED"); + + /** The value {@code "CANCELLED"}. */ + public static final Status CANCELLED = new Status("CANCELLED"); + + /** The value {@code "EXPIRED"}. */ + public static final Status EXPIRED = new Status("EXPIRED"); + + /** The value {@code "COUNTERPARTY_MISMATCHED_DATA"}. */ + public static final Status COUNTERPARTY_MISMATCHED_DATA = new Status("COUNTERPARTY_MISMATCHED_DATA"); + + /** The value {@code "COUNTERPARTY_VASP_NOT_FOUND"}. */ + public static final Status COUNTERPARTY_VASP_NOT_FOUND = new Status("COUNTERPARTY_VASP_NOT_FOUND"); + + /** The value {@code "COUNTERPARTY_VASP_NOT_REACHABLE"}. */ + public static final Status COUNTERPARTY_VASP_NOT_REACHABLE = new Status("COUNTERPARTY_VASP_NOT_REACHABLE"); + + /** The value {@code "UNCONFIRMED_OWNERSHIP"}. */ + public static final Status UNCONFIRMED_OWNERSHIP = new Status("UNCONFIRMED_OWNERSHIP"); + + /** The value {@code "COUNTERPARTY_VASP_GENERAL_DECLINE"}. */ + public static final Status COUNTERPARTY_VASP_GENERAL_DECLINE = new Status("COUNTERPARTY_VASP_GENERAL_DECLINE"); + + /** The value {@code "NOT_ENOUGH_COUNTERPARTY_DATA"}. */ + public static final Status NOT_ENOUGH_COUNTERPARTY_DATA = new Status("NOT_ENOUGH_COUNTERPARTY_DATA"); + + /** The value {@code "NOT_APPLICABLE"}. */ + public static final Status NOT_APPLICABLE = new Status("NOT_APPLICABLE"); + + private static final List KNOWN_VALUES = List.of(AWAITING_COUNTERPARTY, ON_HOLD, COMPLETED, FINISHED, CANCELLED, EXPIRED, COUNTERPARTY_MISMATCHED_DATA, COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, UNCONFIRMED_OWNERSHIP, COUNTERPARTY_VASP_GENERAL_DECLINE, NOT_ENOUGH_COUNTERPARTY_DATA, NOT_APPLICABLE); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Rail extends StringEnum { + /** The value {@code "INTERNAL"}. */ + public static final Rail INTERNAL = new Rail("INTERNAL"); + + /** The value {@code "TRP"}. */ + public static final Rail TRP = new Rail("TRP"); + + /** The value {@code "GTR"}. */ + public static final Rail GTR = new Rail("GTR"); + + /** The value {@code "TRUST"}. */ + public static final Rail TRUST = new Rail("TRUST"); + + /** The value {@code "VERIFYVASP"}. */ + public static final Rail VERIFYVASP = new Rail("VERIFYVASP"); + + /** The value {@code "SYGNA"}. */ + public static final Rail SYGNA = new Rail("SYGNA"); + + /** The value {@code "EMAIL"}. */ + public static final Rail EMAIL = new Rail("EMAIL"); + + /** The value {@code "TEST_VASP"}. */ + public static final Rail TEST_VASP = new Rail("TEST_VASP"); + + private static final List KNOWN_VALUES = List.of(INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL, TEST_VASP); + + private Rail(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Rail of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Rail::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * vasp, self_hosted (declared unhosted) or own (a wallet your address book records as yours). Your own side is always vasp. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class OriginatorWalletType extends StringEnum { + /** The value {@code "vasp"}. */ + public static final OriginatorWalletType VASP = new OriginatorWalletType("vasp"); + + /** The value {@code "self_hosted"}. */ + public static final OriginatorWalletType SELF_HOSTED = new OriginatorWalletType("self_hosted"); + + /** The value {@code "own"}. */ + public static final OriginatorWalletType OWN = new OriginatorWalletType("own"); + + private static final List KNOWN_VALUES = List.of(VASP, SELF_HOSTED, OWN); + + private OriginatorWalletType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static OriginatorWalletType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, OriginatorWalletType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class BeneficiaryWalletType extends StringEnum { + /** The value {@code "vasp"}. */ + public static final BeneficiaryWalletType VASP = new BeneficiaryWalletType("vasp"); + + /** The value {@code "self_hosted"}. */ + public static final BeneficiaryWalletType SELF_HOSTED = new BeneficiaryWalletType("self_hosted"); + + /** The value {@code "own"}. */ + public static final BeneficiaryWalletType OWN = new BeneficiaryWalletType("own"); + + private static final List KNOWN_VALUES = List.of(VASP, SELF_HOSTED, OWN); + + private BeneficiaryWalletType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static BeneficiaryWalletType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, BeneficiaryWalletType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleTransfersListParams.java b/src/main/java/me/didit/types/TravelRuleTransfersListParams.java new file mode 100644 index 0000000..2f84f91 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleTransfersListParams.java @@ -0,0 +1,280 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code travel_rule.transfers.list}. */ +public final class TravelRuleTransfersListParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("direction", "status", "rail", "kind", "currency", "start_date", "end_date", "counterparty", "limit", "offset"); + + private TravelRuleTransfersListParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleTransfersListParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleTransfersListParams fromJson(JsonNode json) { + return new TravelRuleTransfersListParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * INBOUND, OUTBOUND, or both comma-separated. Case-sensitive. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional direction() { + return Json.optional(json, "direction", Conv.STRING); + } + + /** + * One or more transfer statuses, comma-separated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * One or more of INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL, TEST_VASP, comma-separated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rail() { + return Json.optional(json, "rail", Conv.STRING); + } + + /** + * withdrawal, deposit or message, comma-separated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.STRING); + } + + /** + * The transaction's currency, case-insensitive. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional currency() { + return Json.optional(json, "currency", Conv.STRING); + } + + /** + * Transfers created on or after this day (UTC), YYYY-MM-DD. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional startDate() { + return Json.optional(json, "start_date", Conv.DATE); + } + + /** + * Transfers created on or before this day (UTC), YYYY-MM-DD. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional endDate() { + return Json.optional(json, "end_date", Conv.DATE); + } + + /** + * The id of a directory entry or Didit network member from GET /v3/travel-rule/vasps/: your transfers with that counterparty. A value that is not a valid id answers 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional counterparty() { + return Json.optional(json, "counterparty", Conv.STRING); + } + + /** + * Page size, default 50. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Rows to skip. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleTransfersListParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleTransfersListParams from) { + super(from); + } + + /** + * INBOUND, OUTBOUND, or both comma-separated. Case-sensitive. + * + * @param direction the value + * @return this builder + */ + public Builder direction(String direction) { + return set("direction", Conv.STRING, direction); + } + + /** + * One or more transfer statuses, comma-separated. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * One or more of INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL, TEST_VASP, comma-separated. + * + * @param rail the value + * @return this builder + */ + public Builder rail(String rail) { + return set("rail", Conv.STRING, rail); + } + + /** + * withdrawal, deposit or message, comma-separated. + * + * @param kind the value + * @return this builder + */ + public Builder kind(String kind) { + return set("kind", Conv.STRING, kind); + } + + /** + * The transaction's currency, case-insensitive. + * + * @param currency the value + * @return this builder + */ + public Builder currency(String currency) { + return set("currency", Conv.STRING, currency); + } + + /** + * Transfers created on or after this day (UTC), YYYY-MM-DD. + * + * @param startDate the value + * @return this builder + */ + public Builder startDate(LocalDate startDate) { + return set("start_date", Conv.DATE, startDate); + } + + /** + * Transfers created on or before this day (UTC), YYYY-MM-DD. + * + * @param endDate the value + * @return this builder + */ + public Builder endDate(LocalDate endDate) { + return set("end_date", Conv.DATE, endDate); + } + + /** + * The id of a directory entry or Didit network member from GET /v3/travel-rule/vasps/: your transfers with that counterparty. A value that is not a valid id answers 400. + * + * @param counterparty the value + * @return this builder + */ + public Builder counterparty(String counterparty) { + return set("counterparty", Conv.STRING, counterparty); + } + + /** + * Page size, default 50. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Rows to skip. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleTransfersListParams} + */ + public TravelRuleTransfersListParams build() { + return new TravelRuleTransfersListParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/CasesRegulatoryReportsListResponse.java b/src/main/java/me/didit/types/TravelRuleTransfersListResponse.java similarity index 72% rename from src/main/java/me/didit/types/CasesRegulatoryReportsListResponse.java rename to src/main/java/me/didit/types/TravelRuleTransfersListResponse.java index a773093..0ba1fa9 100644 --- a/src/main/java/me/didit/types/CasesRegulatoryReportsListResponse.java +++ b/src/main/java/me/didit/types/TravelRuleTransfersListResponse.java @@ -13,22 +13,22 @@ import me.didit.core.JsonModel; import org.jspecify.annotations.Nullable; -public final class CasesRegulatoryReportsListResponse extends JsonModel { +public final class TravelRuleTransfersListResponse extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); - private CasesRegulatoryReportsListResponse(ObjectNode json, Map> files) { + private TravelRuleTransfersListResponse(ObjectNode json, Map> files) { super(json, files); } /** - * Reads a {@code CasesRegulatoryReportsListResponse} from JSON. Unknown fields are kept and readable + * Reads a {@code TravelRuleTransfersListResponse} from JSON. Unknown fields are kept and readable * through {@link #_additionalProperties()}. * * @param json a JSON object * @return the parsed value */ - public static CasesRegulatoryReportsListResponse fromJson(JsonNode json) { - return new CasesRegulatoryReportsListResponse(Json.copyObject(json), Map.of()); + public static TravelRuleTransfersListResponse fromJson(JsonNode json) { + return new TravelRuleTransfersListResponse(Json.copyObject(json), Map.of()); } /** @@ -50,7 +50,7 @@ public Builder toBuilder() { } /** - * Number of matching rows, capped at 100 for performance. + * The count field. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -85,8 +85,8 @@ public Optional previous() { * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(RegulatoryReportDetail::fromJson))); + public Optional> results() { + return Json.optional(json, "results", Conv.list(Conv.model(TravelRuleTransferRow::fromJson))); } @Override @@ -94,16 +94,16 @@ protected Set knownFields() { return KNOWN_FIELDS; } - /** Builds a {@link CasesRegulatoryReportsListResponse}. */ + /** Builds a {@link TravelRuleTransfersListResponse}. */ public static final class Builder extends JsonModel.BaseBuilder { private Builder() {} - private Builder(CasesRegulatoryReportsListResponse from) { + private Builder(TravelRuleTransfersListResponse from) { super(from); } /** - * Number of matching rows, capped at 100 for performance. + * The count field. * * @param count the value * @return this builder @@ -138,17 +138,17 @@ public Builder previous(@Nullable String previous) { * @param results the value * @return this builder */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(RegulatoryReportDetail::fromJson)), results); + public Builder results(List results) { + return set("results", Conv.list(Conv.model(TravelRuleTransferRow::fromJson)), results); } /** * Builds the value. * - * @return a new immutable {@code CasesRegulatoryReportsListResponse} + * @return a new immutable {@code TravelRuleTransfersListResponse} */ - public CasesRegulatoryReportsListResponse build() { - return new CasesRegulatoryReportsListResponse(buildJson(), buildFiles()); + public TravelRuleTransfersListResponse build() { + return new TravelRuleTransfersListResponse(buildJson(), buildFiles()); } @Override diff --git a/src/main/java/me/didit/types/TravelRuleUtxo.java b/src/main/java/me/didit/types/TravelRuleUtxo.java new file mode 100644 index 0000000..549ebb6 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleUtxo.java @@ -0,0 +1,123 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** + * The inputs and outputs of a UTXO transfer (BTC, LTC, BCH, DOGE). Only accepted on those chains: on any other chain it is a 400. From them Didit infers the originator wallet (the declared one, else the input address that contributed the most) and the beneficiary output (the declared one, else the only output that does not pay an input address back as change, else the one output paying the transfer amount). The result is on the transfer's chain_details. + */ +public final class TravelRuleUtxo extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("inputs", "outputs"); + + private TravelRuleUtxo(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleUtxo} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleUtxo fromJson(JsonNode json) { + return new TravelRuleUtxo(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Optional. Without inputs no originator is inferred and no change output is recognised. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> inputs() { + return Json.optional(json, "inputs", Conv.list(Conv.model(TravelRuleUtxoEntry::fromJson))); + } + + /** + * The outputs field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List outputs() { + return Json.required(json, "outputs", Conv.list(Conv.model(TravelRuleUtxoEntry::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleUtxo}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleUtxo from) { + super(from); + } + + /** + * Optional. Without inputs no originator is inferred and no change output is recognised. + * + * @param inputs the value + * @return this builder + */ + public Builder inputs(List inputs) { + return set("inputs", Conv.list(Conv.model(TravelRuleUtxoEntry::fromJson)), inputs); + } + + /** + * The outputs field. + * + * @param outputs the value + * @return this builder + */ + public Builder outputs(List outputs) { + return set("outputs", Conv.list(Conv.model(TravelRuleUtxoEntry::fromJson)), outputs); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleUtxo} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleUtxo build() { + require("outputs", false); + return new TravelRuleUtxo(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleUtxoEntry.java b/src/main/java/me/didit/types/TravelRuleUtxoEntry.java new file mode 100644 index 0000000..546982c --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleUtxoEntry.java @@ -0,0 +1,162 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** One input or output of a UTXO transaction, as the chain recorded it. */ +public final class TravelRuleUtxoEntry extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("address", "amount", "txid", "vout"); + + private TravelRuleUtxoEntry(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleUtxoEntry} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleUtxoEntry fromJson(JsonNode json) { + return new TravelRuleUtxoEntry(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The address field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String address() { + return Json.required(json, "address", Conv.STRING); + } + + /** + * Decimal string in the coin's own unit (BTC, not satoshis). Optional. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional amount() { + return Json.optional(json, "amount", Conv.STRING); + } + + /** + * Optional. The transaction an input spends. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txid() { + return Json.optional(json, "txid", Conv.STRING); + } + + /** + * Optional. The output index an input spends. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vout() { + return Json.optional(json, "vout", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleUtxoEntry}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleUtxoEntry from) { + super(from); + } + + /** + * The address field. + * + * @param address the value + * @return this builder + */ + public Builder address(String address) { + return set("address", Conv.STRING, address); + } + + /** + * Decimal string in the coin's own unit (BTC, not satoshis). Optional. + * + * @param amount the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder amount(@Nullable String amount) { + return setNullable("amount", Conv.STRING, amount); + } + + /** + * Optional. The transaction an input spends. + * + * @param txid the value + * @return this builder + */ + public Builder txid(String txid) { + return set("txid", Conv.STRING, txid); + } + + /** + * Optional. The output index an input spends. + * + * @param vout the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vout(@Nullable Long vout) { + return setNullable("vout", Conv.LONG, vout); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleUtxoEntry} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleUtxoEntry build() { + require("address", false); + return new TravelRuleUtxoEntry(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleWalletAddressesBulkImportParams.java b/src/main/java/me/didit/types/TravelRuleWalletAddressesBulkImportParams.java new file mode 100644 index 0000000..2ae9238 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleWalletAddressesBulkImportParams.java @@ -0,0 +1,110 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code travel_rule.wallet_addresses.bulk_import}. */ +public final class TravelRuleWalletAddressesBulkImportParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("file"); + + private TravelRuleWalletAddressesBulkImportParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleWalletAddressesBulkImportParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleWalletAddressesBulkImportParams fromJson(JsonNode json) { + return new TravelRuleWalletAddressesBulkImportParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * A UTF-8 CSV file of at most 5 MB with a header row naming its columns (the row fields or their aliases). Empty cells count as not sent; columns the import does not read are ignored. + * + * @return the file to upload, if set + */ + public Optional file() { + return file("file"); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleWalletAddressesBulkImportParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleWalletAddressesBulkImportParams from) { + super(from); + } + + /** + * A UTF-8 CSV file of at most 5 MB with a header row naming its columns (the row fields or their aliases). Empty cells count as not sent; columns the import does not read are ignored. + * + * @param file the value + * @return this builder + */ + public Builder file(FileUpload file) { + return setFile("file", file); + } + + /** + * Uploads the file at {@code path} as {@code file}. + * + * @param path the file to read when the request is sent + * @return this builder + */ + public Builder file(Path path) { + return setFile("file", FileUpload.of(path)); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleWalletAddressesBulkImportParams} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleWalletAddressesBulkImportParams build() { + requireFile("file"); + return new TravelRuleWalletAddressesBulkImportParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleWidgetContext.java b/src/main/java/me/didit/types/TravelRuleWidgetContext.java index 2958c1f..0bef6bb 100644 --- a/src/main/java/me/didit/types/TravelRuleWidgetContext.java +++ b/src/main/java/me/didit/types/TravelRuleWidgetContext.java @@ -16,7 +16,7 @@ import org.jspecify.annotations.Nullable; public final class TravelRuleWidgetContext extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("vasp_name", "wallet_address", "chain", "holder_name", "allowed_methods", "transfer_status", "callback_url", "expires_at", "completed_at", "proofs", "customization"); + private static final Set KNOWN_FIELDS = Set.of("vasp_name", "wallet_address", "chain", "holder_name", "allowed_methods", "transfer_status", "callback_url", "expires_at", "completed_at", "proofs", "max_attempts", "attempts_used", "attempts_remaining", "customization"); private TravelRuleWidgetContext(ObjectNode json, Map> files) { super(json, files); @@ -92,7 +92,7 @@ public Optional holderName() { } /** - * Proof methods available for this session. MESSAGE_SIGNING requires a supported chain family, SATOSHI_TEST requires a satoshi_deposit_address on the session, SCREENSHOT requires the allow_screenshot_proof setting, and SELF_DECLARATION requires the allow_self_declaration setting. + * Proof methods available for this session. MESSAGE_SIGNING requires a supported chain family, SATOSHI_TEST requires a satoshi_deposit_address on the session, SCREENSHOT requires the allow_screenshot_proof setting, and SELF_DECLARATION requires the allow_self_declaration setting and an ownership_validation_mode that accepts a declaration for the linked transfer (never in FULL; in SIMPLIFIED only below simplified_validation_threshold). Initiating a SELF_DECLARATION the mode does not accept answers 403. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -151,6 +151,36 @@ public Optional> proofs() { return Json.optional(json, "proofs", Conv.list(Conv.model(TravelRuleWidgetProofSummary::fromJson))); } + /** + * Rejected wallet proofs this session allows. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional maxAttempts() { + return Json.optional(json, "max_attempts", Conv.LONG); + } + + /** + * Rejected proofs so far. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional attemptsUsed() { + return Json.optional(json, "attempts_used", Conv.LONG); + } + + /** + * When 0, starting another proof answers 409 and you mint a new widget session. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional attemptsRemaining() { + return Json.optional(json, "attempts_remaining", Conv.LONG); + } + /** * The application's whitelabel customization (colors, logo, font family) when white label is enabled - the widget renders with the same branding as the verification flow. Null otherwise. * @@ -215,7 +245,7 @@ public Builder holderName(String holderName) { } /** - * Proof methods available for this session. MESSAGE_SIGNING requires a supported chain family, SATOSHI_TEST requires a satoshi_deposit_address on the session, SCREENSHOT requires the allow_screenshot_proof setting, and SELF_DECLARATION requires the allow_self_declaration setting. + * Proof methods available for this session. MESSAGE_SIGNING requires a supported chain family, SATOSHI_TEST requires a satoshi_deposit_address on the session, SCREENSHOT requires the allow_screenshot_proof setting, and SELF_DECLARATION requires the allow_self_declaration setting and an ownership_validation_mode that accepts a declaration for the linked transfer (never in FULL; in SIMPLIFIED only below simplified_validation_threshold). Initiating a SELF_DECLARATION the mode does not accept answers 403. * * @param allowedMethods the value * @return this builder @@ -274,6 +304,36 @@ public Builder proofs(List proofs) { return set("proofs", Conv.list(Conv.model(TravelRuleWidgetProofSummary::fromJson)), proofs); } + /** + * Rejected wallet proofs this session allows. + * + * @param maxAttempts the value + * @return this builder + */ + public Builder maxAttempts(long maxAttempts) { + return set("max_attempts", Conv.LONG, maxAttempts); + } + + /** + * Rejected proofs so far. + * + * @param attemptsUsed the value + * @return this builder + */ + public Builder attemptsUsed(long attemptsUsed) { + return set("attempts_used", Conv.LONG, attemptsUsed); + } + + /** + * When 0, starting another proof answers 409 and you mint a new widget session. + * + * @param attemptsRemaining the value + * @return this builder + */ + public Builder attemptsRemaining(long attemptsRemaining) { + return set("attempts_remaining", Conv.LONG, attemptsRemaining); + } + /** * The application's whitelabel customization (colors, logo, font family) when white label is enabled - the widget renders with the same branding as the verification flow. Null otherwise. * diff --git a/src/main/java/me/didit/types/TravelRuleWidgetProofSummary.java b/src/main/java/me/didit/types/TravelRuleWidgetProofSummary.java index b9cbbf4..89684e3 100644 --- a/src/main/java/me/didit/types/TravelRuleWidgetProofSummary.java +++ b/src/main/java/me/didit/types/TravelRuleWidgetProofSummary.java @@ -16,7 +16,7 @@ import org.jspecify.annotations.Nullable; public final class TravelRuleWidgetProofSummary extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("proof_id", "method", "status", "wallet_address", "chain", "deposit_address", "expected_amount", "expires_at", "challenge", "created_at"); + private static final Set KNOWN_FIELDS = Set.of("proof_id", "method", "status", "wallet_address", "chain", "deposit_address", "expected_amount", "expires_at", "challenge", "rejection_reason", "created_at"); private TravelRuleWidgetProofSummary(ObjectNode json, Map> files) { super(json, files); @@ -141,6 +141,16 @@ public Optional challenge() { return Json.optional(json, "challenge", Conv.STRING); } + /** + * Why the proof was rejected. Present only when status is REJECTED. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rejectionReason() { + return Json.optional(json, "rejection_reason", Conv.STRING); + } + /** * The created_at field. * @@ -254,6 +264,16 @@ public Builder challenge(@Nullable String challenge) { return setNullable("challenge", Conv.STRING, challenge); } + /** + * Why the proof was rejected. Present only when status is REJECTED. + * + * @param rejectionReason the value + * @return this builder + */ + public Builder rejectionReason(String rejectionReason) { + return set("rejection_reason", Conv.STRING, rejectionReason); + } + /** * The created_at field. * diff --git a/src/main/java/me/didit/types/TravelRuleWidgetSessionCreate.java b/src/main/java/me/didit/types/TravelRuleWidgetSessionCreate.java index c23c5d5..3d547aa 100644 --- a/src/main/java/me/didit/types/TravelRuleWidgetSessionCreate.java +++ b/src/main/java/me/didit/types/TravelRuleWidgetSessionCreate.java @@ -11,6 +11,7 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; public final class TravelRuleWidgetSessionCreate extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("wallet_address", "chain", "holder_name", "vendor_data", "transaction_id", "satoshi_deposit_address", "callback_url", "expires_in_minutes"); @@ -49,23 +50,23 @@ public Builder toBuilder() { } /** - * The wallet address to verify. + * The wallet address to verify. Optional; when empty, the address must be supplied when a proof is started. * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public String walletAddress() { - return Json.required(json, "wallet_address", Conv.STRING); + public Optional walletAddress() { + return Json.optional(json, "wallet_address", Conv.STRING); } /** - * Chain identifier for the address, e.g. "ethereum", "bitcoin", "solana". + * Chain token for the address, for example ETH, BTC or SOL. Use the same token as in your wallet address book. Message signing and the Satoshi test are offered only for a supported chain. * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public String chain() { - return Json.required(json, "chain", Conv.STRING); + public Optional chain() { + return Json.optional(json, "chain", Conv.STRING); } /** @@ -109,7 +110,7 @@ public Optional satoshiDepositAddress() { } /** - * Optional. Where the widget returns the customer after completion. + * Optional. Absolute http(s) URL where the widget returns the customer after completion. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -142,7 +143,7 @@ private Builder(TravelRuleWidgetSessionCreate from) { } /** - * The wallet address to verify. + * The wallet address to verify. Optional; when empty, the address must be supplied when a proof is started. * * @param walletAddress the value * @return this builder @@ -152,7 +153,7 @@ public Builder walletAddress(String walletAddress) { } /** - * Chain identifier for the address, e.g. "ethereum", "bitcoin", "solana". + * Chain token for the address, for example ETH, BTC or SOL. Use the same token as in your wallet address book. Message signing and the Satoshi test are offered only for a supported chain. * * @param chain the value * @return this builder @@ -184,11 +185,11 @@ public Builder vendorData(String vendorData) { /** * Optional. Didit transaction UUID of a Travel Rule transfer to advance when the proof verifies. * - * @param transactionId the value + * @param transactionId the value, or {@code null} to send JSON null * @return this builder */ - public Builder transactionId(String transactionId) { - return set("transaction_id", Conv.STRING, transactionId); + public Builder transactionId(@Nullable String transactionId) { + return setNullable("transaction_id", Conv.STRING, transactionId); } /** @@ -202,7 +203,7 @@ public Builder satoshiDepositAddress(String satoshiDepositAddress) { } /** - * Optional. Where the widget returns the customer after completion. + * Optional. Absolute http(s) URL where the widget returns the customer after completion. * * @param callbackUrl the value * @return this builder @@ -225,11 +226,8 @@ public Builder expiresInMinutes(long expiresInMinutes) { * Builds the value. * * @return a new immutable {@code TravelRuleWidgetSessionCreate} - * @throws IllegalStateException when a required field is missing */ public TravelRuleWidgetSessionCreate build() { - require("wallet_address", false); - require("chain", false); return new TravelRuleWidgetSessionCreate(buildJson(), buildFiles()); } diff --git a/src/main/java/me/didit/types/TravelRuleWorkspace.java b/src/main/java/me/didit/types/TravelRuleWorkspace.java new file mode 100644 index 0000000..cbacb07 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleWorkspace.java @@ -0,0 +1,725 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** The free Travel Rule workspace of an application that joined free. */ +public final class TravelRuleWorkspace extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("joined_free", "work_email", "joined_at", "claim", "prefill", "domain", "billing_enabled", "billing_enabled_at", "didit_network", "settings"); + + private TravelRuleWorkspace(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleWorkspace} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleWorkspace fromJson(JsonNode json) { + return new TravelRuleWorkspace(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The joined_free field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional joinedFree() { + return Json.optional(json, "joined_free", Conv.BOOLEAN); + } + + /** + * The work_email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workEmail() { + return Json.optional(json, "work_email", Conv.STRING); + } + + /** + * The joined_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional joinedAt() { + return Json.optional(json, "joined_at", Conv.DATE_TIME); + } + + /** + * The claim field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional claim() { + return Json.optional(json, "claim", Conv.model(TravelRuleWorkspace.Claim::fromJson)); + } + + /** + * Profile values taken from the directory entry: vasp_id, legal_name, lei, jurisdiction, compliance_email, domains. Nothing is written until the claim is made. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> prefill() { + return Json.optional(json, "prefill", Conv.OBJECT); + } + + /** + * The domain field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional domain() { + return Json.optional(json, "domain", Conv.model(TravelRuleWorkspace.Domain::fromJson)); + } + + /** + * Receiving Travel Rule data is free. Sending is billed only after billing is enabled. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional billingEnabled() { + return Json.optional(json, "billing_enabled", Conv.BOOLEAN); + } + + /** + * The billing_enabled_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional billingEnabledAt() { + return Json.optional(json, "billing_enabled_at", Conv.DATE_TIME); + } + + /** + * Whether the Didit network lists this VASP and, when not, each condition it still misses. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> diditNetwork() { + return Json.optional(json, "didit_network", Conv.OBJECT); + } + + /** + * The settings field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional settings() { + return Json.optional(json, "settings", Conv.model(TravelRuleSettingsDetail::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleWorkspace}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleWorkspace from) { + super(from); + } + + /** + * The joined_free field. + * + * @param joinedFree the value + * @return this builder + */ + public Builder joinedFree(boolean joinedFree) { + return set("joined_free", Conv.BOOLEAN, joinedFree); + } + + /** + * The work_email field. + * + * @param workEmail the value + * @return this builder + */ + public Builder workEmail(String workEmail) { + return set("work_email", Conv.STRING, workEmail); + } + + /** + * The joined_at field. + * + * @param joinedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder joinedAt(@Nullable OffsetDateTime joinedAt) { + return setNullable("joined_at", Conv.DATE_TIME, joinedAt); + } + + /** + * The claim field. + * + * @param claim the value + * @return this builder + */ + public Builder claim(TravelRuleWorkspace.Claim claim) { + return set("claim", Conv.model(TravelRuleWorkspace.Claim::fromJson), claim); + } + + /** + * Profile values taken from the directory entry: vasp_id, legal_name, lei, jurisdiction, compliance_email, domains. Nothing is written until the claim is made. + * + * @param prefill the value + * @return this builder + */ + public Builder prefill(Map prefill) { + return setAny("prefill", prefill); + } + + /** + * The domain field. + * + * @param domain the value + * @return this builder + */ + public Builder domain(TravelRuleWorkspace.Domain domain) { + return set("domain", Conv.model(TravelRuleWorkspace.Domain::fromJson), domain); + } + + /** + * Receiving Travel Rule data is free. Sending is billed only after billing is enabled. + * + * @param billingEnabled the value + * @return this builder + */ + public Builder billingEnabled(boolean billingEnabled) { + return set("billing_enabled", Conv.BOOLEAN, billingEnabled); + } + + /** + * The billing_enabled_at field. + * + * @param billingEnabledAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder billingEnabledAt(@Nullable OffsetDateTime billingEnabledAt) { + return setNullable("billing_enabled_at", Conv.DATE_TIME, billingEnabledAt); + } + + /** + * Whether the Didit network lists this VASP and, when not, each condition it still misses. + * + * @param diditNetwork the value + * @return this builder + */ + public Builder diditNetwork(Map diditNetwork) { + return setAny("didit_network", diditNetwork); + } + + /** + * The settings field. + * + * @param settings the value + * @return this builder + */ + public Builder settings(TravelRuleSettingsDetail settings) { + return set("settings", Conv.model(TravelRuleSettingsDetail::fromJson), settings); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleWorkspace} + */ + public TravelRuleWorkspace build() { + return new TravelRuleWorkspace(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Claim extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("status", "proved_by_domain", "claimed_at", "reviewed_at", "review_note", "vasp_id", "vasp_name"); + + private Claim(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Claim} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Claim fromJson(JsonNode json) { + return new Claim(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(TravelRuleWorkspace.Claim.Status::of)); + } + + /** + * The proved_by_domain field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional provedByDomain() { + return Json.optional(json, "proved_by_domain", Conv.BOOLEAN); + } + + /** + * The claimed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional claimedAt() { + return Json.optional(json, "claimed_at", Conv.DATE_TIME); + } + + /** + * The reviewed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reviewedAt() { + return Json.optional(json, "reviewed_at", Conv.DATE_TIME); + } + + /** + * The review_note field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reviewNote() { + return Json.optional(json, "review_note", Conv.STRING); + } + + /** + * The vasp_id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vaspId() { + return Json.optional(json, "vasp_id", Conv.STRING); + } + + /** + * The vasp_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vaspName() { + return Json.optional(json, "vasp_name", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Claim}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Claim from) { + super(from); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(TravelRuleWorkspace.Claim.Status status) { + return set("status", Conv.enumeration(TravelRuleWorkspace.Claim.Status::of), status); + } + + /** + * The proved_by_domain field. + * + * @param provedByDomain the value + * @return this builder + */ + public Builder provedByDomain(boolean provedByDomain) { + return set("proved_by_domain", Conv.BOOLEAN, provedByDomain); + } + + /** + * The claimed_at field. + * + * @param claimedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder claimedAt(@Nullable OffsetDateTime claimedAt) { + return setNullable("claimed_at", Conv.DATE_TIME, claimedAt); + } + + /** + * The reviewed_at field. + * + * @param reviewedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reviewedAt(@Nullable OffsetDateTime reviewedAt) { + return setNullable("reviewed_at", Conv.DATE_TIME, reviewedAt); + } + + /** + * The review_note field. + * + * @param reviewNote the value + * @return this builder + */ + public Builder reviewNote(String reviewNote) { + return set("review_note", Conv.STRING, reviewNote); + } + + /** + * The vasp_id field. + * + * @param vaspId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vaspId(@Nullable String vaspId) { + return setNullable("vasp_id", Conv.STRING, vaspId); + } + + /** + * The vasp_name field. + * + * @param vaspName the value + * @return this builder + */ + public Builder vaspName(String vaspName) { + return set("vasp_name", Conv.STRING, vaspName); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Claim} + */ + public Claim build() { + return new Claim(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "unclaimed"}. */ + public static final Status UNCLAIMED = new Status("unclaimed"); + + /** The value {@code "pending_review"}. */ + public static final Status PENDING_REVIEW = new Status("pending_review"); + + /** The value {@code "claimed"}. */ + public static final Status CLAIMED = new Status("claimed"); + + /** The value {@code "rejected"}. */ + public static final Status REJECTED = new Status("rejected"); + + private static final List KNOWN_VALUES = List.of(UNCLAIMED, PENDING_REVIEW, CLAIMED, REJECTED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + public static final class Domain extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("domain", "status", "verified_at", "txt"); + + private Domain(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Domain} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Domain fromJson(JsonNode json) { + return new Domain(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The domain field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional domain() { + return Json.optional(json, "domain", Conv.STRING); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(TravelRuleWorkspace.Domain.Status::of)); + } + + /** + * The verified_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verifiedAt() { + return Json.optional(json, "verified_at", Conv.DATE_TIME); + } + + /** + * The TXT record to publish for the DNS proof. Empty until a proof has been started. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txt() { + return Json.optional(json, "txt", Conv.model(TravelRuleWorkspaceTxtRecord::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Domain}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Domain from) { + super(from); + } + + /** + * The domain field. + * + * @param domain the value + * @return this builder + */ + public Builder domain(String domain) { + return set("domain", Conv.STRING, domain); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(TravelRuleWorkspace.Domain.Status status) { + return set("status", Conv.enumeration(TravelRuleWorkspace.Domain.Status::of), status); + } + + /** + * The verified_at field. + * + * @param verifiedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verifiedAt(@Nullable OffsetDateTime verifiedAt) { + return setNullable("verified_at", Conv.DATE_TIME, verifiedAt); + } + + /** + * The TXT record to publish for the DNS proof. Empty until a proof has been started. + * + * @param txt the value + * @return this builder + */ + public Builder txt(TravelRuleWorkspaceTxtRecord txt) { + return set("txt", Conv.model(TravelRuleWorkspaceTxtRecord::fromJson), txt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Domain} + */ + public Domain build() { + return new Domain(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "unverified"}. */ + public static final Status UNVERIFIED = new Status("unverified"); + + /** The value {@code "code_sent"}. */ + public static final Status CODE_SENT = new Status("code_sent"); + + /** The value {@code "verified"}. */ + public static final Status VERIFIED = new Status("verified"); + + private static final List KNOWN_VALUES = List.of(UNVERIFIED, CODE_SENT, VERIFIED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleWorkspaceBillingRequest.java b/src/main/java/me/didit/types/TravelRuleWorkspaceBillingRequest.java new file mode 100644 index 0000000..34da762 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleWorkspaceBillingRequest.java @@ -0,0 +1,98 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleWorkspaceBillingRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("enabled"); + + private TravelRuleWorkspaceBillingRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleWorkspaceBillingRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleWorkspaceBillingRequest fromJson(JsonNode json) { + return new TravelRuleWorkspaceBillingRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Only true is accepted: outbound billing cannot be turned back off through the API. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional enabled() { + return Json.optional(json, "enabled", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleWorkspaceBillingRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleWorkspaceBillingRequest from) { + super(from); + } + + /** + * Only true is accepted: outbound billing cannot be turned back off through the API. + * + * @param enabled the value + * @return this builder + */ + public Builder enabled(boolean enabled) { + return set("enabled", Conv.BOOLEAN, enabled); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleWorkspaceBillingRequest} + */ + public TravelRuleWorkspaceBillingRequest build() { + return new TravelRuleWorkspaceBillingRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleWorkspaceClaimRequest.java b/src/main/java/me/didit/types/TravelRuleWorkspaceClaimRequest.java new file mode 100644 index 0000000..e25c4ac --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleWorkspaceClaimRequest.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleWorkspaceClaimRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("vasp_id"); + + private TravelRuleWorkspaceClaimRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleWorkspaceClaimRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleWorkspaceClaimRequest fromJson(JsonNode json) { + return new TravelRuleWorkspaceClaimRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The directory entry this workspace is. Read ids from GET /v3/travel-rule/vasps/. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String vaspId() { + return Json.required(json, "vasp_id", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleWorkspaceClaimRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleWorkspaceClaimRequest from) { + super(from); + } + + /** + * The directory entry this workspace is. Read ids from GET /v3/travel-rule/vasps/. + * + * @param vaspId the value + * @return this builder + */ + public Builder vaspId(String vaspId) { + return set("vasp_id", Conv.STRING, vaspId); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleWorkspaceClaimRequest} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleWorkspaceClaimRequest build() { + require("vasp_id", false); + return new TravelRuleWorkspaceClaimRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleWorkspaceDomainCodeResponse.java b/src/main/java/me/didit/types/TravelRuleWorkspaceDomainCodeResponse.java new file mode 100644 index 0000000..8e2e0d0 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleWorkspaceDomainCodeResponse.java @@ -0,0 +1,184 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +public final class TravelRuleWorkspaceDomainCodeResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("domain", "status", "txt"); + + private TravelRuleWorkspaceDomainCodeResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleWorkspaceDomainCodeResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleWorkspaceDomainCodeResponse fromJson(JsonNode json) { + return new TravelRuleWorkspaceDomainCodeResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The domain field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional domain() { + return Json.optional(json, "domain", Conv.STRING); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(TravelRuleWorkspaceDomainCodeResponse.Status::of)); + } + + /** + * The TXT record to publish for the DNS proof. Empty until a proof has been started. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional txt() { + return Json.optional(json, "txt", Conv.model(TravelRuleWorkspaceTxtRecord::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleWorkspaceDomainCodeResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleWorkspaceDomainCodeResponse from) { + super(from); + } + + /** + * The domain field. + * + * @param domain the value + * @return this builder + */ + public Builder domain(String domain) { + return set("domain", Conv.STRING, domain); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(TravelRuleWorkspaceDomainCodeResponse.Status status) { + return set("status", Conv.enumeration(TravelRuleWorkspaceDomainCodeResponse.Status::of), status); + } + + /** + * The TXT record to publish for the DNS proof. Empty until a proof has been started. + * + * @param txt the value + * @return this builder + */ + public Builder txt(TravelRuleWorkspaceTxtRecord txt) { + return set("txt", Conv.model(TravelRuleWorkspaceTxtRecord::fromJson), txt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleWorkspaceDomainCodeResponse} + */ + public TravelRuleWorkspaceDomainCodeResponse build() { + return new TravelRuleWorkspaceDomainCodeResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "unverified"}. */ + public static final Status UNVERIFIED = new Status("unverified"); + + /** The value {@code "code_sent"}. */ + public static final Status CODE_SENT = new Status("code_sent"); + + /** The value {@code "verified"}. */ + public static final Status VERIFIED = new Status("verified"); + + private static final List KNOWN_VALUES = List.of(UNVERIFIED, CODE_SENT, VERIFIED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleWorkspaceDomainConfirmRequest.java b/src/main/java/me/didit/types/TravelRuleWorkspaceDomainConfirmRequest.java new file mode 100644 index 0000000..3fd8d2a --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleWorkspaceDomainConfirmRequest.java @@ -0,0 +1,99 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +public final class TravelRuleWorkspaceDomainConfirmRequest extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("code"); + + private TravelRuleWorkspaceDomainConfirmRequest(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleWorkspaceDomainConfirmRequest} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleWorkspaceDomainConfirmRequest fromJson(JsonNode json) { + return new TravelRuleWorkspaceDomainConfirmRequest(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The code field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String code() { + return Json.required(json, "code", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleWorkspaceDomainConfirmRequest}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleWorkspaceDomainConfirmRequest from) { + super(from); + } + + /** + * The code field. + * + * @param code the value + * @return this builder + */ + public Builder code(String code) { + return set("code", Conv.STRING, code); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleWorkspaceDomainConfirmRequest} + * @throws IllegalStateException when a required field is missing + */ + public TravelRuleWorkspaceDomainConfirmRequest build() { + require("code", false); + return new TravelRuleWorkspaceDomainConfirmRequest(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/TravelRuleWorkspaceTxtRecord.java b/src/main/java/me/didit/types/TravelRuleWorkspaceTxtRecord.java new file mode 100644 index 0000000..db44447 --- /dev/null +++ b/src/main/java/me/didit/types/TravelRuleWorkspaceTxtRecord.java @@ -0,0 +1,119 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** The TXT record to publish for the DNS proof. Empty until a proof has been started. */ +public final class TravelRuleWorkspaceTxtRecord extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("host", "value"); + + private TravelRuleWorkspaceTxtRecord(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code TravelRuleWorkspaceTxtRecord} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static TravelRuleWorkspaceTxtRecord fromJson(JsonNode json) { + return new TravelRuleWorkspaceTxtRecord(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The host field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional host() { + return Json.optional(json, "host", Conv.STRING); + } + + /** + * The value field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional value() { + return Json.optional(json, "value", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link TravelRuleWorkspaceTxtRecord}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(TravelRuleWorkspaceTxtRecord from) { + super(from); + } + + /** + * The host field. + * + * @param host the value + * @return this builder + */ + public Builder host(String host) { + return set("host", Conv.STRING, host); + } + + /** + * The value field. + * + * @param value the value + * @return this builder + */ + public Builder value(String value) { + return set("value", Conv.STRING, value); + } + + /** + * Builds the value. + * + * @return a new immutable {@code TravelRuleWorkspaceTxtRecord} + */ + public TravelRuleWorkspaceTxtRecord build() { + return new TravelRuleWorkspaceTxtRecord(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/UserDataUpdatedEvent.java b/src/main/java/me/didit/types/UserDataUpdatedEvent.java new file mode 100644 index 0000000..b65c906 --- /dev/null +++ b/src/main/java/me/didit/types/UserDataUpdatedEvent.java @@ -0,0 +1,593 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Fields of a user changed. Not sent when the user is created. One save can produce both a status and a data event. + */ +public final class UserDataUpdatedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "webhook_type", "vendor_user_id", "vendor_data", "status", "changed_fields", "changes"); + + private UserDataUpdatedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code UserDataUpdatedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static UserDataUpdatedEvent fromJson(JsonNode json) { + return new UserDataUpdatedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * Always user.data.updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public UserDataUpdatedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(UserDataUpdatedEvent.WebhookType::of)); + } + + /** + * The user the event is about. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String vendorUserId() { + return Json.required(json, "vendor_user_id", Conv.STRING); + } + + /** + * Your vendor_data for the user, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * User status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public UserDataUpdatedEvent.Status status() { + return Json.required(json, "status", Conv.enumeration(UserDataUpdatedEvent.Status::of)); + } + + /** + * Names of the fields that changed. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List changedFields() { + return Json.required(json, "changed_fields", Conv.list(Conv.enumeration(UserDataUpdatedEvent.ChangedFields::of))); + } + + /** + * Previous and current value of each changed field, keyed by field name. Values can be personal data (names, dates of birth, portrait links, emails, phone numbers); handle them accordingly. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public Map changes() { + return Json.required(json, "changes", Conv.map(Conv.model(UserDataUpdatedEvent.ChangesValue::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link UserDataUpdatedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(UserDataUpdatedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * Always user.data.updated. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(UserDataUpdatedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(UserDataUpdatedEvent.WebhookType::of), webhookType); + } + + /** + * The user the event is about. + * + * @param vendorUserId the value + * @return this builder + */ + public Builder vendorUserId(String vendorUserId) { + return set("vendor_user_id", Conv.STRING, vendorUserId); + } + + /** + * Your vendor_data for the user, or null. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * User status after the change. + * + * @param status the value + * @return this builder + */ + public Builder status(UserDataUpdatedEvent.Status status) { + return set("status", Conv.enumeration(UserDataUpdatedEvent.Status::of), status); + } + + /** + * Names of the fields that changed. + * + * @param changedFields the value + * @return this builder + */ + public Builder changedFields(List changedFields) { + return set("changed_fields", Conv.list(Conv.enumeration(UserDataUpdatedEvent.ChangedFields::of)), changedFields); + } + + /** + * Previous and current value of each changed field, keyed by field name. Values can be personal data (names, dates of birth, portrait links, emails, phone numbers); handle them accordingly. + * + * @param changes the value + * @return this builder + */ + public Builder changes(Map changes) { + return set("changes", Conv.map(Conv.model(UserDataUpdatedEvent.ChangesValue::fromJson)), changes); + } + + /** + * Builds the value. + * + * @return a new immutable {@code UserDataUpdatedEvent} + * @throws IllegalStateException when a required field is missing + */ + public UserDataUpdatedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("webhook_type", false); + require("vendor_user_id", false); + require("vendor_data", true); + require("status", false); + require("changed_fields", false); + require("changes", false); + return new UserDataUpdatedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always user.data.updated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "user.data.updated"}. */ + public static final WebhookType USER_DATA_UPDATED = new WebhookType("user.data.updated"); + + private static final List KNOWN_VALUES = List.of(USER_DATA_UPDATED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * User status after the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final Status ACTIVE = new Status("ACTIVE"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "FLAGGED"}. */ + public static final Status FLAGGED = new Status("FLAGGED"); + + /** The value {@code "BLOCKED"}. */ + public static final Status BLOCKED = new Status("BLOCKED"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, IN_REVIEW, FLAGGED, BLOCKED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ChangedFields extends StringEnum { + /** The value {@code "vendor_data"}. */ + public static final ChangedFields VENDOR_DATA = new ChangedFields("vendor_data"); + + /** The value {@code "display_name"}. */ + public static final ChangedFields DISPLAY_NAME = new ChangedFields("display_name"); + + /** The value {@code "full_name"}. */ + public static final ChangedFields FULL_NAME = new ChangedFields("full_name"); + + /** The value {@code "date_of_birth"}. */ + public static final ChangedFields DATE_OF_BIRTH = new ChangedFields("date_of_birth"); + + /** The value {@code "metadata"}. */ + public static final ChangedFields METADATA = new ChangedFields("metadata"); + + /** The value {@code "portrait_image"}. */ + public static final ChangedFields PORTRAIT_IMAGE = new ChangedFields("portrait_image"); + + /** The value {@code "issuing_states"}. */ + public static final ChangedFields ISSUING_STATES = new ChangedFields("issuing_states"); + + /** The value {@code "approved_emails"}. */ + public static final ChangedFields APPROVED_EMAILS = new ChangedFields("approved_emails"); + + /** The value {@code "approved_phones"}. */ + public static final ChangedFields APPROVED_PHONES = new ChangedFields("approved_phones"); + + /** The value {@code "features"}. */ + public static final ChangedFields FEATURES = new ChangedFields("features"); + + /** The value {@code "session_count"}. */ + public static final ChangedFields SESSION_COUNT = new ChangedFields("session_count"); + + /** The value {@code "approved_count"}. */ + public static final ChangedFields APPROVED_COUNT = new ChangedFields("approved_count"); + + /** The value {@code "declined_count"}. */ + public static final ChangedFields DECLINED_COUNT = new ChangedFields("declined_count"); + + /** The value {@code "in_review_count"}. */ + public static final ChangedFields IN_REVIEW_COUNT = new ChangedFields("in_review_count"); + + /** The value {@code "first_session_at"}. */ + public static final ChangedFields FIRST_SESSION_AT = new ChangedFields("first_session_at"); + + /** The value {@code "last_session_at"}. */ + public static final ChangedFields LAST_SESSION_AT = new ChangedFields("last_session_at"); + + private static final List KNOWN_VALUES = List.of(VENDOR_DATA, DISPLAY_NAME, FULL_NAME, DATE_OF_BIRTH, METADATA, PORTRAIT_IMAGE, ISSUING_STATES, APPROVED_EMAILS, APPROVED_PHONES, FEATURES, SESSION_COUNT, APPROVED_COUNT, DECLINED_COUNT, IN_REVIEW_COUNT, FIRST_SESSION_AT, LAST_SESSION_AT); + + private ChangedFields(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ChangedFields of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ChangedFields::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class ChangesValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("previous", "current"); + + private ChangesValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ChangesValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ChangesValue fromJson(JsonNode json) { + return new ChangesValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Value before the change. Dates are ISO 8601 strings. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.ANY); + } + + /** + * Value after the change. Dates are ISO 8601 strings. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional current() { + return Json.optional(json, "current", Conv.ANY); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ChangesValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ChangesValue from) { + super(from); + } + + /** + * Value before the change. Dates are ISO 8601 strings. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable Object previous) { + return setAny("previous", previous); + } + + /** + * Value after the change. Dates are ISO 8601 strings. + * + * @param current the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder current(@Nullable Object current) { + return setAny("current", current); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ChangesValue} + */ + public ChangesValue build() { + return new ChangesValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/UserSessionListItem.java b/src/main/java/me/didit/types/UserSessionListItem.java new file mode 100644 index 0000000..250e5f3 --- /dev/null +++ b/src/main/java/me/didit/types/UserSessionListItem.java @@ -0,0 +1,2314 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Summary row for a User Verification (KYC) session. Identity fields (full_name, country, document_type, portrait_image, contact fields) are populated progressively as the user completes verification steps, and are null until the relevant step finishes. Fields restricted by the workflow's response-attributes configuration are also returned as null. + */ +public final class UserSessionListItem extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "session_kind", "session_number", "session_url", "portrait_image", "document_type", "full_name", "country", "status", "vendor_data", "didit_internal_id", "created_at", "features", "phone_number", "email_address", "additional_details"); + + private UserSessionListItem(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code UserSessionListItem} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static UserSessionListItem fromJson(JsonNode json) { + return new UserSessionListItem(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Unique identifier of the verification session. Use it with GET /v3/session/{sessionId}/decision/ to fetch the full decision payload. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Row discriminator. Always "user" for KYC rows; Business Verification (KYB) rows have "business" and a different shape. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public UserSessionListItem.SessionKind sessionKind() { + return Json.required(json, "session_kind", Conv.enumeration(UserSessionListItem.SessionKind::of)); + } + + /** + * Human-friendly sequential number of the session within your application. Also matched by the search parameter. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long sessionNumber() { + return Json.required(json, "session_number", Conv.LONG); + } + + /** + * Hosted verification URL the end user opens to complete the flow. null when the session has no hosted URL. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionUrl() { + return Json.optional(json, "session_url", Conv.STRING); + } + + /** + * Expiring (presigned) URL of the best available portrait: the NFC chip photo when available, otherwise the ID document portrait, otherwise the liveness reference image. null until one of those exists. Download promptly; the link expires. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional portraitImage() { + return Json.optional(json, "portrait_image", Conv.STRING); + } + + /** + * Human-readable type of the verified ID document. null before document verification completes, and also null after completion when the verified code has no display mapping (SSC, WP, VISA, PSC, BC, OTHER). Note: the document_type query parameter filters by short codes (P, ID, DL, RP, ...), not by these display values. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentType() { + return Json.optional(json, "document_type", Conv.enumeration(UserSessionListItem.DocumentType::of)); + } + + /** + * Best available full name for the session, resolved in order: ID document OCR, AML screened data, database-validation screened data, proof-of-address document, then the expected details provided at session creation. null when none is available yet. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fullName() { + return Json.optional(json, "full_name", Conv.STRING); + } + + /** + * ISO 3166-1 alpha-3 country associated with the session, resolved in order: ID document issuing state, database-validation issuing state, proof-of-address issuing state, expected details, then the verified phone's country. May differ from nationality. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + /** + * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page for the lifecycle. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public UserSessionListItem.Status status() { + return Json.required(json, "status", Conv.enumeration(UserSessionListItem.Status::of)); + } + + /** + * Your own identifier for the end user, echoed back exactly as sent at session creation. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * Didit's internal identifier for the verified user profile behind this session. Stable across multiple sessions of the same user; filterable via the didit_internal_id query parameter. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional diditInternalId() { + return Json.optional(json, "didit_internal_id", Conv.STRING); + } + + /** + * UTC timestamp when the session was created. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + /** + * Per-feature status for every verification step configured in the session's workflow, in workflow order. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List features() { + return Json.required(json, "features", Conv.list(Conv.model(UserSessionListItem.Features::fromJson))); + } + + /** + * Phone associated with the session: the verified phone when phone verification ran, otherwise the contact phone provided at session creation. null when neither exists. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneNumber() { + return Json.optional(json, "phone_number", Conv.model(UserSessionListItem.PhoneNumber::fromJson)); + } + + /** + * Email associated with the session: the verified email when email verification ran, otherwise the contact email provided at session creation. null when neither exists. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailAddress() { + return Json.optional(json, "email_address", Conv.model(UserSessionListItem.EmailAddress::fromJson)); + } + + /** + * Compact per-feature extras. Only features that produced data appear as keys; null when no feature produced extras. Fields excluded by the workflow's response-attributes configuration are omitted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional additionalDetails() { + return Json.optional(json, "additional_details", Conv.model(UserSessionListItem.AdditionalDetails::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link UserSessionListItem}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(UserSessionListItem from) { + super(from); + } + + /** + * Unique identifier of the verification session. Use it with GET /v3/session/{sessionId}/decision/ to fetch the full decision payload. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Row discriminator. Always "user" for KYC rows; Business Verification (KYB) rows have "business" and a different shape. + * + * @param sessionKind the value + * @return this builder + */ + public Builder sessionKind(UserSessionListItem.SessionKind sessionKind) { + return set("session_kind", Conv.enumeration(UserSessionListItem.SessionKind::of), sessionKind); + } + + /** + * Human-friendly sequential number of the session within your application. Also matched by the search parameter. + * + * @param sessionNumber the value + * @return this builder + */ + public Builder sessionNumber(long sessionNumber) { + return set("session_number", Conv.LONG, sessionNumber); + } + + /** + * Hosted verification URL the end user opens to complete the flow. null when the session has no hosted URL. + * + * @param sessionUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionUrl(@Nullable String sessionUrl) { + return setNullable("session_url", Conv.STRING, sessionUrl); + } + + /** + * Expiring (presigned) URL of the best available portrait: the NFC chip photo when available, otherwise the ID document portrait, otherwise the liveness reference image. null until one of those exists. Download promptly; the link expires. + * + * @param portraitImage the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder portraitImage(@Nullable String portraitImage) { + return setNullable("portrait_image", Conv.STRING, portraitImage); + } + + /** + * Human-readable type of the verified ID document. null before document verification completes, and also null after completion when the verified code has no display mapping (SSC, WP, VISA, PSC, BC, OTHER). Note: the document_type query parameter filters by short codes (P, ID, DL, RP, ...), not by these display values. + * + * @param documentType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder documentType(UserSessionListItem.@Nullable DocumentType documentType) { + return setNullable("document_type", Conv.enumeration(UserSessionListItem.DocumentType::of), documentType); + } + + /** + * Best available full name for the session, resolved in order: ID document OCR, AML screened data, database-validation screened data, proof-of-address document, then the expected details provided at session creation. null when none is available yet. + * + * @param fullName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fullName(@Nullable String fullName) { + return setNullable("full_name", Conv.STRING, fullName); + } + + /** + * ISO 3166-1 alpha-3 country associated with the session, resolved in order: ID document issuing state, database-validation issuing state, proof-of-address issuing state, expected details, then the verified phone's country. May differ from nationality. + * + * @param country the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder country(@Nullable String country) { + return setNullable("country", Conv.STRING, country); + } + + /** + * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page for the lifecycle. + * + * @param status the value + * @return this builder + */ + public Builder status(UserSessionListItem.Status status) { + return set("status", Conv.enumeration(UserSessionListItem.Status::of), status); + } + + /** + * Your own identifier for the end user, echoed back exactly as sent at session creation. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * Didit's internal identifier for the verified user profile behind this session. Stable across multiple sessions of the same user; filterable via the didit_internal_id query parameter. + * + * @param diditInternalId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder diditInternalId(@Nullable String diditInternalId) { + return setNullable("didit_internal_id", Conv.STRING, diditInternalId); + } + + /** + * UTC timestamp when the session was created. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Per-feature status for every verification step configured in the session's workflow, in workflow order. + * + * @param features the value + * @return this builder + */ + public Builder features(List features) { + return set("features", Conv.list(Conv.model(UserSessionListItem.Features::fromJson)), features); + } + + /** + * Phone associated with the session: the verified phone when phone verification ran, otherwise the contact phone provided at session creation. null when neither exists. + * + * @param phoneNumber the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder phoneNumber(UserSessionListItem.@Nullable PhoneNumber phoneNumber) { + return setNullable("phone_number", Conv.model(UserSessionListItem.PhoneNumber::fromJson), phoneNumber); + } + + /** + * Email associated with the session: the verified email when email verification ran, otherwise the contact email provided at session creation. null when neither exists. + * + * @param emailAddress the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder emailAddress(UserSessionListItem.@Nullable EmailAddress emailAddress) { + return setNullable("email_address", Conv.model(UserSessionListItem.EmailAddress::fromJson), emailAddress); + } + + /** + * Compact per-feature extras. Only features that produced data appear as keys; null when no feature produced extras. Fields excluded by the workflow's response-attributes configuration are omitted. + * + * @param additionalDetails the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder additionalDetails(UserSessionListItem.@Nullable AdditionalDetails additionalDetails) { + return setNullable("additional_details", Conv.model(UserSessionListItem.AdditionalDetails::fromJson), additionalDetails); + } + + /** + * Builds the value. + * + * @return a new immutable {@code UserSessionListItem} + * @throws IllegalStateException when a required field is missing + */ + public UserSessionListItem build() { + require("session_id", false); + require("session_kind", false); + require("session_number", false); + require("session_url", true); + require("portrait_image", true); + require("document_type", true); + require("full_name", true); + require("country", true); + require("status", false); + require("vendor_data", true); + require("didit_internal_id", true); + require("created_at", false); + require("features", false); + require("phone_number", true); + require("email_address", true); + require("additional_details", true); + return new UserSessionListItem(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Row discriminator. Always "user" for KYC rows; Business Verification (KYB) rows have "business" and a different shape. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class SessionKind extends StringEnum { + /** The value {@code "user"}. */ + public static final SessionKind USER = new SessionKind("user"); + + private static final List KNOWN_VALUES = List.of(USER); + + private SessionKind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static SessionKind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, SessionKind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Human-readable type of the verified ID document. null before document verification completes, and also null after completion when the verified code has no display mapping (SSC, WP, VISA, PSC, BC, OTHER). Note: the document_type query parameter filters by short codes (P, ID, DL, RP, ...), not by these display values. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DocumentType extends StringEnum { + /** The value {@code "Passport"}. */ + public static final DocumentType PASSPORT = new DocumentType("Passport"); + + /** The value {@code "Identity Card"}. */ + public static final DocumentType IDENTITY_CARD = new DocumentType("Identity Card"); + + /** The value {@code "Driver's License"}. */ + public static final DocumentType DRIVER_S_LICENSE = new DocumentType("Driver's License"); + + /** The value {@code "Residence Permit"}. */ + public static final DocumentType RESIDENCE_PERMIT = new DocumentType("Residence Permit"); + + /** The value {@code "Health Insurance Card"}. */ + public static final DocumentType HEALTH_INSURANCE_CARD = new DocumentType("Health Insurance Card"); + + /** The value {@code "Tax Card"}. */ + public static final DocumentType TAX_CARD = new DocumentType("Tax Card"); + + private static final List KNOWN_VALUES = List.of(PASSPORT, IDENTITY_CARD, DRIVER_S_LICENSE, RESIDENCE_PERMIT, HEALTH_INSURANCE_CARD, TAX_CARD); + + private DocumentType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DocumentType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DocumentType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Overall session status. See the [Verification Statuses](/integration/verification-statuses) page for the lifecycle. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Started"}. */ + public static final Status NOT_STARTED = new Status("Not Started"); + + /** The value {@code "In Progress"}. */ + public static final Status IN_PROGRESS = new Status("In Progress"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Expired"}. */ + public static final Status EXPIRED = new Status("Expired"); + + /** The value {@code "Abandoned"}. */ + public static final Status ABANDONED = new Status("Abandoned"); + + /** The value {@code "Kyc Expired"}. */ + public static final Status KYC_EXPIRED = new Status("Kyc Expired"); + + /** The value {@code "Resubmitted"}. */ + public static final Status RESUBMITTED = new Status("Resubmitted"); + + /** The value {@code "Awaiting User"}. */ + public static final Status AWAITING_USER = new Status("Awaiting User"); + + private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, APPROVED, DECLINED, IN_REVIEW, EXPIRED, ABANDONED, KYC_EXPIRED, RESUBMITTED, AWAITING_USER); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class Features extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("feature", "status"); + + private Features(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Features} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Features fromJson(JsonNode json) { + return new Features(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Verification feature identifier. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public UserSessionListItem.Features.Feature feature() { + return Json.required(json, "feature", Conv.enumeration(UserSessionListItem.Features.Feature::of)); + } + + /** + * Status of this feature. Not Finished until the step completes. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public UserSessionListItem.Features.Status status() { + return Json.required(json, "status", Conv.enumeration(UserSessionListItem.Features.Status::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Features}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Features from) { + super(from); + } + + /** + * Verification feature identifier. + * + * @param feature the value + * @return this builder + */ + public Builder feature(UserSessionListItem.Features.Feature feature) { + return set("feature", Conv.enumeration(UserSessionListItem.Features.Feature::of), feature); + } + + /** + * Status of this feature. Not Finished until the step completes. + * + * @param status the value + * @return this builder + */ + public Builder status(UserSessionListItem.Features.Status status) { + return set("status", Conv.enumeration(UserSessionListItem.Features.Status::of), status); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Features} + * @throws IllegalStateException when a required field is missing + */ + public Features build() { + require("feature", false); + require("status", false); + return new Features(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Verification feature identifier. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Feature extends StringEnum { + /** The value {@code "ID_VERIFICATION"}. */ + public static final Feature ID_VERIFICATION = new Feature("ID_VERIFICATION"); + + /** The value {@code "NFC"}. */ + public static final Feature NFC = new Feature("NFC"); + + /** The value {@code "LIVENESS"}. */ + public static final Feature LIVENESS = new Feature("LIVENESS"); + + /** The value {@code "FACE_MATCH"}. */ + public static final Feature FACE_MATCH = new Feature("FACE_MATCH"); + + /** The value {@code "POA"}. */ + public static final Feature POA = new Feature("POA"); + + /** The value {@code "QUESTIONNAIRE"}. */ + public static final Feature QUESTIONNAIRE = new Feature("QUESTIONNAIRE"); + + /** The value {@code "EMAIL_VERIFICATION"}. */ + public static final Feature EMAIL_VERIFICATION = new Feature("EMAIL_VERIFICATION"); + + /** The value {@code "PHONE"}. */ + public static final Feature PHONE = new Feature("PHONE"); + + /** The value {@code "AML"}. */ + public static final Feature AML = new Feature("AML"); + + /** The value {@code "IP_ANALYSIS"}. */ + public static final Feature IP_ANALYSIS = new Feature("IP_ANALYSIS"); + + /** The value {@code "AGE_ESTIMATION"}. */ + public static final Feature AGE_ESTIMATION = new Feature("AGE_ESTIMATION"); + + /** The value {@code "DATABASE_VALIDATION"}. */ + public static final Feature DATABASE_VALIDATION = new Feature("DATABASE_VALIDATION"); + + private static final List KNOWN_VALUES = List.of(ID_VERIFICATION, NFC, LIVENESS, FACE_MATCH, POA, QUESTIONNAIRE, EMAIL_VERIFICATION, PHONE, AML, IP_ANALYSIS, AGE_ESTIMATION, DATABASE_VALIDATION); + + private Feature(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Feature of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Feature::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Status of this feature. Not Finished until the step completes. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "Not Finished"}. */ + public static final Status NOT_FINISHED = new Status("Not Finished"); + + /** The value {@code "Approved"}. */ + public static final Status APPROVED = new Status("Approved"); + + /** The value {@code "Declined"}. */ + public static final Status DECLINED = new Status("Declined"); + + /** The value {@code "In Review"}. */ + public static final Status IN_REVIEW = new Status("In Review"); + + /** The value {@code "Resub Requested"}. */ + public static final Status RESUB_REQUESTED = new Status("Resub Requested"); + + private static final List KNOWN_VALUES = List.of(NOT_FINISHED, APPROVED, DECLINED, IN_REVIEW, RESUB_REQUESTED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * Phone associated with the session: the verified phone when phone verification ran, otherwise the contact phone provided at session creation. null when neither exists. + */ + public static final class PhoneNumber extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("number", "is_verified"); + + private PhoneNumber(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code PhoneNumber} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PhoneNumber fromJson(JsonNode json) { + return new PhoneNumber(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Phone number in E.164 format. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String number() { + return Json.required(json, "number", Conv.STRING); + } + + /** + * true only when the phone-verification feature approved this number. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isVerified() { + return Json.required(json, "is_verified", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link PhoneNumber}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(PhoneNumber from) { + super(from); + } + + /** + * Phone number in E.164 format. + * + * @param number the value + * @return this builder + */ + public Builder number(String number) { + return set("number", Conv.STRING, number); + } + + /** + * true only when the phone-verification feature approved this number. + * + * @param isVerified the value + * @return this builder + */ + public Builder isVerified(boolean isVerified) { + return set("is_verified", Conv.BOOLEAN, isVerified); + } + + /** + * Builds the value. + * + * @return a new immutable {@code PhoneNumber} + * @throws IllegalStateException when a required field is missing + */ + public PhoneNumber build() { + require("number", false); + require("is_verified", false); + return new PhoneNumber(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * Email associated with the session: the verified email when email verification ran, otherwise the contact email provided at session creation. null when neither exists. + */ + public static final class EmailAddress extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("email", "is_verified"); + + private EmailAddress(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code EmailAddress} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static EmailAddress fromJson(JsonNode json) { + return new EmailAddress(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The email field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String email() { + return Json.required(json, "email", Conv.STRING); + } + + /** + * true only when the email-verification feature approved this address. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean isVerified() { + return Json.required(json, "is_verified", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link EmailAddress}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(EmailAddress from) { + super(from); + } + + /** + * The email field. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * true only when the email-verification feature approved this address. + * + * @param isVerified the value + * @return this builder + */ + public Builder isVerified(boolean isVerified) { + return set("is_verified", Conv.BOOLEAN, isVerified); + } + + /** + * Builds the value. + * + * @return a new immutable {@code EmailAddress} + * @throws IllegalStateException when a required field is missing + */ + public EmailAddress build() { + require("email", false); + require("is_verified", false); + return new EmailAddress(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * Compact per-feature extras. Only features that produced data appear as keys; null when no feature produced extras. Fields excluded by the workflow's response-attributes configuration are omitted. + */ + public static final class AdditionalDetails extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("aml", "database_validation", "poa", "liveness", "face_match", "phone_verification", "email_verification"); + + private AdditionalDetails(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AdditionalDetails} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AdditionalDetails fromJson(JsonNode json) { + return new AdditionalDetails(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Present when AML screening ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional aml() { + return Json.optional(json, "aml", Conv.model(UserSessionListItem.AdditionalDetails.Aml::fromJson)); + } + + /** + * Present when database validation ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional databaseValidation() { + return Json.optional(json, "database_validation", Conv.model(UserSessionListItem.AdditionalDetails.DatabaseValidation::fromJson)); + } + + /** + * Present when proof of address ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional poa() { + return Json.optional(json, "poa", Conv.model(UserSessionListItem.AdditionalDetails.Poa::fromJson)); + } + + /** + * Present when a liveness check ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional liveness() { + return Json.optional(json, "liveness", Conv.model(UserSessionListItem.AdditionalDetails.Liveness::fromJson)); + } + + /** + * Present when face match ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional faceMatch() { + return Json.optional(json, "face_match", Conv.model(UserSessionListItem.AdditionalDetails.FaceMatch::fromJson)); + } + + /** + * Present when phone verification ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneVerification() { + return Json.optional(json, "phone_verification", Conv.model(UserSessionListItem.AdditionalDetails.PhoneVerification::fromJson)); + } + + /** + * Present when email verification ran. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional emailVerification() { + return Json.optional(json, "email_verification", Conv.model(UserSessionListItem.AdditionalDetails.EmailVerification::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AdditionalDetails}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AdditionalDetails from) { + super(from); + } + + /** + * Present when AML screening ran. + * + * @param aml the value + * @return this builder + */ + public Builder aml(UserSessionListItem.AdditionalDetails.Aml aml) { + return set("aml", Conv.model(UserSessionListItem.AdditionalDetails.Aml::fromJson), aml); + } + + /** + * Present when database validation ran. + * + * @param databaseValidation the value + * @return this builder + */ + public Builder databaseValidation(UserSessionListItem.AdditionalDetails.DatabaseValidation databaseValidation) { + return set("database_validation", Conv.model(UserSessionListItem.AdditionalDetails.DatabaseValidation::fromJson), databaseValidation); + } + + /** + * Present when proof of address ran. + * + * @param poa the value + * @return this builder + */ + public Builder poa(UserSessionListItem.AdditionalDetails.Poa poa) { + return set("poa", Conv.model(UserSessionListItem.AdditionalDetails.Poa::fromJson), poa); + } + + /** + * Present when a liveness check ran. + * + * @param liveness the value + * @return this builder + */ + public Builder liveness(UserSessionListItem.AdditionalDetails.Liveness liveness) { + return set("liveness", Conv.model(UserSessionListItem.AdditionalDetails.Liveness::fromJson), liveness); + } + + /** + * Present when face match ran. + * + * @param faceMatch the value + * @return this builder + */ + public Builder faceMatch(UserSessionListItem.AdditionalDetails.FaceMatch faceMatch) { + return set("face_match", Conv.model(UserSessionListItem.AdditionalDetails.FaceMatch::fromJson), faceMatch); + } + + /** + * Present when phone verification ran. + * + * @param phoneVerification the value + * @return this builder + */ + public Builder phoneVerification(UserSessionListItem.AdditionalDetails.PhoneVerification phoneVerification) { + return set("phone_verification", Conv.model(UserSessionListItem.AdditionalDetails.PhoneVerification::fromJson), phoneVerification); + } + + /** + * Present when email verification ran. + * + * @param emailVerification the value + * @return this builder + */ + public Builder emailVerification(UserSessionListItem.AdditionalDetails.EmailVerification emailVerification) { + return set("email_verification", Conv.model(UserSessionListItem.AdditionalDetails.EmailVerification::fromJson), emailVerification); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AdditionalDetails} + */ + public AdditionalDetails build() { + return new AdditionalDetails(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** Present when AML screening ran. */ + public static final class Aml extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("total_hits", "entity_type", "is_adverse_media_fetched", "screened_data"); + + private Aml(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Aml} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Aml fromJson(JsonNode json) { + return new Aml(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Number of AML screening hits. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalHits() { + return Json.optional(json, "total_hits", Conv.LONG); + } + + /** + * Screened entity type. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.STRING); + } + + /** + * The is_adverse_media_fetched field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isAdverseMediaFetched() { + return Json.optional(json, "is_adverse_media_fetched", Conv.BOOLEAN); + } + + /** + * Subset of the data that was screened. Only non-empty keys are included. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional screenedData() { + return Json.optional(json, "screened_data", Conv.model(UserSessionListItem.AdditionalDetails.Aml.ScreenedData::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Aml}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Aml from) { + super(from); + } + + /** + * Number of AML screening hits. + * + * @param totalHits the value + * @return this builder + */ + public Builder totalHits(long totalHits) { + return set("total_hits", Conv.LONG, totalHits); + } + + /** + * Screened entity type. + * + * @param entityType the value + * @return this builder + */ + public Builder entityType(String entityType) { + return set("entity_type", Conv.STRING, entityType); + } + + /** + * The is_adverse_media_fetched field. + * + * @param isAdverseMediaFetched the value + * @return this builder + */ + public Builder isAdverseMediaFetched(boolean isAdverseMediaFetched) { + return set("is_adverse_media_fetched", Conv.BOOLEAN, isAdverseMediaFetched); + } + + /** + * Subset of the data that was screened. Only non-empty keys are included. + * + * @param screenedData the value + * @return this builder + */ + public Builder screenedData(UserSessionListItem.AdditionalDetails.Aml.ScreenedData screenedData) { + return set("screened_data", Conv.model(UserSessionListItem.AdditionalDetails.Aml.ScreenedData::fromJson), screenedData); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Aml} + */ + public Aml build() { + return new Aml(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** Subset of the data that was screened. Only non-empty keys are included. */ + public static final class ScreenedData extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("nationality", "document_number", "date_of_birth"); + + private ScreenedData(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code ScreenedData} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static ScreenedData fromJson(JsonNode json) { + return new ScreenedData(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The nationality field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nationality() { + return Json.optional(json, "nationality", Conv.STRING); + } + + /** + * The document_number field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentNumber() { + return Json.optional(json, "document_number", Conv.STRING); + } + + /** + * The date_of_birth field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateOfBirth() { + return Json.optional(json, "date_of_birth", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link ScreenedData}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(ScreenedData from) { + super(from); + } + + /** + * The nationality field. + * + * @param nationality the value + * @return this builder + */ + public Builder nationality(String nationality) { + return set("nationality", Conv.STRING, nationality); + } + + /** + * The document_number field. + * + * @param documentNumber the value + * @return this builder + */ + public Builder documentNumber(String documentNumber) { + return set("document_number", Conv.STRING, documentNumber); + } + + /** + * The date_of_birth field. + * + * @param dateOfBirth the value + * @return this builder + */ + public Builder dateOfBirth(String dateOfBirth) { + return set("date_of_birth", Conv.STRING, dateOfBirth); + } + + /** + * Builds the value. + * + * @return a new immutable {@code ScreenedData} + */ + public ScreenedData build() { + return new ScreenedData(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } + + /** Present when database validation ran. */ + public static final class DatabaseValidation extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("validation_type", "match_type", "identification_number"); + + private DatabaseValidation(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code DatabaseValidation} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DatabaseValidation fromJson(JsonNode json) { + return new DatabaseValidation(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The validation_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional validationType() { + return Json.optional(json, "validation_type", Conv.STRING); + } + + /** + * The match_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional matchType() { + return Json.optional(json, "match_type", Conv.STRING); + } + + /** + * Tax, personal, or document number that was validated (first available). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional identificationNumber() { + return Json.optional(json, "identification_number", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link DatabaseValidation}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(DatabaseValidation from) { + super(from); + } + + /** + * The validation_type field. + * + * @param validationType the value + * @return this builder + */ + public Builder validationType(String validationType) { + return set("validation_type", Conv.STRING, validationType); + } + + /** + * The match_type field. + * + * @param matchType the value + * @return this builder + */ + public Builder matchType(String matchType) { + return set("match_type", Conv.STRING, matchType); + } + + /** + * Tax, personal, or document number that was validated (first available). + * + * @param identificationNumber the value + * @return this builder + */ + public Builder identificationNumber(String identificationNumber) { + return set("identification_number", Conv.STRING, identificationNumber); + } + + /** + * Builds the value. + * + * @return a new immutable {@code DatabaseValidation} + */ + public DatabaseValidation build() { + return new DatabaseValidation(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Present when proof of address ran. */ + public static final class Poa extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("document_type", "issuer", "address", "name_on_document"); + + private Poa(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Poa} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Poa fromJson(JsonNode json) { + return new Poa(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The document_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional documentType() { + return Json.optional(json, "document_type", Conv.STRING); + } + + /** + * The issuer field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional issuer() { + return Json.optional(json, "issuer", Conv.STRING); + } + + /** + * The address field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional address() { + return Json.optional(json, "address", Conv.STRING); + } + + /** + * The name_on_document field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional nameOnDocument() { + return Json.optional(json, "name_on_document", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Poa}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Poa from) { + super(from); + } + + /** + * The document_type field. + * + * @param documentType the value + * @return this builder + */ + public Builder documentType(String documentType) { + return set("document_type", Conv.STRING, documentType); + } + + /** + * The issuer field. + * + * @param issuer the value + * @return this builder + */ + public Builder issuer(String issuer) { + return set("issuer", Conv.STRING, issuer); + } + + /** + * The address field. + * + * @param address the value + * @return this builder + */ + public Builder address(String address) { + return set("address", Conv.STRING, address); + } + + /** + * The name_on_document field. + * + * @param nameOnDocument the value + * @return this builder + */ + public Builder nameOnDocument(String nameOnDocument) { + return set("name_on_document", Conv.STRING, nameOnDocument); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Poa} + */ + public Poa build() { + return new Poa(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Present when a liveness check ran. */ + public static final class Liveness extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("method", "score"); + + private Liveness(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Liveness} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Liveness fromJson(JsonNode json) { + return new Liveness(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The method field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional method() { + return Json.optional(json, "method", Conv.STRING); + } + + /** + * Liveness confidence score, rounded to 2 decimals. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.DOUBLE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Liveness}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Liveness from) { + super(from); + } + + /** + * The method field. + * + * @param method the value + * @return this builder + */ + public Builder method(String method) { + return set("method", Conv.STRING, method); + } + + /** + * Liveness confidence score, rounded to 2 decimals. + * + * @param score the value + * @return this builder + */ + public Builder score(double score) { + return set("score", Conv.DOUBLE, score); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Liveness} + */ + public Liveness build() { + return new Liveness(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Present when face match ran. */ + public static final class FaceMatch extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("score", "source_image", "target_image"); + + private FaceMatch(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code FaceMatch} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static FaceMatch fromJson(JsonNode json) { + return new FaceMatch(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Face-match similarity score, rounded to 2 decimals. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional score() { + return Json.optional(json, "score", Conv.DOUBLE); + } + + /** + * Expiring (presigned) URL of the source face image. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sourceImage() { + return Json.optional(json, "source_image", Conv.STRING); + } + + /** + * Expiring (presigned) URL of the target face image. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional targetImage() { + return Json.optional(json, "target_image", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link FaceMatch}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(FaceMatch from) { + super(from); + } + + /** + * Face-match similarity score, rounded to 2 decimals. + * + * @param score the value + * @return this builder + */ + public Builder score(double score) { + return set("score", Conv.DOUBLE, score); + } + + /** + * Expiring (presigned) URL of the source face image. + * + * @param sourceImage the value + * @return this builder + */ + public Builder sourceImage(String sourceImage) { + return set("source_image", Conv.STRING, sourceImage); + } + + /** + * Expiring (presigned) URL of the target face image. + * + * @param targetImage the value + * @return this builder + */ + public Builder targetImage(String targetImage) { + return set("target_image", Conv.STRING, targetImage); + } + + /** + * Builds the value. + * + * @return a new immutable {@code FaceMatch} + */ + public FaceMatch build() { + return new FaceMatch(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Present when phone verification ran. */ + public static final class PhoneVerification extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("country_code", "is_temporary", "is_ported", "is_blocked"); + + private PhoneVerification(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code PhoneVerification} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static PhoneVerification fromJson(JsonNode json) { + return new PhoneVerification(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * ISO 3166-1 alpha-2 country of the phone number. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional countryCode() { + return Json.optional(json, "country_code", Conv.STRING); + } + + /** + * The is_temporary field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isTemporary() { + return Json.optional(json, "is_temporary", Conv.BOOLEAN); + } + + /** + * The is_ported field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isPorted() { + return Json.optional(json, "is_ported", Conv.BOOLEAN); + } + + /** + * The is_blocked field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isBlocked() { + return Json.optional(json, "is_blocked", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link PhoneVerification}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(PhoneVerification from) { + super(from); + } + + /** + * ISO 3166-1 alpha-2 country of the phone number. + * + * @param countryCode the value + * @return this builder + */ + public Builder countryCode(String countryCode) { + return set("country_code", Conv.STRING, countryCode); + } + + /** + * The is_temporary field. + * + * @param isTemporary the value + * @return this builder + */ + public Builder isTemporary(boolean isTemporary) { + return set("is_temporary", Conv.BOOLEAN, isTemporary); + } + + /** + * The is_ported field. + * + * @param isPorted the value + * @return this builder + */ + public Builder isPorted(boolean isPorted) { + return set("is_ported", Conv.BOOLEAN, isPorted); + } + + /** + * The is_blocked field. + * + * @param isBlocked the value + * @return this builder + */ + public Builder isBlocked(boolean isBlocked) { + return set("is_blocked", Conv.BOOLEAN, isBlocked); + } + + /** + * Builds the value. + * + * @return a new immutable {@code PhoneVerification} + */ + public PhoneVerification build() { + return new PhoneVerification(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** Present when email verification ran. */ + public static final class EmailVerification extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("is_breached", "is_disposable", "is_undeliverable", "is_blocklisted"); + + private EmailVerification(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code EmailVerification} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static EmailVerification fromJson(JsonNode json) { + return new EmailVerification(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The is_breached field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isBreached() { + return Json.optional(json, "is_breached", Conv.BOOLEAN); + } + + /** + * The is_disposable field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isDisposable() { + return Json.optional(json, "is_disposable", Conv.BOOLEAN); + } + + /** + * The is_undeliverable field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isUndeliverable() { + return Json.optional(json, "is_undeliverable", Conv.BOOLEAN); + } + + /** + * The is_blocklisted field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isBlocklisted() { + return Json.optional(json, "is_blocklisted", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link EmailVerification}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(EmailVerification from) { + super(from); + } + + /** + * The is_breached field. + * + * @param isBreached the value + * @return this builder + */ + public Builder isBreached(boolean isBreached) { + return set("is_breached", Conv.BOOLEAN, isBreached); + } + + /** + * The is_disposable field. + * + * @param isDisposable the value + * @return this builder + */ + public Builder isDisposable(boolean isDisposable) { + return set("is_disposable", Conv.BOOLEAN, isDisposable); + } + + /** + * The is_undeliverable field. + * + * @param isUndeliverable the value + * @return this builder + */ + public Builder isUndeliverable(boolean isUndeliverable) { + return set("is_undeliverable", Conv.BOOLEAN, isUndeliverable); + } + + /** + * The is_blocklisted field. + * + * @param isBlocklisted the value + * @return this builder + */ + public Builder isBlocklisted(boolean isBlocklisted) { + return set("is_blocklisted", Conv.BOOLEAN, isBlocklisted); + } + + /** + * Builds the value. + * + * @return a new immutable {@code EmailVerification} + */ + public EmailVerification build() { + return new EmailVerification(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + } +} diff --git a/src/main/java/me/didit/types/UserStatusUpdatedEvent.java b/src/main/java/me/didit/types/UserStatusUpdatedEvent.java new file mode 100644 index 0000000..c154e4b --- /dev/null +++ b/src/main/java/me/didit/types/UserStatusUpdatedEvent.java @@ -0,0 +1,430 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** A user changed status. Not sent when the user is created. */ +public final class UserStatusUpdatedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "webhook_type", "vendor_user_id", "vendor_data", "status", "previous_status"); + + private UserStatusUpdatedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code UserStatusUpdatedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static UserStatusUpdatedEvent fromJson(JsonNode json) { + return new UserStatusUpdatedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * Always user.status.updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public UserStatusUpdatedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(UserStatusUpdatedEvent.WebhookType::of)); + } + + /** + * The user the event is about. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String vendorUserId() { + return Json.required(json, "vendor_user_id", Conv.STRING); + } + + /** + * Your vendor_data for the user, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * User status after the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public UserStatusUpdatedEvent.Status status() { + return Json.required(json, "status", Conv.enumeration(UserStatusUpdatedEvent.Status::of)); + } + + /** + * Status before the change. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public UserStatusUpdatedEvent.PreviousStatus previousStatus() { + return Json.required(json, "previous_status", Conv.enumeration(UserStatusUpdatedEvent.PreviousStatus::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link UserStatusUpdatedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(UserStatusUpdatedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * Always user.status.updated. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(UserStatusUpdatedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(UserStatusUpdatedEvent.WebhookType::of), webhookType); + } + + /** + * The user the event is about. + * + * @param vendorUserId the value + * @return this builder + */ + public Builder vendorUserId(String vendorUserId) { + return set("vendor_user_id", Conv.STRING, vendorUserId); + } + + /** + * Your vendor_data for the user, or null. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * User status after the change. + * + * @param status the value + * @return this builder + */ + public Builder status(UserStatusUpdatedEvent.Status status) { + return set("status", Conv.enumeration(UserStatusUpdatedEvent.Status::of), status); + } + + /** + * Status before the change. + * + * @param previousStatus the value + * @return this builder + */ + public Builder previousStatus(UserStatusUpdatedEvent.PreviousStatus previousStatus) { + return set("previous_status", Conv.enumeration(UserStatusUpdatedEvent.PreviousStatus::of), previousStatus); + } + + /** + * Builds the value. + * + * @return a new immutable {@code UserStatusUpdatedEvent} + * @throws IllegalStateException when a required field is missing + */ + public UserStatusUpdatedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("webhook_type", false); + require("vendor_user_id", false); + require("vendor_data", true); + require("status", false); + require("previous_status", false); + return new UserStatusUpdatedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always user.status.updated. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "user.status.updated"}. */ + public static final WebhookType USER_STATUS_UPDATED = new WebhookType("user.status.updated"); + + private static final List KNOWN_VALUES = List.of(USER_STATUS_UPDATED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * User status after the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final Status ACTIVE = new Status("ACTIVE"); + + /** The value {@code "IN_REVIEW"}. */ + public static final Status IN_REVIEW = new Status("IN_REVIEW"); + + /** The value {@code "FLAGGED"}. */ + public static final Status FLAGGED = new Status("FLAGGED"); + + /** The value {@code "BLOCKED"}. */ + public static final Status BLOCKED = new Status("BLOCKED"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, IN_REVIEW, FLAGGED, BLOCKED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Status before the change. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class PreviousStatus extends StringEnum { + /** The value {@code "ACTIVE"}. */ + public static final PreviousStatus ACTIVE = new PreviousStatus("ACTIVE"); + + /** The value {@code "IN_REVIEW"}. */ + public static final PreviousStatus IN_REVIEW = new PreviousStatus("IN_REVIEW"); + + /** The value {@code "FLAGGED"}. */ + public static final PreviousStatus FLAGGED = new PreviousStatus("FLAGGED"); + + /** The value {@code "BLOCKED"}. */ + public static final PreviousStatus BLOCKED = new PreviousStatus("BLOCKED"); + + private static final List KNOWN_VALUES = List.of(ACTIVE, IN_REVIEW, FLAGGED, BLOCKED); + + private PreviousStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static PreviousStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, PreviousStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/UsersCreateParams.java b/src/main/java/me/didit/types/UsersCreateParams.java index 9522b75..da5d988 100644 --- a/src/main/java/me/didit/types/UsersCreateParams.java +++ b/src/main/java/me/didit/types/UsersCreateParams.java @@ -17,7 +17,7 @@ /** Parameters for {@code users.create}. */ public final class UsersCreateParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("vendor_data", "full_name", "display_name", "date_of_birth", "status", "metadata", "approved_emails", "approved_phones", "issuing_states"); + private static final Set KNOWN_FIELDS = Set.of("vendor_data", "full_name", "display_name", "date_of_birth", "status", "metadata", "approved_emails", "approved_phones", "issuing_states", "first_name", "last_name", "email", "phone_number", "country", "send_verification_link", "workflow_id", "language"); private UsersCreateParams(ObjectNode json, Map> files) { super(json, files); @@ -53,7 +53,7 @@ public Builder toBuilder() { } /** - * Your unique identifier for this user (free-form string, NOT a UUID). Must be unique among non-deleted users for the application; matched exactly as sent. + * Your unique identifier for this user (free-form string, NOT a UUID). Must be unique among the application's users; matched exactly as sent. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -142,6 +142,86 @@ public Optional> issuingStates() { return Json.optional(json, "issuing_states", Conv.list(Conv.STRING)); } + /** + * Given name. When full_name is not sent, Didit joins first_name and last_name with a space into full_name. Not returned as a separate field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional firstName() { + return Json.optional(json, "first_name", Conv.STRING); + } + + /** + * Family name. Joined with first_name into full_name when full_name is not sent. Not returned as a separate field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastName() { + return Json.optional(json, "last_name", Conv.STRING); + } + + /** + * Contact email, stored in metadata.contact.email (it replaces an email you send inside metadata.contact). Required when send_verification_link is true: the link is sent to this address. If metadata.contact is not a JSON object, the call answers 400 on this field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + /** + * Contact phone number, stored in metadata.contact.phone_number. If metadata.contact is not a JSON object, the call answers 400 on this field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional phoneNumber() { + return Json.optional(json, "phone_number", Conv.STRING); + } + + /** + * Country as an ISO 3166 code, alpha-2 or alpha-3 (ES or ESP). Stored uppercased in metadata.address.country. If metadata.address is not a JSON object, the call answers 400 on this field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + /** + * When true, Didit creates a verification session for the person on workflow_id and emails them the link at email. Requires email and workflow_id. A scoped API key also needs sessions write. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sendVerificationLink() { + return Json.optional(json, "send_verification_link", Conv.BOOLEAN); + } + + /** + * The workflow of the verification session. Required when send_verification_link is true. It must verify a person: a Business Verification (KYB) workflow is refused. Ignored otherwise. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowId() { + return Json.optional(json, "workflow_id", Conv.STRING); + } + + /** + * Language of the verification email, as a [supported language](/integration/supported-languages) code. English when omitted. Ignored when send_verification_link is not true. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional language() { + return Json.optional(json, "language", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -156,7 +236,7 @@ private Builder(UsersCreateParams from) { } /** - * Your unique identifier for this user (free-form string, NOT a UUID). Must be unique among non-deleted users for the application; matched exactly as sent. + * Your unique identifier for this user (free-form string, NOT a UUID). Must be unique among the application's users; matched exactly as sent. * * @param vendorData the value * @return this builder @@ -245,6 +325,86 @@ public Builder issuingStates(List issuingStates) { return set("issuing_states", Conv.list(Conv.STRING), issuingStates); } + /** + * Given name. When full_name is not sent, Didit joins first_name and last_name with a space into full_name. Not returned as a separate field. + * + * @param firstName the value + * @return this builder + */ + public Builder firstName(String firstName) { + return set("first_name", Conv.STRING, firstName); + } + + /** + * Family name. Joined with first_name into full_name when full_name is not sent. Not returned as a separate field. + * + * @param lastName the value + * @return this builder + */ + public Builder lastName(String lastName) { + return set("last_name", Conv.STRING, lastName); + } + + /** + * Contact email, stored in metadata.contact.email (it replaces an email you send inside metadata.contact). Required when send_verification_link is true: the link is sent to this address. If metadata.contact is not a JSON object, the call answers 400 on this field. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * Contact phone number, stored in metadata.contact.phone_number. If metadata.contact is not a JSON object, the call answers 400 on this field. + * + * @param phoneNumber the value + * @return this builder + */ + public Builder phoneNumber(String phoneNumber) { + return set("phone_number", Conv.STRING, phoneNumber); + } + + /** + * Country as an ISO 3166 code, alpha-2 or alpha-3 (ES or ESP). Stored uppercased in metadata.address.country. If metadata.address is not a JSON object, the call answers 400 on this field. + * + * @param country the value + * @return this builder + */ + public Builder country(String country) { + return set("country", Conv.STRING, country); + } + + /** + * When true, Didit creates a verification session for the person on workflow_id and emails them the link at email. Requires email and workflow_id. A scoped API key also needs sessions write. + * + * @param sendVerificationLink the value + * @return this builder + */ + public Builder sendVerificationLink(boolean sendVerificationLink) { + return set("send_verification_link", Conv.BOOLEAN, sendVerificationLink); + } + + /** + * The workflow of the verification session. Required when send_verification_link is true. It must verify a person: a Business Verification (KYB) workflow is refused. Ignored otherwise. + * + * @param workflowId the value + * @return this builder + */ + public Builder workflowId(String workflowId) { + return set("workflow_id", Conv.STRING, workflowId); + } + + /** + * Language of the verification email, as a [supported language](/integration/supported-languages) code. English when omitted. Ignored when send_verification_link is not true. + * + * @param language the value + * @return this builder + */ + public Builder language(String language) { + return set("language", Conv.STRING, language); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/UsersCreateResponse.java b/src/main/java/me/didit/types/UsersCreateResponse.java new file mode 100644 index 0000000..754b72b --- /dev/null +++ b/src/main/java/me/didit/types/UsersCreateResponse.java @@ -0,0 +1,798 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class UsersCreateResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("didit_internal_id", "vendor_data", "display_name", "full_name", "date_of_birth", "effective_name", "status", "portrait_image_url", "session_count", "approved_count", "declined_count", "in_review_count", "issuing_states", "approved_emails", "approved_phones", "features", "features_list", "last_session_at", "first_session_at", "last_activity_at", "created_at", "metadata", "tags", "comments", "updated_at", "verification_link"); + + private UsersCreateResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code UsersCreateResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static UsersCreateResponse fromJson(JsonNode json) { + return new UsersCreateResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Didit's stable internal UUID for this user. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional diditInternalId() { + return Json.optional(json, "didit_internal_id", Conv.STRING); + } + + /** + * Your unique identifier for this user (passed when creating sessions). This can be null when no vendor identifier was supplied. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional vendorData() { + return Json.optional(json, "vendor_data", Conv.STRING); + } + + /** + * Custom display name set by you + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional displayName() { + return Json.optional(json, "display_name", Conv.STRING); + } + + /** + * Full name extracted from verified documents + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional fullName() { + return Json.optional(json, "full_name", Conv.STRING); + } + + /** + * The date_of_birth field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateOfBirth() { + return Json.optional(json, "date_of_birth", Conv.DATE); + } + + /** + * Best available name: display_name if set, otherwise full_name + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional effectiveName() { + return Json.optional(json, "effective_name", Conv.STRING); + } + + /** + * Lifecycle status of the user record (NOT a session status). ACTIVE is the default, FLAGGED marks the user for manual attention, BLOCKED prevents new sessions for this vendor_data. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(UserListItem.Status::of)); + } + + /** + * Presigned URL of the user's portrait photo (expires after a few hours) + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional portraitImageUrl() { + return Json.optional(json, "portrait_image_url", Conv.STRING); + } + + /** + * Total number of verification sessions for this user + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionCount() { + return Json.optional(json, "session_count", Conv.LONG); + } + + /** + * Number of approved sessions + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional approvedCount() { + return Json.optional(json, "approved_count", Conv.LONG); + } + + /** + * Number of declined sessions + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional declinedCount() { + return Json.optional(json, "declined_count", Conv.LONG); + } + + /** + * Number of sessions in review + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional inReviewCount() { + return Json.optional(json, "in_review_count", Conv.LONG); + } + + /** + * ISO 3166-1 alpha-3 codes of issuing countries seen on this user's approved ID documents, e.g. ["USA", "ESP"]. Empty array when none. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> issuingStates() { + return Json.optional(json, "issuing_states", Conv.list(Conv.STRING)); + } + + /** + * Verified email addresses collected from this user's approved sessions, e.g. ["john@example.com"]. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> approvedEmails() { + return Json.optional(json, "approved_emails", Conv.list(Conv.STRING)); + } + + /** + * Verified phone numbers collected from this user's approved sessions, e.g. ["+14155551234"]. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> approvedPhones() { + return Json.optional(json, "approved_phones", Conv.list(Conv.STRING)); + } + + /** + * Aggregated per-feature status across all of this user's sessions. Possible keys: ID_VERIFICATION, NFC, LIVENESS, FACE_MATCH, POA, QUESTIONNAIRE, EMAIL_VERIFICATION, PHONE, AML, IP_ANALYSIS, AGE_ESTIMATION, DATABASE_VALIDATION. Possible values: Approved, Declined, In Review, Not Finished, Resub Requested. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> features() { + return Json.optional(json, "features", Conv.OBJECT); + } + + /** + * Same data as features, as an ordered array of {feature, status} objects. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> featuresList() { + return Json.optional(json, "features_list", Conv.list(Conv.model(UserListItem.FeaturesList::fromJson))); + } + + /** + * Timestamp of the most recent session + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastSessionAt() { + return Json.optional(json, "last_session_at", Conv.DATE_TIME); + } + + /** + * Timestamp of the first session + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional firstSessionAt() { + return Json.optional(json, "first_session_at", Conv.DATE_TIME); + } + + /** + * Timestamp of the most recent activity on this user (session, transaction, status change, data edit, etc.). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastActivityAt() { + return Json.optional(json, "last_activity_at", Conv.DATE_TIME); + } + + /** + * The created_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.DATE_TIME); + } + + /** + * Custom metadata JSON you attached to this user. Defaults to {}. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> metadata() { + return Json.optional(json, "metadata", Conv.OBJECT); + } + + /** + * Tag assignments. NOTE: on detail responses each entry is a tag *link* object ({uuid, tag: {...}, added_by_email, added_by_name, created_at}), unlike the flat {uuid, name, color} shape used on list responses. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> tags() { + return Json.optional(json, "tags", Conv.list(Conv.model(UserDetailItem.Tags::fromJson))); + } + + /** + * Activity log and comments for this user (status changes, profile edits, manual notes). + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> comments() { + return Json.optional(json, "comments", Conv.list(Conv.model(UserDetailItem.Comments::fromJson))); + } + + /** + * The updated_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional updatedAt() { + return Json.optional(json, "updated_at", Conv.DATE_TIME); + } + + /** + * The verification session Didit created and emailed to the person. Always present: null when send_verification_link was not true. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verificationLink() { + return Json.optional(json, "verification_link", Conv.model(UsersCreateResponse.VerificationLink::fromJson)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link UsersCreateResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(UsersCreateResponse from) { + super(from); + } + + /** + * Didit's stable internal UUID for this user. + * + * @param diditInternalId the value + * @return this builder + */ + public Builder diditInternalId(String diditInternalId) { + return set("didit_internal_id", Conv.STRING, diditInternalId); + } + + /** + * Your unique identifier for this user (passed when creating sessions). This can be null when no vendor identifier was supplied. + * + * @param vendorData the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder vendorData(@Nullable String vendorData) { + return setNullable("vendor_data", Conv.STRING, vendorData); + } + + /** + * Custom display name set by you + * + * @param displayName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder displayName(@Nullable String displayName) { + return setNullable("display_name", Conv.STRING, displayName); + } + + /** + * Full name extracted from verified documents + * + * @param fullName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder fullName(@Nullable String fullName) { + return setNullable("full_name", Conv.STRING, fullName); + } + + /** + * The date_of_birth field. + * + * @param dateOfBirth the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dateOfBirth(@Nullable LocalDate dateOfBirth) { + return setNullable("date_of_birth", Conv.DATE, dateOfBirth); + } + + /** + * Best available name: display_name if set, otherwise full_name + * + * @param effectiveName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder effectiveName(@Nullable String effectiveName) { + return setNullable("effective_name", Conv.STRING, effectiveName); + } + + /** + * Lifecycle status of the user record (NOT a session status). ACTIVE is the default, FLAGGED marks the user for manual attention, BLOCKED prevents new sessions for this vendor_data. + * + * @param status the value + * @return this builder + */ + public Builder status(UserListItem.Status status) { + return set("status", Conv.enumeration(UserListItem.Status::of), status); + } + + /** + * Presigned URL of the user's portrait photo (expires after a few hours) + * + * @param portraitImageUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder portraitImageUrl(@Nullable String portraitImageUrl) { + return setNullable("portrait_image_url", Conv.STRING, portraitImageUrl); + } + + /** + * Total number of verification sessions for this user + * + * @param sessionCount the value + * @return this builder + */ + public Builder sessionCount(long sessionCount) { + return set("session_count", Conv.LONG, sessionCount); + } + + /** + * Number of approved sessions + * + * @param approvedCount the value + * @return this builder + */ + public Builder approvedCount(long approvedCount) { + return set("approved_count", Conv.LONG, approvedCount); + } + + /** + * Number of declined sessions + * + * @param declinedCount the value + * @return this builder + */ + public Builder declinedCount(long declinedCount) { + return set("declined_count", Conv.LONG, declinedCount); + } + + /** + * Number of sessions in review + * + * @param inReviewCount the value + * @return this builder + */ + public Builder inReviewCount(long inReviewCount) { + return set("in_review_count", Conv.LONG, inReviewCount); + } + + /** + * ISO 3166-1 alpha-3 codes of issuing countries seen on this user's approved ID documents, e.g. ["USA", "ESP"]. Empty array when none. + * + * @param issuingStates the value + * @return this builder + */ + public Builder issuingStates(List issuingStates) { + return set("issuing_states", Conv.list(Conv.STRING), issuingStates); + } + + /** + * Verified email addresses collected from this user's approved sessions, e.g. ["john@example.com"]. + * + * @param approvedEmails the value + * @return this builder + */ + public Builder approvedEmails(List approvedEmails) { + return set("approved_emails", Conv.list(Conv.STRING), approvedEmails); + } + + /** + * Verified phone numbers collected from this user's approved sessions, e.g. ["+14155551234"]. + * + * @param approvedPhones the value + * @return this builder + */ + public Builder approvedPhones(List approvedPhones) { + return set("approved_phones", Conv.list(Conv.STRING), approvedPhones); + } + + /** + * Aggregated per-feature status across all of this user's sessions. Possible keys: ID_VERIFICATION, NFC, LIVENESS, FACE_MATCH, POA, QUESTIONNAIRE, EMAIL_VERIFICATION, PHONE, AML, IP_ANALYSIS, AGE_ESTIMATION, DATABASE_VALIDATION. Possible values: Approved, Declined, In Review, Not Finished, Resub Requested. + * + * @param features the value + * @return this builder + */ + public Builder features(Map features) { + return setAny("features", features); + } + + /** + * Same data as features, as an ordered array of {feature, status} objects. + * + * @param featuresList the value + * @return this builder + */ + public Builder featuresList(List featuresList) { + return set("features_list", Conv.list(Conv.model(UserListItem.FeaturesList::fromJson)), featuresList); + } + + /** + * Timestamp of the most recent session + * + * @param lastSessionAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastSessionAt(@Nullable OffsetDateTime lastSessionAt) { + return setNullable("last_session_at", Conv.DATE_TIME, lastSessionAt); + } + + /** + * Timestamp of the first session + * + * @param firstSessionAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder firstSessionAt(@Nullable OffsetDateTime firstSessionAt) { + return setNullable("first_session_at", Conv.DATE_TIME, firstSessionAt); + } + + /** + * Timestamp of the most recent activity on this user (session, transaction, status change, data edit, etc.). + * + * @param lastActivityAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastActivityAt(@Nullable OffsetDateTime lastActivityAt) { + return setNullable("last_activity_at", Conv.DATE_TIME, lastActivityAt); + } + + /** + * The created_at field. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Custom metadata JSON you attached to this user. Defaults to {}. + * + * @param metadata the value + * @return this builder + */ + public Builder metadata(Map metadata) { + return setAny("metadata", metadata); + } + + /** + * Tag assignments. NOTE: on detail responses each entry is a tag *link* object ({uuid, tag: {...}, added_by_email, added_by_name, created_at}), unlike the flat {uuid, name, color} shape used on list responses. + * + * @param tags the value + * @return this builder + */ + public Builder tags(List tags) { + return set("tags", Conv.list(Conv.model(UserDetailItem.Tags::fromJson)), tags); + } + + /** + * Activity log and comments for this user (status changes, profile edits, manual notes). + * + * @param comments the value + * @return this builder + */ + public Builder comments(List comments) { + return set("comments", Conv.list(Conv.model(UserDetailItem.Comments::fromJson)), comments); + } + + /** + * The updated_at field. + * + * @param updatedAt the value + * @return this builder + */ + public Builder updatedAt(OffsetDateTime updatedAt) { + return set("updated_at", Conv.DATE_TIME, updatedAt); + } + + /** + * The verification session Didit created and emailed to the person. Always present: null when send_verification_link was not true. + * + * @param verificationLink the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verificationLink(UsersCreateResponse.@Nullable VerificationLink verificationLink) { + return setNullable("verification_link", Conv.model(UsersCreateResponse.VerificationLink::fromJson), verificationLink); + } + + /** + * Builds the value. + * + * @return a new immutable {@code UsersCreateResponse} + * @throws IllegalStateException when a required field is missing + */ + public UsersCreateResponse build() { + require("verification_link", true); + return new UsersCreateResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * The verification session Didit created and emailed to the person. Always present: null when send_verification_link was not true. + */ + public static final class VerificationLink extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("session_id", "session_number", "status", "url", "workflow_id", "email"); + + private VerificationLink(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code VerificationLink} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static VerificationLink fromJson(JsonNode json) { + return new VerificationLink(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * ID of the verification session. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String sessionId() { + return Json.required(json, "session_id", Conv.STRING); + } + + /** + * Sequential number of the session inside your application. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long sessionNumber() { + return Json.required(json, "session_number", Conv.LONG); + } + + /** + * Status of the session, Not Started for a session that was just created. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String status() { + return Json.required(json, "status", Conv.STRING); + } + + /** + * The verification link Didit emailed to the person. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String url() { + return Json.required(json, "url", Conv.STRING); + } + + /** + * The workflow the session runs. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowId() { + return Json.optional(json, "workflow_id", Conv.STRING); + } + + /** + * The address the link was sent to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String email() { + return Json.required(json, "email", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link VerificationLink}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(VerificationLink from) { + super(from); + } + + /** + * ID of the verification session. + * + * @param sessionId the value + * @return this builder + */ + public Builder sessionId(String sessionId) { + return set("session_id", Conv.STRING, sessionId); + } + + /** + * Sequential number of the session inside your application. + * + * @param sessionNumber the value + * @return this builder + */ + public Builder sessionNumber(long sessionNumber) { + return set("session_number", Conv.LONG, sessionNumber); + } + + /** + * Status of the session, Not Started for a session that was just created. + * + * @param status the value + * @return this builder + */ + public Builder status(String status) { + return set("status", Conv.STRING, status); + } + + /** + * The verification link Didit emailed to the person. + * + * @param url the value + * @return this builder + */ + public Builder url(String url) { + return set("url", Conv.STRING, url); + } + + /** + * The workflow the session runs. + * + * @param workflowId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder workflowId(@Nullable String workflowId) { + return setNullable("workflow_id", Conv.STRING, workflowId); + } + + /** + * The address the link was sent to. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * Builds the value. + * + * @return a new immutable {@code VerificationLink} + * @throws IllegalStateException when a required field is missing + */ + public VerificationLink build() { + require("session_id", false); + require("session_number", false); + require("status", false); + require("url", false); + require("workflow_id", true); + require("email", false); + return new VerificationLink(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/UsersSetAmlMonitoringResponse.java b/src/main/java/me/didit/types/UsersSetAmlMonitoringResponse.java index ea9587f..18b1258 100644 --- a/src/main/java/me/didit/types/UsersSetAmlMonitoringResponse.java +++ b/src/main/java/me/didit/types/UsersSetAmlMonitoringResponse.java @@ -2,20 +2,9 @@ package me.didit.types; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.time.OffsetDateTime; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; -import me.didit.core.JsonUnion; import me.didit.core.MultiStatusResponse; -import me.didit.core.StringEnum; -import org.jspecify.annotations.Nullable; /** Response of {@code users.set_aml_monitoring}; the shape depends on the HTTP status. */ public final class UsersSetAmlMonitoringResponse extends MultiStatusResponse { @@ -48,8 +37,8 @@ public boolean isOk() { * * @return the body, or empty for another status */ - public Optional asOk() { - return isOk() ? Optional.of(Conv.union(UsersSetAmlMonitoringResponse.Ok::fromJson).read(json)) : Optional.empty(); + public Optional asOk() { + return isOk() ? Optional.of(Conv.union(AMLMonitoringToggleResult::fromJson).read(json)) : Optional.empty(); } /** @@ -66,2583 +55,7 @@ public boolean isAccepted() { * * @return the body, or empty for another status */ - public Optional asAccepted() { - return isAccepted() ? Optional.of(Conv.model(UsersSetAmlMonitoringResponse.Accepted::fromJson).read(json)) : Optional.empty(); - } - - /** - * - *

One of: {@code CostPreview}, {@code Variant2}. Check with the {@code is...} - * methods and read with the {@code as...} methods; the fields every variant has are readable directly. - */ - public static final class Ok extends JsonUnion { - private Ok(JsonNode json) { - super(json); - } - - /** - * Reads a {@code Ok} from JSON. - * - * @param json any JSON value - * @return the union - */ - public static Ok fromJson(JsonNode json) { - return new Ok(json.deepCopy()); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public List unmatched() { - return Json.required(json, "unmatched", Conv.list(Conv.STRING)); - } - - /** - * Wraps a {@code CostPreview} value. - * - * @param value the value - * @return the union - */ - public static Ok ofCostPreview(UsersSetAmlMonitoringResponse.Ok.CostPreview value) { - return new Ok(Conv.model(UsersSetAmlMonitoringResponse.Ok.CostPreview::fromJson).write(value)); - } - - /** - * Whether this value is a {@code CostPreview}. - * - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @return true when it matches - */ - public boolean isCostPreview() { - return Json.hasFields(json, "users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient", "unmatched"); - } - - /** - * Reads this value as a {@code CostPreview}. - * - * @return the value, or empty when {@link #isCostPreview()} is false - */ - public Optional asCostPreview() { - return isCostPreview() ? Optional.of(Conv.model(UsersSetAmlMonitoringResponse.Ok.CostPreview::fromJson).read(json)) : Optional.empty(); - } - - /** - * Wraps a {@code Variant2} value. - * - * @param value the value - * @return the union - */ - public static Ok ofVariant2(UsersSetAmlMonitoringResponse.Ok.Variant2 value) { - return new Ok(Conv.model(UsersSetAmlMonitoringResponse.Ok.Variant2::fromJson).write(value)); - } - - /** - * Whether this value is a {@code Variant2}. - * - * @return true when it matches - */ - public boolean isVariant2() { - return Json.hasFields(json, "job_id", "status", "is_enabled", "target_type", "total", "processed", "outcomes", "results", "error", "created_at", "completed_at", "estimate", "unmatched"); - } - - /** - * Reads this value as a {@code Variant2}. - * - * @return the value, or empty when {@link #isVariant2()} is false - */ - public Optional asVariant2() { - return isVariant2() ? Optional.of(Conv.model(UsersSetAmlMonitoringResponse.Ok.Variant2::fromJson).read(json)) : Optional.empty(); - } - - /** Cost preview. Returned directly when estimate_only is true (nothing is written). */ - public static final class CostPreview extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient", "unmatched"); - - private CostPreview(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code CostPreview} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static CostPreview fromJson(JsonNode json) { - return new CostPreview(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Profiles matched by the selection. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long usersTotal() { - return Json.required(json, "users_total", Conv.LONG); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long alreadyEnabled() { - return Json.required(json, "already_enabled", Conv.LONG); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long withExistingAml() { - return Json.required(json, "with_existing_aml", Conv.LONG); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long needingScreening() { - return Json.required(json, "needing_screening", Conv.LONG); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long skippedNoIdentityData() { - return Json.required(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long skippedBlocked() { - return Json.required(json, "skipped_blocked", Conv.LONG); - } - - /** - * One-time cost of the new screenings. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public double estimatedScreeningCostUsd() { - return Json.required(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public double estimatedAnnualMonitoringCostUsd() { - return Json.required(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public boolean balanceSufficient() { - return Json.required(json, "balance_sufficient", Conv.BOOLEAN); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public List unmatched() { - return Json.required(json, "unmatched", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link CostPreview}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(CostPreview from) { - super(from); - } - - /** - * Profiles matched by the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * One-time cost of the new screenings. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. - * - * @param unmatched the value - * @return this builder - */ - public Builder unmatched(List unmatched) { - return set("unmatched", Conv.list(Conv.STRING), unmatched); - } - - /** - * Builds the value. - * - * @return a new immutable {@code CostPreview} - * @throws IllegalStateException when a required field is missing - */ - public CostPreview build() { - require("users_total", false); - require("already_enabled", false); - require("with_existing_aml", false); - require("needing_screening", false); - require("skipped_no_identity_data", false); - require("skipped_blocked", false); - require("estimated_screening_cost_usd", false); - require("estimated_annual_monitoring_cost_usd", false); - require("balance_sufficient", false); - require("unmatched", false); - return new CostPreview(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Variant2 extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "target_type", "total", "processed", "outcomes", "results", "error", "created_at", "completed_at", "estimate", "unmatched"); - - private Variant2(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Variant2} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Variant2 fromJson(JsonNode json) { - return new Variant2(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public String jobId() { - return Json.required(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public UsersSetAmlMonitoringResponse.Ok.Variant2.Status status() { - return Json.required(json, "status", Conv.enumeration(UsersSetAmlMonitoringResponse.Ok.Variant2.Status::of)); - } - - /** - * true when the job enables monitoring, false when it disables it. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public boolean isEnabled() { - return Json.required(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * Which profile family the job toggles. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public UsersSetAmlMonitoringResponse.Ok.Variant2.TargetType targetType() { - return Json.required(json, "target_type", Conv.enumeration(UsersSetAmlMonitoringResponse.Ok.Variant2.TargetType::of)); - } - - /** - * Profiles the job processes. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long total() { - return Json.required(json, "total", Conv.LONG); - } - - /** - * Profiles processed so far. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public long processed() { - return Json.required(json, "processed", Conv.LONG); - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public UsersSetAmlMonitoringResponse.Ok.Variant2.Outcomes outcomes() { - return Json.required(json, "outcomes", Conv.model(UsersSetAmlMonitoringResponse.Ok.Variant2.Outcomes::fromJson)); - } - - /** - * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public List results() { - return Json.required(json, "results", Conv.list(Conv.model(UsersSetAmlMonitoringResponse.Ok.Variant2.Results::fromJson))); - } - - /** - * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * The created_at field. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public OffsetDateTime createdAt() { - return Json.required(json, "created_at", Conv.DATE_TIME); - } - - /** - * The completed_at field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional completedAt() { - return Json.optional(json, "completed_at", Conv.DATE_TIME); - } - - /** - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public UsersSetAmlMonitoringResponse.Ok.Variant2.Estimate estimate() { - return Json.required(json, "estimate", Conv.model(UsersSetAmlMonitoringResponse.Ok.Variant2.Estimate::fromJson)); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. - * - * @return the value - * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type - */ - public List unmatched() { - return Json.required(json, "unmatched", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Variant2}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Variant2 from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(UsersSetAmlMonitoringResponse.Ok.Variant2.Status status) { - return set("status", Conv.enumeration(UsersSetAmlMonitoringResponse.Ok.Variant2.Status::of), status); - } - - /** - * true when the job enables monitoring, false when it disables it. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * Which profile family the job toggles. - * - * @param targetType the value - * @return this builder - */ - public Builder targetType(UsersSetAmlMonitoringResponse.Ok.Variant2.TargetType targetType) { - return set("target_type", Conv.enumeration(UsersSetAmlMonitoringResponse.Ok.Variant2.TargetType::of), targetType); - } - - /** - * Profiles the job processes. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * Profiles processed so far. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(UsersSetAmlMonitoringResponse.Ok.Variant2.Outcomes outcomes) { - return set("outcomes", Conv.model(UsersSetAmlMonitoringResponse.Ok.Variant2.Outcomes::fromJson), outcomes); - } - - /** - * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. - * - * @param results the value - * @return this builder - */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(UsersSetAmlMonitoringResponse.Ok.Variant2.Results::fromJson)), results); - } - - /** - * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * The created_at field. - * - * @param createdAt the value - * @return this builder - */ - public Builder createdAt(OffsetDateTime createdAt) { - return set("created_at", Conv.DATE_TIME, createdAt); - } - - /** - * The completed_at field. - * - * @param completedAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder completedAt(@Nullable OffsetDateTime completedAt) { - return setNullable("completed_at", Conv.DATE_TIME, completedAt); - } - - /** - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(UsersSetAmlMonitoringResponse.Ok.Variant2.Estimate estimate) { - return set("estimate", Conv.model(UsersSetAmlMonitoringResponse.Ok.Variant2.Estimate::fromJson), estimate); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. - * - * @param unmatched the value - * @return this builder - */ - public Builder unmatched(List unmatched) { - return set("unmatched", Conv.list(Conv.STRING), unmatched); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Variant2} - * @throws IllegalStateException when a required field is missing - */ - public Variant2 build() { - require("job_id", false); - require("status", false); - require("is_enabled", false); - require("target_type", false); - require("total", false); - require("processed", false); - require("outcomes", false); - require("results", false); - require("error", true); - require("created_at", false); - require("completed_at", true); - require("estimate", false); - require("unmatched", false); - return new Variant2(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Which profile family the job toggles. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class TargetType extends StringEnum { - /** The value {@code "user"}. */ - public static final TargetType USER = new TargetType("user"); - - /** The value {@code "business"}. */ - public static final TargetType BUSINESS = new TargetType("business"); - - private static final List KNOWN_VALUES = List.of(USER, BUSINESS); - - private TargetType(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static TargetType of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, TargetType::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled on it. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active; nothing changed and nothing was billed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring turned off across every AML screening of the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application, no screening is created. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening could not be completed; retry later. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out while the job was running. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled on it. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active; nothing changed and nothing was billed. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring turned off across every AML screening of the profile. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application, no screening is created. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening could not be completed; retry later. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out while the job was running. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Results extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("didit_internal_id", "vendor_data", "outcome"); - - private Results(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Results} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Results fromJson(JsonNode json) { - return new Results(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Didit internal id of the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional diditInternalId() { - return Json.optional(json, "didit_internal_id", Conv.STRING); - } - - /** - * Your own identifier for the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); - } - - /** - * The outcome field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcome() { - return Json.optional(json, "outcome", Conv.enumeration(UsersSetAmlMonitoringResponse.Ok.Variant2.Results.Outcome::of)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Results}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Results from) { - super(from); - } - - /** - * Didit internal id of the profile. - * - * @param diditInternalId the value - * @return this builder - */ - public Builder diditInternalId(String diditInternalId) { - return set("didit_internal_id", Conv.STRING, diditInternalId); - } - - /** - * Your own identifier for the profile. - * - * @param vendorData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder vendorData(@Nullable String vendorData) { - return setNullable("vendor_data", Conv.STRING, vendorData); - } - - /** - * The outcome field. - * - * @param outcome the value - * @return this builder - */ - public Builder outcome(UsersSetAmlMonitoringResponse.Ok.Variant2.Results.Outcome outcome) { - return set("outcome", Conv.enumeration(UsersSetAmlMonitoringResponse.Ok.Variant2.Results.Outcome::of), outcome); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Results} - */ - public Results build() { - return new Results(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Outcome extends StringEnum { - /** The value {@code "enabled"}. */ - public static final Outcome ENABLED = new Outcome("enabled"); - - /** The value {@code "screened_and_enabled"}. */ - public static final Outcome SCREENED_AND_ENABLED = new Outcome("screened_and_enabled"); - - /** The value {@code "already_enabled"}. */ - public static final Outcome ALREADY_ENABLED = new Outcome("already_enabled"); - - /** The value {@code "disabled"}. */ - public static final Outcome DISABLED = new Outcome("disabled"); - - /** The value {@code "skipped_no_identity_data"}. */ - public static final Outcome SKIPPED_NO_IDENTITY_DATA = new Outcome("skipped_no_identity_data"); - - /** The value {@code "skipped_blocked"}. */ - public static final Outcome SKIPPED_BLOCKED = new Outcome("skipped_blocked"); - - /** The value {@code "skipped_sandbox"}. */ - public static final Outcome SKIPPED_SANDBOX = new Outcome("skipped_sandbox"); - - /** The value {@code "failed_provider"}. */ - public static final Outcome FAILED_PROVIDER = new Outcome("failed_provider"); - - /** The value {@code "failed_insufficient_balance"}. */ - public static final Outcome FAILED_INSUFFICIENT_BALANCE = new Outcome("failed_insufficient_balance"); - - private static final List KNOWN_VALUES = List.of(ENABLED, SCREENED_AND_ENABLED, ALREADY_ENABLED, DISABLED, SKIPPED_NO_IDENTITY_DATA, SKIPPED_BLOCKED, SKIPPED_SANDBOX, FAILED_PROVIDER, FAILED_INSUFFICIENT_BALANCE); - - private Outcome(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Outcome of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (nothing is written). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Profiles matched by the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * One-time cost of the new screenings. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Profiles matched by the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * One-time cost of the new screenings. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - } - - public static final class Accepted extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "target_type", "total", "processed", "outcomes", "results", "error", "created_at", "completed_at", "estimate", "unmatched"); - - private Accepted(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Accepted} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Accepted fromJson(JsonNode json) { - return new Accepted(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(UsersSetAmlMonitoringResponse.Accepted.Status::of)); - } - - /** - * true when the job enables monitoring, false when it disables it. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isEnabled() { - return Json.optional(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * Which profile family the job toggles. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional targetType() { - return Json.optional(json, "target_type", Conv.enumeration(UsersSetAmlMonitoringResponse.Accepted.TargetType::of)); - } - - /** - * Profiles the job processes. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * Profiles processed so far. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(UsersSetAmlMonitoringResponse.Accepted.Outcomes::fromJson)); - } - - /** - * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> results() { - return Json.optional(json, "results", Conv.list(Conv.model(UsersSetAmlMonitoringResponse.Accepted.Results::fromJson))); - } - - /** - * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * The created_at field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional createdAt() { - return Json.optional(json, "created_at", Conv.DATE_TIME); - } - - /** - * The completed_at field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional completedAt() { - return Json.optional(json, "completed_at", Conv.DATE_TIME); - } - - /** - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(UsersSetAmlMonitoringResponse.Accepted.Estimate::fromJson)); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional> unmatched() { - return Json.optional(json, "unmatched", Conv.list(Conv.STRING)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Accepted}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Accepted from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(UsersSetAmlMonitoringResponse.Accepted.Status status) { - return set("status", Conv.enumeration(UsersSetAmlMonitoringResponse.Accepted.Status::of), status); - } - - /** - * true when the job enables monitoring, false when it disables it. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * Which profile family the job toggles. - * - * @param targetType the value - * @return this builder - */ - public Builder targetType(UsersSetAmlMonitoringResponse.Accepted.TargetType targetType) { - return set("target_type", Conv.enumeration(UsersSetAmlMonitoringResponse.Accepted.TargetType::of), targetType); - } - - /** - * Profiles the job processes. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * Profiles processed so far. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(UsersSetAmlMonitoringResponse.Accepted.Outcomes outcomes) { - return set("outcomes", Conv.model(UsersSetAmlMonitoringResponse.Accepted.Outcomes::fromJson), outcomes); - } - - /** - * One entry per processed profile, in processing order. Grows while the job runs; complete once status is COMPLETED. Recorded for jobs started through the API. - * - * @param results the value - * @return this builder - */ - public Builder results(List results) { - return set("results", Conv.list(Conv.model(UsersSetAmlMonitoringResponse.Accepted.Results::fromJson)), results); - } - - /** - * Generic failure message when status is FAILED; the detailed cause is kept on the platform side. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * The created_at field. - * - * @param createdAt the value - * @return this builder - */ - public Builder createdAt(OffsetDateTime createdAt) { - return set("created_at", Conv.DATE_TIME, createdAt); - } - - /** - * The completed_at field. - * - * @param completedAt the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder completedAt(@Nullable OffsetDateTime completedAt) { - return setNullable("completed_at", Conv.DATE_TIME, completedAt); - } - - /** - * Cost preview. Returned directly when estimate_only is true (nothing is written). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(UsersSetAmlMonitoringResponse.Accepted.Estimate estimate) { - return set("estimate", Conv.model(UsersSetAmlMonitoringResponse.Accepted.Estimate::fromJson), estimate); - } - - /** - * Identifiers of the request that matched no profile in this application, exactly as sent. They are not part of the job. - * - * @param unmatched the value - * @return this builder - */ - public Builder unmatched(List unmatched) { - return set("unmatched", Conv.list(Conv.STRING), unmatched); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Accepted} - */ - public Accepted build() { - return new Accepted(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Which profile family the job toggles. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class TargetType extends StringEnum { - /** The value {@code "user"}. */ - public static final TargetType USER = new TargetType("user"); - - /** The value {@code "business"}. */ - public static final TargetType BUSINESS = new TargetType("business"); - - private static final List KNOWN_VALUES = List.of(USER, BUSINESS); - - private TargetType(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static TargetType of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, TargetType::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-record outcome counters. Every selected profile resolves to exactly one counter; nothing is silently skipped. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled on it. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active; nothing changed and nothing was billed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring turned off across every AML screening of the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application, no screening is created. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening could not be completed; retry later. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out while the job was running. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled on it. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active; nothing changed and nothing was billed. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring turned off across every AML screening of the profile. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: the profile lacks the full name, date of birth and country (users) or a name (businesses) needed to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application, no screening is created. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening could not be completed; retry later. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out while the job was running. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Results extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("didit_internal_id", "vendor_data", "outcome"); - - private Results(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Results} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Results fromJson(JsonNode json) { - return new Results(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Didit internal id of the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional diditInternalId() { - return Json.optional(json, "didit_internal_id", Conv.STRING); - } - - /** - * Your own identifier for the profile. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional vendorData() { - return Json.optional(json, "vendor_data", Conv.STRING); - } - - /** - * The outcome field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcome() { - return Json.optional(json, "outcome", Conv.enumeration(UsersSetAmlMonitoringResponse.Accepted.Results.Outcome::of)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Results}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Results from) { - super(from); - } - - /** - * Didit internal id of the profile. - * - * @param diditInternalId the value - * @return this builder - */ - public Builder diditInternalId(String diditInternalId) { - return set("didit_internal_id", Conv.STRING, diditInternalId); - } - - /** - * Your own identifier for the profile. - * - * @param vendorData the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder vendorData(@Nullable String vendorData) { - return setNullable("vendor_data", Conv.STRING, vendorData); - } - - /** - * The outcome field. - * - * @param outcome the value - * @return this builder - */ - public Builder outcome(UsersSetAmlMonitoringResponse.Accepted.Results.Outcome outcome) { - return set("outcome", Conv.enumeration(UsersSetAmlMonitoringResponse.Accepted.Results.Outcome::of), outcome); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Results} - */ - public Results build() { - return new Results(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Outcome extends StringEnum { - /** The value {@code "enabled"}. */ - public static final Outcome ENABLED = new Outcome("enabled"); - - /** The value {@code "screened_and_enabled"}. */ - public static final Outcome SCREENED_AND_ENABLED = new Outcome("screened_and_enabled"); - - /** The value {@code "already_enabled"}. */ - public static final Outcome ALREADY_ENABLED = new Outcome("already_enabled"); - - /** The value {@code "disabled"}. */ - public static final Outcome DISABLED = new Outcome("disabled"); - - /** The value {@code "skipped_no_identity_data"}. */ - public static final Outcome SKIPPED_NO_IDENTITY_DATA = new Outcome("skipped_no_identity_data"); - - /** The value {@code "skipped_blocked"}. */ - public static final Outcome SKIPPED_BLOCKED = new Outcome("skipped_blocked"); - - /** The value {@code "skipped_sandbox"}. */ - public static final Outcome SKIPPED_SANDBOX = new Outcome("skipped_sandbox"); - - /** The value {@code "failed_provider"}. */ - public static final Outcome FAILED_PROVIDER = new Outcome("failed_provider"); - - /** The value {@code "failed_insufficient_balance"}. */ - public static final Outcome FAILED_INSUFFICIENT_BALANCE = new Outcome("failed_insufficient_balance"); - - private static final List KNOWN_VALUES = List.of(ENABLED, SCREENED_AND_ENABLED, ALREADY_ENABLED, DISABLED, SKIPPED_NO_IDENTITY_DATA, SKIPPED_BLOCKED, SKIPPED_SANDBOX, FAILED_PROVIDER, FAILED_INSUFFICIENT_BALANCE); - - private Outcome(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Outcome of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Outcome::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (nothing is written). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Profiles matched by the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * One-time cost of the new screenings. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Profiles matched by the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * Profiles already monitored (nothing to do when enabling; these are what disabling turns off). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Profiles whose existing AML screening will be switched to monitored (no new screening). - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Profiles without any AML screening that will be screened first (billed as a regular AML screening). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * Profiles that cannot be screened because identity data is missing. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Blocked users that will be skipped (always 0 for businesses). - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * One-time cost of the new screenings. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * Yearly monitoring cost of the profiles that will become monitored. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * Whether the organization balance covers the screening plus the first year of monitoring. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } + public Optional asAccepted() { + return isAccepted() ? Optional.of(Conv.model(AMLMonitoringJob::fromJson).read(json)) : Optional.empty(); } } diff --git a/src/main/java/me/didit/types/UsersSetOngoingMonitoringResponse.java b/src/main/java/me/didit/types/UsersSetOngoingMonitoringResponse.java index 8786b3a..ebfd393 100644 --- a/src/main/java/me/didit/types/UsersSetOngoingMonitoringResponse.java +++ b/src/main/java/me/didit/types/UsersSetOngoingMonitoringResponse.java @@ -2,19 +2,9 @@ package me.didit.types; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import me.didit.core.Conv; -import me.didit.core.FileUpload; -import me.didit.core.Json; -import me.didit.core.JsonModel; -import me.didit.core.JsonUnion; import me.didit.core.MultiStatusResponse; -import me.didit.core.StringEnum; -import org.jspecify.annotations.Nullable; /** * Response of {@code users.set_ongoing_monitoring}; the shape depends on the HTTP status. @@ -49,8 +39,8 @@ public boolean isOk() { * * @return the body, or empty for another status */ - public Optional asOk() { - return isOk() ? Optional.of(Conv.union(UsersSetOngoingMonitoringResponse.Ok::fromJson).read(json)) : Optional.empty(); + public Optional asOk() { + return isOk() ? Optional.of(Conv.union(OngoingMonitoringToggleResult::fromJson).read(json)) : Optional.empty(); } /** @@ -67,1866 +57,7 @@ public boolean isAccepted() { * * @return the body, or empty for another status */ - public Optional asAccepted() { - return isAccepted() ? Optional.of(Conv.model(UsersSetOngoingMonitoringResponse.Accepted::fromJson).read(json)) : Optional.empty(); - } - - /** - * - *

One of: {@code CostPreview}, {@code Variant2}. Check with the {@code is...} - * methods and read with the {@code as...} methods. - */ - public static final class Ok extends JsonUnion { - private Ok(JsonNode json) { - super(json); - } - - /** - * Reads a {@code Ok} from JSON. - * - * @param json any JSON value - * @return the union - */ - public static Ok fromJson(JsonNode json) { - return new Ok(json.deepCopy()); - } - - /** - * Wraps a {@code CostPreview} value. - * - * @param value the value - * @return the union - */ - public static Ok ofCostPreview(UsersSetOngoingMonitoringResponse.Ok.CostPreview value) { - return new Ok(Conv.model(UsersSetOngoingMonitoringResponse.Ok.CostPreview::fromJson).write(value)); - } - - /** - * Whether this value is a {@code CostPreview}. - * - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return true when it matches - */ - public boolean isCostPreview() { - return Json.hasAnyField(json, "users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - } - - /** - * Reads this value as a {@code CostPreview}. - * - * @return the value, or empty when {@link #isCostPreview()} is false - */ - public Optional asCostPreview() { - return isCostPreview() ? Optional.of(Conv.model(UsersSetOngoingMonitoringResponse.Ok.CostPreview::fromJson).read(json)) : Optional.empty(); - } - - /** - * Wraps a {@code Variant2} value. - * - * @param value the value - * @return the union - */ - public static Ok ofVariant2(UsersSetOngoingMonitoringResponse.Ok.Variant2 value) { - return new Ok(Conv.model(UsersSetOngoingMonitoringResponse.Ok.Variant2::fromJson).write(value)); - } - - /** - * Whether this value is a {@code Variant2}. - * - * @return true when it matches - */ - public boolean isVariant2() { - return Json.hasAnyField(json, "job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - } - - /** - * Reads this value as a {@code Variant2}. - * - * @return the value, or empty when {@link #isVariant2()} is false - */ - public Optional asVariant2() { - return isVariant2() ? Optional.of(Conv.model(UsersSetOngoingMonitoringResponse.Ok.Variant2::fromJson).read(json)) : Optional.empty(); - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class CostPreview extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private CostPreview(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code CostPreview} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static CostPreview fromJson(JsonNode json) { - return new CostPreview(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link CostPreview}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(CostPreview from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code CostPreview} - */ - public CostPreview build() { - return new CostPreview(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - public static final class Variant2 extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - - private Variant2(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Variant2} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Variant2 fromJson(JsonNode json) { - return new Variant2(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(UsersSetOngoingMonitoringResponse.Ok.Variant2.Status::of)); - } - - /** - * The is_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isEnabled() { - return Json.optional(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * The total field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * The processed field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(UsersSetOngoingMonitoringResponse.Ok.Variant2.Outcomes::fromJson)); - } - - /** - * The error field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(UsersSetOngoingMonitoringResponse.Ok.Variant2.Estimate::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Variant2}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Variant2 from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(UsersSetOngoingMonitoringResponse.Ok.Variant2.Status status) { - return set("status", Conv.enumeration(UsersSetOngoingMonitoringResponse.Ok.Variant2.Status::of), status); - } - - /** - * The is_enabled field. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * The total field. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * The processed field. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(UsersSetOngoingMonitoringResponse.Ok.Variant2.Outcomes outcomes) { - return set("outcomes", Conv.model(UsersSetOngoingMonitoringResponse.Ok.Variant2.Outcomes::fromJson), outcomes); - } - - /** - * The error field. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(UsersSetOngoingMonitoringResponse.Ok.Variant2.Estimate estimate) { - return set("estimate", Conv.model(UsersSetOngoingMonitoringResponse.Ok.Variant2.Estimate::fromJson), estimate); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Variant2} - */ - public Variant2 build() { - return new Variant2(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening provider call failed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out mid-job. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening provider call failed. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out mid-job. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - } - } - - public static final class Accepted extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("job_id", "status", "is_enabled", "total", "processed", "outcomes", "error", "estimate"); - - private Accepted(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Accepted} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Accepted fromJson(JsonNode json) { - return new Accepted(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * The job_id field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional jobId() { - return Json.optional(json, "job_id", Conv.STRING); - } - - /** - * The status field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional status() { - return Json.optional(json, "status", Conv.enumeration(UsersSetOngoingMonitoringResponse.Accepted.Status::of)); - } - - /** - * The is_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional isEnabled() { - return Json.optional(json, "is_enabled", Conv.BOOLEAN); - } - - /** - * The total field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional total() { - return Json.optional(json, "total", Conv.LONG); - } - - /** - * The processed field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional processed() { - return Json.optional(json, "processed", Conv.LONG); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional outcomes() { - return Json.optional(json, "outcomes", Conv.model(UsersSetOngoingMonitoringResponse.Accepted.Outcomes::fromJson)); - } - - /** - * The error field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional error() { - return Json.optional(json, "error", Conv.STRING); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimate() { - return Json.optional(json, "estimate", Conv.model(UsersSetOngoingMonitoringResponse.Accepted.Estimate::fromJson)); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Accepted}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Accepted from) { - super(from); - } - - /** - * The job_id field. - * - * @param jobId the value - * @return this builder - */ - public Builder jobId(String jobId) { - return set("job_id", Conv.STRING, jobId); - } - - /** - * The status field. - * - * @param status the value - * @return this builder - */ - public Builder status(UsersSetOngoingMonitoringResponse.Accepted.Status status) { - return set("status", Conv.enumeration(UsersSetOngoingMonitoringResponse.Accepted.Status::of), status); - } - - /** - * The is_enabled field. - * - * @param isEnabled the value - * @return this builder - */ - public Builder isEnabled(boolean isEnabled) { - return set("is_enabled", Conv.BOOLEAN, isEnabled); - } - - /** - * The total field. - * - * @param total the value - * @return this builder - */ - public Builder total(long total) { - return set("total", Conv.LONG, total); - } - - /** - * The processed field. - * - * @param processed the value - * @return this builder - */ - public Builder processed(long processed) { - return set("processed", Conv.LONG, processed); - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - * - * @param outcomes the value - * @return this builder - */ - public Builder outcomes(UsersSetOngoingMonitoringResponse.Accepted.Outcomes outcomes) { - return set("outcomes", Conv.model(UsersSetOngoingMonitoringResponse.Accepted.Outcomes::fromJson), outcomes); - } - - /** - * The error field. - * - * @param error the value, or {@code null} to send JSON null - * @return this builder - */ - public Builder error(@Nullable String error) { - return setNullable("error", Conv.STRING, error); - } - - /** - * Cost preview. Returned directly when estimate_only is true (no writes are performed). - * - * @param estimate the value - * @return this builder - */ - public Builder estimate(UsersSetOngoingMonitoringResponse.Accepted.Estimate estimate) { - return set("estimate", Conv.model(UsersSetOngoingMonitoringResponse.Accepted.Estimate::fromJson), estimate); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Accepted} - */ - public Accepted build() { - return new Accepted(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "PENDING"}. */ - public static final Status PENDING = new Status("PENDING"); - - /** The value {@code "PROCESSING"}. */ - public static final Status PROCESSING = new Status("PROCESSING"); - - /** The value {@code "COMPLETED"}. */ - public static final Status COMPLETED = new Status("COMPLETED"); - - /** The value {@code "FAILED"}. */ - public static final Status FAILED = new Status("FAILED"); - - private static final List KNOWN_VALUES = List.of(PENDING, PROCESSING, COMPLETED, FAILED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } - - /** - * Per-entity outcome counters. Nothing is silently skipped: every requested entity resolves to exactly one counter. - */ - public static final class Outcomes extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("enabled", "screened_and_enabled", "already_enabled", "disabled", "skipped_no_identity_data", "skipped_blocked", "skipped_sandbox", "failed_provider", "failed_insufficient_balance"); - - private Outcomes(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Outcomes} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Outcomes fromJson(JsonNode json) { - return new Outcomes(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional enabled() { - return Json.optional(json, "enabled", Conv.LONG); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional screenedAndEnabled() { - return Json.optional(json, "screened_and_enabled", Conv.LONG); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional disabled() { - return Json.optional(json, "disabled", Conv.LONG); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * Skipped: the user is blocked. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * Skipped: sandbox application. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedSandbox() { - return Json.optional(json, "skipped_sandbox", Conv.LONG); - } - - /** - * The screening provider call failed. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedProvider() { - return Json.optional(json, "failed_provider", Conv.LONG); - } - - /** - * Balance ran out mid-job. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional failedInsufficientBalance() { - return Json.optional(json, "failed_insufficient_balance", Conv.LONG); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Outcomes}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Outcomes from) { - super(from); - } - - /** - * Monitoring enabled on an existing AML screening. - * - * @param enabled the value - * @return this builder - */ - public Builder enabled(long enabled) { - return set("enabled", Conv.LONG, enabled); - } - - /** - * A new AML screening was created (billed) and monitoring enabled. - * - * @param screenedAndEnabled the value - * @return this builder - */ - public Builder screenedAndEnabled(long screenedAndEnabled) { - return set("screened_and_enabled", Conv.LONG, screenedAndEnabled); - } - - /** - * Monitoring was already active (legacy duplicate rows are consolidated). - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Monitoring disabled on every AML row of the entity. - * - * @param disabled the value - * @return this builder - */ - public Builder disabled(long disabled) { - return set("disabled", Conv.LONG, disabled); - } - - /** - * Skipped: no full name + date of birth + country available to screen. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * Skipped: the user is blocked. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * Skipped: sandbox application. - * - * @param skippedSandbox the value - * @return this builder - */ - public Builder skippedSandbox(long skippedSandbox) { - return set("skipped_sandbox", Conv.LONG, skippedSandbox); - } - - /** - * The screening provider call failed. - * - * @param failedProvider the value - * @return this builder - */ - public Builder failedProvider(long failedProvider) { - return set("failed_provider", Conv.LONG, failedProvider); - } - - /** - * Balance ran out mid-job. - * - * @param failedInsufficientBalance the value - * @return this builder - */ - public Builder failedInsufficientBalance(long failedInsufficientBalance) { - return set("failed_insufficient_balance", Conv.LONG, failedInsufficientBalance); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Outcomes} - */ - public Outcomes build() { - return new Outcomes(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } - - /** Cost preview. Returned directly when estimate_only is true (no writes are performed). */ - public static final class Estimate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("users_total", "already_enabled", "with_existing_aml", "needing_screening", "skipped_no_identity_data", "skipped_blocked", "estimated_screening_cost_usd", "estimated_annual_monitoring_cost_usd", "balance_sufficient"); - - private Estimate(ObjectNode json, Map> files) { - super(json, files); - } - - /** - * Reads a {@code Estimate} from JSON. Unknown fields are kept and readable - * through {@link #_additionalProperties()}. - * - * @param json a JSON object - * @return the parsed value - */ - public static Estimate fromJson(JsonNode json) { - return new Estimate(Json.copyObject(json), Map.of()); - } - - /** - * Returns an empty builder. - * - * @return a new builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Returns a builder holding a copy of every field of this value. - * - * @return a new builder - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Entities in the selection. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional usersTotal() { - return Json.optional(json, "users_total", Conv.LONG); - } - - /** - * The already_enabled field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional alreadyEnabled() { - return Json.optional(json, "already_enabled", Conv.LONG); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional withExistingAml() { - return Json.optional(json, "with_existing_aml", Conv.LONG); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional needingScreening() { - return Json.optional(json, "needing_screening", Conv.LONG); - } - - /** - * The skipped_no_identity_data field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedNoIdentityData() { - return Json.optional(json, "skipped_no_identity_data", Conv.LONG); - } - - /** - * The skipped_blocked field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional skippedBlocked() { - return Json.optional(json, "skipped_blocked", Conv.LONG); - } - - /** - * The estimated_screening_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedScreeningCostUsd() { - return Json.optional(json, "estimated_screening_cost_usd", Conv.DOUBLE); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional estimatedAnnualMonitoringCostUsd() { - return Json.optional(json, "estimated_annual_monitoring_cost_usd", Conv.DOUBLE); - } - - /** - * The balance_sufficient field. - * - * @return the value, or empty when the field is absent or null - * @throws me.didit.errors.UnexpectedResponseException when the field has another type - */ - public Optional balanceSufficient() { - return Json.optional(json, "balance_sufficient", Conv.BOOLEAN); - } - - @Override - protected Set knownFields() { - return KNOWN_FIELDS; - } - - /** Builds a {@link Estimate}. */ - public static final class Builder extends JsonModel.BaseBuilder { - private Builder() {} - - private Builder(Estimate from) { - super(from); - } - - /** - * Entities in the selection. - * - * @param usersTotal the value - * @return this builder - */ - public Builder usersTotal(long usersTotal) { - return set("users_total", Conv.LONG, usersTotal); - } - - /** - * The already_enabled field. - * - * @param alreadyEnabled the value - * @return this builder - */ - public Builder alreadyEnabled(long alreadyEnabled) { - return set("already_enabled", Conv.LONG, alreadyEnabled); - } - - /** - * Entities whose existing AML screening will be switched to monitored. - * - * @param withExistingAml the value - * @return this builder - */ - public Builder withExistingAml(long withExistingAml) { - return set("with_existing_aml", Conv.LONG, withExistingAml); - } - - /** - * Entities without any AML screening that will be screened first (billed). - * - * @param needingScreening the value - * @return this builder - */ - public Builder needingScreening(long needingScreening) { - return set("needing_screening", Conv.LONG, needingScreening); - } - - /** - * The skipped_no_identity_data field. - * - * @param skippedNoIdentityData the value - * @return this builder - */ - public Builder skippedNoIdentityData(long skippedNoIdentityData) { - return set("skipped_no_identity_data", Conv.LONG, skippedNoIdentityData); - } - - /** - * The skipped_blocked field. - * - * @param skippedBlocked the value - * @return this builder - */ - public Builder skippedBlocked(long skippedBlocked) { - return set("skipped_blocked", Conv.LONG, skippedBlocked); - } - - /** - * The estimated_screening_cost_usd field. - * - * @param estimatedScreeningCostUsd the value - * @return this builder - */ - public Builder estimatedScreeningCostUsd(double estimatedScreeningCostUsd) { - return set("estimated_screening_cost_usd", Conv.DOUBLE, estimatedScreeningCostUsd); - } - - /** - * The estimated_annual_monitoring_cost_usd field. - * - * @param estimatedAnnualMonitoringCostUsd the value - * @return this builder - */ - public Builder estimatedAnnualMonitoringCostUsd(double estimatedAnnualMonitoringCostUsd) { - return set("estimated_annual_monitoring_cost_usd", Conv.DOUBLE, estimatedAnnualMonitoringCostUsd); - } - - /** - * The balance_sufficient field. - * - * @param balanceSufficient the value - * @return this builder - */ - public Builder balanceSufficient(boolean balanceSufficient) { - return set("balance_sufficient", Conv.BOOLEAN, balanceSufficient); - } - - /** - * Builds the value. - * - * @return a new immutable {@code Estimate} - */ - public Estimate build() { - return new Estimate(buildJson(), buildFiles()); - } - - @Override - protected Builder self() { - return this; - } - } - } + public Optional asAccepted() { + return isAccepted() ? Optional.of(Conv.model(OngoingMonitoringJob::fromJson).read(json)) : Optional.empty(); } } diff --git a/src/main/java/me/didit/types/UsersUpdateParams.java b/src/main/java/me/didit/types/UsersUpdateParams.java index 3f5be00..a7a26d0 100644 --- a/src/main/java/me/didit/types/UsersUpdateParams.java +++ b/src/main/java/me/didit/types/UsersUpdateParams.java @@ -17,7 +17,7 @@ /** Parameters for {@code users.update}. */ public final class UsersUpdateParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("full_name", "display_name", "date_of_birth", "status", "metadata", "approved_emails", "approved_phones", "issuing_states"); + private static final Set KNOWN_FIELDS = Set.of("full_name", "display_name", "date_of_birth", "status", "metadata", "approved_emails", "approved_phones", "issuing_states", "reason"); private UsersUpdateParams(ObjectNode json, Map> files) { super(json, files); @@ -83,7 +83,7 @@ public Optional dateOfBirth() { } /** - * User lifecycle status. Changing it via this generic PATCH does NOT sync the system blocklist or record a status-change activity; prefer the update-status endpoint. + * User lifecycle status. A change is recorded as a STATUS_CHANGED activity, and BLOCKED adds the vendor_data to the system blocklist (moving away from BLOCKED removes that entry). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -132,6 +132,16 @@ public Optional> issuingStates() { return Json.optional(json, "issuing_states", Conv.list(Conv.STRING)); } + /** + * Why the status changed. Recorded on the status-change entry of the user's activity (comments[].comment, and Activity in the Business Console), only when the status actually changes. Webhooks do not carry it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -176,7 +186,7 @@ public Builder dateOfBirth(@Nullable LocalDate dateOfBirth) { } /** - * User lifecycle status. Changing it via this generic PATCH does NOT sync the system blocklist or record a status-change activity; prefer the update-status endpoint. + * User lifecycle status. A change is recorded as a STATUS_CHANGED activity, and BLOCKED adds the vendor_data to the system blocklist (moving away from BLOCKED removes that entry). * * @param status the value * @return this builder @@ -225,6 +235,16 @@ public Builder issuingStates(List issuingStates) { return set("issuing_states", Conv.list(Conv.STRING), issuingStates); } + /** + * Why the status changed. Recorded on the status-change entry of the user's activity (comments[].comment, and Activity in the Business Console), only when the status actually changes. Webhooks do not carry it. + * + * @param reason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reason(@Nullable String reason) { + return setNullable("reason", Conv.STRING, reason); + } + /** * Builds the value. * @@ -241,7 +261,7 @@ protected Builder self() { } /** - * User lifecycle status. Changing it via this generic PATCH does NOT sync the system blocklist or record a status-change activity; prefer the update-status endpoint. + * User lifecycle status. A change is recorded as a STATUS_CHANGED activity, and BLOCKED adds the vendor_data to the system blocklist (moving away from BLOCKED removes that entry). * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ diff --git a/src/main/java/me/didit/types/UsersUpdateStatusParams.java b/src/main/java/me/didit/types/UsersUpdateStatusParams.java index e37c6fe..cd8bbc7 100644 --- a/src/main/java/me/didit/types/UsersUpdateStatusParams.java +++ b/src/main/java/me/didit/types/UsersUpdateStatusParams.java @@ -5,16 +5,17 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import me.didit.core.Conv; import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; -import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; /** Parameters for {@code users.update_status}. */ public final class UsersUpdateStatusParams extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("status"); + private static final Set KNOWN_FIELDS = Set.of("status", "reason"); private UsersUpdateStatusParams(ObjectNode json, Map> files) { super(json, files); @@ -55,8 +56,18 @@ public Builder toBuilder() { * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type */ - public UsersUpdateStatusParams.Status status() { - return Json.required(json, "status", Conv.enumeration(UsersUpdateStatusParams.Status::of)); + public EntityStatusUpdateRequest.Status status() { + return Json.required(json, "status", Conv.enumeration(EntityStatusUpdateRequest.Status::of)); + } + + /** + * Why the status changed. Recorded on the status-change entry of the user's activity (comments[].comment, and Activity in the Business Console), only when the status actually changes. Webhooks do not carry it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); } @Override @@ -78,8 +89,18 @@ private Builder(UsersUpdateStatusParams from) { * @param status the value * @return this builder */ - public Builder status(UsersUpdateStatusParams.Status status) { - return set("status", Conv.enumeration(UsersUpdateStatusParams.Status::of), status); + public Builder status(EntityStatusUpdateRequest.Status status) { + return set("status", Conv.enumeration(EntityStatusUpdateRequest.Status::of), status); + } + + /** + * Why the status changed. Recorded on the status-change entry of the user's activity (comments[].comment, and Activity in the Business Console), only when the status actually changes. Webhooks do not carry it. + * + * @param reason the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder reason(@Nullable String reason) { + return setNullable("reason", Conv.STRING, reason); } /** @@ -98,50 +119,4 @@ protected Builder self() { return this; } } - - /** - * New lifecycle status. BLOCKED also adds the vendor_data to the system blocklist. - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class Status extends StringEnum { - /** The value {@code "ACTIVE"}. */ - public static final Status ACTIVE = new Status("ACTIVE"); - - /** The value {@code "FLAGGED"}. */ - public static final Status FLAGGED = new Status("FLAGGED"); - - /** The value {@code "BLOCKED"}. */ - public static final Status BLOCKED = new Status("BLOCKED"); - - private static final List KNOWN_VALUES = List.of(ACTIVE, FLAGGED, BLOCKED); - - private Status(String value) { - super(value); - } - - /** - * Returns the constant for a known value, or a new instance for any other value. - * - * @param value the string value - * @return the enum value - */ - public static Status of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, Status::new); - } - - /** - * Returns the values known to this SDK version. - * - * @return the known values - */ - public static List knownValues() { - return KNOWN_VALUES; - } - - @Override - public boolean isKnown() { - return KNOWN_VALUES.contains(this); - } - } } diff --git a/src/main/java/me/didit/types/VaspDirectoryEntry.java b/src/main/java/me/didit/types/VaspDirectoryEntry.java index f3f303f..e365b88 100644 --- a/src/main/java/me/didit/types/VaspDirectoryEntry.java +++ b/src/main/java/me/didit/types/VaspDirectoryEntry.java @@ -15,8 +15,11 @@ import me.didit.core.StringEnum; import org.jspecify.annotations.Nullable; +/** + * A VASP of the directory or a member of the Didit network, with the routes your application has to it. A customer is a member of the Didit network only while its application is live, its Travel Rule is on, its profile is discoverable, has a legal name and a confirmed compliance email, and Didit has not marked the application as a test, demo or internal one. Each row also carries the shared due-diligence assessment (dd_score, risk_level, responsiveness, essential_checks) and your application's own decision, entity screening and questionnaires, which no other customer sees. + */ public final class VaspDirectoryEntry extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("name", "lei", "jurisdiction", "dd_status", "dd_score", "reachable_rails", "is_didit_customer", "dd_score_breakdown", "dd_assessed_at"); + private static final Set KNOWN_FIELDS = Set.of("id", "kind", "name", "lei", "jurisdiction", "domains", "reachable", "aliases", "networks", "routes", "umbrella", "entities", "contacts", "dd_status", "dd_score", "reachable_rails", "is_didit_customer", "dd_score_breakdown", "dd_assessed_at", "dd_score_semantic", "risk_level", "responsiveness", "essential_checks", "decision", "entity_screening", "questionnaires"); private VaspDirectoryEntry(ObjectNode json, Map> files) { super(json, files); @@ -51,6 +54,26 @@ public Builder toBuilder() { return new Builder(this); } + /** + * The entry's id, for GET /v3/travel-rule/vasps/{vasp_id}/. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * directory for an entry of the shared directory; didit for a member of the Didit network. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(VaspDirectoryEntry.Kind::of)); + } + /** * The name field. * @@ -72,7 +95,7 @@ public Optional lei() { } /** - * The jurisdiction field. + * ISO 3166 country code, as the source gives it. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -82,7 +105,87 @@ public Optional jurisdiction() { } /** - * The dd_status field. + * The VASP's web domains. Always empty for Didit network members. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> domains() { + return Json.optional(json, "domains", Conv.list(Conv.STRING)); + } + + /** + * Whether your application has an available route to this VASP, or to one of its entities for a brand. Always true for a Didit network member, except on a sandbox application, where every row is false. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reachable() { + return Json.optional(json, "reachable", Conv.BOOLEAN); + } + + /** + * Other names the VASP is found by. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> aliases() { + return Json.optional(json, "aliases", Conv.list(Conv.model(VaspDirectoryEntry.Aliases::fromJson))); + } + + /** + * The networks field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> networks() { + return Json.optional(json, "networks", Conv.list(Conv.model(VaspDirectoryEntry.Networks::fromJson))); + } + + /** + * The routes field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> routes() { + return Json.optional(json, "routes", Conv.list(Conv.model(VaspDirectoryEntry.Routes::fromJson))); + } + + /** + * The brand this entry is a regulated entity of. Null when it is not part of one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional umbrella() { + return Json.optional(json, "umbrella", Conv.model(VaspDirectoryEntry.Umbrella::fromJson)); + } + + /** + * For a brand, the regulated entities that receive its transfers. A transfer naming the brand goes to the reachable entity in the jurisdiction it states (beneficiary_data.counterparty_vasp_jurisdiction), else to the entity blockchain analytics attribution linked to the destination wallet, else to the only reachable entity when no jurisdiction is stated. Two entities that fit equally are never guessed between. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> entities() { + return Json.optional(json, "entities", Conv.list(Conv.model(VaspDirectoryEntry.Entities::fromJson))); + } + + /** + * The platform's contacts for this VASP and your application's own. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> contacts() { + return Json.optional(json, "contacts", Conv.list(Conv.model(VaspDirectoryEntry.Contacts::fromJson))); + } + + /** + * Didit's verification status of the entry, scored by the verification component of dd_score. Always COMPLETED for a Didit network member. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -92,7 +195,7 @@ public Optional ddStatus() { } /** - * The dd_score field. + * The shared due-diligence score of a directory entry, 0 to 100, where higher is safer (see dd_score_semantic): the sum of the points in dd_score_breakdown. Null for a Didit network member, which is not scored. An entry not assessed yet (dd_assessed_at null) shows 0 with an empty risk_level, so read risk_level rather than the raw score. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -102,7 +205,7 @@ public Optional ddScore() { } /** - * The reachable_rails field. + * Rails recorded on the entry. Read routes for what your application can use today. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -112,7 +215,7 @@ public Optional> reachableRails() { } /** - * true for discoverable Didit applications (dd_status is always COMPLETED, dd_score always 0 for these); false for internal counterparty-registry rows, which carry a real due-diligence status and score. + * true for a member of the Didit network (dd_status is always COMPLETED, dd_score null and risk_level empty for these) and for a directory entry linked to a Didit customer. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -122,17 +225,17 @@ public Optional isDiditCustomer() { } /** - * Per-component contributions to dd_score (licensing/registry, sanctions/PEP, jurisdiction risk, on-chain analytics risk), all derived from checks Didit runs in-house. Empty for Didit-customer rows. + * One object per component of dd_score. Components, their weight (max) and their reason codes: jurisdiction, 25 (jurisdiction_not_high_risk, jurisdiction_high_risk, jurisdiction_unknown); verification, 15 (dd_status_completed, dd_status_in_progress, dd_status_not_started, dd_status_rejected); protocol, 15 (native_rail, email_only, no_rail); lei, 10 (lei_valid, lei_missing); history, 10 (history, no_history); essentials, 15 (essential_checks, no_essential_checks); responsiveness, 10 (responsive, unresponsive, responsiveness_unknown). Empty for a Didit network member. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional> ddScoreBreakdown() { - return Json.optional(json, "dd_score_breakdown", Conv.map(Conv.LONG)); + public Optional> ddScoreBreakdown() { + return Json.optional(json, "dd_score_breakdown", Conv.map(Conv.model(VaspDirectoryEntry.DdScoreBreakdownValue::fromJson))); } /** - * When the due-diligence assessment last ran. Null for Didit-customer rows. + * When dd_score, risk_level and responsiveness were last computed: after an exchange with the entry completes, finishes, expires, mismatches or is declined, when Didit refreshes or curates the entry, and in the weekly directory refresh. Null for a Didit network member. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -141,6 +244,76 @@ public Optional ddAssessedAt() { return Json.optional(json, "dd_assessed_at", Conv.DATE_TIME); } + /** + * Which way dd_score points: always higher_is_safer. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ddScoreSemantic() { + return Json.optional(json, "dd_score_semantic", Conv.enumeration(VaspDirectoryEntry.DdScoreSemantic::of)); + } + + /** + * The band dd_score falls in: LOW (70 or more), MEDIUM (40 to 69) or HIGH (below 40). Empty for a Didit network member and for an entry not assessed yet. Filter on it with risk. Your own flagged screening never changes this shared value: it makes counterparty_risk_level HIGH in your rules instead. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional riskLevel() { + return Json.optional(json, "risk_level", Conv.enumeration(VaspDirectoryEntry.RiskLevel::of)); + } + + /** + * Whether the VASP answers Travel Rule requests: the share of the requests Didit customers started towards it in the last 90 days that it answered. Only requests sent over a network or protocol rail, or by email to a shared address (the entry's compliance address or a platform contact), count; a request sent to an address a customer typed, or to a customer's private contact, never does. An expired request is unanswered; a completed exchange, a decline or a data mismatch is an answer; a cancelled request or an unreachable rail is neither. Always UNKNOWN with zero counts for a Didit network member. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional responsiveness() { + return Json.optional(json, "responsiveness", Conv.model(VaspDirectoryEntry.Responsiveness::fromJson)); + } + + /** + * The essential-checks report of a directory entry, built only from the platform's verified sources: registration (an official register lists the VASP: the EU's interim MiCA register, the public TRISA directory or the global LEI register), licence (a MiCA authorisation is on record and current), domain (the entry's domain matches its TRP endpoint or the website its register gives) and lei (its LEI record is active, issued and consistent with the entry's names and country). The essentials component of dd_score is the share of decided checks that pass. Empty until the weekly directory refresh first checks the entry, and always empty for a Didit network member. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional essentialChecks() { + return Json.optional(json, "essential_checks", Conv.model(VaspDirectoryEntry.EssentialChecks::fromJson)); + } + + /** + * Your application's decision on this VASP, or for an entity with no decision of its own its brand's (inherited_from names the brand). Null when there is none. Private to your application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional decision() { + return Json.optional(json, "decision", Conv.model(TravelRuleCounterpartyDecision::fromJson)); + } + + /** + * Your application's latest sanctions and adverse-media screening of this VASP. Null when you never screened it. Private to your application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entityScreening() { + return Json.optional(json, "entity_screening", Conv.model(TravelRuleCounterpartyScreening::fromJson)); + } + + /** + * Your application's due-diligence questionnaires with this VASP, newest first. Private to your application. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> questionnaires() { + return Json.optional(json, "questionnaires", Conv.list(Conv.model(TravelRuleCounterpartyQuestionnaire::fromJson))); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -154,6 +327,26 @@ private Builder(VaspDirectoryEntry from) { super(from); } + /** + * The entry's id, for GET /v3/travel-rule/vasps/{vasp_id}/. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * directory for an entry of the shared directory; didit for a member of the Didit network. + * + * @param kind the value + * @return this builder + */ + public Builder kind(VaspDirectoryEntry.Kind kind) { + return set("kind", Conv.enumeration(VaspDirectoryEntry.Kind::of), kind); + } + /** * The name field. * @@ -175,7 +368,7 @@ public Builder lei(String lei) { } /** - * The jurisdiction field. + * ISO 3166 country code, as the source gives it. * * @param jurisdiction the value * @return this builder @@ -185,7 +378,87 @@ public Builder jurisdiction(String jurisdiction) { } /** - * The dd_status field. + * The VASP's web domains. Always empty for Didit network members. + * + * @param domains the value + * @return this builder + */ + public Builder domains(List domains) { + return set("domains", Conv.list(Conv.STRING), domains); + } + + /** + * Whether your application has an available route to this VASP, or to one of its entities for a brand. Always true for a Didit network member, except on a sandbox application, where every row is false. + * + * @param reachable the value + * @return this builder + */ + public Builder reachable(boolean reachable) { + return set("reachable", Conv.BOOLEAN, reachable); + } + + /** + * Other names the VASP is found by. + * + * @param aliases the value + * @return this builder + */ + public Builder aliases(List aliases) { + return set("aliases", Conv.list(Conv.model(VaspDirectoryEntry.Aliases::fromJson)), aliases); + } + + /** + * The networks field. + * + * @param networks the value + * @return this builder + */ + public Builder networks(List networks) { + return set("networks", Conv.list(Conv.model(VaspDirectoryEntry.Networks::fromJson)), networks); + } + + /** + * The routes field. + * + * @param routes the value + * @return this builder + */ + public Builder routes(List routes) { + return set("routes", Conv.list(Conv.model(VaspDirectoryEntry.Routes::fromJson)), routes); + } + + /** + * The brand this entry is a regulated entity of. Null when it is not part of one. + * + * @param umbrella the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder umbrella(VaspDirectoryEntry.@Nullable Umbrella umbrella) { + return setNullable("umbrella", Conv.model(VaspDirectoryEntry.Umbrella::fromJson), umbrella); + } + + /** + * For a brand, the regulated entities that receive its transfers. A transfer naming the brand goes to the reachable entity in the jurisdiction it states (beneficiary_data.counterparty_vasp_jurisdiction), else to the entity blockchain analytics attribution linked to the destination wallet, else to the only reachable entity when no jurisdiction is stated. Two entities that fit equally are never guessed between. + * + * @param entities the value + * @return this builder + */ + public Builder entities(List entities) { + return set("entities", Conv.list(Conv.model(VaspDirectoryEntry.Entities::fromJson)), entities); + } + + /** + * The platform's contacts for this VASP and your application's own. + * + * @param contacts the value + * @return this builder + */ + public Builder contacts(List contacts) { + return set("contacts", Conv.list(Conv.model(VaspDirectoryEntry.Contacts::fromJson)), contacts); + } + + /** + * Didit's verification status of the entry, scored by the verification component of dd_score. Always COMPLETED for a Didit network member. * * @param ddStatus the value * @return this builder @@ -195,17 +468,17 @@ public Builder ddStatus(VaspDirectoryEntry.DdStatus ddStatus) { } /** - * The dd_score field. + * The shared due-diligence score of a directory entry, 0 to 100, where higher is safer (see dd_score_semantic): the sum of the points in dd_score_breakdown. Null for a Didit network member, which is not scored. An entry not assessed yet (dd_assessed_at null) shows 0 with an empty risk_level, so read risk_level rather than the raw score. * - * @param ddScore the value + * @param ddScore the value, or {@code null} to send JSON null * @return this builder */ - public Builder ddScore(long ddScore) { - return set("dd_score", Conv.LONG, ddScore); + public Builder ddScore(@Nullable Long ddScore) { + return setNullable("dd_score", Conv.LONG, ddScore); } /** - * The reachable_rails field. + * Rails recorded on the entry. Read routes for what your application can use today. * * @param reachableRails the value * @return this builder @@ -215,7 +488,7 @@ public Builder reachableRails(List reachableR } /** - * true for discoverable Didit applications (dd_status is always COMPLETED, dd_score always 0 for these); false for internal counterparty-registry rows, which carry a real due-diligence status and score. + * true for a member of the Didit network (dd_status is always COMPLETED, dd_score null and risk_level empty for these) and for a directory entry linked to a Didit customer. * * @param isDiditCustomer the value * @return this builder @@ -225,17 +498,17 @@ public Builder isDiditCustomer(boolean isDiditCustomer) { } /** - * Per-component contributions to dd_score (licensing/registry, sanctions/PEP, jurisdiction risk, on-chain analytics risk), all derived from checks Didit runs in-house. Empty for Didit-customer rows. + * One object per component of dd_score. Components, their weight (max) and their reason codes: jurisdiction, 25 (jurisdiction_not_high_risk, jurisdiction_high_risk, jurisdiction_unknown); verification, 15 (dd_status_completed, dd_status_in_progress, dd_status_not_started, dd_status_rejected); protocol, 15 (native_rail, email_only, no_rail); lei, 10 (lei_valid, lei_missing); history, 10 (history, no_history); essentials, 15 (essential_checks, no_essential_checks); responsiveness, 10 (responsive, unresponsive, responsiveness_unknown). Empty for a Didit network member. * * @param ddScoreBreakdown the value * @return this builder */ - public Builder ddScoreBreakdown(Map ddScoreBreakdown) { - return set("dd_score_breakdown", Conv.map(Conv.LONG), ddScoreBreakdown); + public Builder ddScoreBreakdown(Map ddScoreBreakdown) { + return set("dd_score_breakdown", Conv.map(Conv.model(VaspDirectoryEntry.DdScoreBreakdownValue::fromJson)), ddScoreBreakdown); } /** - * When the due-diligence assessment last ran. Null for Didit-customer rows. + * When dd_score, risk_level and responsiveness were last computed: after an exchange with the entry completes, finishes, expires, mismatches or is declined, when Didit refreshes or curates the entry, and in the weekly directory refresh. Null for a Didit network member. * * @param ddAssessedAt the value, or {@code null} to send JSON null * @return this builder @@ -244,6 +517,76 @@ public Builder ddAssessedAt(@Nullable OffsetDateTime ddAssessedAt) { return setNullable("dd_assessed_at", Conv.DATE_TIME, ddAssessedAt); } + /** + * Which way dd_score points: always higher_is_safer. + * + * @param ddScoreSemantic the value + * @return this builder + */ + public Builder ddScoreSemantic(VaspDirectoryEntry.DdScoreSemantic ddScoreSemantic) { + return set("dd_score_semantic", Conv.enumeration(VaspDirectoryEntry.DdScoreSemantic::of), ddScoreSemantic); + } + + /** + * The band dd_score falls in: LOW (70 or more), MEDIUM (40 to 69) or HIGH (below 40). Empty for a Didit network member and for an entry not assessed yet. Filter on it with risk. Your own flagged screening never changes this shared value: it makes counterparty_risk_level HIGH in your rules instead. + * + * @param riskLevel the value + * @return this builder + */ + public Builder riskLevel(VaspDirectoryEntry.RiskLevel riskLevel) { + return set("risk_level", Conv.enumeration(VaspDirectoryEntry.RiskLevel::of), riskLevel); + } + + /** + * Whether the VASP answers Travel Rule requests: the share of the requests Didit customers started towards it in the last 90 days that it answered. Only requests sent over a network or protocol rail, or by email to a shared address (the entry's compliance address or a platform contact), count; a request sent to an address a customer typed, or to a customer's private contact, never does. An expired request is unanswered; a completed exchange, a decline or a data mismatch is an answer; a cancelled request or an unreachable rail is neither. Always UNKNOWN with zero counts for a Didit network member. + * + * @param responsiveness the value + * @return this builder + */ + public Builder responsiveness(VaspDirectoryEntry.Responsiveness responsiveness) { + return set("responsiveness", Conv.model(VaspDirectoryEntry.Responsiveness::fromJson), responsiveness); + } + + /** + * The essential-checks report of a directory entry, built only from the platform's verified sources: registration (an official register lists the VASP: the EU's interim MiCA register, the public TRISA directory or the global LEI register), licence (a MiCA authorisation is on record and current), domain (the entry's domain matches its TRP endpoint or the website its register gives) and lei (its LEI record is active, issued and consistent with the entry's names and country). The essentials component of dd_score is the share of decided checks that pass. Empty until the weekly directory refresh first checks the entry, and always empty for a Didit network member. + * + * @param essentialChecks the value + * @return this builder + */ + public Builder essentialChecks(VaspDirectoryEntry.EssentialChecks essentialChecks) { + return set("essential_checks", Conv.model(VaspDirectoryEntry.EssentialChecks::fromJson), essentialChecks); + } + + /** + * Your application's decision on this VASP, or for an entity with no decision of its own its brand's (inherited_from names the brand). Null when there is none. Private to your application. + * + * @param decision the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder decision(@Nullable TravelRuleCounterpartyDecision decision) { + return setNullable("decision", Conv.model(TravelRuleCounterpartyDecision::fromJson), decision); + } + + /** + * Your application's latest sanctions and adverse-media screening of this VASP. Null when you never screened it. Private to your application. + * + * @param entityScreening the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder entityScreening(@Nullable TravelRuleCounterpartyScreening entityScreening) { + return setNullable("entity_screening", Conv.model(TravelRuleCounterpartyScreening::fromJson), entityScreening); + } + + /** + * Your application's due-diligence questionnaires with this VASP, newest first. Private to your application. + * + * @param questionnaires the value + * @return this builder + */ + public Builder questionnaires(List questionnaires) { + return set("questionnaires", Conv.list(Conv.model(TravelRuleCounterpartyQuestionnaire::fromJson)), questionnaires); + } + /** * Builds the value. * @@ -260,25 +603,20 @@ protected Builder self() { } /** + * directory for an entry of the shared directory; didit for a member of the Didit network. * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ - public static final class DdStatus extends StringEnum { - /** The value {@code "NOT_STARTED"}. */ - public static final DdStatus NOT_STARTED = new DdStatus("NOT_STARTED"); - - /** The value {@code "IN_PROGRESS"}. */ - public static final DdStatus IN_PROGRESS = new DdStatus("IN_PROGRESS"); + public static final class Kind extends StringEnum { + /** The value {@code "directory"}. */ + public static final Kind DIRECTORY = new Kind("directory"); - /** The value {@code "COMPLETED"}. */ - public static final DdStatus COMPLETED = new DdStatus("COMPLETED"); - - /** The value {@code "REJECTED"}. */ - public static final DdStatus REJECTED = new DdStatus("REJECTED"); + /** The value {@code "didit"}. */ + public static final Kind DIDIT = new Kind("didit"); - private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, COMPLETED, REJECTED); + private static final List KNOWN_VALUES = List.of(DIRECTORY, DIDIT); - private DdStatus(String value) { + private Kind(String value) { super(value); } @@ -288,8 +626,8 @@ private DdStatus(String value) { * @param value the string value * @return the enum value */ - public static DdStatus of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, DdStatus::new); + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); } /** @@ -297,7 +635,7 @@ public static DdStatus of(String value) { * * @return the known values */ - public static List knownValues() { + public static List knownValues() { return KNOWN_VALUES; } @@ -307,26 +645,1629 @@ public boolean isKnown() { } } - /** - * - *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. - */ - public static final class ReachableRails extends StringEnum { - /** The value {@code "INTERNAL"}. */ - public static final ReachableRails INTERNAL = new ReachableRails("INTERNAL"); + public static final class Aliases extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("name", "kind"); - /** The value {@code "TRP"}. */ - public static final ReachableRails TRP = new ReachableRails("TRP"); + private Aliases(ObjectNode json, Map> files) { + super(json, files); + } - /** The value {@code "GTR"}. */ - public static final ReachableRails GTR = new ReachableRails("GTR"); + /** + * Reads a {@code Aliases} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Aliases fromJson(JsonNode json) { + return new Aliases(Json.copyObject(json), Map.of()); + } - /** The value {@code "EMAIL"}. */ - public static final ReachableRails EMAIL = new ReachableRails("EMAIL"); + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * trading_name, other_legal_name (from the global LEI register), network_name (the name on a Travel Rule network), attribution (the entity name blockchain analytics gives the VASP's wallets), merged (the name of a duplicate entry merged into this one) or staff. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(VaspDirectoryEntry.Aliases.Kind::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Aliases}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Aliases from) { + super(from); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * trading_name, other_legal_name (from the global LEI register), network_name (the name on a Travel Rule network), attribution (the entity name blockchain analytics gives the VASP's wallets), merged (the name of a duplicate entry merged into this one) or staff. + * + * @param kind the value + * @return this builder + */ + public Builder kind(VaspDirectoryEntry.Aliases.Kind kind) { + return set("kind", Conv.enumeration(VaspDirectoryEntry.Aliases.Kind::of), kind); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Aliases} + */ + public Aliases build() { + return new Aliases(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * trading_name, other_legal_name (from the global LEI register), network_name (the name on a Travel Rule network), attribution (the entity name blockchain analytics gives the VASP's wallets), merged (the name of a duplicate entry merged into this one) or staff. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "trading_name"}. */ + public static final Kind TRADING_NAME = new Kind("trading_name"); + + /** The value {@code "other_legal_name"}. */ + public static final Kind OTHER_LEGAL_NAME = new Kind("other_legal_name"); + + /** The value {@code "network_name"}. */ + public static final Kind NETWORK_NAME = new Kind("network_name"); + + /** The value {@code "attribution"}. */ + public static final Kind ATTRIBUTION = new Kind("attribution"); + + /** The value {@code "merged"}. */ + public static final Kind MERGED = new Kind("merged"); + + /** The value {@code "staff"}. */ + public static final Kind STAFF = new Kind("staff"); + + private static final List KNOWN_VALUES = List.of(TRADING_NAME, OTHER_LEGAL_NAME, NETWORK_NAME, ATTRIBUTION, MERGED, STAFF); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * A current listing of this VASP in a Travel Rule network's member directory. A TRISA listing says who the VASP is on TRISA; Didit has no TRISA rail, so it is never a route. + */ + public static final class Networks extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("network", "code", "listed_name", "country", "verified_at", "last_seen_at", "source"); + + private Networks(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Networks} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Networks fromJson(JsonNode json) { + return new Networks(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The network field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional network() { + return Json.optional(json, "network", Conv.enumeration(VaspDirectoryEntry.Networks.Network::of)); + } + + /** + * The VASP's identifier on that network. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional code() { + return Json.optional(json, "code", Conv.STRING); + } + + /** + * The name the network lists the VASP under. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional listedName() { + return Json.optional(json, "listed_name", Conv.STRING); + } + + /** + * The country the network lists, when it gives one. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional country() { + return Json.optional(json, "country", Conv.STRING); + } + + /** + * When Didit staff verified the listing. Null for listings read from a public directory. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verifiedAt() { + return Json.optional(json, "verified_at", Conv.DATE_TIME); + } + + /** + * When the source last showed the listing. A listing the source has not shown for three weeks is removed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastSeenAt() { + return Json.optional(json, "last_seen_at", Conv.DATE_TIME); + } + + /** + * Where the listing comes from: the public TRISA directory, or Didit staff. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(VaspDirectoryEntry.Networks.Source::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Networks}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Networks from) { + super(from); + } + + /** + * The network field. + * + * @param network the value + * @return this builder + */ + public Builder network(VaspDirectoryEntry.Networks.Network network) { + return set("network", Conv.enumeration(VaspDirectoryEntry.Networks.Network::of), network); + } + + /** + * The VASP's identifier on that network. + * + * @param code the value + * @return this builder + */ + public Builder code(String code) { + return set("code", Conv.STRING, code); + } + + /** + * The name the network lists the VASP under. + * + * @param listedName the value + * @return this builder + */ + public Builder listedName(String listedName) { + return set("listed_name", Conv.STRING, listedName); + } + + /** + * The country the network lists, when it gives one. + * + * @param country the value + * @return this builder + */ + public Builder country(String country) { + return set("country", Conv.STRING, country); + } + + /** + * When Didit staff verified the listing. Null for listings read from a public directory. + * + * @param verifiedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verifiedAt(@Nullable OffsetDateTime verifiedAt) { + return setNullable("verified_at", Conv.DATE_TIME, verifiedAt); + } + + /** + * When the source last showed the listing. A listing the source has not shown for three weeks is removed. + * + * @param lastSeenAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastSeenAt(@Nullable OffsetDateTime lastSeenAt) { + return setNullable("last_seen_at", Conv.DATE_TIME, lastSeenAt); + } + + /** + * Where the listing comes from: the public TRISA directory, or Didit staff. + * + * @param source the value + * @return this builder + */ + public Builder source(VaspDirectoryEntry.Networks.Source source) { + return set("source", Conv.enumeration(VaspDirectoryEntry.Networks.Source::of), source); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Networks} + */ + public Networks build() { + return new Networks(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Network extends StringEnum { + /** The value {@code "GTR"}. */ + public static final Network GTR = new Network("GTR"); + + /** The value {@code "TRUST"}. */ + public static final Network TRUST = new Network("TRUST"); + + /** The value {@code "VERIFYVASP"}. */ + public static final Network VERIFYVASP = new Network("VERIFYVASP"); + + /** The value {@code "SYGNA"}. */ + public static final Network SYGNA = new Network("SYGNA"); + + /** The value {@code "TRISA"}. */ + public static final Network TRISA = new Network("TRISA"); + + private static final List KNOWN_VALUES = List.of(GTR, TRUST, VERIFYVASP, SYGNA, TRISA); + + private Network(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Network of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Network::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Where the listing comes from: the public TRISA directory, or Didit staff. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "trisa_directory"}. */ + public static final Source TRISA_DIRECTORY = new Source("trisa_directory"); + + /** The value {@code "staff"}. */ + public static final Source STAFF = new Source("staff"); + + private static final List KNOWN_VALUES = List.of(TRISA_DIRECTORY, STAFF); + + private Source(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * One way your application can deliver Travel Rule data to this VASP, in the order dispatch tries them. A network route needs a membership on that network: your own (connected in the Marketplace) or Didit's once Didit holds one. Without it the route is listed as unavailable with the reason, never as a way to reach the VASP. + */ + public static final class Routes extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("rail", "status", "reason", "via"); + + private Routes(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Routes} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Routes fromJson(JsonNode json) { + return new Routes(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The rail field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rail() { + return Json.optional(json, "rail", Conv.enumeration(VaspDirectoryEntry.Routes.Rail::of)); + } + + /** + * The status field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(VaspDirectoryEntry.Routes.Status::of)); + } + + /** + * Why an unavailable route cannot be used: no_membership (neither you nor Didit holds a membership on that network), network_switched_off (you turned that network off in your settings) or sandbox_application (a sandbox application sends only to the Didit Test VASP, so no route is available to it). Empty when the route is available. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.enumeration(VaspDirectoryEntry.Routes.Reason::of)); + } + + /** + * How an available route is reached: own_membership or didit_membership for a network rail, directory for a TRP endpoint the directory verified, contact for the EMAIL rail, didit_network for a Didit network member. Empty when the route is unavailable. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional via() { + return Json.optional(json, "via", Conv.enumeration(VaspDirectoryEntry.Routes.Via::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Routes}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Routes from) { + super(from); + } + + /** + * The rail field. + * + * @param rail the value + * @return this builder + */ + public Builder rail(VaspDirectoryEntry.Routes.Rail rail) { + return set("rail", Conv.enumeration(VaspDirectoryEntry.Routes.Rail::of), rail); + } + + /** + * The status field. + * + * @param status the value + * @return this builder + */ + public Builder status(VaspDirectoryEntry.Routes.Status status) { + return set("status", Conv.enumeration(VaspDirectoryEntry.Routes.Status::of), status); + } + + /** + * Why an unavailable route cannot be used: no_membership (neither you nor Didit holds a membership on that network), network_switched_off (you turned that network off in your settings) or sandbox_application (a sandbox application sends only to the Didit Test VASP, so no route is available to it). Empty when the route is available. + * + * @param reason the value + * @return this builder + */ + public Builder reason(VaspDirectoryEntry.Routes.Reason reason) { + return set("reason", Conv.enumeration(VaspDirectoryEntry.Routes.Reason::of), reason); + } + + /** + * How an available route is reached: own_membership or didit_membership for a network rail, directory for a TRP endpoint the directory verified, contact for the EMAIL rail, didit_network for a Didit network member. Empty when the route is unavailable. + * + * @param via the value + * @return this builder + */ + public Builder via(VaspDirectoryEntry.Routes.Via via) { + return set("via", Conv.enumeration(VaspDirectoryEntry.Routes.Via::of), via); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Routes} + */ + public Routes build() { + return new Routes(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Rail extends StringEnum { + /** The value {@code "INTERNAL"}. */ + public static final Rail INTERNAL = new Rail("INTERNAL"); + + /** The value {@code "TRP"}. */ + public static final Rail TRP = new Rail("TRP"); + + /** The value {@code "GTR"}. */ + public static final Rail GTR = new Rail("GTR"); + + /** The value {@code "TRUST"}. */ + public static final Rail TRUST = new Rail("TRUST"); + + /** The value {@code "VERIFYVASP"}. */ + public static final Rail VERIFYVASP = new Rail("VERIFYVASP"); + + /** The value {@code "SYGNA"}. */ + public static final Rail SYGNA = new Rail("SYGNA"); + + /** The value {@code "EMAIL"}. */ + public static final Rail EMAIL = new Rail("EMAIL"); + + private static final List KNOWN_VALUES = List.of(INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL); + + private Rail(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Rail of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Rail::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "available"}. */ + public static final Status AVAILABLE = new Status("available"); + + /** The value {@code "unavailable"}. */ + public static final Status UNAVAILABLE = new Status("unavailable"); + + private static final List KNOWN_VALUES = List.of(AVAILABLE, UNAVAILABLE); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Why an unavailable route cannot be used: no_membership (neither you nor Didit holds a membership on that network), network_switched_off (you turned that network off in your settings) or sandbox_application (a sandbox application sends only to the Didit Test VASP, so no route is available to it). Empty when the route is available. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Reason extends StringEnum { + /** The value {@code ""}. */ + public static final Reason EMPTY = new Reason(""); + + /** The value {@code "no_membership"}. */ + public static final Reason NO_MEMBERSHIP = new Reason("no_membership"); + + /** The value {@code "network_switched_off"}. */ + public static final Reason NETWORK_SWITCHED_OFF = new Reason("network_switched_off"); + + /** The value {@code "sandbox_application"}. */ + public static final Reason SANDBOX_APPLICATION = new Reason("sandbox_application"); + + private static final List KNOWN_VALUES = List.of(EMPTY, NO_MEMBERSHIP, NETWORK_SWITCHED_OFF, SANDBOX_APPLICATION); + + private Reason(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Reason of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Reason::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * How an available route is reached: own_membership or didit_membership for a network rail, directory for a TRP endpoint the directory verified, contact for the EMAIL rail, didit_network for a Didit network member. Empty when the route is unavailable. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Via extends StringEnum { + /** The value {@code ""}. */ + public static final Via EMPTY = new Via(""); + + /** The value {@code "own_membership"}. */ + public static final Via OWN_MEMBERSHIP = new Via("own_membership"); + + /** The value {@code "didit_membership"}. */ + public static final Via DIDIT_MEMBERSHIP = new Via("didit_membership"); + + /** The value {@code "directory"}. */ + public static final Via DIRECTORY = new Via("directory"); + + /** The value {@code "contact"}. */ + public static final Via CONTACT = new Via("contact"); + + /** The value {@code "didit_network"}. */ + public static final Via DIDIT_NETWORK = new Via("didit_network"); + + private static final List KNOWN_VALUES = List.of(EMPTY, OWN_MEMBERSHIP, DIDIT_MEMBERSHIP, DIRECTORY, CONTACT, DIDIT_NETWORK); + + private Via(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Via of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Via::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** The brand this entry is a regulated entity of. Null when it is not part of one. */ + public static final class Umbrella extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "name"); + + private Umbrella(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Umbrella} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Umbrella fromJson(JsonNode json) { + return new Umbrella(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Umbrella}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Umbrella from) { + super(from); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Umbrella} + */ + public Umbrella build() { + return new Umbrella(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** A regulated entity of a brand: the legal entity that receives a transfer. */ + public static final class Entities extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "name", "lei", "jurisdiction", "reachable"); + + private Entities(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Entities} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Entities fromJson(JsonNode json) { + return new Entities(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * The lei field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lei() { + return Json.optional(json, "lei", Conv.STRING); + } + + /** + * The jurisdiction field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional jurisdiction() { + return Json.optional(json, "jurisdiction", Conv.STRING); + } + + /** + * Whether your application has an available route to it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reachable() { + return Json.optional(json, "reachable", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Entities}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Entities from) { + super(from); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * The lei field. + * + * @param lei the value + * @return this builder + */ + public Builder lei(String lei) { + return set("lei", Conv.STRING, lei); + } + + /** + * The jurisdiction field. + * + * @param jurisdiction the value + * @return this builder + */ + public Builder jurisdiction(String jurisdiction) { + return set("jurisdiction", Conv.STRING, jurisdiction); + } + + /** + * Whether your application has an available route to it. + * + * @param reachable the value + * @return this builder + */ + public Builder reachable(boolean reachable) { + return set("reachable", Conv.BOOLEAN, reachable); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Entities} + */ + public Entities build() { + return new Entities(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** A compliance contact the EMAIL rail can write to. */ + public static final class Contacts extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "email", "name", "source", "scope", "verified_at"); + + private Contacts(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Contacts} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Contacts fromJson(JsonNode json) { + return new Contacts(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * The email field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional email() { + return Json.optional(json, "email", Conv.STRING); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * staff: curated by Didit; application: added by your application; learned: an address that answered a Travel Rule request through its response link. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional source() { + return Json.optional(json, "source", Conv.enumeration(VaspDirectoryEntry.Contacts.Source::of)); + } + + /** + * platform contacts are used for every Didit customer's transfers; application contacts are private to your application, which sees only its own. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional scope() { + return Json.optional(json, "scope", Conv.enumeration(VaspDirectoryEntry.Contacts.Scope::of)); + } + + /** + * When someone at this address answered a request. Null when it has not yet. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional verifiedAt() { + return Json.optional(json, "verified_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Contacts}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Contacts from) { + super(from); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * The email field. + * + * @param email the value + * @return this builder + */ + public Builder email(String email) { + return set("email", Conv.STRING, email); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * staff: curated by Didit; application: added by your application; learned: an address that answered a Travel Rule request through its response link. + * + * @param source the value + * @return this builder + */ + public Builder source(VaspDirectoryEntry.Contacts.Source source) { + return set("source", Conv.enumeration(VaspDirectoryEntry.Contacts.Source::of), source); + } + + /** + * platform contacts are used for every Didit customer's transfers; application contacts are private to your application, which sees only its own. + * + * @param scope the value + * @return this builder + */ + public Builder scope(VaspDirectoryEntry.Contacts.Scope scope) { + return set("scope", Conv.enumeration(VaspDirectoryEntry.Contacts.Scope::of), scope); + } + + /** + * When someone at this address answered a request. Null when it has not yet. + * + * @param verifiedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder verifiedAt(@Nullable OffsetDateTime verifiedAt) { + return setNullable("verified_at", Conv.DATE_TIME, verifiedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Contacts} + */ + public Contacts build() { + return new Contacts(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * staff: curated by Didit; application: added by your application; learned: an address that answered a Travel Rule request through its response link. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Source extends StringEnum { + /** The value {@code "staff"}. */ + public static final Source STAFF = new Source("staff"); + + /** The value {@code "application"}. */ + public static final Source APPLICATION = new Source("application"); + + /** The value {@code "learned"}. */ + public static final Source LEARNED = new Source("learned"); + + private static final List KNOWN_VALUES = List.of(STAFF, APPLICATION, LEARNED); + + private Source(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Source of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Source::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * platform contacts are used for every Didit customer's transfers; application contacts are private to your application, which sees only its own. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Scope extends StringEnum { + /** The value {@code "platform"}. */ + public static final Scope PLATFORM = new Scope("platform"); + + /** The value {@code "application"}. */ + public static final Scope APPLICATION = new Scope("application"); + + private static final List KNOWN_VALUES = List.of(PLATFORM, APPLICATION); + + private Scope(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Scope of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Scope::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * Didit's verification status of the entry, scored by the verification component of dd_score. Always COMPLETED for a Didit network member. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DdStatus extends StringEnum { + /** The value {@code "NOT_STARTED"}. */ + public static final DdStatus NOT_STARTED = new DdStatus("NOT_STARTED"); + + /** The value {@code "IN_PROGRESS"}. */ + public static final DdStatus IN_PROGRESS = new DdStatus("IN_PROGRESS"); + + /** The value {@code "COMPLETED"}. */ + public static final DdStatus COMPLETED = new DdStatus("COMPLETED"); + + /** The value {@code "REJECTED"}. */ + public static final DdStatus REJECTED = new DdStatus("REJECTED"); + + private static final List KNOWN_VALUES = List.of(NOT_STARTED, IN_PROGRESS, COMPLETED, REJECTED); + + private DdStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static DdStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DdStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class ReachableRails extends StringEnum { + /** The value {@code "INTERNAL"}. */ + public static final ReachableRails INTERNAL = new ReachableRails("INTERNAL"); + + /** The value {@code "TRP"}. */ + public static final ReachableRails TRP = new ReachableRails("TRP"); + + /** The value {@code "GTR"}. */ + public static final ReachableRails GTR = new ReachableRails("GTR"); + + /** The value {@code "TRUST"}. */ + public static final ReachableRails TRUST = new ReachableRails("TRUST"); + + /** The value {@code "VERIFYVASP"}. */ + public static final ReachableRails VERIFYVASP = new ReachableRails("VERIFYVASP"); + + /** The value {@code "SYGNA"}. */ + public static final ReachableRails SYGNA = new ReachableRails("SYGNA"); + + /** The value {@code "EMAIL"}. */ + public static final ReachableRails EMAIL = new ReachableRails("EMAIL"); + + private static final List KNOWN_VALUES = List.of(INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL); + + private ReachableRails(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static ReachableRails of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, ReachableRails::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class DdScoreBreakdownValue extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("points", "max", "reason", "detail"); + + private DdScoreBreakdownValue(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code DdScoreBreakdownValue} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DdScoreBreakdownValue fromJson(JsonNode json) { + return new DdScoreBreakdownValue(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Points the component contributes, rounded to one decimal. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional points() { + return Json.optional(json, "points", Conv.DOUBLE); + } - private static final List KNOWN_VALUES = List.of(INTERNAL, TRP, GTR, EMAIL); + /** + * The component's weight: the points it contributes at best. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional max() { + return Json.optional(json, "max", Conv.LONG); + } - private ReachableRails(String value) { + /** + * Machine-readable code of the component's verdict. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * One-line English explanation. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional detail() { + return Json.optional(json, "detail", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link DdScoreBreakdownValue}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(DdScoreBreakdownValue from) { + super(from); + } + + /** + * Points the component contributes, rounded to one decimal. + * + * @param points the value + * @return this builder + */ + public Builder points(double points) { + return set("points", Conv.DOUBLE, points); + } + + /** + * The component's weight: the points it contributes at best. + * + * @param max the value + * @return this builder + */ + public Builder max(long max) { + return set("max", Conv.LONG, max); + } + + /** + * Machine-readable code of the component's verdict. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * One-line English explanation. + * + * @param detail the value + * @return this builder + */ + public Builder detail(String detail) { + return set("detail", Conv.STRING, detail); + } + + /** + * Builds the value. + * + * @return a new immutable {@code DdScoreBreakdownValue} + */ + public DdScoreBreakdownValue build() { + return new DdScoreBreakdownValue(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + /** + * Which way dd_score points: always higher_is_safer. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class DdScoreSemantic extends StringEnum { + /** The value {@code "higher_is_safer"}. */ + public static final DdScoreSemantic HIGHER_IS_SAFER = new DdScoreSemantic("higher_is_safer"); + + private static final List KNOWN_VALUES = List.of(HIGHER_IS_SAFER); + + private DdScoreSemantic(String value) { super(value); } @@ -336,8 +2277,8 @@ private ReachableRails(String value) { * @param value the string value * @return the enum value */ - public static ReachableRails of(String value) { - return StringEnum.lookup(KNOWN_VALUES, value, ReachableRails::new); + public static DdScoreSemantic of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, DdScoreSemantic::new); } /** @@ -345,7 +2286,56 @@ public static ReachableRails of(String value) { * * @return the known values */ - public static List knownValues() { + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * The band dd_score falls in: LOW (70 or more), MEDIUM (40 to 69) or HIGH (below 40). Empty for a Didit network member and for an entry not assessed yet. Filter on it with risk. Your own flagged screening never changes this shared value: it makes counterparty_risk_level HIGH in your rules instead. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class RiskLevel extends StringEnum { + /** The value {@code "LOW"}. */ + public static final RiskLevel LOW = new RiskLevel("LOW"); + + /** The value {@code "MEDIUM"}. */ + public static final RiskLevel MEDIUM = new RiskLevel("MEDIUM"); + + /** The value {@code "HIGH"}. */ + public static final RiskLevel HIGH = new RiskLevel("HIGH"); + + /** The value {@code ""}. */ + public static final RiskLevel EMPTY = new RiskLevel(""); + + private static final List KNOWN_VALUES = List.of(LOW, MEDIUM, HIGH, EMPTY); + + private RiskLevel(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static RiskLevel of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, RiskLevel::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { return KNOWN_VALUES; } @@ -354,4 +2344,684 @@ public boolean isKnown() { return KNOWN_VALUES.contains(this); } } + + /** + * Whether the VASP answers Travel Rule requests: the share of the requests Didit customers started towards it in the last 90 days that it answered. Only requests sent over a network or protocol rail, or by email to a shared address (the entry's compliance address or a platform contact), count; a request sent to an address a customer typed, or to a customer's private contact, never does. An expired request is unanswered; a completed exchange, a decline or a data mismatch is an answer; a cancelled request or an unreachable rail is neither. Always UNKNOWN with zero counts for a Didit network member. + */ + public static final class Responsiveness extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("status", "answered", "total", "computed_at"); + + private Responsiveness(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Responsiveness} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Responsiveness fromJson(JsonNode json) { + return new Responsiveness(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * UNKNOWN with fewer than 5 decided requests in the window; UNRESPONSIVE when less than 10 percent of them were answered; RESPONSIVE otherwise. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(VaspDirectoryEntry.Responsiveness.Status::of)); + } + + /** + * Decided requests in the window that the VASP answered. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional answered() { + return Json.optional(json, "answered", Conv.LONG); + } + + /** + * Decided requests in the window: answered plus expired. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional total() { + return Json.optional(json, "total", Conv.LONG); + } + + /** + * The computed_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional computedAt() { + return Json.optional(json, "computed_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Responsiveness}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Responsiveness from) { + super(from); + } + + /** + * UNKNOWN with fewer than 5 decided requests in the window; UNRESPONSIVE when less than 10 percent of them were answered; RESPONSIVE otherwise. + * + * @param status the value + * @return this builder + */ + public Builder status(VaspDirectoryEntry.Responsiveness.Status status) { + return set("status", Conv.enumeration(VaspDirectoryEntry.Responsiveness.Status::of), status); + } + + /** + * Decided requests in the window that the VASP answered. + * + * @param answered the value + * @return this builder + */ + public Builder answered(long answered) { + return set("answered", Conv.LONG, answered); + } + + /** + * Decided requests in the window: answered plus expired. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * The computed_at field. + * + * @param computedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder computedAt(@Nullable OffsetDateTime computedAt) { + return setNullable("computed_at", Conv.DATE_TIME, computedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Responsiveness} + */ + public Responsiveness build() { + return new Responsiveness(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * UNKNOWN with fewer than 5 decided requests in the window; UNRESPONSIVE when less than 10 percent of them were answered; RESPONSIVE otherwise. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "RESPONSIVE"}. */ + public static final Status RESPONSIVE = new Status("RESPONSIVE"); + + /** The value {@code "UNRESPONSIVE"}. */ + public static final Status UNRESPONSIVE = new Status("UNRESPONSIVE"); + + /** The value {@code "UNKNOWN"}. */ + public static final Status UNKNOWN = new Status("UNKNOWN"); + + private static final List KNOWN_VALUES = List.of(RESPONSIVE, UNRESPONSIVE, UNKNOWN); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * The essential-checks report of a directory entry, built only from the platform's verified sources: registration (an official register lists the VASP: the EU's interim MiCA register, the public TRISA directory or the global LEI register), licence (a MiCA authorisation is on record and current), domain (the entry's domain matches its TRP endpoint or the website its register gives) and lei (its LEI record is active, issued and consistent with the entry's names and country). The essentials component of dd_score is the share of decided checks that pass. Empty until the weekly directory refresh first checks the entry, and always empty for a Didit network member. + */ + public static final class EssentialChecks extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("level", "checked_at", "checks", "passed", "failed", "unknown", "total"); + + private EssentialChecks(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code EssentialChecks} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static EssentialChecks fromJson(JsonNode json) { + return new EssentialChecks(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The level field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional level() { + return Json.optional(json, "level", Conv.enumeration(VaspDirectoryEntry.EssentialChecks.Level::of)); + } + + /** + * The checked_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional checkedAt() { + return Json.optional(json, "checked_at", Conv.DATE_TIME); + } + + /** + * The checks field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> checks() { + return Json.optional(json, "checks", Conv.list(Conv.model(VaspDirectoryEntry.EssentialChecks.Checks::fromJson))); + } + + /** + * The passed field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional passed() { + return Json.optional(json, "passed", Conv.LONG); + } + + /** + * The failed field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failed() { + return Json.optional(json, "failed", Conv.LONG); + } + + /** + * The unknown field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional unknown() { + return Json.optional(json, "unknown", Conv.LONG); + } + + /** + * The total field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional total() { + return Json.optional(json, "total", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link EssentialChecks}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(EssentialChecks from) { + super(from); + } + + /** + * The level field. + * + * @param level the value + * @return this builder + */ + public Builder level(VaspDirectoryEntry.EssentialChecks.Level level) { + return set("level", Conv.enumeration(VaspDirectoryEntry.EssentialChecks.Level::of), level); + } + + /** + * The checked_at field. + * + * @param checkedAt the value + * @return this builder + */ + public Builder checkedAt(OffsetDateTime checkedAt) { + return set("checked_at", Conv.DATE_TIME, checkedAt); + } + + /** + * The checks field. + * + * @param checks the value + * @return this builder + */ + public Builder checks(List checks) { + return set("checks", Conv.list(Conv.model(VaspDirectoryEntry.EssentialChecks.Checks::fromJson)), checks); + } + + /** + * The passed field. + * + * @param passed the value + * @return this builder + */ + public Builder passed(long passed) { + return set("passed", Conv.LONG, passed); + } + + /** + * The failed field. + * + * @param failed the value + * @return this builder + */ + public Builder failed(long failed) { + return set("failed", Conv.LONG, failed); + } + + /** + * The unknown field. + * + * @param unknown the value + * @return this builder + */ + public Builder unknown(long unknown) { + return set("unknown", Conv.LONG, unknown); + } + + /** + * The total field. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * Builds the value. + * + * @return a new immutable {@code EssentialChecks} + */ + public EssentialChecks build() { + return new EssentialChecks(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Level extends StringEnum { + /** The value {@code "essential"}. */ + public static final Level ESSENTIAL = new Level("essential"); + + private static final List KNOWN_VALUES = List.of(ESSENTIAL); + + private Level(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Level of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Level::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class Checks extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "status", "reason", "detail"); + + private Checks(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Checks} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Checks fromJson(JsonNode json) { + return new Checks(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The id field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.enumeration(VaspDirectoryEntry.EssentialChecks.Checks.Id::of)); + } + + /** + * unknown when no source on record can decide the check. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(VaspDirectoryEntry.EssentialChecks.Checks.Status::of)); + } + + /** + * Machine-readable code of the verdict. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reason() { + return Json.optional(json, "reason", Conv.STRING); + } + + /** + * One-line English explanation. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional detail() { + return Json.optional(json, "detail", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Checks}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Checks from) { + super(from); + } + + /** + * The id field. + * + * @param id the value + * @return this builder + */ + public Builder id(VaspDirectoryEntry.EssentialChecks.Checks.Id id) { + return set("id", Conv.enumeration(VaspDirectoryEntry.EssentialChecks.Checks.Id::of), id); + } + + /** + * unknown when no source on record can decide the check. + * + * @param status the value + * @return this builder + */ + public Builder status(VaspDirectoryEntry.EssentialChecks.Checks.Status status) { + return set("status", Conv.enumeration(VaspDirectoryEntry.EssentialChecks.Checks.Status::of), status); + } + + /** + * Machine-readable code of the verdict. + * + * @param reason the value + * @return this builder + */ + public Builder reason(String reason) { + return set("reason", Conv.STRING, reason); + } + + /** + * One-line English explanation. + * + * @param detail the value + * @return this builder + */ + public Builder detail(String detail) { + return set("detail", Conv.STRING, detail); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Checks} + */ + public Checks build() { + return new Checks(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Id extends StringEnum { + /** The value {@code "registration"}. */ + public static final Id REGISTRATION = new Id("registration"); + + /** The value {@code "licence"}. */ + public static final Id LICENCE = new Id("licence"); + + /** The value {@code "domain"}. */ + public static final Id DOMAIN = new Id("domain"); + + /** The value {@code "lei"}. */ + public static final Id LEI = new Id("lei"); + + private static final List KNOWN_VALUES = List.of(REGISTRATION, LICENCE, DOMAIN, LEI); + + private Id(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Id of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Id::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * unknown when no source on record can decide the check. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "pass"}. */ + public static final Status PASS = new Status("pass"); + + /** The value {@code "fail"}. */ + public static final Status FAIL = new Status("fail"); + + /** The value {@code "unknown"}. */ + public static final Status UNKNOWN = new Status("unknown"); + + private static final List KNOWN_VALUES = List.of(PASS, FAIL, UNKNOWN); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + } } diff --git a/src/main/java/me/didit/types/VaspDirectoryWidgetEntry.java b/src/main/java/me/didit/types/VaspDirectoryWidgetEntry.java new file mode 100644 index 0000000..ce0f825 --- /dev/null +++ b/src/main/java/me/didit/types/VaspDirectoryWidgetEntry.java @@ -0,0 +1,263 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** What the end user's VASP picker shows: who the VASP is and whether it can be reached. */ +public final class VaspDirectoryWidgetEntry extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("id", "kind", "name", "lei", "jurisdiction", "domains", "reachable"); + + private VaspDirectoryWidgetEntry(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code VaspDirectoryWidgetEntry} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static VaspDirectoryWidgetEntry fromJson(JsonNode json) { + return new VaspDirectoryWidgetEntry(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The entry's id, for GET /v3/travel-rule/vasps/{vasp_id}/. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional id() { + return Json.optional(json, "id", Conv.STRING); + } + + /** + * directory for an entry of the shared directory; didit for a member of the Didit network. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional kind() { + return Json.optional(json, "kind", Conv.enumeration(VaspDirectoryWidgetEntry.Kind::of)); + } + + /** + * The name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional name() { + return Json.optional(json, "name", Conv.STRING); + } + + /** + * The lei field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lei() { + return Json.optional(json, "lei", Conv.STRING); + } + + /** + * ISO 3166 country code, as the source gives it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional jurisdiction() { + return Json.optional(json, "jurisdiction", Conv.STRING); + } + + /** + * The VASP's web domains. Always empty for Didit network members. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> domains() { + return Json.optional(json, "domains", Conv.list(Conv.STRING)); + } + + /** + * Whether your application has an available route to this VASP, or to one of its entities for a brand. Always true for a Didit network member. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional reachable() { + return Json.optional(json, "reachable", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link VaspDirectoryWidgetEntry}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(VaspDirectoryWidgetEntry from) { + super(from); + } + + /** + * The entry's id, for GET /v3/travel-rule/vasps/{vasp_id}/. + * + * @param id the value + * @return this builder + */ + public Builder id(String id) { + return set("id", Conv.STRING, id); + } + + /** + * directory for an entry of the shared directory; didit for a member of the Didit network. + * + * @param kind the value + * @return this builder + */ + public Builder kind(VaspDirectoryWidgetEntry.Kind kind) { + return set("kind", Conv.enumeration(VaspDirectoryWidgetEntry.Kind::of), kind); + } + + /** + * The name field. + * + * @param name the value + * @return this builder + */ + public Builder name(String name) { + return set("name", Conv.STRING, name); + } + + /** + * The lei field. + * + * @param lei the value + * @return this builder + */ + public Builder lei(String lei) { + return set("lei", Conv.STRING, lei); + } + + /** + * ISO 3166 country code, as the source gives it. + * + * @param jurisdiction the value + * @return this builder + */ + public Builder jurisdiction(String jurisdiction) { + return set("jurisdiction", Conv.STRING, jurisdiction); + } + + /** + * The VASP's web domains. Always empty for Didit network members. + * + * @param domains the value + * @return this builder + */ + public Builder domains(List domains) { + return set("domains", Conv.list(Conv.STRING), domains); + } + + /** + * Whether your application has an available route to this VASP, or to one of its entities for a brand. Always true for a Didit network member. + * + * @param reachable the value + * @return this builder + */ + public Builder reachable(boolean reachable) { + return set("reachable", Conv.BOOLEAN, reachable); + } + + /** + * Builds the value. + * + * @return a new immutable {@code VaspDirectoryWidgetEntry} + */ + public VaspDirectoryWidgetEntry build() { + return new VaspDirectoryWidgetEntry(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * directory for an entry of the shared directory; didit for a member of the Didit network. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Kind extends StringEnum { + /** The value {@code "directory"}. */ + public static final Kind DIRECTORY = new Kind("directory"); + + /** The value {@code "didit"}. */ + public static final Kind DIDIT = new Kind("didit"); + + private static final List KNOWN_VALUES = List.of(DIRECTORY, DIDIT); + + private Kind(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Kind of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Kind::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/WalletAddressEntry.java b/src/main/java/me/didit/types/WalletAddressEntry.java index 93f7512..ebfb91c 100644 --- a/src/main/java/me/didit/types/WalletAddressEntry.java +++ b/src/main/java/me/didit/types/WalletAddressEntry.java @@ -12,10 +12,9 @@ import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.core.JsonModel; -import org.jspecify.annotations.Nullable; public final class WalletAddressEntry extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "address", "chain", "holder_vendor_data", "holder_name", "entity_type", "is_ownership_verified", "created_at", "self_declared", "travel_address"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "address", "chain", "holder_vendor_data", "holder_name", "entity_type", "is_ownership_verified", "created_at", "travel_address", "address_hash", "memo", "receiving_travel_address"); private WalletAddressEntry(ObjectNode json, Map> files) { super(json, files); @@ -61,7 +60,7 @@ public Optional uuid() { } /** - * The wallet address. Unique per application and chain among non-deleted entries. + * The wallet address; "" for an entry imported by its hash only. Unique per application, chain and memo, comparing the address without regard to case. After you delete an entry, you can register the same address again. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -71,7 +70,7 @@ public Optional address() { } /** - * Free-text chain identifier, e.g. "ethereum", "bitcoin". + * Chain token for the address, for example ETH or BTC. When Didit routes or registers a transfer, it matches address book entries on the wallet address and, when the transfer carries a chain, on this value too, ignoring case. A transfer's chain is beneficiary_data.chain, filled from the transaction's crypto chain or currency symbol (for example ETH) when you do not send it, or the chain of an after-deposit registration, so use the same token everywhere. An entry with an empty chain matches any chain. A Lightning payee (a node id or a Lightning Address) registered on bitcoin is stored lowercased on LIGHTNING, where Lightning transfers are matched. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -131,23 +130,43 @@ public Optional createdAt() { } /** - * Whether the entry was created as a self-declared ownership proof. + * The counterparty VASP's travel address for this wallet, if known. Lets Didit route this beneficiary's transfers over the TRP rail, for the assets TRP carries (BTC, ETH, and USDC on a named network). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional selfDeclared() { - return Json.optional(json, "self_declared", Conv.BOOLEAN); + public Optional travelAddress() { + return Json.optional(json, "travel_address", Conv.STRING); } /** - * The counterparty VASP's travel address for this wallet, if known. Lets Didit route this beneficiary's transfers over the TRP rail. + * Read-only. SHA-256 of the lowercased address, in lowercase hex. An entry imported by its hash only is matched on it. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type */ - public Optional travelAddress() { - return Json.optional(json, "travel_address", Conv.STRING); + public Optional addressHash() { + return Json.optional(json, "address_hash", Conv.STRING); + } + + /** + * The destination tag or memo that is part of the wallet on a memo network (XRP, XLM, EOS, ATOM, HBAR); "" for the whole address. An entry with a memo matches only transfers naming that memo; one without matches the whole address. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional memo() { + return Json.optional(json, "memo", Conv.STRING); + } + + /** + * Your per-wallet travel address for this wallet: give it to the sending VASP so its TRP inquiry names this wallet. Empty for a wallet recorded as another VASP's, while Travel Rule is off, and before your VASP profile is saved. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional receivingTravelAddress() { + return Json.optional(json, "receiving_travel_address", Conv.STRING); } @Override @@ -174,7 +193,7 @@ public Builder uuid(String uuid) { } /** - * The wallet address. Unique per application and chain among non-deleted entries. + * The wallet address; "" for an entry imported by its hash only. Unique per application, chain and memo, comparing the address without regard to case. After you delete an entry, you can register the same address again. * * @param address the value * @return this builder @@ -184,33 +203,33 @@ public Builder address(String address) { } /** - * Free-text chain identifier, e.g. "ethereum", "bitcoin". + * Chain token for the address, for example ETH or BTC. When Didit routes or registers a transfer, it matches address book entries on the wallet address and, when the transfer carries a chain, on this value too, ignoring case. A transfer's chain is beneficiary_data.chain, filled from the transaction's crypto chain or currency symbol (for example ETH) when you do not send it, or the chain of an after-deposit registration, so use the same token everywhere. An entry with an empty chain matches any chain. A Lightning payee (a node id or a Lightning Address) registered on bitcoin is stored lowercased on LIGHTNING, where Lightning transfers are matched. * - * @param chain the value, or {@code null} to send JSON null + * @param chain the value * @return this builder */ - public Builder chain(@Nullable String chain) { - return setNullable("chain", Conv.STRING, chain); + public Builder chain(String chain) { + return set("chain", Conv.STRING, chain); } /** * Your internal identifier for the holder. * - * @param holderVendorData the value, or {@code null} to send JSON null + * @param holderVendorData the value * @return this builder */ - public Builder holderVendorData(@Nullable String holderVendorData) { - return setNullable("holder_vendor_data", Conv.STRING, holderVendorData); + public Builder holderVendorData(String holderVendorData) { + return set("holder_vendor_data", Conv.STRING, holderVendorData); } /** * Name of the wallet holder, used for the beneficiary name match. * - * @param holderName the value, or {@code null} to send JSON null + * @param holderName the value * @return this builder */ - public Builder holderName(@Nullable String holderName) { - return setNullable("holder_name", Conv.STRING, holderName); + public Builder holderName(String holderName) { + return set("holder_name", Conv.STRING, holderName); } /** @@ -244,23 +263,43 @@ public Builder createdAt(OffsetDateTime createdAt) { } /** - * Whether the entry was created as a self-declared ownership proof. + * The counterparty VASP's travel address for this wallet, if known. Lets Didit route this beneficiary's transfers over the TRP rail, for the assets TRP carries (BTC, ETH, and USDC on a named network). + * + * @param travelAddress the value + * @return this builder + */ + public Builder travelAddress(String travelAddress) { + return set("travel_address", Conv.STRING, travelAddress); + } + + /** + * Read-only. SHA-256 of the lowercased address, in lowercase hex. An entry imported by its hash only is matched on it. + * + * @param addressHash the value + * @return this builder + */ + public Builder addressHash(String addressHash) { + return set("address_hash", Conv.STRING, addressHash); + } + + /** + * The destination tag or memo that is part of the wallet on a memo network (XRP, XLM, EOS, ATOM, HBAR); "" for the whole address. An entry with a memo matches only transfers naming that memo; one without matches the whole address. * - * @param selfDeclared the value + * @param memo the value * @return this builder */ - public Builder selfDeclared(boolean selfDeclared) { - return set("self_declared", Conv.BOOLEAN, selfDeclared); + public Builder memo(String memo) { + return set("memo", Conv.STRING, memo); } /** - * The counterparty VASP's travel address for this wallet, if known. Lets Didit route this beneficiary's transfers over the TRP rail. + * Your per-wallet travel address for this wallet: give it to the sending VASP so its TRP inquiry names this wallet. Empty for a wallet recorded as another VASP's, while Travel Rule is off, and before your VASP profile is saved. * - * @param travelAddress the value, or {@code null} to send JSON null + * @param receivingTravelAddress the value * @return this builder */ - public Builder travelAddress(@Nullable String travelAddress) { - return setNullable("travel_address", Conv.STRING, travelAddress); + public Builder receivingTravelAddress(String receivingTravelAddress) { + return set("receiving_travel_address", Conv.STRING, receivingTravelAddress); } /** diff --git a/src/main/java/me/didit/types/WalletAddressEntryCreate.java b/src/main/java/me/didit/types/WalletAddressEntryCreate.java index 4fbe4bc..5034fe5 100644 --- a/src/main/java/me/didit/types/WalletAddressEntryCreate.java +++ b/src/main/java/me/didit/types/WalletAddressEntryCreate.java @@ -13,7 +13,7 @@ import me.didit.core.JsonModel; public final class WalletAddressEntryCreate extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("address", "chain", "holder_vendor_data", "holder_name", "entity_type", "self_declared", "travel_address"); + private static final Set KNOWN_FIELDS = Set.of("address", "chain", "holder_vendor_data", "holder_name", "entity_type", "self_declared", "travel_address", "memo"); private WalletAddressEntryCreate(ObjectNode json, Map> files) { super(json, files); @@ -49,7 +49,7 @@ public Builder toBuilder() { } /** - * The address field. + * The wallet address. An XRP X-address is stored as its classic address and its tag, so it matches transfers that name either form. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -59,7 +59,7 @@ public String address() { } /** - * The chain field. + * Chain token for the address, for example ETH or BTC. When Didit routes or registers a transfer, it matches address book entries on the wallet address and, when the transfer carries a chain, on this value too, ignoring case. A transfer's chain is beneficiary_data.chain, filled from the transaction's crypto chain or currency symbol (for example ETH) when you do not send it, or the chain of an after-deposit registration, so use the same token everywhere. An entry with an empty chain matches any chain. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -99,7 +99,7 @@ public Optional entityType() { } /** - * Write-only. When true, the entry is created already ownership-verified via a SELF_DECLARATION proof. + * Write-only. When true, the entry is created already ownership-verified via a SELF_DECLARATION proof. Returns 400 when allow_self_declaration is off in your Travel Rule settings. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -109,7 +109,7 @@ public Optional selfDeclared() { } /** - * Optional. The counterparty VASP's travel address for this wallet, enabling TRP routing. + * Optional. The counterparty VASP's travel address for this wallet, enabling TRP routing for the assets TRP carries (BTC, ETH, and USDC on a named network). An invalid travel address returns 400. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -118,6 +118,16 @@ public Optional travelAddress() { return Json.optional(json, "travel_address", Conv.STRING); } + /** + * Optional. The destination tag or memo that is part of the wallet. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. Alias: destinationTag or destination_tag. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional memo() { + return Json.optional(json, "memo", Conv.STRING); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -132,7 +142,7 @@ private Builder(WalletAddressEntryCreate from) { } /** - * The address field. + * The wallet address. An XRP X-address is stored as its classic address and its tag, so it matches transfers that name either form. * * @param address the value * @return this builder @@ -142,7 +152,7 @@ public Builder address(String address) { } /** - * The chain field. + * Chain token for the address, for example ETH or BTC. When Didit routes or registers a transfer, it matches address book entries on the wallet address and, when the transfer carries a chain, on this value too, ignoring case. A transfer's chain is beneficiary_data.chain, filled from the transaction's crypto chain or currency symbol (for example ETH) when you do not send it, or the chain of an after-deposit registration, so use the same token everywhere. An entry with an empty chain matches any chain. * * @param chain the value * @return this builder @@ -182,7 +192,7 @@ public Builder entityType(String entityType) { } /** - * Write-only. When true, the entry is created already ownership-verified via a SELF_DECLARATION proof. + * Write-only. When true, the entry is created already ownership-verified via a SELF_DECLARATION proof. Returns 400 when allow_self_declaration is off in your Travel Rule settings. * * @param selfDeclared the value * @return this builder @@ -192,7 +202,7 @@ public Builder selfDeclared(boolean selfDeclared) { } /** - * Optional. The counterparty VASP's travel address for this wallet, enabling TRP routing. + * Optional. The counterparty VASP's travel address for this wallet, enabling TRP routing for the assets TRP carries (BTC, ETH, and USDC on a named network). An invalid travel address returns 400. * * @param travelAddress the value * @return this builder @@ -201,6 +211,16 @@ public Builder travelAddress(String travelAddress) { return set("travel_address", Conv.STRING, travelAddress); } + /** + * Optional. The destination tag or memo that is part of the wallet. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. Alias: destinationTag or destination_tag. + * + * @param memo the value + * @return this builder + */ + public Builder memo(String memo) { + return set("memo", Conv.STRING, memo); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/WalletAddressEntryUpdate.java b/src/main/java/me/didit/types/WalletAddressEntryUpdate.java index c6c49a8..5ad9956 100644 --- a/src/main/java/me/didit/types/WalletAddressEntryUpdate.java +++ b/src/main/java/me/didit/types/WalletAddressEntryUpdate.java @@ -13,7 +13,7 @@ import me.didit.core.JsonModel; /** - * PATCH is a partial update; the response echoes only holder_name, holder_vendor_data, entity_type, and travel_address. + * PATCH is a partial update; the response echoes only holder_name, holder_vendor_data, entity_type, travel_address, receiving_travel_address, label, and owner_type. */ public final class WalletAddressEntryUpdate extends JsonModel { private static final Set KNOWN_FIELDS = Set.of("holder_name", "holder_vendor_data", "entity_type", "travel_address"); diff --git a/src/main/java/me/didit/types/WalletAddressImportReport.java b/src/main/java/me/didit/types/WalletAddressImportReport.java new file mode 100644 index 0000000..07d25b9 --- /dev/null +++ b/src/main/java/me/didit/types/WalletAddressImportReport.java @@ -0,0 +1,415 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** What happened to each row of an import, in the order sent. */ +public final class WalletAddressImportReport extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("total", "created", "existing", "duplicate", "failed", "rows"); + + private WalletAddressImportReport(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WalletAddressImportReport} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WalletAddressImportReport fromJson(JsonNode json) { + return new WalletAddressImportReport(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The total field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional total() { + return Json.optional(json, "total", Conv.LONG); + } + + /** + * The created field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional created() { + return Json.optional(json, "created", Conv.LONG); + } + + /** + * The existing field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional existing() { + return Json.optional(json, "existing", Conv.LONG); + } + + /** + * The duplicate field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional duplicate() { + return Json.optional(json, "duplicate", Conv.LONG); + } + + /** + * The failed field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failed() { + return Json.optional(json, "failed", Conv.LONG); + } + + /** + * The rows field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> rows() { + return Json.optional(json, "rows", Conv.list(Conv.model(WalletAddressImportReport.Rows::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WalletAddressImportReport}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WalletAddressImportReport from) { + super(from); + } + + /** + * The total field. + * + * @param total the value + * @return this builder + */ + public Builder total(long total) { + return set("total", Conv.LONG, total); + } + + /** + * The created field. + * + * @param created the value + * @return this builder + */ + public Builder created(long created) { + return set("created", Conv.LONG, created); + } + + /** + * The existing field. + * + * @param existing the value + * @return this builder + */ + public Builder existing(long existing) { + return set("existing", Conv.LONG, existing); + } + + /** + * The duplicate field. + * + * @param duplicate the value + * @return this builder + */ + public Builder duplicate(long duplicate) { + return set("duplicate", Conv.LONG, duplicate); + } + + /** + * The failed field. + * + * @param failed the value + * @return this builder + */ + public Builder failed(long failed) { + return set("failed", Conv.LONG, failed); + } + + /** + * The rows field. + * + * @param rows the value + * @return this builder + */ + public Builder rows(List rows) { + return set("rows", Conv.list(Conv.model(WalletAddressImportReport.Rows::fromJson)), rows); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WalletAddressImportReport} + */ + public WalletAddressImportReport build() { + return new WalletAddressImportReport(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + public static final class Rows extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("row", "status", "entry_id", "errors", "duplicate_of_row"); + + private Rows(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Rows} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Rows fromJson(JsonNode json) { + return new Rows(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The row's number, from 1. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional row() { + return Json.optional(json, "row", Conv.LONG); + } + + /** + * created: added to the book. existing: the wallet is already in the book and was left untouched. duplicate: repeats an earlier row of this import. failed: see errors. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(WalletAddressImportReport.Rows.Status::of)); + } + + /** + * The address-book entry the row created or found. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entryId() { + return Json.optional(json, "entry_id", Conv.STRING); + } + + /** + * Field errors of a failed row; empty otherwise. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> errors() { + return Json.optional(json, "errors", Conv.OBJECT); + } + + /** + * On a duplicate row, the row it repeats. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional duplicateOfRow() { + return Json.optional(json, "duplicate_of_row", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Rows}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Rows from) { + super(from); + } + + /** + * The row's number, from 1. + * + * @param row the value + * @return this builder + */ + public Builder row(long row) { + return set("row", Conv.LONG, row); + } + + /** + * created: added to the book. existing: the wallet is already in the book and was left untouched. duplicate: repeats an earlier row of this import. failed: see errors. + * + * @param status the value + * @return this builder + */ + public Builder status(WalletAddressImportReport.Rows.Status status) { + return set("status", Conv.enumeration(WalletAddressImportReport.Rows.Status::of), status); + } + + /** + * The address-book entry the row created or found. + * + * @param entryId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder entryId(@Nullable String entryId) { + return setNullable("entry_id", Conv.STRING, entryId); + } + + /** + * Field errors of a failed row; empty otherwise. + * + * @param errors the value + * @return this builder + */ + public Builder errors(Map errors) { + return setAny("errors", errors); + } + + /** + * On a duplicate row, the row it repeats. + * + * @param duplicateOfRow the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder duplicateOfRow(@Nullable Long duplicateOfRow) { + return setNullable("duplicate_of_row", Conv.LONG, duplicateOfRow); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Rows} + */ + public Rows build() { + return new Rows(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * created: added to the book. existing: the wallet is already in the book and was left untouched. duplicate: repeats an earlier row of this import. failed: see errors. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "created"}. */ + public static final Status CREATED = new Status("created"); + + /** The value {@code "existing"}. */ + public static final Status EXISTING = new Status("existing"); + + /** The value {@code "duplicate"}. */ + public static final Status DUPLICATE = new Status("duplicate"); + + /** The value {@code "failed"}. */ + public static final Status FAILED = new Status("failed"); + + private static final List KNOWN_VALUES = List.of(CREATED, EXISTING, DUPLICATE, FAILED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } +} diff --git a/src/main/java/me/didit/types/WalletAddressImportRow.java b/src/main/java/me/didit/types/WalletAddressImportRow.java new file mode 100644 index 0000000..db4ed1f --- /dev/null +++ b/src/main/java/me/didit/types/WalletAddressImportRow.java @@ -0,0 +1,389 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * One wallet to import: by its address, its address_hash, or both. The camelCase forms of these fields are accepted too, as are walletAddress, walletAddressHash, destinationTag and externalUserId. + */ +public final class WalletAddressImportRow extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("address", "address_hash", "chain", "memo", "holder_name", "holder_vendor_data", "entity_type", "label", "owner_type", "travel_address", "self_declared"); + + private WalletAddressImportRow(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WalletAddressImportRow} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WalletAddressImportRow fromJson(JsonNode json) { + return new WalletAddressImportRow(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The wallet address. Alias: walletAddress. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional address() { + return Json.optional(json, "address", Conv.STRING); + } + + /** + * SHA-256 of the lowercased address, as 64 hex characters or 44 base64 characters. Send it alone to register a wallet without disclosing its address; sent with address, it must be that address's hash. Alias: walletAddressHash. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional addressHash() { + return Json.optional(json, "address_hash", Conv.STRING); + } + + /** + * The chain field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional chain() { + return Json.optional(json, "chain", Conv.STRING); + } + + /** + * The destination tag or memo. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional memo() { + return Json.optional(json, "memo", Conv.STRING); + } + + /** + * The holder_name field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional holderName() { + return Json.optional(json, "holder_name", Conv.STRING); + } + + /** + * Your internal identifier for the holder. Alias: externalUserId. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional holderVendorData() { + return Json.optional(json, "holder_vendor_data", Conv.STRING); + } + + /** + * The entity_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional entityType() { + return Json.optional(json, "entity_type", Conv.enumeration(WalletAddressImportRow.EntityType::of)); + } + + /** + * The label field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional label() { + return Json.optional(json, "label", Conv.STRING); + } + + /** + * The owner_type field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional ownerType() { + return Json.optional(json, "owner_type", Conv.enumeration(WalletAddressImportRow.OwnerType::of)); + } + + /** + * The travel_address field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional travelAddress() { + return Json.optional(json, "travel_address", Conv.STRING); + } + + /** + * Create the entry already verified through a self-declaration, when your settings allow it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional selfDeclared() { + return Json.optional(json, "self_declared", Conv.BOOLEAN); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WalletAddressImportRow}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WalletAddressImportRow from) { + super(from); + } + + /** + * The wallet address. Alias: walletAddress. + * + * @param address the value + * @return this builder + */ + public Builder address(String address) { + return set("address", Conv.STRING, address); + } + + /** + * SHA-256 of the lowercased address, as 64 hex characters or 44 base64 characters. Send it alone to register a wallet without disclosing its address; sent with address, it must be that address's hash. Alias: walletAddressHash. + * + * @param addressHash the value + * @return this builder + */ + public Builder addressHash(String addressHash) { + return set("address_hash", Conv.STRING, addressHash); + } + + /** + * The chain field. + * + * @param chain the value + * @return this builder + */ + public Builder chain(String chain) { + return set("chain", Conv.STRING, chain); + } + + /** + * The destination tag or memo. XRP: a destination tag, a whole number from 0 to 4294967295 (a JSON number is accepted). XLM: a text memo of up to 28 bytes, a 64-bit id or a 32-byte hash in hex. EOS: up to 256 bytes. ATOM: up to 256 characters. HBAR: up to 100 bytes. Bitcoin, EVM, Solana, Tron, Litecoin, Bitcoin Cash, Dogecoin and Lightning wallets take no memo: one there is a 400. + * + * @param memo the value + * @return this builder + */ + public Builder memo(String memo) { + return set("memo", Conv.STRING, memo); + } + + /** + * The holder_name field. + * + * @param holderName the value + * @return this builder + */ + public Builder holderName(String holderName) { + return set("holder_name", Conv.STRING, holderName); + } + + /** + * Your internal identifier for the holder. Alias: externalUserId. + * + * @param holderVendorData the value + * @return this builder + */ + public Builder holderVendorData(String holderVendorData) { + return set("holder_vendor_data", Conv.STRING, holderVendorData); + } + + /** + * The entity_type field. + * + * @param entityType the value + * @return this builder + */ + public Builder entityType(WalletAddressImportRow.EntityType entityType) { + return set("entity_type", Conv.enumeration(WalletAddressImportRow.EntityType::of), entityType); + } + + /** + * The label field. + * + * @param label the value + * @return this builder + */ + public Builder label(String label) { + return set("label", Conv.STRING, label); + } + + /** + * The owner_type field. + * + * @param ownerType the value + * @return this builder + */ + public Builder ownerType(WalletAddressImportRow.OwnerType ownerType) { + return set("owner_type", Conv.enumeration(WalletAddressImportRow.OwnerType::of), ownerType); + } + + /** + * The travel_address field. + * + * @param travelAddress the value + * @return this builder + */ + public Builder travelAddress(String travelAddress) { + return set("travel_address", Conv.STRING, travelAddress); + } + + /** + * Create the entry already verified through a self-declaration, when your settings allow it. + * + * @param selfDeclared the value + * @return this builder + */ + public Builder selfDeclared(boolean selfDeclared) { + return set("self_declared", Conv.BOOLEAN, selfDeclared); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WalletAddressImportRow} + */ + public WalletAddressImportRow build() { + return new WalletAddressImportRow(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class EntityType extends StringEnum { + /** The value {@code "individual"}. */ + public static final EntityType INDIVIDUAL = new EntityType("individual"); + + /** The value {@code "company"}. */ + public static final EntityType COMPANY = new EntityType("company"); + + private static final List KNOWN_VALUES = List.of(INDIVIDUAL, COMPANY); + + private EntityType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static EntityType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, EntityType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class OwnerType extends StringEnum { + /** The value {@code "customer"}. */ + public static final OwnerType CUSTOMER = new OwnerType("customer"); + + /** The value {@code "internal"}. */ + public static final OwnerType INTERNAL = new OwnerType("internal"); + + /** The value {@code "vasp"}. */ + public static final OwnerType VASP = new OwnerType("vasp"); + + private static final List KNOWN_VALUES = List.of(CUSTOMER, INTERNAL, VASP); + + private OwnerType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static OwnerType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, OwnerType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/WalletScreeningScreenParams.java b/src/main/java/me/didit/types/WalletScreeningScreenParams.java index 817f91b..9bd3f88 100644 --- a/src/main/java/me/didit/types/WalletScreeningScreenParams.java +++ b/src/main/java/me/didit/types/WalletScreeningScreenParams.java @@ -61,7 +61,7 @@ public String walletAddress() { } /** - * Network identifier. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism), and ARB (Arbitrum One). USDT and USDC are legacy Ethereum aliases, not multi-network searches. Connected-provider coverage varies. LIGHTNING requires a supported connected provider. Celo, Mode and Plume are not currently supported. Alias: currency. + * Network identifier. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism), and ARB (Arbitrum One). USDT and USDC are legacy Ethereum aliases, not multi-network searches. Connected-provider coverage varies. LIGHTNING requires a supported connected provider. CELO (Celo), MODE (Mode) and PLUME (Plume) are screened through a connected TRM Labs account; managed screening answers 400 for them. Alias: currency. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -104,7 +104,7 @@ public Builder walletAddress(String walletAddress) { } /** - * Network identifier. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism), and ARB (Arbitrum One). USDT and USDC are legacy Ethereum aliases, not multi-network searches. Connected-provider coverage varies. LIGHTNING requires a supported connected provider. Celo, Mode and Plume are not currently supported. Alias: currency. + * Network identifier. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism), and ARB (Arbitrum One). USDT and USDC are legacy Ethereum aliases, not multi-network searches. Connected-provider coverage varies. LIGHTNING requires a supported connected provider. CELO (Celo), MODE (Mode) and PLUME (Plume) are screened through a connected TRM Labs account; managed screening answers 400 for them. Alias: currency. * * @param blockchain the value * @return this builder @@ -142,7 +142,7 @@ protected Builder self() { } /** - * Network identifier. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism), and ARB (Arbitrum One). USDT and USDC are legacy Ethereum aliases, not multi-network searches. Connected-provider coverage varies. LIGHTNING requires a supported connected provider. Celo, Mode and Plume are not currently supported. Alias: currency. + * Network identifier. Managed screening supports BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, BASE (Base), OP (Optimism), and ARB (Arbitrum One). USDT and USDC are legacy Ethereum aliases, not multi-network searches. Connected-provider coverage varies. LIGHTNING requires a supported connected provider. CELO (Celo), MODE (Mode) and PLUME (Plume) are screened through a connected TRM Labs account; managed screening answers 400 for them. Alias: currency. * *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. */ @@ -195,7 +195,16 @@ public static final class Blockchain extends StringEnum { /** The value {@code "ARB"}. */ public static final Blockchain ARB = new Blockchain("ARB"); - private static final List KNOWN_VALUES = List.of(BTC, LIGHTNING, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, USDT, USDC, BASE, OP, ARB); + /** The value {@code "CELO"}. */ + public static final Blockchain CELO = new Blockchain("CELO"); + + /** The value {@code "MODE"}. */ + public static final Blockchain MODE = new Blockchain("MODE"); + + /** The value {@code "PLUME"}. */ + public static final Blockchain PLUME = new Blockchain("PLUME"); + + private static final List KNOWN_VALUES = List.of(BTC, LIGHTNING, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, USDT, USDC, BASE, OP, ARB, CELO, MODE, PLUME); private Blockchain(String value) { super(value); diff --git a/src/main/java/me/didit/types/WebhookConfig.java b/src/main/java/me/didit/types/WebhookConfig.java new file mode 100644 index 0000000..a017f0b --- /dev/null +++ b/src/main/java/me/didit/types/WebhookConfig.java @@ -0,0 +1,534 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * Application-level webhook configuration, retention policy and Digital ID wallet return hosts, with every webhook destination. + */ +public final class WebhookConfig extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("webhook_url", "webhook_version", "secret_shared_key", "capture_method", "data_retention_months", "face_retention_policy", "face_retention_days", "available_events", "allowed_return_hosts", "endpoints"); + + private WebhookConfig(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhookConfig} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhookConfig fromJson(JsonNode json) { + return new WebhookConfig(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Application-level (legacy) webhook URL. Didit sends to it only while the application has no enabled destination, and then only status.updated and data.updated. Every create, update or delete of a destination sets it to the URL of the oldest enabled destination, or null when none is enabled. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional webhookUrl() { + return Json.optional(json, "webhook_url", Conv.STRING); + } + + /** + * Payload version of the legacy webhook_url. Each destination has its own webhook_version. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookConfig.WebhookVersion webhookVersion() { + return Json.required(json, "webhook_version", Conv.enumeration(WebhookConfig.WebhookVersion::of)); + } + + /** + * Application-level signing secret (43-character URL-safe token). It signs only deliveries to the legacy webhook_url; each destination signs with its own secret_shared_key. API-key callers always receive it in full; Console users without read:webhooks receive an empty string. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String secretSharedKey() { + return Json.required(json, "secret_shared_key", Conv.STRING); + } + + /** + * Legacy application setting. The verification flow does not read it: capture options are configured per workflow. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookConfig.CaptureMethod captureMethod() { + return Json.required(json, "capture_method", Conv.enumeration(WebhookConfig.CaptureMethod::of)); + } + + /** + * Automatic deletion window. User verification sessions created more than this many months ago (a month counts as 30 days) are deleted automatically, and that deletion is irreversible. null means sessions are never deleted automatically. See Data retention. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataRetentionMonths() { + return Json.optional(json, "data_retention_months", Conv.LONG); + } + + /** + * What happens to a session's face embedding when the session is deleted. delete_with_session (default) deletes it with the session. retain_until_user_deleted is the opt-in biometric template retention: an image-free biometric template linked to the User is managed separately until its retention period ends, the User is deleted, or you purge it. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookConfig.FaceRetentionPolicy faceRetentionPolicy() { + return Json.required(json, "face_retention_policy", Conv.enumeration(WebhookConfig.FaceRetentionPolicy::of)); + } + + /** + * Retention period, in days, of biometric templates under retain_until_user_deleted. Required when that policy is set. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional faceRetentionDays() { + return Json.optional(json, "face_retention_days", Conv.LONG); + } + + /** + * Every event type a destination can subscribe to, with a display label. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List availableEvents() { + return Json.required(json, "available_events", Conv.list(Conv.model(WebhookConfig.AvailableEvents::fromJson))); + } + + /** + * Hostnames (exact match, HTTPS only) a standalone Digital ID wallet verification may send the user back to with its return_url. Your white-label domain is always allowed as well. See Register your return destination. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List allowedReturnHosts() { + return Json.required(json, "allowed_return_hosts", Conv.list(Conv.STRING)); + } + + /** + * Every webhook destination of the application, oldest first, each with its signing secret. Read only here: manage destinations with the /v3/webhook/destinations/ endpoints. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List endpoints() { + return Json.required(json, "endpoints", Conv.list(Conv.model(WebhookConfigDestination::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhookConfig}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhookConfig from) { + super(from); + } + + /** + * Application-level (legacy) webhook URL. Didit sends to it only while the application has no enabled destination, and then only status.updated and data.updated. Every create, update or delete of a destination sets it to the URL of the oldest enabled destination, or null when none is enabled. + * + * @param webhookUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder webhookUrl(@Nullable String webhookUrl) { + return setNullable("webhook_url", Conv.STRING, webhookUrl); + } + + /** + * Payload version of the legacy webhook_url. Each destination has its own webhook_version. + * + * @param webhookVersion the value + * @return this builder + */ + public Builder webhookVersion(WebhookConfig.WebhookVersion webhookVersion) { + return set("webhook_version", Conv.enumeration(WebhookConfig.WebhookVersion::of), webhookVersion); + } + + /** + * Application-level signing secret (43-character URL-safe token). It signs only deliveries to the legacy webhook_url; each destination signs with its own secret_shared_key. API-key callers always receive it in full; Console users without read:webhooks receive an empty string. + * + * @param secretSharedKey the value + * @return this builder + */ + public Builder secretSharedKey(String secretSharedKey) { + return set("secret_shared_key", Conv.STRING, secretSharedKey); + } + + /** + * Legacy application setting. The verification flow does not read it: capture options are configured per workflow. + * + * @param captureMethod the value + * @return this builder + */ + public Builder captureMethod(WebhookConfig.CaptureMethod captureMethod) { + return set("capture_method", Conv.enumeration(WebhookConfig.CaptureMethod::of), captureMethod); + } + + /** + * Automatic deletion window. User verification sessions created more than this many months ago (a month counts as 30 days) are deleted automatically, and that deletion is irreversible. null means sessions are never deleted automatically. See Data retention. + * + * @param dataRetentionMonths the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dataRetentionMonths(@Nullable Long dataRetentionMonths) { + return setNullable("data_retention_months", Conv.LONG, dataRetentionMonths); + } + + /** + * What happens to a session's face embedding when the session is deleted. delete_with_session (default) deletes it with the session. retain_until_user_deleted is the opt-in biometric template retention: an image-free biometric template linked to the User is managed separately until its retention period ends, the User is deleted, or you purge it. + * + * @param faceRetentionPolicy the value + * @return this builder + */ + public Builder faceRetentionPolicy(WebhookConfig.FaceRetentionPolicy faceRetentionPolicy) { + return set("face_retention_policy", Conv.enumeration(WebhookConfig.FaceRetentionPolicy::of), faceRetentionPolicy); + } + + /** + * Retention period, in days, of biometric templates under retain_until_user_deleted. Required when that policy is set. + * + * @param faceRetentionDays the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder faceRetentionDays(@Nullable Long faceRetentionDays) { + return setNullable("face_retention_days", Conv.LONG, faceRetentionDays); + } + + /** + * Every event type a destination can subscribe to, with a display label. + * + * @param availableEvents the value + * @return this builder + */ + public Builder availableEvents(List availableEvents) { + return set("available_events", Conv.list(Conv.model(WebhookConfig.AvailableEvents::fromJson)), availableEvents); + } + + /** + * Hostnames (exact match, HTTPS only) a standalone Digital ID wallet verification may send the user back to with its return_url. Your white-label domain is always allowed as well. See Register your return destination. + * + * @param allowedReturnHosts the value + * @return this builder + */ + public Builder allowedReturnHosts(List allowedReturnHosts) { + return set("allowed_return_hosts", Conv.list(Conv.STRING), allowedReturnHosts); + } + + /** + * Every webhook destination of the application, oldest first, each with its signing secret. Read only here: manage destinations with the /v3/webhook/destinations/ endpoints. + * + * @param endpoints the value + * @return this builder + */ + public Builder endpoints(List endpoints) { + return set("endpoints", Conv.list(Conv.model(WebhookConfigDestination::fromJson)), endpoints); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhookConfig} + * @throws IllegalStateException when a required field is missing + */ + public WebhookConfig build() { + require("webhook_url", true); + require("webhook_version", false); + require("secret_shared_key", false); + require("capture_method", false); + require("data_retention_months", true); + require("face_retention_policy", false); + require("face_retention_days", true); + require("available_events", false); + require("allowed_return_hosts", false); + require("endpoints", false); + return new WebhookConfig(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Payload version of the legacy webhook_url. Each destination has its own webhook_version. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookVersion extends StringEnum { + /** The value {@code "v1"}. */ + public static final WebhookVersion V_1 = new WebhookVersion("v1"); + + /** The value {@code "v2"}. */ + public static final WebhookVersion V_2 = new WebhookVersion("v2"); + + /** The value {@code "v3"}. */ + public static final WebhookVersion V_3 = new WebhookVersion("v3"); + + private static final List KNOWN_VALUES = List.of(V_1, V_2, V_3); + + private WebhookVersion(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookVersion of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookVersion::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Legacy application setting. The verification flow does not read it: capture options are configured per workflow. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class CaptureMethod extends StringEnum { + /** The value {@code "MOBILE"}. */ + public static final CaptureMethod MOBILE = new CaptureMethod("MOBILE"); + + /** The value {@code "MOBILE_AND_DESKTOP"}. */ + public static final CaptureMethod MOBILE_AND_DESKTOP = new CaptureMethod("MOBILE_AND_DESKTOP"); + + private static final List KNOWN_VALUES = List.of(MOBILE, MOBILE_AND_DESKTOP); + + private CaptureMethod(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static CaptureMethod of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, CaptureMethod::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * What happens to a session's face embedding when the session is deleted. delete_with_session (default) deletes it with the session. retain_until_user_deleted is the opt-in biometric template retention: an image-free biometric template linked to the User is managed separately until its retention period ends, the User is deleted, or you purge it. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class FaceRetentionPolicy extends StringEnum { + /** The value {@code "delete_with_session"}. */ + public static final FaceRetentionPolicy DELETE_WITH_SESSION = new FaceRetentionPolicy("delete_with_session"); + + /** The value {@code "retain_until_user_deleted"}. */ + public static final FaceRetentionPolicy RETAIN_UNTIL_USER_DELETED = new FaceRetentionPolicy("retain_until_user_deleted"); + + private static final List KNOWN_VALUES = List.of(DELETE_WITH_SESSION, RETAIN_UNTIL_USER_DELETED); + + private FaceRetentionPolicy(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static FaceRetentionPolicy of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, FaceRetentionPolicy::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class AvailableEvents extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("value", "label"); + + private AvailableEvents(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code AvailableEvents} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static AvailableEvents fromJson(JsonNode json) { + return new AvailableEvents(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEvent value() { + return Json.required(json, "value", Conv.enumeration(WebhookEvent::of)); + } + + /** + * Display label, for example Session status updated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String label() { + return Json.required(json, "label", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link AvailableEvents}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(AvailableEvents from) { + super(from); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @param value the value + * @return this builder + */ + public Builder value(WebhookEvent value) { + return set("value", Conv.enumeration(WebhookEvent::of), value); + } + + /** + * Display label, for example Session status updated. + * + * @param label the value + * @return this builder + */ + public Builder label(String label) { + return set("label", Conv.STRING, label); + } + + /** + * Builds the value. + * + * @return a new immutable {@code AvailableEvents} + * @throws IllegalStateException when a required field is missing + */ + public AvailableEvents build() { + require("value", false); + require("label", false); + return new AvailableEvents(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/WebhookConfigDestination.java b/src/main/java/me/didit/types/WebhookConfigDestination.java new file mode 100644 index 0000000..997b494 --- /dev/null +++ b/src/main/java/me/didit/types/WebhookConfigDestination.java @@ -0,0 +1,318 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * A webhook destination as listed in the webhook configuration, without delivery metrics. GET /v3/webhook/destinations/{destination_uuid}/ adds its delivery summary. + */ +public final class WebhookConfigDestination extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "url", "enabled", "webhook_version", "secret_shared_key", "subscribed_events", "created_at", "updated_at"); + + private WebhookConfigDestination(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhookConfigDestination} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhookConfigDestination fromJson(JsonNode json) { + return new WebhookConfigDestination(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Destination ID. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * Human-readable name. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String label() { + return Json.required(json, "label", Conv.STRING); + } + + /** + * HTTPS endpoint Didit posts events to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String url() { + return Json.required(json, "url", Conv.STRING); + } + + /** + * false pauses deliveries to this destination. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public boolean enabled() { + return Json.required(json, "enabled", Conv.BOOLEAN); + } + + /** + * Payload version sent to this destination. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookConfigDestination.WebhookVersion webhookVersion() { + return Json.required(json, "webhook_version", Conv.enumeration(WebhookConfigDestination.WebhookVersion::of)); + } + + /** + * This destination's signing secret (43-character URL-safe token). Verify each webhook with the secret of the destination it was sent to. API-key callers always receive it in full; Console users without read:webhooks receive an empty string. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String secretSharedKey() { + return Json.required(json, "secret_shared_key", Conv.STRING); + } + + /** + * Event types delivered to this destination. Empty when the destination was created without events, in which case it receives nothing. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List subscribedEvents() { + return Json.required(json, "subscribed_events", Conv.list(Conv.enumeration(WebhookEvent::of))); + } + + /** + * The created_at field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + /** + * The updated_at field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime updatedAt() { + return Json.required(json, "updated_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhookConfigDestination}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhookConfigDestination from) { + super(from); + } + + /** + * Destination ID. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Human-readable name. + * + * @param label the value + * @return this builder + */ + public Builder label(String label) { + return set("label", Conv.STRING, label); + } + + /** + * HTTPS endpoint Didit posts events to. + * + * @param url the value + * @return this builder + */ + public Builder url(String url) { + return set("url", Conv.STRING, url); + } + + /** + * false pauses deliveries to this destination. + * + * @param enabled the value + * @return this builder + */ + public Builder enabled(boolean enabled) { + return set("enabled", Conv.BOOLEAN, enabled); + } + + /** + * Payload version sent to this destination. + * + * @param webhookVersion the value + * @return this builder + */ + public Builder webhookVersion(WebhookConfigDestination.WebhookVersion webhookVersion) { + return set("webhook_version", Conv.enumeration(WebhookConfigDestination.WebhookVersion::of), webhookVersion); + } + + /** + * This destination's signing secret (43-character URL-safe token). Verify each webhook with the secret of the destination it was sent to. API-key callers always receive it in full; Console users without read:webhooks receive an empty string. + * + * @param secretSharedKey the value + * @return this builder + */ + public Builder secretSharedKey(String secretSharedKey) { + return set("secret_shared_key", Conv.STRING, secretSharedKey); + } + + /** + * Event types delivered to this destination. Empty when the destination was created without events, in which case it receives nothing. + * + * @param subscribedEvents the value + * @return this builder + */ + public Builder subscribedEvents(List subscribedEvents) { + return set("subscribed_events", Conv.list(Conv.enumeration(WebhookEvent::of)), subscribedEvents); + } + + /** + * The created_at field. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * The updated_at field. + * + * @param updatedAt the value + * @return this builder + */ + public Builder updatedAt(OffsetDateTime updatedAt) { + return set("updated_at", Conv.DATE_TIME, updatedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhookConfigDestination} + * @throws IllegalStateException when a required field is missing + */ + public WebhookConfigDestination build() { + require("uuid", false); + require("label", false); + require("url", false); + require("enabled", false); + require("webhook_version", false); + require("secret_shared_key", false); + require("subscribed_events", false); + require("created_at", false); + require("updated_at", false); + return new WebhookConfigDestination(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Payload version sent to this destination. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookVersion extends StringEnum { + /** The value {@code "v1"}. */ + public static final WebhookVersion V_1 = new WebhookVersion("v1"); + + /** The value {@code "v2"}. */ + public static final WebhookVersion V_2 = new WebhookVersion("v2"); + + /** The value {@code "v3"}. */ + public static final WebhookVersion V_3 = new WebhookVersion("v3"); + + private static final List KNOWN_VALUES = List.of(V_1, V_2, V_3); + + private WebhookVersion(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookVersion of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookVersion::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/WebhookDestinationDelivery.java b/src/main/java/me/didit/types/WebhookDestinationDelivery.java new file mode 100644 index 0000000..36c8c55 --- /dev/null +++ b/src/main/java/me/didit/types/WebhookDestinationDelivery.java @@ -0,0 +1,252 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** + * One delivery attempt to a webhook destination. The first send of an event, each automatic retry and each resend are separate deliveries. + */ +public final class WebhookDestinationDelivery extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "session_id", "request_url", "response_status", "response_time_ms", "status", "webhook_type", "created_at"); + + private WebhookDestinationDelivery(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhookDestinationDelivery} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhookDestinationDelivery fromJson(JsonNode json) { + return new WebhookDestinationDelivery(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Delivery ID, for the detail and resend calls. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * User verification session the event is about. null for business session, user, business, transaction, Travel Rule and workflow events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * URL the delivery was sent to: the destination's URL when the event was generated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional requestUrl() { + return Json.optional(json, "request_url", Conv.STRING); + } + + /** + * HTTP status your endpoint returned. null while the delivery is queued and not yet attempted. When no HTTP answer was received Didit records a status of its own: 504 when your endpoint did not answer within 5 seconds, 503 for a connection failure, 400 when the URL was refused as unsafe (for example a private or internal address), and 404 for any other request error. 400 or above counts as a failed delivery. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional responseStatus() { + return Json.optional(json, "response_status", Conv.LONG); + } + + /** + * Time the attempt took, in milliseconds. null while the delivery is queued. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional responseTimeMs() { + return Json.optional(json, "response_time_ms", Conv.LONG); + } + + /** + * Status the event reported when it was generated: the session status for session events (for example Approved or In Review), the person's or company's status for user and business events, and the transaction status for transaction events. null for events that carry no status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEvent webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(WebhookEvent::of)); + } + + /** + * When the delivery was created. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhookDestinationDelivery}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhookDestinationDelivery from) { + super(from); + } + + /** + * Delivery ID, for the detail and resend calls. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * User verification session the event is about. null for business session, user, business, transaction, Travel Rule and workflow events. + * + * @param sessionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionId(@Nullable String sessionId) { + return setNullable("session_id", Conv.STRING, sessionId); + } + + /** + * URL the delivery was sent to: the destination's URL when the event was generated. + * + * @param requestUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder requestUrl(@Nullable String requestUrl) { + return setNullable("request_url", Conv.STRING, requestUrl); + } + + /** + * HTTP status your endpoint returned. null while the delivery is queued and not yet attempted. When no HTTP answer was received Didit records a status of its own: 504 when your endpoint did not answer within 5 seconds, 503 for a connection failure, 400 when the URL was refused as unsafe (for example a private or internal address), and 404 for any other request error. 400 or above counts as a failed delivery. + * + * @param responseStatus the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder responseStatus(@Nullable Long responseStatus) { + return setNullable("response_status", Conv.LONG, responseStatus); + } + + /** + * Time the attempt took, in milliseconds. null while the delivery is queued. + * + * @param responseTimeMs the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder responseTimeMs(@Nullable Long responseTimeMs) { + return setNullable("response_time_ms", Conv.LONG, responseTimeMs); + } + + /** + * Status the event reported when it was generated: the session status for session events (for example Approved or In Review), the person's or company's status for user and business events, and the transaction status for transaction events. null for events that carry no status. + * + * @param status the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder status(@Nullable String status) { + return setNullable("status", Conv.STRING, status); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(WebhookEvent webhookType) { + return set("webhook_type", Conv.enumeration(WebhookEvent::of), webhookType); + } + + /** + * When the delivery was created. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhookDestinationDelivery} + * @throws IllegalStateException when a required field is missing + */ + public WebhookDestinationDelivery build() { + require("uuid", false); + require("session_id", true); + require("request_url", true); + require("response_status", true); + require("response_time_ms", true); + require("status", true); + require("webhook_type", false); + require("created_at", false); + return new WebhookDestinationDelivery(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/WebhookDestinationDeliveryDetail.java b/src/main/java/me/didit/types/WebhookDestinationDeliveryDetail.java new file mode 100644 index 0000000..b0c567f --- /dev/null +++ b/src/main/java/me/didit/types/WebhookDestinationDeliveryDetail.java @@ -0,0 +1,294 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +/** + * One delivery attempt to a webhook destination, with the exact request headers and body Didit sent. + */ +public final class WebhookDestinationDeliveryDetail extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "session_id", "request_url", "response_status", "response_time_ms", "status", "webhook_type", "created_at", "request_header", "request_body"); + + private WebhookDestinationDeliveryDetail(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhookDestinationDeliveryDetail} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhookDestinationDeliveryDetail fromJson(JsonNode json) { + return new WebhookDestinationDeliveryDetail(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Delivery ID, for the detail and resend calls. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String uuid() { + return Json.required(json, "uuid", Conv.STRING); + } + + /** + * User verification session the event is about. null for business session, user, business, transaction, Travel Rule and workflow events. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionId() { + return Json.optional(json, "session_id", Conv.STRING); + } + + /** + * URL the delivery was sent to: the destination's URL when the event was generated. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional requestUrl() { + return Json.optional(json, "request_url", Conv.STRING); + } + + /** + * HTTP status your endpoint returned. null while the delivery is queued and not yet attempted. When no HTTP answer was received Didit records a status of its own: 504 when your endpoint did not answer within 5 seconds, 503 for a connection failure, 400 when the URL was refused as unsafe (for example a private or internal address), and 404 for any other request error. 400 or above counts as a failed delivery. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional responseStatus() { + return Json.optional(json, "response_status", Conv.LONG); + } + + /** + * Time the attempt took, in milliseconds. null while the delivery is queued. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional responseTimeMs() { + return Json.optional(json, "response_time_ms", Conv.LONG); + } + + /** + * Status the event reported when it was generated: the session status for session events (for example Approved or In Review), the person's or company's status for user and business events, and the transaction status for transaction events. null for events that carry no status. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.STRING); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEvent webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(WebhookEvent::of)); + } + + /** + * When the delivery was created. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public OffsetDateTime createdAt() { + return Json.required(json, "created_at", Conv.DATE_TIME); + } + + /** + * JSON text (a string) of the request headers Didit sent: Content-Type, X-Signature, X-Signature-V2, X-Signature-Simple, X-Timestamp and User-Agent. Parse it with a JSON parser. It holds signatures, never the signing secret. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String requestHeader() { + return Json.required(json, "request_header", Conv.STRING); + } + + /** + * JSON text (a string) of the exact payload Didit sent, keys sorted, including event_id and timestamp. Parse it with a JSON parser; see Payload shape. For a scoped key without media: read, media URLs inside it are null; without sessions: write, session tokens and hosted verification links are null. A body changed that way no longer matches its signatures. Didit does not record the response body your endpoint returned. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String requestBody() { + return Json.required(json, "request_body", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhookDestinationDeliveryDetail}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhookDestinationDeliveryDetail from) { + super(from); + } + + /** + * Delivery ID, for the detail and resend calls. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * User verification session the event is about. null for business session, user, business, transaction, Travel Rule and workflow events. + * + * @param sessionId the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder sessionId(@Nullable String sessionId) { + return setNullable("session_id", Conv.STRING, sessionId); + } + + /** + * URL the delivery was sent to: the destination's URL when the event was generated. + * + * @param requestUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder requestUrl(@Nullable String requestUrl) { + return setNullable("request_url", Conv.STRING, requestUrl); + } + + /** + * HTTP status your endpoint returned. null while the delivery is queued and not yet attempted. When no HTTP answer was received Didit records a status of its own: 504 when your endpoint did not answer within 5 seconds, 503 for a connection failure, 400 when the URL was refused as unsafe (for example a private or internal address), and 404 for any other request error. 400 or above counts as a failed delivery. + * + * @param responseStatus the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder responseStatus(@Nullable Long responseStatus) { + return setNullable("response_status", Conv.LONG, responseStatus); + } + + /** + * Time the attempt took, in milliseconds. null while the delivery is queued. + * + * @param responseTimeMs the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder responseTimeMs(@Nullable Long responseTimeMs) { + return setNullable("response_time_ms", Conv.LONG, responseTimeMs); + } + + /** + * Status the event reported when it was generated: the session status for session events (for example Approved or In Review), the person's or company's status for user and business events, and the transaction status for transaction events. null for events that carry no status. + * + * @param status the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder status(@Nullable String status) { + return setNullable("status", Conv.STRING, status); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(WebhookEvent webhookType) { + return set("webhook_type", Conv.enumeration(WebhookEvent::of), webhookType); + } + + /** + * When the delivery was created. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * JSON text (a string) of the request headers Didit sent: Content-Type, X-Signature, X-Signature-V2, X-Signature-Simple, X-Timestamp and User-Agent. Parse it with a JSON parser. It holds signatures, never the signing secret. + * + * @param requestHeader the value + * @return this builder + */ + public Builder requestHeader(String requestHeader) { + return set("request_header", Conv.STRING, requestHeader); + } + + /** + * JSON text (a string) of the exact payload Didit sent, keys sorted, including event_id and timestamp. Parse it with a JSON parser; see Payload shape. For a scoped key without media: read, media URLs inside it are null; without sessions: write, session tokens and hosted verification links are null. A body changed that way no longer matches its signatures. Didit does not record the response body your endpoint returned. + * + * @param requestBody the value + * @return this builder + */ + public Builder requestBody(String requestBody) { + return set("request_body", Conv.STRING, requestBody); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhookDestinationDeliveryDetail} + * @throws IllegalStateException when a required field is missing + */ + public WebhookDestinationDeliveryDetail build() { + require("uuid", false); + require("session_id", true); + require("request_url", true); + require("response_status", true); + require("response_time_ms", true); + require("status", true); + require("webhook_type", false); + require("created_at", false); + require("request_header", false); + require("request_body", false); + return new WebhookDestinationDeliveryDetail(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/WebhookEvent.java b/src/main/java/me/didit/types/WebhookEvent.java index 6e38886..4dc9ed9 100644 --- a/src/main/java/me/didit/types/WebhookEvent.java +++ b/src/main/java/me/didit/types/WebhookEvent.java @@ -40,7 +40,70 @@ public final class WebhookEvent extends StringEnum { /** The value {@code "travel_rule.status.updated"}. */ public static final WebhookEvent TRAVEL_RULE_STATUS_UPDATED = new WebhookEvent("travel_rule.status.updated"); - private static final List KNOWN_VALUES = List.of(STATUS_UPDATED, DATA_UPDATED, USER_STATUS_UPDATED, USER_DATA_UPDATED, BUSINESS_STATUS_UPDATED, BUSINESS_DATA_UPDATED, ACTIVITY_CREATED, TRANSACTION_CREATED, TRANSACTION_STATUS_UPDATED, TRAVEL_RULE_STATUS_UPDATED); + /** The value {@code "workflow.compliance_changed"}. */ + public static final WebhookEvent WORKFLOW_COMPLIANCE_CHANGED = new WebhookEvent("workflow.compliance_changed"); + + /** The value {@code "auth.signup"}. */ + public static final WebhookEvent AUTH_SIGNUP = new WebhookEvent("auth.signup"); + + /** The value {@code "auth.login"}. */ + public static final WebhookEvent AUTH_LOGIN = new WebhookEvent("auth.login"); + + /** The value {@code "auth.login_failed"}. */ + public static final WebhookEvent AUTH_LOGIN_FAILED = new WebhookEvent("auth.login_failed"); + + /** The value {@code "auth.password_reset_requested"}. */ + public static final WebhookEvent AUTH_PASSWORD_RESET_REQUESTED = new WebhookEvent("auth.password_reset_requested"); + + /** The value {@code "auth.password_reset_completed"}. */ + public static final WebhookEvent AUTH_PASSWORD_RESET_COMPLETED = new WebhookEvent("auth.password_reset_completed"); + + /** The value {@code "auth.mfa_changed"}. */ + public static final WebhookEvent AUTH_MFA_CHANGED = new WebhookEvent("auth.mfa_changed"); + + /** The value {@code "auth.new_device"}. */ + public static final WebhookEvent AUTH_NEW_DEVICE = new WebhookEvent("auth.new_device"); + + /** The value {@code "auth.account_locked"}. */ + public static final WebhookEvent AUTH_ACCOUNT_LOCKED = new WebhookEvent("auth.account_locked"); + + /** The value {@code "investigator.report.ready"}. */ + public static final WebhookEvent INVESTIGATOR_REPORT_READY = new WebhookEvent("investigator.report.ready"); + + /** The value {@code "investigator.decision.automated"}. */ + public static final WebhookEvent INVESTIGATOR_DECISION_AUTOMATED = new WebhookEvent("investigator.decision.automated"); + + /** The value {@code "investigator.decision.overridden"}. */ + public static final WebhookEvent INVESTIGATOR_DECISION_OVERRIDDEN = new WebhookEvent("investigator.decision.overridden"); + + /** The value {@code "investigator.decision.contested"}. */ + public static final WebhookEvent INVESTIGATOR_DECISION_CONTESTED = new WebhookEvent("investigator.decision.contested"); + + /** The value {@code "investigator.question.raised"}. */ + public static final WebhookEvent INVESTIGATOR_QUESTION_RAISED = new WebhookEvent("investigator.question.raised"); + + /** The value {@code "investigator.automation.paused"}. */ + public static final WebhookEvent INVESTIGATOR_AUTOMATION_PAUSED = new WebhookEvent("investigator.automation.paused"); + + /** The value {@code "investigator.rfi.answered"}. */ + public static final WebhookEvent INVESTIGATOR_RFI_ANSWERED = new WebhookEvent("investigator.rfi.answered"); + + /** The value {@code "investigator.rfi.overdue"}. */ + public static final WebhookEvent INVESTIGATOR_RFI_OVERDUE = new WebhookEvent("investigator.rfi.overdue"); + + /** The value {@code "investigator.drift.detected"}. */ + public static final WebhookEvent INVESTIGATOR_DRIFT_DETECTED = new WebhookEvent("investigator.drift.detected"); + + /** The value {@code "investigator.sla.breached"}. */ + public static final WebhookEvent INVESTIGATOR_SLA_BREACHED = new WebhookEvent("investigator.sla.breached"); + + /** The value {@code "investigator.qa.failed"}. */ + public static final WebhookEvent INVESTIGATOR_QA_FAILED = new WebhookEvent("investigator.qa.failed"); + + /** The value {@code "investigator.external.report.ready"}. */ + public static final WebhookEvent INVESTIGATOR_EXTERNAL_REPORT_READY = new WebhookEvent("investigator.external.report.ready"); + + private static final List KNOWN_VALUES = List.of(STATUS_UPDATED, DATA_UPDATED, USER_STATUS_UPDATED, USER_DATA_UPDATED, BUSINESS_STATUS_UPDATED, BUSINESS_DATA_UPDATED, ACTIVITY_CREATED, TRANSACTION_CREATED, TRANSACTION_STATUS_UPDATED, TRAVEL_RULE_STATUS_UPDATED, WORKFLOW_COMPLIANCE_CHANGED, AUTH_SIGNUP, AUTH_LOGIN, AUTH_LOGIN_FAILED, AUTH_PASSWORD_RESET_REQUESTED, AUTH_PASSWORD_RESET_COMPLETED, AUTH_MFA_CHANGED, AUTH_NEW_DEVICE, AUTH_ACCOUNT_LOCKED, INVESTIGATOR_REPORT_READY, INVESTIGATOR_DECISION_AUTOMATED, INVESTIGATOR_DECISION_OVERRIDDEN, INVESTIGATOR_DECISION_CONTESTED, INVESTIGATOR_QUESTION_RAISED, INVESTIGATOR_AUTOMATION_PAUSED, INVESTIGATOR_RFI_ANSWERED, INVESTIGATOR_RFI_OVERDUE, INVESTIGATOR_DRIFT_DETECTED, INVESTIGATOR_SLA_BREACHED, INVESTIGATOR_QA_FAILED, INVESTIGATOR_EXTERNAL_REPORT_READY); private WebhookEvent(String value) { super(value); diff --git a/src/main/java/me/didit/types/WebhookEventEnvelope.java b/src/main/java/me/didit/types/WebhookEventEnvelope.java new file mode 100644 index 0000000..9702042 --- /dev/null +++ b/src/main/java/me/didit/types/WebhookEventEnvelope.java @@ -0,0 +1,251 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; + +/** + * Fields Didit adds to every signed webhook delivery, whatever the event type. Read webhook_type to know which payload you received. + */ +public final class WebhookEventEnvelope extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "webhook_type", "timestamp", "created_at", "application_id", "environment"); + + private WebhookEventEnvelope(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhookEventEnvelope} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhookEventEnvelope fromJson(JsonNode json) { + return new WebhookEventEnvelope(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEvent webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(WebhookEvent::of)); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhookEventEnvelope}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhookEventEnvelope from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(WebhookEvent webhookType) { + return set("webhook_type", Conv.enumeration(WebhookEvent::of), webhookType); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhookEventEnvelope} + * @throws IllegalStateException when a required field is missing + */ + public WebhookEventEnvelope build() { + require("event_id", false); + require("webhook_type", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + return new WebhookEventEnvelope(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Environment extends StringEnum { + /** The value {@code "live"}. */ + public static final Environment LIVE = new Environment("live"); + + /** The value {@code "sandbox"}. */ + public static final Environment SANDBOX = new Environment("sandbox"); + + private static final List KNOWN_VALUES = List.of(LIVE, SANDBOX); + + private Environment(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Environment of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Environment::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/WebhookEventPayload.java b/src/main/java/me/didit/types/WebhookEventPayload.java new file mode 100644 index 0000000..dfcc11c --- /dev/null +++ b/src/main/java/me/didit/types/WebhookEventPayload.java @@ -0,0 +1,380 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.Optional; +import me.didit.core.Conv; +import me.didit.core.Json; +import me.didit.core.JsonUnion; + +/** + * Body of a signed webhook delivery, discriminated by webhook_type. activity.created can be subscribed to but is not sent today. Treat any other webhook_type as a new event type and keep the raw body. + * + *

One of: {@code SessionStatusUpdatedEvent}, {@code SessionDataUpdatedEvent}, {@code UserStatusUpdatedEvent}, {@code UserDataUpdatedEvent}, {@code BusinessStatusUpdatedEvent}, {@code BusinessDataUpdatedEvent}, {@code TransactionCreatedEvent}, {@code TransactionStatusUpdatedEvent}, {@code TravelRuleStatusUpdatedEvent}, {@code WorkflowComplianceChangedEvent}. Check with the {@code is...} + * methods and read with the {@code as...} methods; the fields every variant has are readable directly. + */ +public final class WebhookEventPayload extends JsonUnion { + private WebhookEventPayload(JsonNode json) { + super(json); + } + + /** + * Reads a {@code WebhookEventPayload} from JSON. + * + * @param json any JSON value + * @return the union + */ + public static WebhookEventPayload fromJson(JsonNode json) { + return new WebhookEventPayload(json.deepCopy()); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * Wraps a {@code SessionStatusUpdatedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofSessionStatusUpdatedEvent(SessionStatusUpdatedEvent value) { + return new WebhookEventPayload(Conv.model(SessionStatusUpdatedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code SessionStatusUpdatedEvent}. + * + * A verification session changed status. decision is sent only for Approved, Declined, In Review and Abandoned; it has the shape of GET /v3/session/{sessionId}/decision/ for destinations on webhook version 3 (the default), and the older reduced shape for version 1 or 2 destinations. Business sessions always receive the version 3 business decision. + * + * @return true when it matches + */ + public boolean isSessionStatusUpdatedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code SessionStatusUpdatedEvent}. + * + * @return the value, or empty when {@link #isSessionStatusUpdatedEvent()} is false + */ + public Optional asSessionStatusUpdatedEvent() { + return isSessionStatusUpdatedEvent() ? Optional.of(Conv.model(SessionStatusUpdatedEvent::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code SessionDataUpdatedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofSessionDataUpdatedEvent(SessionDataUpdatedEvent value) { + return new WebhookEventPayload(Conv.model(SessionDataUpdatedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code SessionDataUpdatedEvent}. + * + * Data of a verification session changed without a status change, for example after a manual review edit, a step override or an ongoing-monitoring rescreen. decision is sent only for Approved, Declined, In Review and Abandoned; it has the shape of GET /v3/session/{sessionId}/decision/ for destinations on webhook version 3 (the default), and the older reduced shape for version 1 or 2 destinations. Business sessions always receive the version 3 business decision. + * + * @return true when it matches + */ + public boolean isSessionDataUpdatedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code SessionDataUpdatedEvent}. + * + * @return the value, or empty when {@link #isSessionDataUpdatedEvent()} is false + */ + public Optional asSessionDataUpdatedEvent() { + return isSessionDataUpdatedEvent() ? Optional.of(Conv.model(SessionDataUpdatedEvent::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code UserStatusUpdatedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofUserStatusUpdatedEvent(UserStatusUpdatedEvent value) { + return new WebhookEventPayload(Conv.model(UserStatusUpdatedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code UserStatusUpdatedEvent}. + * + * A user changed status. Not sent when the user is created. + * + * @return true when it matches + */ + public boolean isUserStatusUpdatedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code UserStatusUpdatedEvent}. + * + * @return the value, or empty when {@link #isUserStatusUpdatedEvent()} is false + */ + public Optional asUserStatusUpdatedEvent() { + return isUserStatusUpdatedEvent() ? Optional.of(Conv.model(UserStatusUpdatedEvent::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code UserDataUpdatedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofUserDataUpdatedEvent(UserDataUpdatedEvent value) { + return new WebhookEventPayload(Conv.model(UserDataUpdatedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code UserDataUpdatedEvent}. + * + * Fields of a user changed. Not sent when the user is created. One save can produce both a status and a data event. + * + * @return true when it matches + */ + public boolean isUserDataUpdatedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code UserDataUpdatedEvent}. + * + * @return the value, or empty when {@link #isUserDataUpdatedEvent()} is false + */ + public Optional asUserDataUpdatedEvent() { + return isUserDataUpdatedEvent() ? Optional.of(Conv.model(UserDataUpdatedEvent::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code BusinessStatusUpdatedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofBusinessStatusUpdatedEvent(BusinessStatusUpdatedEvent value) { + return new WebhookEventPayload(Conv.model(BusinessStatusUpdatedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code BusinessStatusUpdatedEvent}. + * + * A business changed status. Not sent when the business is created. + * + * @return true when it matches + */ + public boolean isBusinessStatusUpdatedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code BusinessStatusUpdatedEvent}. + * + * @return the value, or empty when {@link #isBusinessStatusUpdatedEvent()} is false + */ + public Optional asBusinessStatusUpdatedEvent() { + return isBusinessStatusUpdatedEvent() ? Optional.of(Conv.model(BusinessStatusUpdatedEvent::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code BusinessDataUpdatedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofBusinessDataUpdatedEvent(BusinessDataUpdatedEvent value) { + return new WebhookEventPayload(Conv.model(BusinessDataUpdatedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code BusinessDataUpdatedEvent}. + * + * Fields of a business changed. Not sent when the business is created. One save can produce both a status and a data event. + * + * @return true when it matches + */ + public boolean isBusinessDataUpdatedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code BusinessDataUpdatedEvent}. + * + * @return the value, or empty when {@link #isBusinessDataUpdatedEvent()} is false + */ + public Optional asBusinessDataUpdatedEvent() { + return isBusinessDataUpdatedEvent() ? Optional.of(Conv.model(BusinessDataUpdatedEvent::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code TransactionCreatedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofTransactionCreatedEvent(TransactionCreatedEvent value) { + return new WebhookEventPayload(Conv.model(TransactionCreatedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code TransactionCreatedEvent}. + * + * A transaction was created and screened. Sent for live applications only. + * + * @return true when it matches + */ + public boolean isTransactionCreatedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code TransactionCreatedEvent}. + * + * @return the value, or empty when {@link #isTransactionCreatedEvent()} is false + */ + public Optional asTransactionCreatedEvent() { + return isTransactionCreatedEvent() ? Optional.of(Conv.model(TransactionCreatedEvent::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code TransactionStatusUpdatedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofTransactionStatusUpdatedEvent(TransactionStatusUpdatedEvent value) { + return new WebhookEventPayload(Conv.model(TransactionStatusUpdatedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code TransactionStatusUpdatedEvent}. + * + * A transaction changed status, from a rule, a review or an end-user action. Sent for live applications only. + * + * @return true when it matches + */ + public boolean isTransactionStatusUpdatedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code TransactionStatusUpdatedEvent}. + * + * @return the value, or empty when {@link #isTransactionStatusUpdatedEvent()} is false + */ + public Optional asTransactionStatusUpdatedEvent() { + return isTransactionStatusUpdatedEvent() ? Optional.of(Conv.model(TransactionStatusUpdatedEvent::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code TravelRuleStatusUpdatedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofTravelRuleStatusUpdatedEvent(TravelRuleStatusUpdatedEvent value) { + return new WebhookEventPayload(Conv.model(TravelRuleStatusUpdatedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code TravelRuleStatusUpdatedEvent}. + * + * A Travel Rule exchange changed status. Sent on every transition up to and including FINISHED, CANCELLED or EXPIRED, never after. Sent for live applications only. There is no top-level status. + * + * @return true when it matches + */ + public boolean isTravelRuleStatusUpdatedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code TravelRuleStatusUpdatedEvent}. + * + * @return the value, or empty when {@link #isTravelRuleStatusUpdatedEvent()} is false + */ + public Optional asTravelRuleStatusUpdatedEvent() { + return isTravelRuleStatusUpdatedEvent() ? Optional.of(Conv.model(TravelRuleStatusUpdatedEvent::fromJson).read(json)) : Optional.empty(); + } + + /** + * Wraps a {@code WorkflowComplianceChangedEvent} value. + * + * @param value the value + * @return the union + */ + public static WebhookEventPayload ofWorkflowComplianceChangedEvent(WorkflowComplianceChangedEvent value) { + return new WebhookEventPayload(Conv.model(WorkflowComplianceChangedEvent::fromJson).write(value)); + } + + /** + * Whether this value is a {@code WorkflowComplianceChangedEvent}. + * + * The compliance status of a published workflow changed after a re-evaluation. One event per workflow. There is no top-level status. + * + * @return true when it matches + */ + public boolean isWorkflowComplianceChangedEvent() { + return json.isObject(); + } + + /** + * Reads this value as a {@code WorkflowComplianceChangedEvent}. + * + * @return the value, or empty when {@link #isWorkflowComplianceChangedEvent()} is false + */ + public Optional asWorkflowComplianceChangedEvent() { + return isWorkflowComplianceChangedEvent() ? Optional.of(Conv.model(WorkflowComplianceChangedEvent::fromJson).read(json)) : Optional.empty(); + } +} diff --git a/src/main/java/me/didit/types/WebhooksDestinationsCreateParams.java b/src/main/java/me/didit/types/WebhooksDestinationsCreateParams.java index de35fa7..995a054 100644 --- a/src/main/java/me/didit/types/WebhooksDestinationsCreateParams.java +++ b/src/main/java/me/didit/types/WebhooksDestinationsCreateParams.java @@ -61,7 +61,7 @@ public String label() { } /** - * HTTPS URL that will receive POST requests. Must be unique per application among non-deleted destinations. Use a stable, hardened endpoint — Didit retries failed deliveries on a backoff schedule. + * HTTPS URL that will receive POST requests. Must be unique among the application's destinations. Use a stable, hardened endpoint: Didit retries failed deliveries on a backoff schedule. * * @return the value * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type @@ -124,7 +124,7 @@ public Builder label(String label) { } /** - * HTTPS URL that will receive POST requests. Must be unique per application among non-deleted destinations. Use a stable, hardened endpoint — Didit retries failed deliveries on a backoff schedule. + * HTTPS URL that will receive POST requests. Must be unique among the application's destinations. Use a stable, hardened endpoint: Didit retries failed deliveries on a backoff schedule. * * @param url the value * @return this builder diff --git a/src/main/java/me/didit/types/WebhooksDestinationsCreateResponse.java b/src/main/java/me/didit/types/WebhooksDestinationsCreateResponse.java index 3d7c12c..a576355 100644 --- a/src/main/java/me/didit/types/WebhooksDestinationsCreateResponse.java +++ b/src/main/java/me/didit/types/WebhooksDestinationsCreateResponse.java @@ -16,7 +16,7 @@ import org.jspecify.annotations.Nullable; public final class WebhooksDestinationsCreateResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "url", "enabled", "webhook_version", "secret_shared_key", "subscribed_events", "created_at", "updated_at", "summary"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "url", "enabled", "webhook_version", "secret_shared_key", "secret_last4", "previous_secret_expires_at", "subscribed_events", "created_by_email", "created_by_name", "created_at", "updated_at", "health_status", "failing_streak", "failing_since", "last_success_at", "environment", "summary"); private WebhooksDestinationsCreateResponse(ObjectNode json, Map> files) { super(json, files); @@ -111,6 +111,26 @@ public Optional secretSharedKey() { return Json.optional(json, "secret_shared_key", Conv.STRING); } + /** + * Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional secretLast4() { + return Json.optional(json, "secret_last4", Conv.STRING); + } + + /** + * Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries X-Signature-Previous, X-Signature-V2-Previous and X-Signature-Simple-Previous, made with the replaced secret (see [Signature verification](/integration/webhooks#signature-verification)). null when no rotation happened in the last 24 hours. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previousSecretExpiresAt() { + return Json.optional(json, "previous_secret_expires_at", Conv.DATE_TIME); + } + /** * Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit [] is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged. * @@ -121,6 +141,26 @@ public Optional> subscribedEvents() { return Json.optional(json, "subscribed_events", Conv.list(Conv.enumeration(WebhookEvent::of))); } + /** + * Email of the Business Console member who created the destination. null for a destination created with an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdByEmail() { + return Json.optional(json, "created_by_email", Conv.STRING); + } + + /** + * Name of the Business Console member who created the destination. null for a destination created with an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdByName() { + return Json.optional(json, "created_by_name", Conv.STRING); + } + /** * The created_at field. * @@ -142,7 +182,57 @@ public Optional updatedAt() { } /** - * Delivery health summary (all zero/null on a brand-new destination). + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional healthStatus() { + return Json.optional(json, "health_status", Conv.enumeration(WebhooksDestinationsCreateResponse.HealthStatus::of)); + } + + /** + * How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. 0 when no event has failed since the last success. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failingStreak() { + return Json.optional(json, "failing_streak", Conv.LONG); + } + + /** + * When the first failed attempt of the current failing_streak was made. null when failing_streak is 0. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failingSince() { + return Json.optional(json, "failing_since", Conv.DATE_TIME); + } + + /** + * When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. null if it has not in that window. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastSuccessAt() { + return Json.optional(json, "last_success_at", Conv.DATE_TIME); + } + + /** + * Environment of the application that owns the destination. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional environment() { + return Json.optional(json, "environment", Conv.enumeration(WebhooksDestinationsCreateResponse.Environment::of)); + } + + /** + * Delivery metrics for the last metrics_window_days (30) days. last_delivery_at is the most recent attempt, whenever it was made. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -224,6 +314,26 @@ public Builder secretSharedKey(String secretSharedKey) { return set("secret_shared_key", Conv.STRING, secretSharedKey); } + /** + * Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature. + * + * @param secretLast4 the value + * @return this builder + */ + public Builder secretLast4(String secretLast4) { + return set("secret_last4", Conv.STRING, secretLast4); + } + + /** + * Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries X-Signature-Previous, X-Signature-V2-Previous and X-Signature-Simple-Previous, made with the replaced secret (see [Signature verification](/integration/webhooks#signature-verification)). null when no rotation happened in the last 24 hours. + * + * @param previousSecretExpiresAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previousSecretExpiresAt(@Nullable OffsetDateTime previousSecretExpiresAt) { + return setNullable("previous_secret_expires_at", Conv.DATE_TIME, previousSecretExpiresAt); + } + /** * Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit [] is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged. * @@ -234,6 +344,26 @@ public Builder subscribedEvents(List subscribedEvents) { return set("subscribed_events", Conv.list(Conv.enumeration(WebhookEvent::of)), subscribedEvents); } + /** + * Email of the Business Console member who created the destination. null for a destination created with an API key. + * + * @param createdByEmail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder createdByEmail(@Nullable String createdByEmail) { + return setNullable("created_by_email", Conv.STRING, createdByEmail); + } + + /** + * Name of the Business Console member who created the destination. null for a destination created with an API key. + * + * @param createdByName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder createdByName(@Nullable String createdByName) { + return setNullable("created_by_name", Conv.STRING, createdByName); + } + /** * The created_at field. * @@ -255,7 +385,57 @@ public Builder updatedAt(OffsetDateTime updatedAt) { } /** - * Delivery health summary (all zero/null on a brand-new destination). + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + * @param healthStatus the value + * @return this builder + */ + public Builder healthStatus(WebhooksDestinationsCreateResponse.HealthStatus healthStatus) { + return set("health_status", Conv.enumeration(WebhooksDestinationsCreateResponse.HealthStatus::of), healthStatus); + } + + /** + * How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. 0 when no event has failed since the last success. + * + * @param failingStreak the value + * @return this builder + */ + public Builder failingStreak(long failingStreak) { + return set("failing_streak", Conv.LONG, failingStreak); + } + + /** + * When the first failed attempt of the current failing_streak was made. null when failing_streak is 0. + * + * @param failingSince the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder failingSince(@Nullable OffsetDateTime failingSince) { + return setNullable("failing_since", Conv.DATE_TIME, failingSince); + } + + /** + * When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. null if it has not in that window. + * + * @param lastSuccessAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastSuccessAt(@Nullable OffsetDateTime lastSuccessAt) { + return setNullable("last_success_at", Conv.DATE_TIME, lastSuccessAt); + } + + /** + * Environment of the application that owns the destination. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhooksDestinationsCreateResponse.Environment environment) { + return set("environment", Conv.enumeration(WebhooksDestinationsCreateResponse.Environment::of), environment); + } + + /** + * Delivery metrics for the last metrics_window_days (30) days. last_delivery_at is the most recent attempt, whenever it was made. * * @param summary the value * @return this builder @@ -324,9 +504,100 @@ public boolean isKnown() { } } - /** Delivery health summary (all zero/null on a brand-new destination). */ + /** + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class HealthStatus extends StringEnum { + /** The value {@code "healthy"}. */ + public static final HealthStatus HEALTHY = new HealthStatus("healthy"); + + /** The value {@code "degraded"}. */ + public static final HealthStatus DEGRADED = new HealthStatus("degraded"); + + /** The value {@code "failing"}. */ + public static final HealthStatus FAILING = new HealthStatus("failing"); + + private static final List KNOWN_VALUES = List.of(HEALTHY, DEGRADED, FAILING); + + private HealthStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static HealthStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, HealthStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Environment of the application that owns the destination. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Environment extends StringEnum { + /** The value {@code "live"}. */ + public static final Environment LIVE = new Environment("live"); + + /** The value {@code "sandbox"}. */ + public static final Environment SANDBOX = new Environment("sandbox"); + + private static final List KNOWN_VALUES = List.of(LIVE, SANDBOX); + + private Environment(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Environment of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Environment::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Delivery metrics for the last metrics_window_days (30) days. last_delivery_at is the most recent attempt, whenever it was made. + */ public static final class Summary extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("total_deliveries", "failed_deliveries", "error_rate_percentage", "min_response_time_ms", "avg_response_time_ms", "max_response_time_ms", "last_delivery_at"); + private static final Set KNOWN_FIELDS = Set.of("total_deliveries", "failed_deliveries", "error_rate_percentage", "min_response_time_ms", "avg_response_time_ms", "max_response_time_ms", "last_delivery_at", "metrics_window_days"); private Summary(ObjectNode json, Map> files) { super(json, files); @@ -431,6 +702,16 @@ public Optional lastDeliveryAt() { return Json.optional(json, "last_delivery_at", Conv.DATE_TIME); } + /** + * Length in days of the window the other summary metrics cover. Always 30. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional metricsWindowDays() { + return Json.optional(json, "metrics_window_days", Conv.LONG); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -514,6 +795,16 @@ public Builder lastDeliveryAt(@Nullable OffsetDateTime lastDeliveryAt) { return setNullable("last_delivery_at", Conv.DATE_TIME, lastDeliveryAt); } + /** + * Length in days of the window the other summary metrics cover. Always 30. + * + * @param metricsWindowDays the value + * @return this builder + */ + public Builder metricsWindowDays(long metricsWindowDays) { + return set("metrics_window_days", Conv.LONG, metricsWindowDays); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/WebhooksDestinationsListDeliveriesParams.java b/src/main/java/me/didit/types/WebhooksDestinationsListDeliveriesParams.java new file mode 100644 index 0000000..283f215 --- /dev/null +++ b/src/main/java/me/didit/types/WebhooksDestinationsListDeliveriesParams.java @@ -0,0 +1,160 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code webhooks.destinations.list_deliveries}. */ +public final class WebhooksDestinationsListDeliveriesParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("limit", "offset", "date_from", "date_to"); + + private WebhooksDestinationsListDeliveriesParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhooksDestinationsListDeliveriesParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhooksDestinationsListDeliveriesParams fromJson(JsonNode json) { + return new WebhooksDestinationsListDeliveriesParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Maximum number of deliveries per page. Defaults to 50. A value that is not a positive integer falls back to 50; a value above 1000000 returns 400. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional limit() { + return Json.optional(json, "limit", Conv.LONG); + } + + /** + * Number of deliveries to skip. Defaults to 0. Prefer following the next and previous URLs from the response. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional offset() { + return Json.optional(json, "offset", Conv.LONG); + } + + /** + * First UTC day to include, as YYYY-MM-DD (inclusive). Omit to start from the oldest delivery available. dateFrom is accepted as an alias. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateFrom() { + return Json.optional(json, "date_from", Conv.DATE); + } + + /** + * Last UTC day to include, as YYYY-MM-DD (inclusive). Must not be earlier than date_from. dateTo is accepted as an alias. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateTo() { + return Json.optional(json, "date_to", Conv.DATE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhooksDestinationsListDeliveriesParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhooksDestinationsListDeliveriesParams from) { + super(from); + } + + /** + * Maximum number of deliveries per page. Defaults to 50. A value that is not a positive integer falls back to 50; a value above 1000000 returns 400. + * + * @param limit the value + * @return this builder + */ + public Builder limit(long limit) { + return set("limit", Conv.LONG, limit); + } + + /** + * Number of deliveries to skip. Defaults to 0. Prefer following the next and previous URLs from the response. + * + * @param offset the value + * @return this builder + */ + public Builder offset(long offset) { + return set("offset", Conv.LONG, offset); + } + + /** + * First UTC day to include, as YYYY-MM-DD (inclusive). Omit to start from the oldest delivery available. dateFrom is accepted as an alias. + * + * @param dateFrom the value + * @return this builder + */ + public Builder dateFrom(LocalDate dateFrom) { + return set("date_from", Conv.DATE, dateFrom); + } + + /** + * Last UTC day to include, as YYYY-MM-DD (inclusive). Must not be earlier than date_from. dateTo is accepted as an alias. + * + * @param dateTo the value + * @return this builder + */ + public Builder dateTo(LocalDate dateTo) { + return set("date_to", Conv.DATE, dateTo); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhooksDestinationsListDeliveriesParams} + */ + public WebhooksDestinationsListDeliveriesParams build() { + return new WebhooksDestinationsListDeliveriesParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/WebhooksDestinationsListDeliveriesResponse.java b/src/main/java/me/didit/types/WebhooksDestinationsListDeliveriesResponse.java new file mode 100644 index 0000000..0d0d6cb --- /dev/null +++ b/src/main/java/me/didit/types/WebhooksDestinationsListDeliveriesResponse.java @@ -0,0 +1,164 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class WebhooksDestinationsListDeliveriesResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("count", "next", "previous", "results"); + + private WebhooksDestinationsListDeliveriesResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhooksDestinationsListDeliveriesResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhooksDestinationsListDeliveriesResponse fromJson(JsonNode json) { + return new WebhooksDestinationsListDeliveriesResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Matching deliveries, exact up to 100 and capped at 100 above that. Use next to know whether more pages exist. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long count() { + return Json.required(json, "count", Conv.LONG); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional next() { + return Json.optional(json, "next", Conv.STRING); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previous() { + return Json.optional(json, "previous", Conv.STRING); + } + + /** + * The results field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List results() { + return Json.required(json, "results", Conv.list(Conv.model(WebhookDestinationDelivery::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhooksDestinationsListDeliveriesResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhooksDestinationsListDeliveriesResponse from) { + super(from); + } + + /** + * Matching deliveries, exact up to 100 and capped at 100 above that. Use next to know whether more pages exist. + * + * @param count the value + * @return this builder + */ + public Builder count(long count) { + return set("count", Conv.LONG, count); + } + + /** + * Absolute URL of the next page, or null on the last page. + * + * @param next the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder next(@Nullable String next) { + return setNullable("next", Conv.STRING, next); + } + + /** + * Absolute URL of the previous page, or null on the first page. + * + * @param previous the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previous(@Nullable String previous) { + return setNullable("previous", Conv.STRING, previous); + } + + /** + * The results field. + * + * @param results the value + * @return this builder + */ + public Builder results(List results) { + return set("results", Conv.list(Conv.model(WebhookDestinationDelivery::fromJson)), results); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhooksDestinationsListDeliveriesResponse} + * @throws IllegalStateException when a required field is missing + */ + public WebhooksDestinationsListDeliveriesResponse build() { + require("count", false); + require("next", true); + require("previous", true); + require("results", false); + return new WebhooksDestinationsListDeliveriesResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/WebhooksDestinationsListResponse.java b/src/main/java/me/didit/types/WebhooksDestinationsListResponse.java index ba94f67..1007228 100644 --- a/src/main/java/me/didit/types/WebhooksDestinationsListResponse.java +++ b/src/main/java/me/didit/types/WebhooksDestinationsListResponse.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; import java.time.OffsetDateTime; import java.util.List; import java.util.Map; @@ -16,7 +17,7 @@ import org.jspecify.annotations.Nullable; public final class WebhooksDestinationsListResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "url", "enabled", "webhook_version", "subscribed_events", "subscribed_events_count", "deliveries_count", "failed_deliveries_count", "average_response_time_ms", "error_rate_percentage", "last_delivery_at", "created_at", "updated_at"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "url", "enabled", "webhook_version", "subscribed_events", "subscribed_events_count", "deliveries_count", "failed_deliveries_count", "average_response_time_ms", "error_rate_percentage", "last_delivery_at", "metrics_window_days", "health_status", "failing_streak", "failing_since", "last_success_at", "environment", "daily_points", "secret_last4", "created_by_email", "created_by_name", "created_at", "updated_at"); private WebhooksDestinationsListResponse(ObjectNode json, Map> files) { super(json, files); @@ -122,7 +123,7 @@ public Optional subscribedEventsCount() { } /** - * Total delivery attempts (successful + failed) recorded for this destination. + * Delivery attempts (retries included) made to this destination in the last 30 days. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -132,7 +133,7 @@ public Optional deliveriesCount() { } /** - * Delivery attempts whose target responded with HTTP ≥ 400 (or did not respond). + * Delivery attempts in the last 30 days that the destination answered with HTTP 400 or above, or did not answer (timeout, refused connection or blocked address). * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -142,7 +143,7 @@ public Optional failedDeliveriesCount() { } /** - * Average target response time in milliseconds, rounded to an integer. null if no deliveries yet. + * Average response time in milliseconds over the last 30 days, rounded to an integer. null when there were no deliveries in that window. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -152,7 +153,7 @@ public Optional averageResponseTimeMs() { } /** - * round(failed_deliveries_count / deliveries_count * 100); 0 when there are no deliveries. + * round(failed_deliveries_count / deliveries_count * 100) over the last 30 days; 0 when there are no deliveries. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -171,6 +172,106 @@ public Optional lastDeliveryAt() { return Json.optional(json, "last_delivery_at", Conv.DATE_TIME); } + /** + * Length in days of the window that deliveries_count, failed_deliveries_count, average_response_time_ms, error_rate_percentage and daily_points cover. Always 30. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional metricsWindowDays() { + return Json.optional(json, "metrics_window_days", Conv.LONG); + } + + /** + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional healthStatus() { + return Json.optional(json, "health_status", Conv.enumeration(WebhooksDestinationsListResponse.HealthStatus::of)); + } + + /** + * How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. 0 when no event has failed since the last success. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failingStreak() { + return Json.optional(json, "failing_streak", Conv.LONG); + } + + /** + * When the first failed attempt of the current failing_streak was made. null when failing_streak is 0. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failingSince() { + return Json.optional(json, "failing_since", Conv.DATE_TIME); + } + + /** + * When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. null if it has not in that window. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastSuccessAt() { + return Json.optional(json, "last_success_at", Conv.DATE_TIME); + } + + /** + * Environment of the application that owns the destination. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional environment() { + return Json.optional(json, "environment", Conv.enumeration(WebhooksDestinationsListResponse.Environment::of)); + } + + /** + * One entry per UTC day of the last 30 days, oldest first, including days with no deliveries. Counts every attempt, retries included. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> dailyPoints() { + return Json.optional(json, "daily_points", Conv.list(Conv.model(WebhooksDestinationsListResponse.DailyPoints::fromJson))); + } + + /** + * Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional secretLast4() { + return Json.optional(json, "secret_last4", Conv.STRING); + } + + /** + * Email of the Business Console member who created the destination. null for a destination created with an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdByEmail() { + return Json.optional(json, "created_by_email", Conv.STRING); + } + + /** + * Name of the Business Console member who created the destination. null for a destination created with an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdByName() { + return Json.optional(json, "created_by_name", Conv.STRING); + } + /** * The created_at field. * @@ -275,7 +376,7 @@ public Builder subscribedEventsCount(long subscribedEventsCount) { } /** - * Total delivery attempts (successful + failed) recorded for this destination. + * Delivery attempts (retries included) made to this destination in the last 30 days. * * @param deliveriesCount the value * @return this builder @@ -285,7 +386,7 @@ public Builder deliveriesCount(long deliveriesCount) { } /** - * Delivery attempts whose target responded with HTTP ≥ 400 (or did not respond). + * Delivery attempts in the last 30 days that the destination answered with HTTP 400 or above, or did not answer (timeout, refused connection or blocked address). * * @param failedDeliveriesCount the value * @return this builder @@ -295,7 +396,7 @@ public Builder failedDeliveriesCount(long failedDeliveriesCount) { } /** - * Average target response time in milliseconds, rounded to an integer. null if no deliveries yet. + * Average response time in milliseconds over the last 30 days, rounded to an integer. null when there were no deliveries in that window. * * @param averageResponseTimeMs the value, or {@code null} to send JSON null * @return this builder @@ -305,7 +406,7 @@ public Builder averageResponseTimeMs(@Nullable Long averageResponseTimeMs) { } /** - * round(failed_deliveries_count / deliveries_count * 100); 0 when there are no deliveries. + * round(failed_deliveries_count / deliveries_count * 100) over the last 30 days; 0 when there are no deliveries. * * @param errorRatePercentage the value * @return this builder @@ -324,6 +425,106 @@ public Builder lastDeliveryAt(@Nullable OffsetDateTime lastDeliveryAt) { return setNullable("last_delivery_at", Conv.DATE_TIME, lastDeliveryAt); } + /** + * Length in days of the window that deliveries_count, failed_deliveries_count, average_response_time_ms, error_rate_percentage and daily_points cover. Always 30. + * + * @param metricsWindowDays the value + * @return this builder + */ + public Builder metricsWindowDays(long metricsWindowDays) { + return set("metrics_window_days", Conv.LONG, metricsWindowDays); + } + + /** + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + * @param healthStatus the value + * @return this builder + */ + public Builder healthStatus(WebhooksDestinationsListResponse.HealthStatus healthStatus) { + return set("health_status", Conv.enumeration(WebhooksDestinationsListResponse.HealthStatus::of), healthStatus); + } + + /** + * How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. 0 when no event has failed since the last success. + * + * @param failingStreak the value + * @return this builder + */ + public Builder failingStreak(long failingStreak) { + return set("failing_streak", Conv.LONG, failingStreak); + } + + /** + * When the first failed attempt of the current failing_streak was made. null when failing_streak is 0. + * + * @param failingSince the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder failingSince(@Nullable OffsetDateTime failingSince) { + return setNullable("failing_since", Conv.DATE_TIME, failingSince); + } + + /** + * When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. null if it has not in that window. + * + * @param lastSuccessAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastSuccessAt(@Nullable OffsetDateTime lastSuccessAt) { + return setNullable("last_success_at", Conv.DATE_TIME, lastSuccessAt); + } + + /** + * Environment of the application that owns the destination. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhooksDestinationsListResponse.Environment environment) { + return set("environment", Conv.enumeration(WebhooksDestinationsListResponse.Environment::of), environment); + } + + /** + * One entry per UTC day of the last 30 days, oldest first, including days with no deliveries. Counts every attempt, retries included. + * + * @param dailyPoints the value + * @return this builder + */ + public Builder dailyPoints(List dailyPoints) { + return set("daily_points", Conv.list(Conv.model(WebhooksDestinationsListResponse.DailyPoints::fromJson)), dailyPoints); + } + + /** + * Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature. + * + * @param secretLast4 the value + * @return this builder + */ + public Builder secretLast4(String secretLast4) { + return set("secret_last4", Conv.STRING, secretLast4); + } + + /** + * Email of the Business Console member who created the destination. null for a destination created with an API key. + * + * @param createdByEmail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder createdByEmail(@Nullable String createdByEmail) { + return setNullable("created_by_email", Conv.STRING, createdByEmail); + } + + /** + * Name of the Business Console member who created the destination. null for a destination created with an API key. + * + * @param createdByName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder createdByName(@Nullable String createdByName) { + return setNullable("created_by_name", Conv.STRING, createdByName); + } + /** * The created_at field. * @@ -404,4 +605,218 @@ public boolean isKnown() { return KNOWN_VALUES.contains(this); } } + + /** + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class HealthStatus extends StringEnum { + /** The value {@code "healthy"}. */ + public static final HealthStatus HEALTHY = new HealthStatus("healthy"); + + /** The value {@code "degraded"}. */ + public static final HealthStatus DEGRADED = new HealthStatus("degraded"); + + /** The value {@code "failing"}. */ + public static final HealthStatus FAILING = new HealthStatus("failing"); + + private static final List KNOWN_VALUES = List.of(HEALTHY, DEGRADED, FAILING); + + private HealthStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static HealthStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, HealthStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Environment of the application that owns the destination. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Environment extends StringEnum { + /** The value {@code "live"}. */ + public static final Environment LIVE = new Environment("live"); + + /** The value {@code "sandbox"}. */ + public static final Environment SANDBOX = new Environment("sandbox"); + + private static final List KNOWN_VALUES = List.of(LIVE, SANDBOX); + + private Environment(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Environment of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Environment::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class DailyPoints extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("date", "total_deliveries", "failed_deliveries"); + + private DailyPoints(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code DailyPoints} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static DailyPoints fromJson(JsonNode json) { + return new DailyPoints(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * The UTC day. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional date() { + return Json.optional(json, "date", Conv.DATE); + } + + /** + * Delivery attempts made that day. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalDeliveries() { + return Json.optional(json, "total_deliveries", Conv.LONG); + } + + /** + * Attempts made that day that were answered with HTTP 400 or above, or not answered at all. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failedDeliveries() { + return Json.optional(json, "failed_deliveries", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link DailyPoints}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(DailyPoints from) { + super(from); + } + + /** + * The UTC day. + * + * @param date the value + * @return this builder + */ + public Builder date(LocalDate date) { + return set("date", Conv.DATE, date); + } + + /** + * Delivery attempts made that day. + * + * @param totalDeliveries the value + * @return this builder + */ + public Builder totalDeliveries(long totalDeliveries) { + return set("total_deliveries", Conv.LONG, totalDeliveries); + } + + /** + * Attempts made that day that were answered with HTTP 400 or above, or not answered at all. + * + * @param failedDeliveries the value + * @return this builder + */ + public Builder failedDeliveries(long failedDeliveries) { + return set("failed_deliveries", Conv.LONG, failedDeliveries); + } + + /** + * Builds the value. + * + * @return a new immutable {@code DailyPoints} + */ + public DailyPoints build() { + return new DailyPoints(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } } diff --git a/src/main/java/me/didit/types/WebhooksDestinationsRetrieveAnalyticsParams.java b/src/main/java/me/didit/types/WebhooksDestinationsRetrieveAnalyticsParams.java new file mode 100644 index 0000000..2dbf41a --- /dev/null +++ b/src/main/java/me/didit/types/WebhooksDestinationsRetrieveAnalyticsParams.java @@ -0,0 +1,120 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; + +/** Parameters for {@code webhooks.destinations.retrieve_analytics}. */ +public final class WebhooksDestinationsRetrieveAnalyticsParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("date_from", "date_to"); + + private WebhooksDestinationsRetrieveAnalyticsParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhooksDestinationsRetrieveAnalyticsParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhooksDestinationsRetrieveAnalyticsParams fromJson(JsonNode json) { + return new WebhooksDestinationsRetrieveAnalyticsParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * First UTC day to include, as YYYY-MM-DD (inclusive). Omit to start from the oldest delivery available. dateFrom is accepted as an alias. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateFrom() { + return Json.optional(json, "date_from", Conv.DATE); + } + + /** + * Last UTC day to include, as YYYY-MM-DD (inclusive). Must not be earlier than date_from. dateTo is accepted as an alias. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dateTo() { + return Json.optional(json, "date_to", Conv.DATE); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhooksDestinationsRetrieveAnalyticsParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhooksDestinationsRetrieveAnalyticsParams from) { + super(from); + } + + /** + * First UTC day to include, as YYYY-MM-DD (inclusive). Omit to start from the oldest delivery available. dateFrom is accepted as an alias. + * + * @param dateFrom the value + * @return this builder + */ + public Builder dateFrom(LocalDate dateFrom) { + return set("date_from", Conv.DATE, dateFrom); + } + + /** + * Last UTC day to include, as YYYY-MM-DD (inclusive). Must not be earlier than date_from. dateTo is accepted as an alias. + * + * @param dateTo the value + * @return this builder + */ + public Builder dateTo(LocalDate dateTo) { + return set("date_to", Conv.DATE, dateTo); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhooksDestinationsRetrieveAnalyticsParams} + */ + public WebhooksDestinationsRetrieveAnalyticsParams build() { + return new WebhooksDestinationsRetrieveAnalyticsParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } +} diff --git a/src/main/java/me/didit/types/WebhooksDestinationsRetrieveAnalyticsResponse.java b/src/main/java/me/didit/types/WebhooksDestinationsRetrieveAnalyticsResponse.java new file mode 100644 index 0000000..ffbd358 --- /dev/null +++ b/src/main/java/me/didit/types/WebhooksDestinationsRetrieveAnalyticsResponse.java @@ -0,0 +1,508 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import org.jspecify.annotations.Nullable; + +public final class WebhooksDestinationsRetrieveAnalyticsResponse extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("summary", "points"); + + private WebhooksDestinationsRetrieveAnalyticsResponse(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhooksDestinationsRetrieveAnalyticsResponse} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhooksDestinationsRetrieveAnalyticsResponse fromJson(JsonNode json) { + return new WebhooksDestinationsRetrieveAnalyticsResponse(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Totals over the requested range. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhooksDestinationsRetrieveAnalyticsResponse.Summary summary() { + return Json.required(json, "summary", Conv.model(WebhooksDestinationsRetrieveAnalyticsResponse.Summary::fromJson)); + } + + /** + * One entry per UTC day that had deliveries, oldest first. Days without deliveries are omitted, so fill the gaps with zeros when you chart them. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public List points() { + return Json.required(json, "points", Conv.list(Conv.model(WebhooksDestinationsRetrieveAnalyticsResponse.Points::fromJson))); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhooksDestinationsRetrieveAnalyticsResponse}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhooksDestinationsRetrieveAnalyticsResponse from) { + super(from); + } + + /** + * Totals over the requested range. + * + * @param summary the value + * @return this builder + */ + public Builder summary(WebhooksDestinationsRetrieveAnalyticsResponse.Summary summary) { + return set("summary", Conv.model(WebhooksDestinationsRetrieveAnalyticsResponse.Summary::fromJson), summary); + } + + /** + * One entry per UTC day that had deliveries, oldest first. Days without deliveries are omitted, so fill the gaps with zeros when you chart them. + * + * @param points the value + * @return this builder + */ + public Builder points(List points) { + return set("points", Conv.list(Conv.model(WebhooksDestinationsRetrieveAnalyticsResponse.Points::fromJson)), points); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhooksDestinationsRetrieveAnalyticsResponse} + * @throws IllegalStateException when a required field is missing + */ + public WebhooksDestinationsRetrieveAnalyticsResponse build() { + require("summary", false); + require("points", false); + return new WebhooksDestinationsRetrieveAnalyticsResponse(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** Totals over the requested range. */ + public static final class Summary extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("total_deliveries", "failed_deliveries", "min_response_time_ms", "avg_response_time_ms", "max_response_time_ms", "error_rate_percentage"); + + private Summary(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Summary} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Summary fromJson(JsonNode json) { + return new Summary(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Delivery attempts, including queued ones. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long totalDeliveries() { + return Json.required(json, "total_deliveries", Conv.LONG); + } + + /** + * Attempts whose recorded HTTP status is 400 or above. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long failedDeliveries() { + return Json.required(json, "failed_deliveries", Conv.LONG); + } + + /** + * Fastest attempt in milliseconds, or null when no attempt has a recorded time. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional minResponseTimeMs() { + return Json.optional(json, "min_response_time_ms", Conv.LONG); + } + + /** + * Mean attempt time in milliseconds, rounded, or null when no attempt has a recorded time. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional avgResponseTimeMs() { + return Json.optional(json, "avg_response_time_ms", Conv.LONG); + } + + /** + * Slowest attempt in milliseconds, or null when no attempt has a recorded time. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional maxResponseTimeMs() { + return Json.optional(json, "max_response_time_ms", Conv.LONG); + } + + /** + * failed_deliveries / total_deliveries x 100, rounded to a whole number. 0 when there are no deliveries. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long errorRatePercentage() { + return Json.required(json, "error_rate_percentage", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Summary}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Summary from) { + super(from); + } + + /** + * Delivery attempts, including queued ones. + * + * @param totalDeliveries the value + * @return this builder + */ + public Builder totalDeliveries(long totalDeliveries) { + return set("total_deliveries", Conv.LONG, totalDeliveries); + } + + /** + * Attempts whose recorded HTTP status is 400 or above. + * + * @param failedDeliveries the value + * @return this builder + */ + public Builder failedDeliveries(long failedDeliveries) { + return set("failed_deliveries", Conv.LONG, failedDeliveries); + } + + /** + * Fastest attempt in milliseconds, or null when no attempt has a recorded time. + * + * @param minResponseTimeMs the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder minResponseTimeMs(@Nullable Long minResponseTimeMs) { + return setNullable("min_response_time_ms", Conv.LONG, minResponseTimeMs); + } + + /** + * Mean attempt time in milliseconds, rounded, or null when no attempt has a recorded time. + * + * @param avgResponseTimeMs the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder avgResponseTimeMs(@Nullable Long avgResponseTimeMs) { + return setNullable("avg_response_time_ms", Conv.LONG, avgResponseTimeMs); + } + + /** + * Slowest attempt in milliseconds, or null when no attempt has a recorded time. + * + * @param maxResponseTimeMs the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder maxResponseTimeMs(@Nullable Long maxResponseTimeMs) { + return setNullable("max_response_time_ms", Conv.LONG, maxResponseTimeMs); + } + + /** + * failed_deliveries / total_deliveries x 100, rounded to a whole number. 0 when there are no deliveries. + * + * @param errorRatePercentage the value + * @return this builder + */ + public Builder errorRatePercentage(long errorRatePercentage) { + return set("error_rate_percentage", Conv.LONG, errorRatePercentage); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Summary} + * @throws IllegalStateException when a required field is missing + */ + public Summary build() { + require("total_deliveries", false); + require("failed_deliveries", false); + require("min_response_time_ms", true); + require("avg_response_time_ms", true); + require("max_response_time_ms", true); + require("error_rate_percentage", false); + return new Summary(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } + + public static final class Points extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("date", "total_deliveries", "failed_deliveries", "min_response_time_ms", "avg_response_time_ms", "max_response_time_ms"); + + private Points(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Points} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Points fromJson(JsonNode json) { + return new Points(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * UTC day, YYYY-MM-DD. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public LocalDate date() { + return Json.required(json, "date", Conv.DATE); + } + + /** + * Delivery attempts, including queued ones. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long totalDeliveries() { + return Json.required(json, "total_deliveries", Conv.LONG); + } + + /** + * Attempts whose recorded HTTP status is 400 or above. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long failedDeliveries() { + return Json.required(json, "failed_deliveries", Conv.LONG); + } + + /** + * Fastest attempt in milliseconds, or null when no attempt has a recorded time. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional minResponseTimeMs() { + return Json.optional(json, "min_response_time_ms", Conv.LONG); + } + + /** + * Mean attempt time in milliseconds, rounded, or null when no attempt has a recorded time. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional avgResponseTimeMs() { + return Json.optional(json, "avg_response_time_ms", Conv.LONG); + } + + /** + * Slowest attempt in milliseconds, or null when no attempt has a recorded time. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional maxResponseTimeMs() { + return Json.optional(json, "max_response_time_ms", Conv.LONG); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Points}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Points from) { + super(from); + } + + /** + * UTC day, YYYY-MM-DD. + * + * @param date the value + * @return this builder + */ + public Builder date(LocalDate date) { + return set("date", Conv.DATE, date); + } + + /** + * Delivery attempts, including queued ones. + * + * @param totalDeliveries the value + * @return this builder + */ + public Builder totalDeliveries(long totalDeliveries) { + return set("total_deliveries", Conv.LONG, totalDeliveries); + } + + /** + * Attempts whose recorded HTTP status is 400 or above. + * + * @param failedDeliveries the value + * @return this builder + */ + public Builder failedDeliveries(long failedDeliveries) { + return set("failed_deliveries", Conv.LONG, failedDeliveries); + } + + /** + * Fastest attempt in milliseconds, or null when no attempt has a recorded time. + * + * @param minResponseTimeMs the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder minResponseTimeMs(@Nullable Long minResponseTimeMs) { + return setNullable("min_response_time_ms", Conv.LONG, minResponseTimeMs); + } + + /** + * Mean attempt time in milliseconds, rounded, or null when no attempt has a recorded time. + * + * @param avgResponseTimeMs the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder avgResponseTimeMs(@Nullable Long avgResponseTimeMs) { + return setNullable("avg_response_time_ms", Conv.LONG, avgResponseTimeMs); + } + + /** + * Slowest attempt in milliseconds, or null when no attempt has a recorded time. + * + * @param maxResponseTimeMs the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder maxResponseTimeMs(@Nullable Long maxResponseTimeMs) { + return setNullable("max_response_time_ms", Conv.LONG, maxResponseTimeMs); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Points} + * @throws IllegalStateException when a required field is missing + */ + public Points build() { + require("date", false); + require("total_deliveries", false); + require("failed_deliveries", false); + require("min_response_time_ms", true); + require("avg_response_time_ms", true); + require("max_response_time_ms", true); + return new Points(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + } +} diff --git a/src/main/java/me/didit/types/WebhooksDestinationsRetrieveResponse.java b/src/main/java/me/didit/types/WebhooksDestinationsRetrieveResponse.java index 995b3d5..620dbcd 100644 --- a/src/main/java/me/didit/types/WebhooksDestinationsRetrieveResponse.java +++ b/src/main/java/me/didit/types/WebhooksDestinationsRetrieveResponse.java @@ -16,7 +16,7 @@ import org.jspecify.annotations.Nullable; public final class WebhooksDestinationsRetrieveResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "url", "enabled", "webhook_version", "secret_shared_key", "subscribed_events", "created_at", "updated_at", "summary"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "url", "enabled", "webhook_version", "secret_shared_key", "secret_last4", "previous_secret_expires_at", "subscribed_events", "created_by_email", "created_by_name", "created_at", "updated_at", "health_status", "failing_streak", "failing_since", "last_success_at", "environment", "summary"); private WebhooksDestinationsRetrieveResponse(ObjectNode json, Map> files) { super(json, files); @@ -111,6 +111,26 @@ public Optional secretSharedKey() { return Json.optional(json, "secret_shared_key", Conv.STRING); } + /** + * Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional secretLast4() { + return Json.optional(json, "secret_last4", Conv.STRING); + } + + /** + * Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries X-Signature-Previous, X-Signature-V2-Previous and X-Signature-Simple-Previous, made with the replaced secret (see [Signature verification](/integration/webhooks#signature-verification)). null when no rotation happened in the last 24 hours. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previousSecretExpiresAt() { + return Json.optional(json, "previous_secret_expires_at", Conv.DATE_TIME); + } + /** * Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit [] is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged. * @@ -121,6 +141,26 @@ public Optional> subscribedEvents() { return Json.optional(json, "subscribed_events", Conv.list(Conv.enumeration(WebhookEvent::of))); } + /** + * Email of the Business Console member who created the destination. null for a destination created with an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdByEmail() { + return Json.optional(json, "created_by_email", Conv.STRING); + } + + /** + * Name of the Business Console member who created the destination. null for a destination created with an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdByName() { + return Json.optional(json, "created_by_name", Conv.STRING); + } + /** * The created_at field. * @@ -142,7 +182,57 @@ public Optional updatedAt() { } /** - * Delivery health metrics aggregated over the destination's lifetime. + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional healthStatus() { + return Json.optional(json, "health_status", Conv.enumeration(WebhooksDestinationsRetrieveResponse.HealthStatus::of)); + } + + /** + * How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. 0 when no event has failed since the last success. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failingStreak() { + return Json.optional(json, "failing_streak", Conv.LONG); + } + + /** + * When the first failed attempt of the current failing_streak was made. null when failing_streak is 0. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failingSince() { + return Json.optional(json, "failing_since", Conv.DATE_TIME); + } + + /** + * When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. null if it has not in that window. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastSuccessAt() { + return Json.optional(json, "last_success_at", Conv.DATE_TIME); + } + + /** + * Environment of the application that owns the destination. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional environment() { + return Json.optional(json, "environment", Conv.enumeration(WebhooksDestinationsRetrieveResponse.Environment::of)); + } + + /** + * Delivery metrics for the last metrics_window_days (30) days. last_delivery_at is the most recent attempt, whenever it was made. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -224,6 +314,26 @@ public Builder secretSharedKey(String secretSharedKey) { return set("secret_shared_key", Conv.STRING, secretSharedKey); } + /** + * Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature. + * + * @param secretLast4 the value + * @return this builder + */ + public Builder secretLast4(String secretLast4) { + return set("secret_last4", Conv.STRING, secretLast4); + } + + /** + * Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries X-Signature-Previous, X-Signature-V2-Previous and X-Signature-Simple-Previous, made with the replaced secret (see [Signature verification](/integration/webhooks#signature-verification)). null when no rotation happened in the last 24 hours. + * + * @param previousSecretExpiresAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previousSecretExpiresAt(@Nullable OffsetDateTime previousSecretExpiresAt) { + return setNullable("previous_secret_expires_at", Conv.DATE_TIME, previousSecretExpiresAt); + } + /** * Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit [] is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged. * @@ -234,6 +344,26 @@ public Builder subscribedEvents(List subscribedEvents) { return set("subscribed_events", Conv.list(Conv.enumeration(WebhookEvent::of)), subscribedEvents); } + /** + * Email of the Business Console member who created the destination. null for a destination created with an API key. + * + * @param createdByEmail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder createdByEmail(@Nullable String createdByEmail) { + return setNullable("created_by_email", Conv.STRING, createdByEmail); + } + + /** + * Name of the Business Console member who created the destination. null for a destination created with an API key. + * + * @param createdByName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder createdByName(@Nullable String createdByName) { + return setNullable("created_by_name", Conv.STRING, createdByName); + } + /** * The created_at field. * @@ -255,7 +385,57 @@ public Builder updatedAt(OffsetDateTime updatedAt) { } /** - * Delivery health metrics aggregated over the destination's lifetime. + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + * @param healthStatus the value + * @return this builder + */ + public Builder healthStatus(WebhooksDestinationsRetrieveResponse.HealthStatus healthStatus) { + return set("health_status", Conv.enumeration(WebhooksDestinationsRetrieveResponse.HealthStatus::of), healthStatus); + } + + /** + * How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. 0 when no event has failed since the last success. + * + * @param failingStreak the value + * @return this builder + */ + public Builder failingStreak(long failingStreak) { + return set("failing_streak", Conv.LONG, failingStreak); + } + + /** + * When the first failed attempt of the current failing_streak was made. null when failing_streak is 0. + * + * @param failingSince the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder failingSince(@Nullable OffsetDateTime failingSince) { + return setNullable("failing_since", Conv.DATE_TIME, failingSince); + } + + /** + * When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. null if it has not in that window. + * + * @param lastSuccessAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastSuccessAt(@Nullable OffsetDateTime lastSuccessAt) { + return setNullable("last_success_at", Conv.DATE_TIME, lastSuccessAt); + } + + /** + * Environment of the application that owns the destination. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhooksDestinationsRetrieveResponse.Environment environment) { + return set("environment", Conv.enumeration(WebhooksDestinationsRetrieveResponse.Environment::of), environment); + } + + /** + * Delivery metrics for the last metrics_window_days (30) days. last_delivery_at is the most recent attempt, whenever it was made. * * @param summary the value * @return this builder @@ -324,9 +504,100 @@ public boolean isKnown() { } } - /** Delivery health metrics aggregated over the destination's lifetime. */ + /** + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class HealthStatus extends StringEnum { + /** The value {@code "healthy"}. */ + public static final HealthStatus HEALTHY = new HealthStatus("healthy"); + + /** The value {@code "degraded"}. */ + public static final HealthStatus DEGRADED = new HealthStatus("degraded"); + + /** The value {@code "failing"}. */ + public static final HealthStatus FAILING = new HealthStatus("failing"); + + private static final List KNOWN_VALUES = List.of(HEALTHY, DEGRADED, FAILING); + + private HealthStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static HealthStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, HealthStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Environment of the application that owns the destination. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Environment extends StringEnum { + /** The value {@code "live"}. */ + public static final Environment LIVE = new Environment("live"); + + /** The value {@code "sandbox"}. */ + public static final Environment SANDBOX = new Environment("sandbox"); + + private static final List KNOWN_VALUES = List.of(LIVE, SANDBOX); + + private Environment(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Environment of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Environment::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Delivery metrics for the last metrics_window_days (30) days. last_delivery_at is the most recent attempt, whenever it was made. + */ public static final class Summary extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("total_deliveries", "failed_deliveries", "error_rate_percentage", "min_response_time_ms", "avg_response_time_ms", "max_response_time_ms", "last_delivery_at"); + private static final Set KNOWN_FIELDS = Set.of("total_deliveries", "failed_deliveries", "error_rate_percentage", "min_response_time_ms", "avg_response_time_ms", "max_response_time_ms", "last_delivery_at", "metrics_window_days"); private Summary(ObjectNode json, Map> files) { super(json, files); @@ -431,6 +702,16 @@ public Optional lastDeliveryAt() { return Json.optional(json, "last_delivery_at", Conv.DATE_TIME); } + /** + * Length in days of the window the other summary metrics cover. Always 30. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional metricsWindowDays() { + return Json.optional(json, "metrics_window_days", Conv.LONG); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -514,6 +795,16 @@ public Builder lastDeliveryAt(@Nullable OffsetDateTime lastDeliveryAt) { return setNullable("last_delivery_at", Conv.DATE_TIME, lastDeliveryAt); } + /** + * Length in days of the window the other summary metrics cover. Always 30. + * + * @param metricsWindowDays the value + * @return this builder + */ + public Builder metricsWindowDays(long metricsWindowDays) { + return set("metrics_window_days", Conv.LONG, metricsWindowDays); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/WebhooksDestinationsUpdateResponse.java b/src/main/java/me/didit/types/WebhooksDestinationsUpdateResponse.java index 0386f6b..081ec23 100644 --- a/src/main/java/me/didit/types/WebhooksDestinationsUpdateResponse.java +++ b/src/main/java/me/didit/types/WebhooksDestinationsUpdateResponse.java @@ -16,7 +16,7 @@ import org.jspecify.annotations.Nullable; public final class WebhooksDestinationsUpdateResponse extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "url", "enabled", "webhook_version", "secret_shared_key", "subscribed_events", "created_at", "updated_at", "summary"); + private static final Set KNOWN_FIELDS = Set.of("uuid", "label", "url", "enabled", "webhook_version", "secret_shared_key", "secret_last4", "previous_secret_expires_at", "subscribed_events", "created_by_email", "created_by_name", "created_at", "updated_at", "health_status", "failing_streak", "failing_since", "last_success_at", "environment", "summary"); private WebhooksDestinationsUpdateResponse(ObjectNode json, Map> files) { super(json, files); @@ -111,6 +111,26 @@ public Optional secretSharedKey() { return Json.optional(json, "secret_shared_key", Conv.STRING); } + /** + * Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional secretLast4() { + return Json.optional(json, "secret_last4", Conv.STRING); + } + + /** + * Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries X-Signature-Previous, X-Signature-V2-Previous and X-Signature-Simple-Previous, made with the replaced secret (see [Signature verification](/integration/webhooks#signature-verification)). null when no rotation happened in the last 24 hours. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional previousSecretExpiresAt() { + return Json.optional(json, "previous_secret_expires_at", Conv.DATE_TIME); + } + /** * Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit [] is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged. * @@ -121,6 +141,26 @@ public Optional> subscribedEvents() { return Json.optional(json, "subscribed_events", Conv.list(Conv.enumeration(WebhookEvent::of))); } + /** + * Email of the Business Console member who created the destination. null for a destination created with an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdByEmail() { + return Json.optional(json, "created_by_email", Conv.STRING); + } + + /** + * Name of the Business Console member who created the destination. null for a destination created with an API key. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdByName() { + return Json.optional(json, "created_by_name", Conv.STRING); + } + /** * The created_at field. * @@ -142,7 +182,57 @@ public Optional updatedAt() { } /** - * The summary field. + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional healthStatus() { + return Json.optional(json, "health_status", Conv.enumeration(WebhooksDestinationsUpdateResponse.HealthStatus::of)); + } + + /** + * How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. 0 when no event has failed since the last success. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failingStreak() { + return Json.optional(json, "failing_streak", Conv.LONG); + } + + /** + * When the first failed attempt of the current failing_streak was made. null when failing_streak is 0. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional failingSince() { + return Json.optional(json, "failing_since", Conv.DATE_TIME); + } + + /** + * When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. null if it has not in that window. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional lastSuccessAt() { + return Json.optional(json, "last_success_at", Conv.DATE_TIME); + } + + /** + * Environment of the application that owns the destination. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional environment() { + return Json.optional(json, "environment", Conv.enumeration(WebhooksDestinationsUpdateResponse.Environment::of)); + } + + /** + * Delivery metrics for the last metrics_window_days (30) days. last_delivery_at is the most recent attempt, whenever it was made. * * @return the value, or empty when the field is absent or null * @throws me.didit.errors.UnexpectedResponseException when the field has another type @@ -224,6 +314,26 @@ public Builder secretSharedKey(String secretSharedKey) { return set("secret_shared_key", Conv.STRING, secretSharedKey); } + /** + * Last four characters of the destination's current signing secret, so you can tell secrets apart. Not enough to verify a signature. + * + * @param secretLast4 the value + * @return this builder + */ + public Builder secretLast4(String secretLast4) { + return set("secret_last4", Conv.STRING, secretLast4); + } + + /** + * Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries X-Signature-Previous, X-Signature-V2-Previous and X-Signature-Simple-Previous, made with the replaced secret (see [Signature verification](/integration/webhooks#signature-verification)). null when no rotation happened in the last 24 hours. + * + * @param previousSecretExpiresAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder previousSecretExpiresAt(@Nullable OffsetDateTime previousSecretExpiresAt) { + return setNullable("previous_secret_expires_at", Conv.DATE_TIME, previousSecretExpiresAt); + } + /** * Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit [] is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged. * @@ -234,6 +344,26 @@ public Builder subscribedEvents(List subscribedEvents) { return set("subscribed_events", Conv.list(Conv.enumeration(WebhookEvent::of)), subscribedEvents); } + /** + * Email of the Business Console member who created the destination. null for a destination created with an API key. + * + * @param createdByEmail the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder createdByEmail(@Nullable String createdByEmail) { + return setNullable("created_by_email", Conv.STRING, createdByEmail); + } + + /** + * Name of the Business Console member who created the destination. null for a destination created with an API key. + * + * @param createdByName the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder createdByName(@Nullable String createdByName) { + return setNullable("created_by_name", Conv.STRING, createdByName); + } + /** * The created_at field. * @@ -255,7 +385,57 @@ public Builder updatedAt(OffsetDateTime updatedAt) { } /** - * The summary field. + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + * @param healthStatus the value + * @return this builder + */ + public Builder healthStatus(WebhooksDestinationsUpdateResponse.HealthStatus healthStatus) { + return set("health_status", Conv.enumeration(WebhooksDestinationsUpdateResponse.HealthStatus::of), healthStatus); + } + + /** + * How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. 0 when no event has failed since the last success. + * + * @param failingStreak the value + * @return this builder + */ + public Builder failingStreak(long failingStreak) { + return set("failing_streak", Conv.LONG, failingStreak); + } + + /** + * When the first failed attempt of the current failing_streak was made. null when failing_streak is 0. + * + * @param failingSince the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder failingSince(@Nullable OffsetDateTime failingSince) { + return setNullable("failing_since", Conv.DATE_TIME, failingSince); + } + + /** + * When the destination last answered a delivery with an HTTP status under 400, within the last 30 days. null if it has not in that window. + * + * @param lastSuccessAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder lastSuccessAt(@Nullable OffsetDateTime lastSuccessAt) { + return setNullable("last_success_at", Conv.DATE_TIME, lastSuccessAt); + } + + /** + * Environment of the application that owns the destination. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhooksDestinationsUpdateResponse.Environment environment) { + return set("environment", Conv.enumeration(WebhooksDestinationsUpdateResponse.Environment::of), environment); + } + + /** + * Delivery metrics for the last metrics_window_days (30) days. last_delivery_at is the most recent attempt, whenever it was made. * * @param summary the value * @return this builder @@ -324,8 +504,100 @@ public boolean isKnown() { } } + /** + * Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class HealthStatus extends StringEnum { + /** The value {@code "healthy"}. */ + public static final HealthStatus HEALTHY = new HealthStatus("healthy"); + + /** The value {@code "degraded"}. */ + public static final HealthStatus DEGRADED = new HealthStatus("degraded"); + + /** The value {@code "failing"}. */ + public static final HealthStatus FAILING = new HealthStatus("failing"); + + private static final List KNOWN_VALUES = List.of(HEALTHY, DEGRADED, FAILING); + + private HealthStatus(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static HealthStatus of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, HealthStatus::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Environment of the application that owns the destination. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Environment extends StringEnum { + /** The value {@code "live"}. */ + public static final Environment LIVE = new Environment("live"); + + /** The value {@code "sandbox"}. */ + public static final Environment SANDBOX = new Environment("sandbox"); + + private static final List KNOWN_VALUES = List.of(LIVE, SANDBOX); + + private Environment(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Environment of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Environment::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Delivery metrics for the last metrics_window_days (30) days. last_delivery_at is the most recent attempt, whenever it was made. + */ public static final class Summary extends JsonModel { - private static final Set KNOWN_FIELDS = Set.of("total_deliveries", "failed_deliveries", "error_rate_percentage", "min_response_time_ms", "avg_response_time_ms", "max_response_time_ms", "last_delivery_at"); + private static final Set KNOWN_FIELDS = Set.of("total_deliveries", "failed_deliveries", "error_rate_percentage", "min_response_time_ms", "avg_response_time_ms", "max_response_time_ms", "last_delivery_at", "metrics_window_days"); private Summary(ObjectNode json, Map> files) { super(json, files); @@ -430,6 +702,16 @@ public Optional lastDeliveryAt() { return Json.optional(json, "last_delivery_at", Conv.DATE_TIME); } + /** + * Length in days of the window the other summary metrics cover. Always 30. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional metricsWindowDays() { + return Json.optional(json, "metrics_window_days", Conv.LONG); + } + @Override protected Set knownFields() { return KNOWN_FIELDS; @@ -513,6 +795,16 @@ public Builder lastDeliveryAt(@Nullable OffsetDateTime lastDeliveryAt) { return setNullable("last_delivery_at", Conv.DATE_TIME, lastDeliveryAt); } + /** + * Length in days of the window the other summary metrics cover. Always 30. + * + * @param metricsWindowDays the value + * @return this builder + */ + public Builder metricsWindowDays(long metricsWindowDays) { + return set("metrics_window_days", Conv.LONG, metricsWindowDays); + } + /** * Builds the value. * diff --git a/src/main/java/me/didit/types/WebhooksUpdateConfigParams.java b/src/main/java/me/didit/types/WebhooksUpdateConfigParams.java new file mode 100644 index 0000000..c930a06 --- /dev/null +++ b/src/main/java/me/didit/types/WebhooksUpdateConfigParams.java @@ -0,0 +1,373 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** Parameters for {@code webhooks.update_config}. */ +public final class WebhooksUpdateConfigParams extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("webhook_url", "webhook_version", "rotate_secret_key", "capture_method", "data_retention_months", "face_retention_policy", "face_retention_days", "allowed_return_hosts"); + + private WebhooksUpdateConfigParams(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WebhooksUpdateConfigParams} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WebhooksUpdateConfigParams fromJson(JsonNode json) { + return new WebhooksUpdateConfigParams(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Legacy application-level webhook URL. Didit uses it only while the application has no enabled destination, and then only for status.updated and data.updated. It does not create a destination, and the next create, update or delete of a destination replaces it. To receive webhooks, create a destination with POST /v3/webhook/destinations/ instead. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional webhookUrl() { + return Json.optional(json, "webhook_url", Conv.STRING); + } + + /** + * Payload version of the legacy webhook_url. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional webhookVersion() { + return Json.optional(json, "webhook_version", Conv.enumeration(WebhooksUpdateConfigParams.WebhookVersion::of)); + } + + /** + * true replaces the application-level secret_shared_key with a new random value at once; the new value is in the response. Destination secrets are not changed. Every call with true generates a new secret, so it is not idempotent. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional rotateSecretKey() { + return Json.optional(json, "rotate_secret_key", Conv.BOOLEAN); + } + + /** + * Legacy application setting. The verification flow does not read it. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional captureMethod() { + return Json.optional(json, "capture_method", Conv.enumeration(WebhooksUpdateConfigParams.CaptureMethod::of)); + } + + /** + * Automatic deletion window in months (a month counts as 30 days), or null for no automatic deletion. A change applies to existing sessions too: every user verification session older than the new window is deleted automatically, and that deletion is irreversible. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional dataRetentionMonths() { + return Json.optional(json, "data_retention_months", Conv.LONG); + } + + /** + * What happens to a session's face embedding when the session is deleted. retain_until_user_deleted needs face_retention_days in the same request or already set. A change applies to session deletions from then on; it creates or purges no existing biometric template. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional faceRetentionPolicy() { + return Json.optional(json, "face_retention_policy", Conv.enumeration(WebhooksUpdateConfigParams.FaceRetentionPolicy::of)); + } + + /** + * Retention period, in days, of biometric templates under retain_until_user_deleted. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional faceRetentionDays() { + return Json.optional(json, "face_retention_days", Conv.LONG); + } + + /** + * Replaces the whole list of Digital ID wallet return hosts. Bare hostnames only, such as shop.example.com: no scheme, port, path or wildcard. Values are lowercased and duplicates are dropped. Send [] to clear the list. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> allowedReturnHosts() { + return Json.optional(json, "allowed_return_hosts", Conv.list(Conv.STRING)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WebhooksUpdateConfigParams}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WebhooksUpdateConfigParams from) { + super(from); + } + + /** + * Legacy application-level webhook URL. Didit uses it only while the application has no enabled destination, and then only for status.updated and data.updated. It does not create a destination, and the next create, update or delete of a destination replaces it. To receive webhooks, create a destination with POST /v3/webhook/destinations/ instead. + * + * @param webhookUrl the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder webhookUrl(@Nullable String webhookUrl) { + return setNullable("webhook_url", Conv.STRING, webhookUrl); + } + + /** + * Payload version of the legacy webhook_url. + * + * @param webhookVersion the value + * @return this builder + */ + public Builder webhookVersion(WebhooksUpdateConfigParams.WebhookVersion webhookVersion) { + return set("webhook_version", Conv.enumeration(WebhooksUpdateConfigParams.WebhookVersion::of), webhookVersion); + } + + /** + * true replaces the application-level secret_shared_key with a new random value at once; the new value is in the response. Destination secrets are not changed. Every call with true generates a new secret, so it is not idempotent. + * + * @param rotateSecretKey the value + * @return this builder + */ + public Builder rotateSecretKey(boolean rotateSecretKey) { + return set("rotate_secret_key", Conv.BOOLEAN, rotateSecretKey); + } + + /** + * Legacy application setting. The verification flow does not read it. + * + * @param captureMethod the value + * @return this builder + */ + public Builder captureMethod(WebhooksUpdateConfigParams.CaptureMethod captureMethod) { + return set("capture_method", Conv.enumeration(WebhooksUpdateConfigParams.CaptureMethod::of), captureMethod); + } + + /** + * Automatic deletion window in months (a month counts as 30 days), or null for no automatic deletion. A change applies to existing sessions too: every user verification session older than the new window is deleted automatically, and that deletion is irreversible. + * + * @param dataRetentionMonths the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder dataRetentionMonths(@Nullable Long dataRetentionMonths) { + return setNullable("data_retention_months", Conv.LONG, dataRetentionMonths); + } + + /** + * What happens to a session's face embedding when the session is deleted. retain_until_user_deleted needs face_retention_days in the same request or already set. A change applies to session deletions from then on; it creates or purges no existing biometric template. + * + * @param faceRetentionPolicy the value + * @return this builder + */ + public Builder faceRetentionPolicy(WebhooksUpdateConfigParams.FaceRetentionPolicy faceRetentionPolicy) { + return set("face_retention_policy", Conv.enumeration(WebhooksUpdateConfigParams.FaceRetentionPolicy::of), faceRetentionPolicy); + } + + /** + * Retention period, in days, of biometric templates under retain_until_user_deleted. + * + * @param faceRetentionDays the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder faceRetentionDays(@Nullable Long faceRetentionDays) { + return setNullable("face_retention_days", Conv.LONG, faceRetentionDays); + } + + /** + * Replaces the whole list of Digital ID wallet return hosts. Bare hostnames only, such as shop.example.com: no scheme, port, path or wildcard. Values are lowercased and duplicates are dropped. Send [] to clear the list. + * + * @param allowedReturnHosts the value + * @return this builder + */ + public Builder allowedReturnHosts(List allowedReturnHosts) { + return set("allowed_return_hosts", Conv.list(Conv.STRING), allowedReturnHosts); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WebhooksUpdateConfigParams} + */ + public WebhooksUpdateConfigParams build() { + return new WebhooksUpdateConfigParams(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Payload version of the legacy webhook_url. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookVersion extends StringEnum { + /** The value {@code "v1"}. */ + public static final WebhookVersion V_1 = new WebhookVersion("v1"); + + /** The value {@code "v2"}. */ + public static final WebhookVersion V_2 = new WebhookVersion("v2"); + + /** The value {@code "v3"}. */ + public static final WebhookVersion V_3 = new WebhookVersion("v3"); + + private static final List KNOWN_VALUES = List.of(V_1, V_2, V_3); + + private WebhookVersion(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookVersion of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookVersion::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Legacy application setting. The verification flow does not read it. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class CaptureMethod extends StringEnum { + /** The value {@code "MOBILE"}. */ + public static final CaptureMethod MOBILE = new CaptureMethod("MOBILE"); + + /** The value {@code "MOBILE_AND_DESKTOP"}. */ + public static final CaptureMethod MOBILE_AND_DESKTOP = new CaptureMethod("MOBILE_AND_DESKTOP"); + + private static final List KNOWN_VALUES = List.of(MOBILE, MOBILE_AND_DESKTOP); + + private CaptureMethod(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static CaptureMethod of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, CaptureMethod::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * What happens to a session's face embedding when the session is deleted. retain_until_user_deleted needs face_retention_days in the same request or already set. A change applies to session deletions from then on; it creates or purges no existing biometric template. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class FaceRetentionPolicy extends StringEnum { + /** The value {@code "delete_with_session"}. */ + public static final FaceRetentionPolicy DELETE_WITH_SESSION = new FaceRetentionPolicy("delete_with_session"); + + /** The value {@code "retain_until_user_deleted"}. */ + public static final FaceRetentionPolicy RETAIN_UNTIL_USER_DELETED = new FaceRetentionPolicy("retain_until_user_deleted"); + + private static final List KNOWN_VALUES = List.of(DELETE_WITH_SESSION, RETAIN_UNTIL_USER_DELETED); + + private FaceRetentionPolicy(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static FaceRetentionPolicy of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, FaceRetentionPolicy::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/WorkflowComplianceChangedEvent.java b/src/main/java/me/didit/types/WorkflowComplianceChangedEvent.java new file mode 100644 index 0000000..19f6203 --- /dev/null +++ b/src/main/java/me/didit/types/WorkflowComplianceChangedEvent.java @@ -0,0 +1,634 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * The compliance status of a published workflow changed after a re-evaluation. One event per workflow. There is no top-level status. + */ +public final class WorkflowComplianceChangedEvent extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("event_id", "timestamp", "created_at", "application_id", "environment", "webhook_type", "workflow_id", "workflow_version", "workflow_label", "transition", "severity", "kb_version"); + + private WorkflowComplianceChangedEvent(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WorkflowComplianceChangedEvent} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WorkflowComplianceChangedEvent fromJson(JsonNode json) { + return new WorkflowComplianceChangedEvent(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String eventId() { + return Json.required(json, "event_id", Conv.STRING); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long timestamp() { + return Json.required(json, "timestamp", Conv.LONG); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long createdAt() { + return Json.required(json, "created_at", Conv.LONG); + } + + /** + * The application the event belongs to. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String applicationId() { + return Json.required(json, "application_id", Conv.STRING); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WebhookEventEnvelope.Environment environment() { + return Json.required(json, "environment", Conv.enumeration(WebhookEventEnvelope.Environment::of)); + } + + /** + * Always workflow.compliance_changed. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WorkflowComplianceChangedEvent.WebhookType webhookType() { + return Json.required(json, "webhook_type", Conv.enumeration(WorkflowComplianceChangedEvent.WebhookType::of)); + } + + /** + * The workflow that changed. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String workflowId() { + return Json.required(json, "workflow_id", Conv.STRING); + } + + /** + * Version that was evaluated. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public long workflowVersion() { + return Json.required(json, "workflow_version", Conv.LONG); + } + + /** + * Display name of the workflow. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowLabel() { + return Json.optional(json, "workflow_label", Conv.STRING); + } + + /** + * The transition field. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WorkflowComplianceChangedEvent.Transition transition() { + return Json.required(json, "transition", Conv.model(WorkflowComplianceChangedEvent.Transition::fromJson)); + } + + /** + * actionable when gaps were detected, indicative for a pending change, informative when gaps were resolved. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WorkflowComplianceChangedEvent.Severity severity() { + return Json.required(json, "severity", Conv.enumeration(WorkflowComplianceChangedEvent.Severity::of)); + } + + /** + * Version of the regulatory knowledge base used. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public String kbVersion() { + return Json.required(json, "kb_version", Conv.STRING); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WorkflowComplianceChangedEvent}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WorkflowComplianceChangedEvent from) { + super(from); + } + + /** + * Identifier of the event. Every destination receives the same value and a redelivery keeps it, so use it to deduplicate. + * + * @param eventId the value + * @return this builder + */ + public Builder eventId(String eventId) { + return set("event_id", Conv.STRING, eventId); + } + + /** + * Unix time in seconds at which this delivery was signed. Equal to the X-Timestamp header; a redelivery carries a new value. + * + * @param timestamp the value + * @return this builder + */ + public Builder timestamp(long timestamp) { + return set("timestamp", Conv.LONG, timestamp); + } + + /** + * Unix time in seconds of the change that produced the event. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(long createdAt) { + return set("created_at", Conv.LONG, createdAt); + } + + /** + * The application the event belongs to. + * + * @param applicationId the value + * @return this builder + */ + public Builder applicationId(String applicationId) { + return set("application_id", Conv.STRING, applicationId); + } + + /** + * sandbox for sandbox sessions and sandbox applications, otherwise live. + * + * @param environment the value + * @return this builder + */ + public Builder environment(WebhookEventEnvelope.Environment environment) { + return set("environment", Conv.enumeration(WebhookEventEnvelope.Environment::of), environment); + } + + /** + * Always workflow.compliance_changed. + * + * @param webhookType the value + * @return this builder + */ + public Builder webhookType(WorkflowComplianceChangedEvent.WebhookType webhookType) { + return set("webhook_type", Conv.enumeration(WorkflowComplianceChangedEvent.WebhookType::of), webhookType); + } + + /** + * The workflow that changed. + * + * @param workflowId the value + * @return this builder + */ + public Builder workflowId(String workflowId) { + return set("workflow_id", Conv.STRING, workflowId); + } + + /** + * Version that was evaluated. + * + * @param workflowVersion the value + * @return this builder + */ + public Builder workflowVersion(long workflowVersion) { + return set("workflow_version", Conv.LONG, workflowVersion); + } + + /** + * Display name of the workflow. + * + * @param workflowLabel the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder workflowLabel(@Nullable String workflowLabel) { + return setNullable("workflow_label", Conv.STRING, workflowLabel); + } + + /** + * The transition field. + * + * @param transition the value + * @return this builder + */ + public Builder transition(WorkflowComplianceChangedEvent.Transition transition) { + return set("transition", Conv.model(WorkflowComplianceChangedEvent.Transition::fromJson), transition); + } + + /** + * actionable when gaps were detected, indicative for a pending change, informative when gaps were resolved. + * + * @param severity the value + * @return this builder + */ + public Builder severity(WorkflowComplianceChangedEvent.Severity severity) { + return set("severity", Conv.enumeration(WorkflowComplianceChangedEvent.Severity::of), severity); + } + + /** + * Version of the regulatory knowledge base used. + * + * @param kbVersion the value + * @return this builder + */ + public Builder kbVersion(String kbVersion) { + return set("kb_version", Conv.STRING, kbVersion); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WorkflowComplianceChangedEvent} + * @throws IllegalStateException when a required field is missing + */ + public WorkflowComplianceChangedEvent build() { + require("event_id", false); + require("timestamp", false); + require("created_at", false); + require("application_id", false); + require("environment", false); + require("webhook_type", false); + require("workflow_id", false); + require("workflow_version", false); + require("workflow_label", true); + require("transition", false); + require("severity", false); + require("kb_version", false); + return new WorkflowComplianceChangedEvent(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Always workflow.compliance_changed. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WebhookType extends StringEnum { + /** The value {@code "workflow.compliance_changed"}. */ + public static final WebhookType WORKFLOW_COMPLIANCE_CHANGED = new WebhookType("workflow.compliance_changed"); + + private static final List KNOWN_VALUES = List.of(WORKFLOW_COMPLIANCE_CHANGED); + + private WebhookType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WebhookType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WebhookType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + public static final class Transition extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("from", "to"); + + private Transition(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code Transition} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static Transition fromJson(JsonNode json) { + return new Transition(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Status before the re-evaluation. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WorkflowComplianceChangedEvent.Transition.From from() { + return Json.required(json, "from", Conv.enumeration(WorkflowComplianceChangedEvent.Transition.From::of)); + } + + /** + * Status after the re-evaluation. + * + * @return the value + * @throws me.didit.errors.UnexpectedResponseException when the field is absent or has another type + */ + public WorkflowComplianceChangedEvent.Transition.To to() { + return Json.required(json, "to", Conv.enumeration(WorkflowComplianceChangedEvent.Transition.To::of)); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link Transition}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(Transition from) { + super(from); + } + + /** + * Status before the re-evaluation. + * + * @param from the value + * @return this builder + */ + public Builder from(WorkflowComplianceChangedEvent.Transition.From from) { + return set("from", Conv.enumeration(WorkflowComplianceChangedEvent.Transition.From::of), from); + } + + /** + * Status after the re-evaluation. + * + * @param to the value + * @return this builder + */ + public Builder to(WorkflowComplianceChangedEvent.Transition.To to) { + return set("to", Conv.enumeration(WorkflowComplianceChangedEvent.Transition.To::of), to); + } + + /** + * Builds the value. + * + * @return a new immutable {@code Transition} + * @throws IllegalStateException when a required field is missing + */ + public Transition build() { + require("from", false); + require("to", false); + return new Transition(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Status before the re-evaluation. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class From extends StringEnum { + /** The value {@code "compliant"}. */ + public static final From COMPLIANT = new From("compliant"); + + /** The value {@code "gaps_detected"}. */ + public static final From GAPS_DETECTED = new From("gaps_detected"); + + /** The value {@code "pending_change"}. */ + public static final From PENDING_CHANGE = new From("pending_change"); + + /** The value {@code "unknown_coverage"}. */ + public static final From UNKNOWN_COVERAGE = new From("unknown_coverage"); + + /** The value {@code "not_evaluated"}. */ + public static final From NOT_EVALUATED = new From("not_evaluated"); + + private static final List KNOWN_VALUES = List.of(COMPLIANT, GAPS_DETECTED, PENDING_CHANGE, UNKNOWN_COVERAGE, NOT_EVALUATED); + + private From(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static From of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, From::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Status after the re-evaluation. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class To extends StringEnum { + /** The value {@code "compliant"}. */ + public static final To COMPLIANT = new To("compliant"); + + /** The value {@code "gaps_detected"}. */ + public static final To GAPS_DETECTED = new To("gaps_detected"); + + /** The value {@code "pending_change"}. */ + public static final To PENDING_CHANGE = new To("pending_change"); + + /** The value {@code "unknown_coverage"}. */ + public static final To UNKNOWN_COVERAGE = new To("unknown_coverage"); + + /** The value {@code "not_evaluated"}. */ + public static final To NOT_EVALUATED = new To("not_evaluated"); + + private static final List KNOWN_VALUES = List.of(COMPLIANT, GAPS_DETECTED, PENDING_CHANGE, UNKNOWN_COVERAGE, NOT_EVALUATED); + + private To(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static To of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, To::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + } + + /** + * actionable when gaps were detected, indicative for a pending change, informative when gaps were resolved. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Severity extends StringEnum { + /** The value {@code "actionable"}. */ + public static final Severity ACTIONABLE = new Severity("actionable"); + + /** The value {@code "indicative"}. */ + public static final Severity INDICATIVE = new Severity("indicative"); + + /** The value {@code "informative"}. */ + public static final Severity INFORMATIVE = new Severity("informative"); + + private static final List KNOWN_VALUES = List.of(ACTIONABLE, INDICATIVE, INFORMATIVE); + + private Severity(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Severity of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Severity::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/types/WorkflowDetail.java b/src/main/java/me/didit/types/WorkflowDetail.java new file mode 100644 index 0000000..57ad033 --- /dev/null +++ b/src/main/java/me/didit/types/WorkflowDetail.java @@ -0,0 +1,688 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.types; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import me.didit.core.Conv; +import me.didit.core.FileUpload; +import me.didit.core.Json; +import me.didit.core.JsonModel; +import me.didit.core.StringEnum; +import org.jspecify.annotations.Nullable; + +/** + * The fields below are typed; the object also carries every other column of the workflow configuration (feature toggles, thresholds, warning actions, allowed documents, callbacks) as additional properties. Fields that do not apply to the workflow_type are null. + */ +public final class WorkflowDetail extends JsonModel { + private static final Set KNOWN_FIELDS = Set.of("uuid", "workflow_id", "workflow_label", "workflow_type", "is_default", "is_archived", "version", "status", "published_at", "is_simple_workflow", "is_editable", "workflow_url", "workflow_graph", "features", "total_price", "min_price", "max_price", "special_add_on_prices", "max_retry_attempts", "retry_window_days", "session_expiration_time", "questionnaire", "kyb_role_settings", "created_at", "updated_at"); + + private WorkflowDetail(ObjectNode json, Map> files) { + super(json, files); + } + + /** + * Reads a {@code WorkflowDetail} from JSON. Unknown fields are kept and readable + * through {@link #_additionalProperties()}. + * + * @param json a JSON object + * @return the parsed value + */ + public static WorkflowDetail fromJson(JsonNode json) { + return new WorkflowDetail(Json.copyObject(json), Map.of()); + } + + /** + * Returns an empty builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a builder holding a copy of every field of this value. + * + * @return a new builder + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Per-version identifier (settings_uuid). Every version of a workflow has its own. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional uuid() { + return Json.optional(json, "uuid", Conv.STRING); + } + + /** + * Stable identifier shared by every version of the workflow. Sessions created with it use the latest published version. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowId() { + return Json.optional(json, "workflow_id", Conv.STRING); + } + + /** + * Display name of the workflow. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowLabel() { + return Json.optional(json, "workflow_label", Conv.STRING); + } + + /** + * Workflow template. null for graph workflows created without a template. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowType() { + return Json.optional(json, "workflow_type", Conv.enumeration(WorkflowDetail.WorkflowType::of)); + } + + /** + * Whether this is the application's default workflow. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isDefault() { + return Json.optional(json, "is_default", Conv.BOOLEAN); + } + + /** + * Archived workflows cannot start new sessions. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isArchived() { + return Json.optional(json, "is_archived", Conv.BOOLEAN); + } + + /** + * Version number within the workflow. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional version() { + return Json.optional(json, "version", Conv.LONG); + } + + /** + * Draft versions are editable; published versions are immutable and used for new sessions. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional status() { + return Json.optional(json, "status", Conv.enumeration(WorkflowDetail.Status::of)); + } + + /** + * When this version was published. null for drafts. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional publishedAt() { + return Json.optional(json, "published_at", Conv.DATE_TIME); + } + + /** + * true when the workflow has no custom workflow_graph. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isSimpleWorkflow() { + return Json.optional(json, "is_simple_workflow", Conv.BOOLEAN); + } + + /** + * true only for draft versions. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional isEditable() { + return Json.optional(json, "is_editable", Conv.BOOLEAN); + } + + /** + * Public URL that starts a session with this workflow. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional workflowUrl() { + return Json.optional(json, "workflow_url", Conv.STRING); + } + + /** + * Graph structure of the workflow (nodes and edges). null for simple workflows. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> workflowGraph() { + return Json.optional(json, "workflow_graph", Conv.OBJECT); + } + + /** + * Enabled features joined with " + ", for example "OCR + LIVENESS". + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional features() { + return Json.optional(json, "features", Conv.STRING); + } + + /** + * Price per session; for graph workflows the most expensive path. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional totalPrice() { + return Json.optional(json, "total_price", Conv.DOUBLE); + } + + /** + * Price of the cheapest path through the workflow. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional minPrice() { + return Json.optional(json, "min_price", Conv.DOUBLE); + } + + /** + * Price of the most expensive path through the workflow. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional maxPrice() { + return Json.optional(json, "max_price", Conv.DOUBLE); + } + + /** + * Organization-specific add-on prices, or null when none are set. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> specialAddOnPrices() { + return Json.optional(json, "special_add_on_prices", Conv.OBJECT); + } + + /** + * Retries allowed after a declined verification. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional maxRetryAttempts() { + return Json.optional(json, "max_retry_attempts", Conv.LONG); + } + + /** + * Days during which the retries are allowed. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional retryWindowDays() { + return Json.optional(json, "retry_window_days", Conv.LONG); + } + + /** + * Seconds before an unfinished session expires. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional sessionExpirationTime() { + return Json.optional(json, "session_expiration_time", Conv.LONG); + } + + /** + * The questionnaire attached to the workflow, or null. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional> questionnaire() { + return Json.optional(json, "questionnaire", Conv.OBJECT); + } + + /** + * Per-role verification settings for KYB key people. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional>> kybRoleSettings() { + return Json.optional(json, "kyb_role_settings", Conv.list(Conv.OBJECT)); + } + + /** + * The created_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional createdAt() { + return Json.optional(json, "created_at", Conv.DATE_TIME); + } + + /** + * The updated_at field. + * + * @return the value, or empty when the field is absent or null + * @throws me.didit.errors.UnexpectedResponseException when the field has another type + */ + public Optional updatedAt() { + return Json.optional(json, "updated_at", Conv.DATE_TIME); + } + + @Override + protected Set knownFields() { + return KNOWN_FIELDS; + } + + /** Builds a {@link WorkflowDetail}. */ + public static final class Builder extends JsonModel.BaseBuilder { + private Builder() {} + + private Builder(WorkflowDetail from) { + super(from); + } + + /** + * Per-version identifier (settings_uuid). Every version of a workflow has its own. + * + * @param uuid the value + * @return this builder + */ + public Builder uuid(String uuid) { + return set("uuid", Conv.STRING, uuid); + } + + /** + * Stable identifier shared by every version of the workflow. Sessions created with it use the latest published version. + * + * @param workflowId the value + * @return this builder + */ + public Builder workflowId(String workflowId) { + return set("workflow_id", Conv.STRING, workflowId); + } + + /** + * Display name of the workflow. + * + * @param workflowLabel the value + * @return this builder + */ + public Builder workflowLabel(String workflowLabel) { + return set("workflow_label", Conv.STRING, workflowLabel); + } + + /** + * Workflow template. null for graph workflows created without a template. + * + * @param workflowType the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder workflowType(WorkflowDetail.@Nullable WorkflowType workflowType) { + return setNullable("workflow_type", Conv.enumeration(WorkflowDetail.WorkflowType::of), workflowType); + } + + /** + * Whether this is the application's default workflow. + * + * @param isDefault the value + * @return this builder + */ + public Builder isDefault(boolean isDefault) { + return set("is_default", Conv.BOOLEAN, isDefault); + } + + /** + * Archived workflows cannot start new sessions. + * + * @param isArchived the value + * @return this builder + */ + public Builder isArchived(boolean isArchived) { + return set("is_archived", Conv.BOOLEAN, isArchived); + } + + /** + * Version number within the workflow. + * + * @param version the value + * @return this builder + */ + public Builder version(long version) { + return set("version", Conv.LONG, version); + } + + /** + * Draft versions are editable; published versions are immutable and used for new sessions. + * + * @param status the value + * @return this builder + */ + public Builder status(WorkflowDetail.Status status) { + return set("status", Conv.enumeration(WorkflowDetail.Status::of), status); + } + + /** + * When this version was published. null for drafts. + * + * @param publishedAt the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder publishedAt(@Nullable OffsetDateTime publishedAt) { + return setNullable("published_at", Conv.DATE_TIME, publishedAt); + } + + /** + * true when the workflow has no custom workflow_graph. + * + * @param isSimpleWorkflow the value + * @return this builder + */ + public Builder isSimpleWorkflow(boolean isSimpleWorkflow) { + return set("is_simple_workflow", Conv.BOOLEAN, isSimpleWorkflow); + } + + /** + * true only for draft versions. + * + * @param isEditable the value + * @return this builder + */ + public Builder isEditable(boolean isEditable) { + return set("is_editable", Conv.BOOLEAN, isEditable); + } + + /** + * Public URL that starts a session with this workflow. + * + * @param workflowUrl the value + * @return this builder + */ + public Builder workflowUrl(String workflowUrl) { + return set("workflow_url", Conv.STRING, workflowUrl); + } + + /** + * Graph structure of the workflow (nodes and edges). null for simple workflows. + * + * @param workflowGraph the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder workflowGraph(@Nullable Map workflowGraph) { + return setAny("workflow_graph", workflowGraph); + } + + /** + * Enabled features joined with " + ", for example "OCR + LIVENESS". + * + * @param features the value + * @return this builder + */ + public Builder features(String features) { + return set("features", Conv.STRING, features); + } + + /** + * Price per session; for graph workflows the most expensive path. + * + * @param totalPrice the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder totalPrice(@Nullable Double totalPrice) { + return setNullable("total_price", Conv.DOUBLE, totalPrice); + } + + /** + * Price of the cheapest path through the workflow. + * + * @param minPrice the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder minPrice(@Nullable Double minPrice) { + return setNullable("min_price", Conv.DOUBLE, minPrice); + } + + /** + * Price of the most expensive path through the workflow. + * + * @param maxPrice the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder maxPrice(@Nullable Double maxPrice) { + return setNullable("max_price", Conv.DOUBLE, maxPrice); + } + + /** + * Organization-specific add-on prices, or null when none are set. + * + * @param specialAddOnPrices the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder specialAddOnPrices(@Nullable Map specialAddOnPrices) { + return setAny("special_add_on_prices", specialAddOnPrices); + } + + /** + * Retries allowed after a declined verification. + * + * @param maxRetryAttempts the value + * @return this builder + */ + public Builder maxRetryAttempts(long maxRetryAttempts) { + return set("max_retry_attempts", Conv.LONG, maxRetryAttempts); + } + + /** + * Days during which the retries are allowed. + * + * @param retryWindowDays the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder retryWindowDays(@Nullable Long retryWindowDays) { + return setNullable("retry_window_days", Conv.LONG, retryWindowDays); + } + + /** + * Seconds before an unfinished session expires. + * + * @param sessionExpirationTime the value + * @return this builder + */ + public Builder sessionExpirationTime(long sessionExpirationTime) { + return set("session_expiration_time", Conv.LONG, sessionExpirationTime); + } + + /** + * The questionnaire attached to the workflow, or null. + * + * @param questionnaire the value, or {@code null} to send JSON null + * @return this builder + */ + public Builder questionnaire(@Nullable Map questionnaire) { + return setAny("questionnaire", questionnaire); + } + + /** + * Per-role verification settings for KYB key people. + * + * @param kybRoleSettings the value + * @return this builder + */ + public Builder kybRoleSettings(List> kybRoleSettings) { + return setAny("kyb_role_settings", kybRoleSettings); + } + + /** + * The created_at field. + * + * @param createdAt the value + * @return this builder + */ + public Builder createdAt(OffsetDateTime createdAt) { + return set("created_at", Conv.DATE_TIME, createdAt); + } + + /** + * The updated_at field. + * + * @param updatedAt the value + * @return this builder + */ + public Builder updatedAt(OffsetDateTime updatedAt) { + return set("updated_at", Conv.DATE_TIME, updatedAt); + } + + /** + * Builds the value. + * + * @return a new immutable {@code WorkflowDetail} + */ + public WorkflowDetail build() { + return new WorkflowDetail(buildJson(), buildFiles()); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Workflow template. null for graph workflows created without a template. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class WorkflowType extends StringEnum { + /** The value {@code "kyc"}. */ + public static final WorkflowType KYC = new WorkflowType("kyc"); + + /** The value {@code "kyb"}. */ + public static final WorkflowType KYB = new WorkflowType("kyb"); + + /** The value {@code "adaptive_age_verification"}. */ + public static final WorkflowType ADAPTIVE_AGE_VERIFICATION = new WorkflowType("adaptive_age_verification"); + + /** The value {@code "biometric_authentication"}. */ + public static final WorkflowType BIOMETRIC_AUTHENTICATION = new WorkflowType("biometric_authentication"); + + /** The value {@code "address_verification"}. */ + public static final WorkflowType ADDRESS_VERIFICATION = new WorkflowType("address_verification"); + + /** The value {@code "questionnaire_verification"}. */ + public static final WorkflowType QUESTIONNAIRE_VERIFICATION = new WorkflowType("questionnaire_verification"); + + /** The value {@code "email_verification"}. */ + public static final WorkflowType EMAIL_VERIFICATION = new WorkflowType("email_verification"); + + /** The value {@code "phone_verification"}. */ + public static final WorkflowType PHONE_VERIFICATION = new WorkflowType("phone_verification"); + + private static final List KNOWN_VALUES = List.of(KYC, KYB, ADAPTIVE_AGE_VERIFICATION, BIOMETRIC_AUTHENTICATION, ADDRESS_VERIFICATION, QUESTIONNAIRE_VERIFICATION, EMAIL_VERIFICATION, PHONE_VERIFICATION); + + private WorkflowType(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static WorkflowType of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, WorkflowType::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } + + /** + * Draft versions are editable; published versions are immutable and used for new sessions. + * + *

An open enum: {@link #of(String)} accepts values this SDK version does not know yet. + */ + public static final class Status extends StringEnum { + /** The value {@code "draft"}. */ + public static final Status DRAFT = new Status("draft"); + + /** The value {@code "published"}. */ + public static final Status PUBLISHED = new Status("published"); + + private static final List KNOWN_VALUES = List.of(DRAFT, PUBLISHED); + + private Status(String value) { + super(value); + } + + /** + * Returns the constant for a known value, or a new instance for any other value. + * + * @param value the string value + * @return the enum value + */ + public static Status of(String value) { + return StringEnum.lookup(KNOWN_VALUES, value, Status::new); + } + + /** + * Returns the values known to this SDK version. + * + * @return the known values + */ + public static List knownValues() { + return KNOWN_VALUES; + } + + @Override + public boolean isKnown() { + return KNOWN_VALUES.contains(this); + } + } +} diff --git a/src/main/java/me/didit/webhooks/SessionEvent.java b/src/main/java/me/didit/webhooks/SessionEvent.java index f90f474..ff3e3e7 100644 --- a/src/main/java/me/didit/webhooks/SessionEvent.java +++ b/src/main/java/me/didit/webhooks/SessionEvent.java @@ -5,7 +5,7 @@ import java.util.Map; import java.util.Optional; import me.didit.core.Conv; -import me.didit.types.SessionsRetrieveDecisionResponse; +import me.didit.types.SessionDecision; /** A verification session event: {@code status.updated} or {@code data.updated}. */ public abstract class SessionEvent extends WebhookEvent { @@ -88,12 +88,12 @@ public final Optional> metadata() { /** * The decision, sent with {@code Approved}, {@code Declined}, {@code In Review} and {@code * Abandoned}. Its fields depend on the application's webhook version; read raw values with {@link - * SessionsRetrieveDecisionResponse#_json()}. + * SessionDecision#_json()}. * * @return the decision */ - public final Optional decision() { - return optional("decision", Conv.model(SessionsRetrieveDecisionResponse::fromJson)); + public final Optional decision() { + return optional("decision", Conv.model(SessionDecision::fromJson)); } /** diff --git a/src/test/java/me/didit/resources/AccountApplicationsResourceTest.java b/src/test/java/me/didit/resources/AccountApplicationsResourceTest.java index 896fd73..f2f8c12 100644 --- a/src/test/java/me/didit/resources/AccountApplicationsResourceTest.java +++ b/src/test/java/me/didit/resources/AccountApplicationsResourceTest.java @@ -14,6 +14,7 @@ import me.didit.testing.SpecExamples; import me.didit.testing.TestJson; import me.didit.testing.TestModels; +import me.didit.types.AccountApplicationsListParams; import me.didit.types.ApplicationCreateRequest; import me.didit.types.ApplicationUpdateRequest; import org.junit.jupiter.api.Test; @@ -26,18 +27,21 @@ final class AccountApplicationsResourceTest { @Test void list() { - JsonNode example = TestJson.parse("{\"ok\":true}"); + AccountApplicationsListParams params = AccountApplicationsListParams.builder().limit(1L).offset(1L).build(); + JsonNode example = SpecExamples.response("account.json", "GET", "/organizations/me/{org_id}/applications/", "200"); transport.respondJson(200, example); - var result = client.account().applications().list("00000000-0000-4000-8000-00000000c91f"); + var result = client.account().applications().list("00000000-0000-4000-8000-00000000c91f", params); RecordedRequest request = transport.only(); assertEquals("GET", request.method()); assertEquals("/organizations/me/00000000-0000-4000-8000-00000000c91f/applications/", request.path()); - assertEquals(Map.of(), request.query()); + assertEquals(Map.ofEntries(Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); assertEquals("Bearer test-account-token", request.header("Authorization")); assertEquals(null, request.header("x-api-key")); assertEquals("https://apx.didit.me", request.origin()); assertEquals(0, request.body().length); - TestJson.assertJsonEquals(example, result); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); } @Test diff --git a/src/test/java/me/didit/resources/AccountOrganizationsResourceTest.java b/src/test/java/me/didit/resources/AccountOrganizationsResourceTest.java index ae66d63..aeab6c2 100644 --- a/src/test/java/me/didit/resources/AccountOrganizationsResourceTest.java +++ b/src/test/java/me/didit/resources/AccountOrganizationsResourceTest.java @@ -14,6 +14,7 @@ import me.didit.testing.SpecExamples; import me.didit.testing.TestJson; import me.didit.testing.TestModels; +import me.didit.types.AccountOrganizationsListParams; import org.junit.jupiter.api.Test; /** One test per operation of {@code account.organizations}, against a stubbed HTTP layer. */ @@ -24,18 +25,20 @@ final class AccountOrganizationsResourceTest { @Test void list() { + AccountOrganizationsListParams params = AccountOrganizationsListParams.builder().limit(1L).offset(1L).build(); JsonNode example = SpecExamples.response("account.json", "GET", "/organizations/me/", "200"); transport.respondJson(200, example); - var result = client.account().organizations().list(); + var result = client.account().organizations().list(params); RecordedRequest request = transport.only(); assertEquals("GET", request.method()); assertEquals("/organizations/me/", request.path()); - assertEquals(Map.of(), request.query()); + assertEquals(Map.ofEntries(Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); assertEquals("Bearer test-account-token", request.header("Authorization")); assertEquals(null, request.header("x-api-key")); assertEquals("https://apx.didit.me", request.origin()); assertEquals(0, request.body().length); - TestJson.assertJsonEquals(example, Json.toNode(result)); - TestModels.readAll(result); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); } } diff --git a/src/test/java/me/didit/resources/AccountResourceTest.java b/src/test/java/me/didit/resources/AccountResourceTest.java index 445a6b5..4fc77d3 100644 --- a/src/test/java/me/didit/resources/AccountResourceTest.java +++ b/src/test/java/me/didit/resources/AccountResourceTest.java @@ -90,6 +90,7 @@ void resendOtp() { assertEquals(null, request.header("x-api-key")); assertEquals(null, request.header("Authorization")); TestJson.assertJsonEquals("{\"email\":\"user@example.com\",\"password\":\"value-password\"}", request.body()); - TestJson.assertJsonEquals(example, result); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); } } diff --git a/src/test/java/me/didit/resources/AmlResourceTest.java b/src/test/java/me/didit/resources/AmlResourceTest.java index d415675..3adfd3d 100644 --- a/src/test/java/me/didit/resources/AmlResourceTest.java +++ b/src/test/java/me/didit/resources/AmlResourceTest.java @@ -43,4 +43,20 @@ void screen() { TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } + + @Test + void retrieveDatasetFreshness() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/aml/dataset-freshness/", "200"); + transport.respondJson(200, example); + var result = client.aml().retrieveDatasetFreshness(); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/aml/dataset-freshness/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } } diff --git a/src/test/java/me/didit/resources/AnalyticsResourceTest.java b/src/test/java/me/didit/resources/AnalyticsResourceTest.java new file mode 100644 index 0000000..78b406f --- /dev/null +++ b/src/test/java/me/didit/resources/AnalyticsResourceTest.java @@ -0,0 +1,43 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.AnalyticsRetrieveParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code analytics}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class AnalyticsResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void retrieve() { + AnalyticsRetrieveParams params = AnalyticsRetrieveParams.builder().startDate(LocalDate.parse("2026-11-01")).endDate(LocalDate.parse("2026-11-01")).scope(AnalyticsRetrieveParams.Scope.HOME).widgetKeys("value-widget-keys").workflowIds("value-workflow-ids").versions("value-versions").branchPathIds("value-branch-path-ids").tag("value-tag").transactionType("value-transaction-type").actionType("value-action-type").build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/organization/{organization_id}/application/{application_id}/analytics/", "200"); + transport.respondJson(200, example); + var result = client.analytics().retrieve(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/organization/00000000-0000-4000-8000-0000000000a1/application/00000000-0000-4000-8000-0000000000a2/analytics/", request.path()); + assertEquals(Map.ofEntries(Map.entry("start_date", List.of("2026-11-01")), Map.entry("end_date", List.of("2026-11-01")), Map.entry("scope", List.of("home")), Map.entry("widget_keys", List.of("value-widget-keys")), Map.entry("workflow_ids", List.of("value-workflow-ids")), Map.entry("versions", List.of("value-versions")), Map.entry("branch_path_ids", List.of("value-branch-path-ids")), Map.entry("tag", List.of("value-tag")), Map.entry("transaction_type", List.of("value-transaction-type")), Map.entry("action_type", List.of("value-action-type"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } +} diff --git a/src/test/java/me/didit/resources/BlocklistResourceTest.java b/src/test/java/me/didit/resources/BlocklistResourceTest.java new file mode 100644 index 0000000..469045a --- /dev/null +++ b/src/test/java/me/didit/resources/BlocklistResourceTest.java @@ -0,0 +1,79 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.BlocklistAddParams; +import me.didit.types.BlocklistListParams; +import me.didit.types.BlocklistRemoveParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code blocklist}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class BlocklistResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void list() { + BlocklistListParams params = BlocklistListParams.builder().itemType(BlocklistListParams.ItemType.FACE).limit(1L).offset(1L).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/blocklist/", "200"); + transport.respondJson(200, example); + var result = client.blocklist().list(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/blocklist/", request.path()); + assertEquals(Map.ofEntries(Map.entry("item_type", List.of("face")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void add() { + BlocklistAddParams params = BlocklistAddParams.builder().sessionId("00000000-0000-4000-8000-00000000169c").blocklistFace(true).blocklistDocument(true).blocklistPhone(true).blocklistEmail(true).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/blocklist/add/", "200"); + transport.respondJson(200, example); + var result = client.blocklist().add(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/blocklist/add/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"session_id\":\"00000000-0000-4000-8000-00000000169c\",\"blocklist_face\":true,\"blocklist_document\":true,\"blocklist_phone\":true,\"blocklist_email\":true}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void remove() { + BlocklistRemoveParams params = BlocklistRemoveParams.builder().sessionId("00000000-0000-4000-8000-00000000169c").unblockFace(true).unblockDocument(true).unblockPhone(true).unblockEmail(true).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/blocklist/remove/", "200"); + transport.respondJson(200, example); + var result = client.blocklist().remove(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/blocklist/remove/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"session_id\":\"00000000-0000-4000-8000-00000000169c\",\"unblock_face\":true,\"unblock_document\":true,\"unblock_phone\":true,\"unblock_email\":true}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } +} diff --git a/src/test/java/me/didit/resources/BusinessesResourceTest.java b/src/test/java/me/didit/resources/BusinessesResourceTest.java index be32178..ad41414 100644 --- a/src/test/java/me/didit/resources/BusinessesResourceTest.java +++ b/src/test/java/me/didit/resources/BusinessesResourceTest.java @@ -19,10 +19,11 @@ import me.didit.types.BusinessesCreateParams; import me.didit.types.BusinessesListParams; import me.didit.types.BusinessesSetAmlMonitoringParams; -import me.didit.types.BusinessesSetOngoingMonitoringParams; import me.didit.types.BusinessesSetSessionOngoingMonitoringParams; import me.didit.types.BusinessesUpdateParams; import me.didit.types.BusinessesUpdateStatusParams; +import me.didit.types.EntityStatusUpdateRequest; +import me.didit.types.OngoingMonitoringToggleRequest; import org.junit.jupiter.api.Test; /** One test per operation of {@code businesses}, against a stubbed HTTP layer. */ @@ -67,7 +68,7 @@ void retrieve() { @Test void update() { - BusinessesUpdateParams params = BusinessesUpdateParams.builder().displayName("value-display-name").legalName("value-legal-name").registrationNumber("value-registration-number").countryCode("value-country-code").status(BusinessesUpdateParams.Status.ACTIVE).metadata(Map.of("key", "value")).build(); + BusinessesUpdateParams params = BusinessesUpdateParams.builder().displayName("value-display-name").legalName("value-legal-name").registrationNumber("value-registration-number").countryCode("value-country-code").status(BusinessesUpdateParams.Status.ACTIVE).metadata(Map.of("key", "value")).reason("value-reason").build(); JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/businesses/{vendor_data}/", "200"); transport.respondJson(200, example); var result = client.businesses().update("00000000-0000-4000-8000-00000000a594", params); @@ -77,14 +78,14 @@ void update() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"display_name\":\"value-display-name\",\"legal_name\":\"value-legal-name\",\"registration_number\":\"value-registration-number\",\"country_code\":\"value-country-code\",\"status\":\"ACTIVE\",\"metadata\":{\"key\":\"value\"}}", request.body()); + TestJson.assertJsonEquals("{\"display_name\":\"value-display-name\",\"legal_name\":\"value-legal-name\",\"registration_number\":\"value-registration-number\",\"country_code\":\"value-country-code\",\"status\":\"ACTIVE\",\"metadata\":{\"key\":\"value\"},\"reason\":\"value-reason\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @Test void updateStatus() { - BusinessesUpdateStatusParams params = BusinessesUpdateStatusParams.builder().status(BusinessesUpdateStatusParams.Status.ACTIVE).build(); + BusinessesUpdateStatusParams params = BusinessesUpdateStatusParams.builder().status(EntityStatusUpdateRequest.Status.ACTIVE).reason("value-reason").build(); JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/businesses/{vendor_data}/update-status/", "200"); transport.respondJson(200, example); var result = client.businesses().updateStatus("00000000-0000-4000-8000-0000000104c4", params); @@ -94,7 +95,7 @@ void updateStatus() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"status\":\"ACTIVE\"}", request.body()); + TestJson.assertJsonEquals("{\"status\":\"ACTIVE\",\"reason\":\"value-reason\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @@ -153,7 +154,7 @@ void create() { @Test void setOngoingMonitoring() { - BusinessesSetOngoingMonitoringParams params = BusinessesSetOngoingMonitoringParams.builder().isEnabled(true).estimateOnly(true).updateAll(true).filterSearch("value-filter-search").filterStatus(List.of("value-filter-status")).filterCountry("value-filter-country").filterDateFrom(LocalDate.parse("2026-11-01")).filterDateTo(LocalDate.parse("2026-11-01")).diditInternalIdList(List.of("00000000-0000-4000-8000-0000000068ce")).build(); + OngoingMonitoringToggleRequest params = OngoingMonitoringToggleRequest.builder().isEnabled(true).estimateOnly(true).updateAll(true).filterSearch("value-filter-search").filterStatus(List.of("value-filter-status")).filterCountry("value-filter-country").filterDateFrom(LocalDate.parse("2026-11-01")).filterDateTo(LocalDate.parse("2026-11-01")).diditInternalIdList(List.of("00000000-0000-4000-8000-0000000068ce")).build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(200, example); var result = client.businesses().setOngoingMonitoring(params); @@ -234,4 +235,19 @@ void listNetworks() { TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } + + @Test + void generateSessionPdf() { + transport.respondBinary(200, "application/pdf", new byte[] {37, 80, 68, 70}); + var result = client.businesses().generateSessionPdf("00000000-0000-4000-8000-00000002122a"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/organization/00000000-0000-4000-8000-0000000000a1/application/00000000-0000-4000-8000-0000000000a2/businesses/00000000-0000-4000-8000-00000002122a/pdf/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals("application/pdf, application/json", request.header("Accept")); + assertEquals(0, request.body().length); + assertArrayEquals(new byte[] {37, 80, 68, 70}, result.bytes()); + } } diff --git a/src/test/java/me/didit/resources/CasesBlueprintsResourceTest.java b/src/test/java/me/didit/resources/CasesBlueprintsResourceTest.java index 28d149c..0ce61ad 100644 --- a/src/test/java/me/didit/resources/CasesBlueprintsResourceTest.java +++ b/src/test/java/me/didit/resources/CasesBlueprintsResourceTest.java @@ -141,7 +141,6 @@ void installPresets() { assertEquals("https://verification.didit.me", request.origin()); TestJson.assertJsonEquals("{\"preset_keys\":[\"value-preset-keys\"]}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); - assertEquals(200, result.statusCode()); TestModels.readAll(result); } diff --git a/src/test/java/me/didit/resources/CasesRequestsResourceTest.java b/src/test/java/me/didit/resources/CasesRequestsResourceTest.java index 9d9dc6e..90b4a68 100644 --- a/src/test/java/me/didit/resources/CasesRequestsResourceTest.java +++ b/src/test/java/me/didit/resources/CasesRequestsResourceTest.java @@ -126,4 +126,20 @@ void preview() { TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } + + @Test + void remind() { + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/requests/{request_uuid}/remind/", "200"); + transport.respondJson(200, example); + var result = client.cases().requests().remind("00000000-0000-4000-8000-00000000bd2b", "00000000-0000-4000-8000-00000000e986"); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/organization/00000000-0000-4000-8000-0000000000a1/application/00000000-0000-4000-8000-0000000000a2/cases/00000000-0000-4000-8000-00000000bd2b/requests/00000000-0000-4000-8000-00000000e986/remind/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } } diff --git a/src/test/java/me/didit/resources/CasesResourceTest.java b/src/test/java/me/didit/resources/CasesResourceTest.java index 2eb9f87..791ed18 100644 --- a/src/test/java/me/didit/resources/CasesResourceTest.java +++ b/src/test/java/me/didit/resources/CasesResourceTest.java @@ -97,7 +97,7 @@ void retrieve() { @Test void update() { - CasesUpdateParams params = CasesUpdateParams.builder().title("value-title").priority(CasesUpdateParams.Priority.LOW).dueAt(OffsetDateTime.parse("2026-11-01T10:15:30Z")).tags(List.of("value-tags")).build(); + CasesUpdateParams params = CasesUpdateParams.builder().title("value-title").priority(CasesUpdateParams.Priority.LOW).dueAt(OffsetDateTime.parse("2026-11-01T10:15:30Z")).tags(List.of("value-tags")).team("00000000-0000-4000-8000-000000000415").build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(200, example); var result = client.cases().update("00000000-0000-4000-8000-000000005e20", params); @@ -107,7 +107,7 @@ void update() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"title\":\"value-title\",\"priority\":\"LOW\",\"due_at\":\"2026-11-01T10:15:30Z\",\"tags\":[\"value-tags\"]}", request.body()); + TestJson.assertJsonEquals("{\"title\":\"value-title\",\"priority\":\"LOW\",\"due_at\":\"2026-11-01T10:15:30Z\",\"tags\":[\"value-tags\"],\"team\":\"00000000-0000-4000-8000-000000000415\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @@ -144,7 +144,7 @@ void assign() { @Test void resolve() { - CasesResolveParams params = CasesResolveParams.builder().resolution(CasesResolveParams.Resolution.FALSE_POSITIVE).note("value-note").build(); + CasesResolveParams params = CasesResolveParams.builder().resolution(CasesResolveParams.Resolution.FALSE_POSITIVE).note("value-note").checker("00000000-0000-4000-8000-000000000b81").build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(200, example); var result = client.cases().resolve("00000000-0000-4000-8000-00000000680d", params); @@ -154,7 +154,7 @@ void resolve() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"resolution\":\"FALSE_POSITIVE\",\"note\":\"value-note\"}", request.body()); + TestJson.assertJsonEquals("{\"resolution\":\"FALSE_POSITIVE\",\"note\":\"value-note\",\"checker\":\"00000000-0000-4000-8000-000000000b81\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } diff --git a/src/test/java/me/didit/resources/CustomizationResourceTest.java b/src/test/java/me/didit/resources/CustomizationResourceTest.java index e8f794f..49df3b8 100644 --- a/src/test/java/me/didit/resources/CustomizationResourceTest.java +++ b/src/test/java/me/didit/resources/CustomizationResourceTest.java @@ -42,7 +42,7 @@ void retrieve() { @Test void update() { - CustomizationUpdateParams params = CustomizationUpdateParams.builder().imageSquare(FileUpload.of(new byte[] {37, 80, 68, 70}, "image_square.pdf", "application/pdf")).imageRectangular(FileUpload.of(new byte[] {37, 80, 68, 70}, "image_rectangular.pdf", "application/pdf")).imageFavicon(FileUpload.of(new byte[] {37, 80, 68, 70}, "image_favicon.pdf", "application/pdf")).build(); + CustomizationUpdateParams params = CustomizationUpdateParams.builder().imageSquare(FileUpload.of(new byte[] {37, 80, 68, 70}, "image_square.pdf", "application/pdf")).imageRectangular(FileUpload.of(new byte[] {37, 80, 68, 70}, "image_rectangular.pdf", "application/pdf")).imageRectangularDark(FileUpload.of(new byte[] {37, 80, 68, 70}, "image_rectangular_dark.pdf", "application/pdf")).imageFavicon(FileUpload.of(new byte[] {37, 80, 68, 70}, "image_favicon.pdf", "application/pdf")).build(); JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/customization/", "200"); transport.respondJson(200, example); var result = client.customization().update(params); @@ -53,7 +53,7 @@ void update() { assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); assertEquals(Map.of(), request.multipartFields()); - assertEquals(Map.ofEntries(Map.entry("image_square", List.of("image_square.pdf")), Map.entry("image_rectangular", List.of("image_rectangular.pdf")), Map.entry("image_favicon", List.of("image_favicon.pdf"))), request.multipartFiles()); + assertEquals(Map.ofEntries(Map.entry("image_square", List.of("image_square.pdf")), Map.entry("image_rectangular", List.of("image_rectangular.pdf")), Map.entry("image_rectangular_dark", List.of("image_rectangular_dark.pdf")), Map.entry("image_favicon", List.of("image_favicon.pdf"))), request.multipartFiles()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } diff --git a/src/test/java/me/didit/resources/InvestigatorResourceTest.java b/src/test/java/me/didit/resources/InvestigatorResourceTest.java new file mode 100644 index 0000000..15d7a11 --- /dev/null +++ b/src/test/java/me/didit/resources/InvestigatorResourceTest.java @@ -0,0 +1,270 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.InvestigatorAcceptRequest; +import me.didit.types.InvestigatorBatchAcceptRequest; +import me.didit.types.InvestigatorExportActivityParams; +import me.didit.types.InvestigatorListActivityParams; +import me.didit.types.InvestigatorListQueueParams; +import me.didit.types.InvestigatorOverrideRequest; +import me.didit.types.InvestigatorRetrieveDashboardParams; +import me.didit.types.InvestigatorRetrievePerformanceParams; +import me.didit.types.InvestigatorRetrieveSummaryParams; +import me.didit.types.InvestigatorSettings; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code investigator}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class InvestigatorResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void retrieveSummary() { + InvestigatorRetrieveSummaryParams params = InvestigatorRetrieveSummaryParams.builder().scope(InvestigatorRetrieveSummaryParams.Scope.ALL).timezone("value-timezone").build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().retrieveSummary(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/summary/", request.path()); + assertEquals(Map.ofEntries(Map.entry("scope", List.of("all")), Map.entry("timezone", List.of("value-timezone"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void listQueue() { + InvestigatorListQueueParams params = InvestigatorListQueueParams.builder().scope(InvestigatorListQueueParams.Scope.ALL).kind("value-kind").queue(InvestigatorListQueueParams.Queue.KYC).recommendation(InvestigatorListQueueParams.Recommendation.APPROVE).confidence(InvestigatorListQueueParams.Confidence.HIGH).ready(true).search("value-search").limit(1L).offset(1L).build(); + JsonNode example = TestJson.parse("{\"count\":0,\"next\":null,\"previous\":null,\"results\":[]}"); + transport.respondJson(200, example); + var result = client.investigator().listQueue(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/queue/", request.path()); + assertEquals(Map.ofEntries(Map.entry("scope", List.of("all")), Map.entry("kind", List.of("value-kind")), Map.entry("queue", List.of("kyc")), Map.entry("recommendation", List.of("approve")), Map.entry("confidence", List.of("high")), Map.entry("ready", List.of("true")), Map.entry("search", List.of("value-search")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void retrieveRun() { + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().retrieveRun("00000000-0000-4000-8000-00000000ce46"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/runs/00000000-0000-4000-8000-00000000ce46/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void acceptRun() { + InvestigatorAcceptRequest params = InvestigatorAcceptRequest.builder().narrative("value-narrative").sendEmail(true).emailAddress("user@example.com").emailLanguage("value-email-language").build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().acceptRun("00000000-0000-4000-8000-00000000b2fd", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/investigator/runs/00000000-0000-4000-8000-00000000b2fd/accept/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"narrative\":\"value-narrative\",\"send_email\":true,\"email_address\":\"user@example.com\",\"email_language\":\"value-email-language\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void overrideRun() { + InvestigatorOverrideRequest params = InvestigatorOverrideRequest.builder().action(InvestigatorOverrideRequest.Action.APPROVE).category(InvestigatorOverrideRequest.Category.MISSING_CONTEXT).reason("value-reason").sendEmail(true).emailAddress("user@example.com").emailLanguage("value-email-language").build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().overrideRun("00000000-0000-4000-8000-00000000cdb1", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/investigator/runs/00000000-0000-4000-8000-00000000cdb1/override/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"action\":\"approve\",\"category\":\"missing_context\",\"reason\":\"value-reason\",\"send_email\":true,\"email_address\":\"user@example.com\",\"email_language\":\"value-email-language\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void batchAcceptRuns() { + InvestigatorBatchAcceptRequest params = InvestigatorBatchAcceptRequest.builder().runIds(List.of("00000000-0000-4000-8000-000000000bac")).build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().batchAcceptRuns(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/investigator/runs/batch-accept/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"run_ids\":[\"00000000-0000-4000-8000-000000000bac\"]}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void retrieveTransaction() { + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().retrieveTransaction("00000000-0000-4000-8000-00000001d465"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/transactions/00000000-0000-4000-8000-00000001d465/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void listGuardrails() { + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().listGuardrails(); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/guardrails/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void retrieveDashboard() { + InvestigatorRetrieveDashboardParams params = InvestigatorRetrieveDashboardParams.builder().scope(InvestigatorRetrieveDashboardParams.Scope.ALL).build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().retrieveDashboard(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/dashboard/", request.path()); + assertEquals(Map.ofEntries(Map.entry("scope", List.of("all"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void retrievePerformance() { + InvestigatorRetrievePerformanceParams params = InvestigatorRetrievePerformanceParams.builder().scope(InvestigatorRetrievePerformanceParams.Scope.ALL).period(InvestigatorRetrievePerformanceParams.Period._7).build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().retrievePerformance(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/performance/", request.path()); + assertEquals(Map.ofEntries(Map.entry("scope", List.of("all")), Map.entry("period", List.of("7"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void listActivity() { + InvestigatorListActivityParams params = InvestigatorListActivityParams.builder().scope(InvestigatorListActivityParams.Scope.ALL).group(InvestigatorListActivityParams.Group.AGENT).actor("value-actor").since(OffsetDateTime.parse("2026-11-01T10:15:30Z")).until(OffsetDateTime.parse("2026-11-01T10:15:30Z")).limit(1L).offset(1L).build(); + JsonNode example = TestJson.parse("{\"count\":0,\"next\":null,\"previous\":null,\"results\":[]}"); + transport.respondJson(200, example); + var result = client.investigator().listActivity(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/activity/", request.path()); + assertEquals(Map.ofEntries(Map.entry("scope", List.of("all")), Map.entry("group", List.of("agent")), Map.entry("actor", List.of("value-actor")), Map.entry("since", List.of("2026-11-01T10:15:30Z")), Map.entry("until", List.of("2026-11-01T10:15:30Z")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void exportActivity() { + InvestigatorExportActivityParams params = InvestigatorExportActivityParams.builder().scope(InvestigatorExportActivityParams.Scope.ALL).group(InvestigatorExportActivityParams.Group.AGENT).actor("value-actor").since(OffsetDateTime.parse("2026-11-01T10:15:30Z")).until(OffsetDateTime.parse("2026-11-01T10:15:30Z")).build(); + transport.respondBinary(200, "text/csv", new byte[] {37, 80, 68, 70}); + var result = client.investigator().exportActivity(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/activity/export/", request.path()); + assertEquals(Map.ofEntries(Map.entry("scope", List.of("all")), Map.entry("group", List.of("agent")), Map.entry("actor", List.of("value-actor")), Map.entry("since", List.of("2026-11-01T10:15:30Z")), Map.entry("until", List.of("2026-11-01T10:15:30Z"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals("text/csv, application/json", request.header("Accept")); + assertEquals(0, request.body().length); + assertArrayEquals(new byte[] {37, 80, 68, 70}, result.bytes()); + } + + @Test + void retrieveSettings() { + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().retrieveSettings(); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/investigator/settings/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void updateSettings() { + InvestigatorSettings params = InvestigatorSettings.builder().stage(InvestigatorSettings.Stage.NONE).stageChangedAt(OffsetDateTime.parse("2026-11-01T10:15:30Z")).stageChangedBy("value-stage-changed-by").monthlySpendLimit("value-monthly-spend-limit").spendLimit(InvestigatorSettings.SpendLimit.builder().enabled(true).limit("value-limit").build()).build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.investigator().updateSettings(params); + RecordedRequest request = transport.only(); + assertEquals("PATCH", request.method()); + assertEquals("/v3/investigator/settings/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"stage\":\"none\",\"stage_changed_at\":\"2026-11-01T10:15:30Z\",\"stage_changed_by\":\"value-stage-changed-by\",\"monthly_spend_limit\":\"value-monthly-spend-limit\",\"spend_limit\":{\"enabled\":true,\"limit\":\"value-limit\"}}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } +} diff --git a/src/test/java/me/didit/resources/KybResourceTest.java b/src/test/java/me/didit/resources/KybResourceTest.java index 3ff6dfb..05d73dc 100644 --- a/src/test/java/me/didit/resources/KybResourceTest.java +++ b/src/test/java/me/didit/resources/KybResourceTest.java @@ -14,10 +14,8 @@ import me.didit.testing.SpecExamples; import me.didit.testing.TestJson; import me.didit.testing.TestModels; -import me.didit.types.KybListShareholdersParams; -import me.didit.types.KybListUbosParams; +import me.didit.types.KYBRegistryCandidateRequest; import me.didit.types.KybSearchParams; -import me.didit.types.KybSelectParams; import org.junit.jupiter.api.Test; /** One test per operation of {@code kyb}, against a stubbed HTTP layer. */ @@ -28,7 +26,7 @@ final class KybResourceTest { @Test void search() { - KybSearchParams params = KybSearchParams.builder().countryCode("value-country-code").name("value-name").registrationNumber("value-registration-number").searchType(KybSearchParams.SearchType.CONTAINS).vendorData("value-vendor-data").metadata(Map.of("key", "value")).webhookUrl("https://example.com/callback").build(); + KybSearchParams params = KybSearchParams.builder().countryCode("value-country-code").name("value-name").registrationNumber("value-registration-number").tier(KybSearchParams.Tier.BASIC).searchType(KybSearchParams.SearchType.CONTAINS).vendorData("value-vendor-data").metadata(Map.of("key", "value")).webhookUrl("https://example.com/callback").build(); JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/kyb/search/", "200"); transport.respondJson(200, example); var result = client.kyb().search(params); @@ -38,14 +36,14 @@ void search() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"country_code\":\"value-country-code\",\"name\":\"value-name\",\"registration_number\":\"value-registration-number\",\"search_type\":\"contains\",\"vendor_data\":\"value-vendor-data\",\"metadata\":{\"key\":\"value\"},\"webhook_url\":\"https://example.com/callback\"}", request.body()); + TestJson.assertJsonEquals("{\"country_code\":\"value-country-code\",\"name\":\"value-name\",\"registration_number\":\"value-registration-number\",\"tier\":\"basic\",\"search_type\":\"contains\",\"vendor_data\":\"value-vendor-data\",\"metadata\":{\"key\":\"value\"},\"webhook_url\":\"https://example.com/callback\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @Test void select() { - KybSelectParams params = KybSelectParams.builder().kybResponseId("value-kyb-response-id").vendorData("value-vendor-data").metadata(Map.of("key", "value")).saveApiRequest(true).build(); + KYBRegistryCandidateRequest params = KYBRegistryCandidateRequest.builder().kybResponseId("value-kyb-response-id").vendorData("value-vendor-data").metadata(Map.of("key", "value")).saveApiRequest(true).build(); JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/kyb/select/", "200"); transport.respondJson(200, example); var result = client.kyb().select(params); @@ -62,7 +60,7 @@ void select() { @Test void listShareholders() { - KybListShareholdersParams params = KybListShareholdersParams.builder().kybResponseId("value-kyb-response-id").vendorData("value-vendor-data").metadata(Map.of("key", "value")).saveApiRequest(true).build(); + KYBRegistryCandidateRequest params = KYBRegistryCandidateRequest.builder().kybResponseId("value-kyb-response-id").vendorData("value-vendor-data").metadata(Map.of("key", "value")).saveApiRequest(true).build(); JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/kyb/shareholders/", "200"); transport.respondJson(200, example); var result = client.kyb().listShareholders(params); @@ -79,7 +77,7 @@ void listShareholders() { @Test void listUbos() { - KybListUbosParams params = KybListUbosParams.builder().kybResponseId("value-kyb-response-id").vendorData("value-vendor-data").metadata(Map.of("key", "value")).saveApiRequest(true).build(); + KYBRegistryCandidateRequest params = KYBRegistryCandidateRequest.builder().kybResponseId("value-kyb-response-id").vendorData("value-vendor-data").metadata(Map.of("key", "value")).saveApiRequest(true).build(); JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/kyb/ubo/", "200"); transport.respondJson(200, example); var result = client.kyb().listUbos(params); diff --git a/src/test/java/me/didit/resources/ListsEntriesResourceTest.java b/src/test/java/me/didit/resources/ListsEntriesResourceTest.java new file mode 100644 index 0000000..2eabed7 --- /dev/null +++ b/src/test/java/me/didit/resources/ListsEntriesResourceTest.java @@ -0,0 +1,92 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.ListsEntriesCreateParams; +import me.didit.types.ListsEntriesListParams; +import me.didit.types.ListsEntriesUploadFaceParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code lists.entries}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class ListsEntriesResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void list() { + ListsEntriesListParams params = ListsEntriesListParams.builder().search("value-search").referenceSessionId("00000000-0000-4000-8000-000000005673").limit(1L).offset(1L).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/lists/{list_uuid}/entries/", "200"); + transport.respondJson(200, example); + var result = client.lists().entries().list("00000000-0000-4000-8000-000000009c81", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/lists/00000000-0000-4000-8000-000000009c81/entries/", request.path()); + assertEquals(Map.ofEntries(Map.entry("search", List.of("value-search")), Map.entry("reference_session_id", List.of("00000000-0000-4000-8000-000000005673")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void create() { + ListsEntriesCreateParams params = ListsEntriesCreateParams.builder().value("value-value").referenceSessionId("00000000-0000-4000-8000-000000005673").referenceObjectUuid("00000000-0000-4000-8000-000000005ee9").faceImageSource(ListsEntriesCreateParams.FaceImageSource.LIVENESS).referenceNodeId("value-reference-node-id").displayLabel("value-display-label").comment("value-comment").metadata(ListsEntriesCreateParams.Metadata.builder().referenceType(ListsEntriesCreateParams.Metadata.ReferenceType.TRANSACTION).referenceId("value-reference-id").build()).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/lists/{list_uuid}/entries/", "201"); + transport.respondJson(201, example); + var result = client.lists().entries().create("00000000-0000-4000-8000-00000000b26a", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/lists/00000000-0000-4000-8000-00000000b26a/entries/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"value\":\"value-value\",\"reference_session_id\":\"00000000-0000-4000-8000-000000005673\",\"reference_object_uuid\":\"00000000-0000-4000-8000-000000005ee9\",\"face_image_source\":\"liveness\",\"reference_node_id\":\"value-reference-node-id\",\"display_label\":\"value-display-label\",\"comment\":\"value-comment\",\"metadata\":{\"reference_type\":\"transaction\",\"reference_id\":\"value-reference-id\"}}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void delete() { + transport.respond(204, ""); + client.lists().entries().delete("00000000-0000-4000-8000-00000000b268", "00000000-0000-4000-8000-00000000c011"); + RecordedRequest request = transport.only(); + assertEquals("DELETE", request.method()); + assertEquals("/v3/lists/00000000-0000-4000-8000-00000000b268/entries/00000000-0000-4000-8000-00000000c011/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + } + + @Test + void uploadFace() { + ListsEntriesUploadFaceParams params = ListsEntriesUploadFaceParams.builder().image("value-image").comment("value-comment").build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/lists/{list_uuid}/entries/face-upload/", "201"); + transport.respondJson(201, example); + var result = client.lists().entries().uploadFace("00000000-0000-4000-8000-00000000f2d3", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/lists/00000000-0000-4000-8000-00000000f2d3/entries/face-upload/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"image\":\"value-image\",\"comment\":\"value-comment\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } +} diff --git a/src/test/java/me/didit/resources/ListsResourceTest.java b/src/test/java/me/didit/resources/ListsResourceTest.java new file mode 100644 index 0000000..6e731ae --- /dev/null +++ b/src/test/java/me/didit/resources/ListsResourceTest.java @@ -0,0 +1,108 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.ListsCreateParams; +import me.didit.types.ListsListParams; +import me.didit.types.ListsUpdateParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code lists}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class ListsResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void list() { + ListsListParams params = ListsListParams.builder().listType(ListsListParams.ListType.BLOCKLIST).entryType(ListsListParams.EntryType.FACE).ordering(ListsListParams.Ordering.CREATED_AT).limit(1L).offset(1L).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/lists/", "200"); + transport.respondJson(200, example); + var result = client.lists().list(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/lists/", request.path()); + assertEquals(Map.ofEntries(Map.entry("list_type", List.of("blocklist")), Map.entry("entry_type", List.of("face")), Map.entry("ordering", List.of("-created_at")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void create() { + ListsCreateParams params = ListsCreateParams.builder().name("value-name").description("value-description").listType(ListsCreateParams.ListType.ALLOWLIST).entryType(ListsCreateParams.EntryType.FACE).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/lists/", "201"); + transport.respondJson(201, example); + var result = client.lists().create(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/lists/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"name\":\"value-name\",\"description\":\"value-description\",\"list_type\":\"allowlist\",\"entry_type\":\"face\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void retrieve() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/lists/{list_uuid}/", "200"); + transport.respondJson(200, example); + var result = client.lists().retrieve("00000000-0000-4000-8000-0000000073c6"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/lists/00000000-0000-4000-8000-0000000073c6/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void update() { + ListsUpdateParams params = ListsUpdateParams.builder().name("value-name").description("value-description").build(); + JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/lists/{list_uuid}/", "200"); + transport.respondJson(200, example); + var result = client.lists().update("00000000-0000-4000-8000-00000000604a", params); + RecordedRequest request = transport.only(); + assertEquals("PATCH", request.method()); + assertEquals("/v3/lists/00000000-0000-4000-8000-00000000604a/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"name\":\"value-name\",\"description\":\"value-description\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void delete() { + transport.respond(204, ""); + client.lists().delete("00000000-0000-4000-8000-000000005feb"); + RecordedRequest request = transport.only(); + assertEquals("DELETE", request.method()); + assertEquals("/v3/lists/00000000-0000-4000-8000-000000005feb/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + } +} diff --git a/src/test/java/me/didit/resources/MonitoringEventsResourceTest.java b/src/test/java/me/didit/resources/MonitoringEventsResourceTest.java new file mode 100644 index 0000000..d1f5c40 --- /dev/null +++ b/src/test/java/me/didit/resources/MonitoringEventsResourceTest.java @@ -0,0 +1,165 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.MonitoringEventCreateRequest; +import me.didit.types.MonitoringEventsCreateBatchParams; +import me.didit.types.MonitoringEventsCreateSdkTokenParams; +import me.didit.types.MonitoringEventsListParams; +import me.didit.types.MonitoringEventsRetrieveEnrichmentMetricsParams; +import me.didit.types.MonitoringEventsRetrieveSubjectTimelineParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code monitoring_events}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class MonitoringEventsResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void list() { + MonitoringEventsListParams params = MonitoringEventsListParams.builder().limit(1L).offset(1L).eventCategory(List.of(MonitoringEventsListParams.EventCategory.AUTH)).actionType(List.of("value-action-type")).status(List.of(MonitoringEventsListParams.Status.ACCEPTED)).retentionClass(List.of(MonitoringEventsListParams.RetentionClass.LOW_VALUE)).subjectVendorData("value-subject-vendor-data").subjectEntityType(MonitoringEventsListParams.SubjectEntityType.INDIVIDUAL).businessVendorData("value-business-vendor-data").sessionId("value-session-id").deviceFingerprint("value-device-fingerprint").ipAddress("value-ip-address").gameId("value-game-id").roundId("value-round-id").affiliateCode("value-affiliate-code").paymentReferenceId("value-payment-reference-id").counterpartyVendorData("value-counterparty-vendor-data").matchedOnly(true).occurredFrom(OffsetDateTime.parse("2026-11-01T10:15:30Z")).occurredTo(OffsetDateTime.parse("2026-11-01T10:15:30Z")).search("value-search").build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/events/", "200"); + transport.respondJson(200, example); + var result = client.monitoringEvents().list(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/events/", request.path()); + assertEquals(Map.ofEntries(Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1")), Map.entry("event_category", List.of("auth")), Map.entry("action_type", List.of("value-action-type")), Map.entry("status", List.of("ACCEPTED")), Map.entry("retention_class", List.of("low_value")), Map.entry("subject_vendor_data", List.of("value-subject-vendor-data")), Map.entry("subject_entity_type", List.of("individual")), Map.entry("business_vendor_data", List.of("value-business-vendor-data")), Map.entry("session_id", List.of("value-session-id")), Map.entry("device_fingerprint", List.of("value-device-fingerprint")), Map.entry("ip_address", List.of("value-ip-address")), Map.entry("game_id", List.of("value-game-id")), Map.entry("round_id", List.of("value-round-id")), Map.entry("affiliate_code", List.of("value-affiliate-code")), Map.entry("payment_reference_id", List.of("value-payment-reference-id")), Map.entry("counterparty_vendor_data", List.of("value-counterparty-vendor-data")), Map.entry("matched_only", List.of("true")), Map.entry("occurred_from", List.of("2026-11-01T10:15:30Z")), Map.entry("occurred_to", List.of("2026-11-01T10:15:30Z")), Map.entry("search", List.of("value-search"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void create() { + MonitoringEventCreateRequest params = MonitoringEventCreateRequest.builder().eventId("value-event-id").eventCategory(MonitoringEventCreateRequest.EventCategory.AUTH).actionType("value-action-type").occurredAt(OffsetDateTime.parse("2026-11-01T10:15:30Z")).timeZone("value-time-zone").subject(MonitoringEventCreateRequest.Subject.builder().vendorData("value-vendor-data").entityType("value-entity-type").fullName("value-full-name").build()).business(MonitoringEventCreateRequest.Business.builder().vendorData("value-vendor-data").build()).session(MonitoringEventCreateRequest.Session.builder().sessionId("value-session-id").sequence(1L).build()).deviceContext(MonitoringEventCreateRequest.DeviceContext.builder().fingerprint("value-fingerprint").persistentDeviceId("value-persistent-device-id").build()).transactionContext(MonitoringEventCreateRequest.TransactionContext.builder().amount("value-amount").currency("value-currency").build()).payload(Map.of("key", "value")).customValues(Map.of("key", MonitoringEventCreateRequest.CustomValuesValue.ofString("value-custom-values"))).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/events/", "200"); + transport.respondJson(200, example); + var result = client.monitoringEvents().create(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/events/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"event_id\":\"value-event-id\",\"event_category\":\"auth\",\"action_type\":\"value-action-type\",\"occurred_at\":\"2026-11-01T10:15:30Z\",\"time_zone\":\"value-time-zone\",\"subject\":{\"vendor_data\":\"value-vendor-data\",\"entity_type\":\"value-entity-type\",\"full_name\":\"value-full-name\"},\"business\":{\"vendor_data\":\"value-vendor-data\"},\"session\":{\"session_id\":\"value-session-id\",\"sequence\":1},\"device_context\":{\"fingerprint\":\"value-fingerprint\",\"persistent_device_id\":\"value-persistent-device-id\"},\"transaction_context\":{\"amount\":\"value-amount\",\"currency\":\"value-currency\"},\"payload\":{\"key\":\"value\"},\"custom_values\":{\"key\":\"value-custom-values\"}}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void createBatch() { + MonitoringEventsCreateBatchParams params = MonitoringEventsCreateBatchParams.builder().events(List.of(MonitoringEventCreateRequest.builder().eventId("value-event-id").eventCategory(MonitoringEventCreateRequest.EventCategory.AUTH).actionType("value-action-type").occurredAt(OffsetDateTime.parse("2026-11-01T10:15:30Z")).timeZone("value-time-zone").subject(MonitoringEventCreateRequest.Subject.builder().vendorData("value-vendor-data").entityType("value-entity-type").fullName("value-full-name").build()).business(MonitoringEventCreateRequest.Business.builder().vendorData("value-vendor-data").build()).build())).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/events/batch/", "201"); + transport.respondJson(201, example); + var result = client.monitoringEvents().createBatch(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/events/batch/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"events\":[{\"event_id\":\"value-event-id\",\"event_category\":\"auth\",\"action_type\":\"value-action-type\",\"occurred_at\":\"2026-11-01T10:15:30Z\",\"time_zone\":\"value-time-zone\",\"subject\":{\"vendor_data\":\"value-vendor-data\",\"entity_type\":\"value-entity-type\",\"full_name\":\"value-full-name\"},\"business\":{\"vendor_data\":\"value-vendor-data\"}}]}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + assertEquals(201, result.statusCode()); + TestModels.readAll(result); + } + + @Test + void createSdkToken() { + MonitoringEventsCreateSdkTokenParams params = MonitoringEventsCreateSdkTokenParams.builder().vendorData("value-vendor-data").entityType("value-entity-type").allowedEventCategories(List.of(MonitoringEventsCreateSdkTokenParams.AllowedEventCategories.AUTH)).ttlSeconds(1L).maxUses(1L).bindToOrigin("value-bind-to-origin").build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/events/sdk-token/", "201"); + transport.respondJson(201, example); + var result = client.monitoringEvents().createSdkToken(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/events/sdk-token/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"vendor_data\":\"value-vendor-data\",\"entity_type\":\"value-entity-type\",\"allowed_event_categories\":[\"auth\"],\"ttl_seconds\":1,\"max_uses\":1,\"bind_to_origin\":\"value-bind-to-origin\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void retrieve() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/events/{event_uuid}/", "200"); + transport.respondJson(200, example); + var result = client.monitoringEvents().retrieve("00000000-0000-4000-8000-00000001157a"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/events/00000000-0000-4000-8000-00000001157a/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void retrieveSubjectTimeline() { + MonitoringEventsRetrieveSubjectTimelineParams params = MonitoringEventsRetrieveSubjectTimelineParams.builder().entityType(MonitoringEventsRetrieveSubjectTimelineParams.EntityType.INDIVIDUAL).limit(1L).offset(1L).eventCategory(List.of(MonitoringEventsRetrieveSubjectTimelineParams.EventCategory.AUTH)).actionType(List.of("value-action-type")).status(List.of(MonitoringEventsRetrieveSubjectTimelineParams.Status.ACCEPTED)).retentionClass(List.of(MonitoringEventsRetrieveSubjectTimelineParams.RetentionClass.LOW_VALUE)).businessVendorData("value-business-vendor-data").sessionId("value-session-id").deviceFingerprint("value-device-fingerprint").ipAddress("value-ip-address").gameId("value-game-id").roundId("value-round-id").affiliateCode("value-affiliate-code").paymentReferenceId("value-payment-reference-id").counterpartyVendorData("value-counterparty-vendor-data").matchedOnly(true).occurredFrom(OffsetDateTime.parse("2026-11-01T10:15:30Z")).occurredTo(OffsetDateTime.parse("2026-11-01T10:15:30Z")).search("value-search").build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/events/subjects/{vendor_data}/", "200"); + transport.respondJson(200, example); + var result = client.monitoringEvents().retrieveSubjectTimeline("00000000-0000-4000-8000-0000000263c9", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/events/subjects/00000000-0000-4000-8000-0000000263c9/", request.path()); + assertEquals(Map.ofEntries(Map.entry("entity_type", List.of("individual")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1")), Map.entry("event_category", List.of("auth")), Map.entry("action_type", List.of("value-action-type")), Map.entry("status", List.of("ACCEPTED")), Map.entry("retention_class", List.of("low_value")), Map.entry("business_vendor_data", List.of("value-business-vendor-data")), Map.entry("session_id", List.of("value-session-id")), Map.entry("device_fingerprint", List.of("value-device-fingerprint")), Map.entry("ip_address", List.of("value-ip-address")), Map.entry("game_id", List.of("value-game-id")), Map.entry("round_id", List.of("value-round-id")), Map.entry("affiliate_code", List.of("value-affiliate-code")), Map.entry("payment_reference_id", List.of("value-payment-reference-id")), Map.entry("counterparty_vendor_data", List.of("value-counterparty-vendor-data")), Map.entry("matched_only", List.of("true")), Map.entry("occurred_from", List.of("2026-11-01T10:15:30Z")), Map.entry("occurred_to", List.of("2026-11-01T10:15:30Z")), Map.entry("search", List.of("value-search"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void retrieveEnrichmentMetrics() { + MonitoringEventsRetrieveEnrichmentMetricsParams params = MonitoringEventsRetrieveEnrichmentMetricsParams.builder().days(1L).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/events/enrichment-metrics/", "200"); + transport.respondJson(200, example); + var result = client.monitoringEvents().retrieveEnrichmentMetrics(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/events/enrichment-metrics/", request.path()); + assertEquals(Map.ofEntries(Map.entry("days", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void delete() { + transport.respond(204, ""); + client.monitoringEvents().delete("00000000-0000-4000-8000-00000000f56f"); + RecordedRequest request = transport.only(); + assertEquals("DELETE", request.method()); + assertEquals("/v3/events/00000000-0000-4000-8000-00000000f56f/delete/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + } +} diff --git a/src/test/java/me/didit/resources/NetworksResourceTest.java b/src/test/java/me/didit/resources/NetworksResourceTest.java index 123eae3..e2278ae 100644 --- a/src/test/java/me/didit/resources/NetworksResourceTest.java +++ b/src/test/java/me/didit/resources/NetworksResourceTest.java @@ -15,8 +15,10 @@ import me.didit.testing.SpecExamples; import me.didit.testing.TestJson; import me.didit.testing.TestModels; +import me.didit.types.NetworkTagRequest; import me.didit.types.NetworksListParams; import me.didit.types.NetworksRetrieveGraphParams; +import me.didit.types.NetworksRetrieveTimelineParams; import org.junit.jupiter.api.Test; /** One test per operation of {@code networks}, against a stubbed HTTP layer. */ @@ -27,14 +29,14 @@ final class NetworksResourceTest { @Test void list() { - NetworksListParams params = NetworksListParams.builder().status("value-status").signalType("value-signal-type").patternType("value-pattern-type").riskBand("value-risk-band").minSize(1L).maxSize(1L).dateFrom(LocalDate.parse("2026-11-01")).dateTo(LocalDate.parse("2026-11-01")).q("value-q").ordering("value-ordering").limit(1L).offset(1L).build(); + NetworksListParams params = NetworksListParams.builder().status("value-status").scope(NetworksListParams.Scope.IDENTITY).signalType("value-signal-type").patternType("value-pattern-type").riskBand("value-risk-band").minSize(1L).maxSize(1L).assignee("value-assignee").priority("value-priority").riskMin(1L).hasCase(true).dateFrom(LocalDate.parse("2026-11-01")).dateTo(LocalDate.parse("2026-11-01")).q("value-q").ordering("value-ordering").limit(1L).offset(1L).summary(true).build(); JsonNode example = TestJson.parse("{\"count\":0,\"next\":null,\"previous\":null,\"results\":[]}"); transport.respondJson(200, example); var result = client.networks().list(params); RecordedRequest request = transport.only(); assertEquals("GET", request.method()); assertEquals("/v3/organization/00000000-0000-4000-8000-0000000000a1/application/00000000-0000-4000-8000-0000000000a2/networks/", request.path()); - assertEquals(Map.ofEntries(Map.entry("status", List.of("value-status")), Map.entry("signal_type", List.of("value-signal-type")), Map.entry("pattern_type", List.of("value-pattern-type")), Map.entry("risk_band", List.of("value-risk-band")), Map.entry("min_size", List.of("1")), Map.entry("max_size", List.of("1")), Map.entry("date_from", List.of("2026-11-01")), Map.entry("date_to", List.of("2026-11-01")), Map.entry("q", List.of("value-q")), Map.entry("ordering", List.of("value-ordering")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals(Map.ofEntries(Map.entry("status", List.of("value-status")), Map.entry("scope", List.of("identity")), Map.entry("signal_type", List.of("value-signal-type")), Map.entry("pattern_type", List.of("value-pattern-type")), Map.entry("risk_band", List.of("value-risk-band")), Map.entry("min_size", List.of("1")), Map.entry("max_size", List.of("1")), Map.entry("assignee", List.of("value-assignee")), Map.entry("priority", List.of("value-priority")), Map.entry("risk_min", List.of("1")), Map.entry("has_case", List.of("true")), Map.entry("date_from", List.of("2026-11-01")), Map.entry("date_to", List.of("2026-11-01")), Map.entry("q", List.of("value-q")), Map.entry("ordering", List.of("value-ordering")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1")), Map.entry("summary", List.of("true"))), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); assertEquals(0, request.body().length); @@ -45,7 +47,7 @@ void list() { @Test void retrieve() { - JsonNode example = TestJson.parse("{}"); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/", "200"); transport.respondJson(200, example); var result = client.networks().retrieve("00000000-0000-4000-8000-00000000b75b"); RecordedRequest request = transport.only(); @@ -110,13 +112,14 @@ void listSignals() { @Test void retrieveTimeline() { - JsonNode example = TestJson.parse("{}"); + NetworksRetrieveTimelineParams params = NetworksRetrieveTimelineParams.builder().contract(NetworksRetrieveTimelineParams.Contract._2).limit(1L).cursor("value-cursor").eventType(List.of(NetworksRetrieveTimelineParams.EventType.NETWORK_DETECTED)).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/timeline/", "200"); transport.respondJson(200, example); - var result = client.networks().retrieveTimeline("00000000-0000-4000-8000-000000013724"); + var result = client.networks().retrieveTimeline("00000000-0000-4000-8000-000000013724", params); RecordedRequest request = transport.only(); assertEquals("GET", request.method()); assertEquals("/v3/organization/00000000-0000-4000-8000-0000000000a1/application/00000000-0000-4000-8000-0000000000a2/networks/00000000-0000-4000-8000-000000013724/timeline/", request.path()); - assertEquals(Map.of(), request.query()); + assertEquals(Map.ofEntries(Map.entry("contract", List.of("2")), Map.entry("limit", List.of("1")), Map.entry("cursor", List.of("value-cursor")), Map.entry("event_type", List.of("network_detected"))), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); assertEquals(0, request.body().length); @@ -154,4 +157,38 @@ void downloadReport() { assertEquals(0, request.body().length); assertArrayEquals(new byte[] {37, 80, 68, 70}, result.bytes()); } + + @Test + void addTag() { + NetworkTagRequest params = NetworkTagRequest.builder().tagUuid("00000000-0000-4000-8000-000000000eed").build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/tags/add/", "200"); + transport.respondJson(200, example); + var result = client.networks().addTag("00000000-0000-4000-8000-00000000a888", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/organization/00000000-0000-4000-8000-0000000000a1/application/00000000-0000-4000-8000-0000000000a2/networks/00000000-0000-4000-8000-00000000a888/tags/add/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"tag_uuid\":\"00000000-0000-4000-8000-000000000eed\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void removeTag() { + NetworkTagRequest params = NetworkTagRequest.builder().tagUuid("00000000-0000-4000-8000-000000000eed").build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/organization/{organization_id}/application/{application_id}/networks/{network_uuid}/tags/remove/", "200"); + transport.respondJson(200, example); + var result = client.networks().removeTag("00000000-0000-4000-8000-00000000cfdc", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/organization/00000000-0000-4000-8000-0000000000a1/application/00000000-0000-4000-8000-0000000000a2/networks/00000000-0000-4000-8000-00000000cfdc/tags/remove/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"tag_uuid\":\"00000000-0000-4000-8000-000000000eed\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } } diff --git a/src/test/java/me/didit/resources/SessionsResourceTest.java b/src/test/java/me/didit/resources/SessionsResourceTest.java index 2b34731..954d892 100644 --- a/src/test/java/me/didit/resources/SessionsResourceTest.java +++ b/src/test/java/me/didit/resources/SessionsResourceTest.java @@ -10,28 +10,39 @@ import java.util.List; import java.util.Map; import me.didit.Didit; +import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.testing.MockTransport; import me.didit.testing.RecordedRequest; import me.didit.testing.SpecExamples; import me.didit.testing.TestJson; import me.didit.testing.TestModels; +import me.didit.types.SandboxSimulateRequest; +import me.didit.types.SessionSignDocument; +import me.didit.types.SessionsAddImagesParams; import me.didit.types.SessionsBatchDeleteParams; import me.didit.types.SessionsBulkUpdateAmlHitStatusParams; import me.didit.types.SessionsCreateParams; import me.didit.types.SessionsDeleteParams; +import me.didit.types.SessionsExportAmlScreeningHistoryParams; import me.didit.types.SessionsImportSharedParams; +import me.didit.types.SessionsListAmlScreeningHistoryParams; +import me.didit.types.SessionsListEventsParams; import me.didit.types.SessionsListParams; +import me.didit.types.SessionsPreviewDecisionEmailParams; import me.didit.types.SessionsPreviewResubmissionParams; import me.didit.types.SessionsRetrieveDecisionParams; import me.didit.types.SessionsShareParams; import me.didit.types.SessionsUpdateAmlHitStatusParams; +import me.didit.types.SessionsUpdateAmlStatusParams; import me.didit.types.SessionsUpdateDataParams; +import me.didit.types.SessionsUpdateDocumentAiDataParams; import me.didit.types.SessionsUpdateFeatureStatusParams; import me.didit.types.SessionsUpdateKybCompanyDataParams; import me.didit.types.SessionsUpdateKybPartyParams; import me.didit.types.SessionsUpdatePoaDataParams; import me.didit.types.SessionsUpdateStatusParams; +import me.didit.types.SignatureField; import org.junit.jupiter.api.Test; /** One test per operation of {@code sessions}, against a stubbed HTTP layer. */ @@ -42,7 +53,7 @@ final class SessionsResourceTest { @Test void create() { - SessionsCreateParams params = SessionsCreateParams.builder().workflowId("00000000-0000-4000-8000-000000001b5b").vendorData("value-vendor-data").callback("value-callback").callbackMethod(SessionsCreateParams.CallbackMethod.INITIATOR).metadata("value").language(SessionsCreateParams.Language.EN).contactDetails(SessionsCreateParams.ContactDetails.builder().email("user@example.com").sendNotificationEmails(true).build()).expectedDetails(SessionsCreateParams.ExpectedDetails.builder().firstName("value-first-name").lastName("value-last-name").build()).portraitImage("value-portrait-image").sandboxScenario("value-sandbox-scenario").build(); + SessionsCreateParams params = SessionsCreateParams.builder().workflowId("00000000-0000-4000-8000-000000001b5b").vendorData("value-vendor-data").callback("value-callback").callbackMethod(SessionsCreateParams.CallbackMethod.INITIATOR).metadata("value").language(SessionsCreateParams.Language.EN).contactDetails(SessionsCreateParams.ContactDetails.builder().email("user@example.com").sendNotificationEmails(true).build()).expectedDetails(SessionsCreateParams.ExpectedDetails.builder().firstName("value-first-name").lastName("value-last-name").build()).portraitImage("value-portrait-image").sandboxScenario("value-sandbox-scenario").sign(SessionsCreateParams.Sign.builder().documents(List.of(SessionSignDocument.builder().title("value-title").fileName("value-file-name").contentBase64("value-content-base64").fields(List.of(SignatureField.builder().key("value-key").type(SignatureField.Type.SIGNATURE).role(SignatureField.Role.VERIFIED_PERSON).page(1L).build())).build())).fieldValues(Map.of("key", SessionsCreateParams.Sign.FieldValuesValue.ofString("value-field-values"))).build()).build(); JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/session/", "201"); transport.respondJson(201, example); var result = client.sessions().create(params); @@ -52,7 +63,7 @@ void create() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"workflow_id\":\"00000000-0000-4000-8000-000000001b5b\",\"vendor_data\":\"value-vendor-data\",\"callback\":\"value-callback\",\"callback_method\":\"initiator\",\"metadata\":\"value\",\"language\":\"en\",\"contact_details\":{\"email\":\"user@example.com\",\"send_notification_emails\":true},\"expected_details\":{\"first_name\":\"value-first-name\",\"last_name\":\"value-last-name\"},\"portrait_image\":\"value-portrait-image\",\"sandbox_scenario\":\"value-sandbox-scenario\"}", request.body()); + TestJson.assertJsonEquals("{\"workflow_id\":\"00000000-0000-4000-8000-000000001b5b\",\"vendor_data\":\"value-vendor-data\",\"callback\":\"value-callback\",\"callback_method\":\"initiator\",\"metadata\":\"value\",\"language\":\"en\",\"contact_details\":{\"email\":\"user@example.com\",\"send_notification_emails\":true},\"expected_details\":{\"first_name\":\"value-first-name\",\"last_name\":\"value-last-name\"},\"portrait_image\":\"value-portrait-image\",\"sandbox_scenario\":\"value-sandbox-scenario\",\"sign\":{\"documents\":[{\"title\":\"value-title\",\"file_name\":\"value-file-name\",\"content_base64\":\"value-content-base64\",\"fields\":[{\"key\":\"value-key\",\"type\":\"signature\",\"role\":\"verified_person\",\"page\":1}]}],\"field_values\":{\"key\":\"value-field-values\"}}}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @@ -171,12 +182,13 @@ void importShared() { assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); TestJson.assertJsonEquals("{\"share_token\":\"value-share-token\",\"trust_review\":true,\"workflow_id\":\"00000000-0000-4000-8000-000000001b5b\",\"vendor_data\":\"value-vendor-data\"}", request.body()); - TestJson.assertJsonEquals(example, result); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); } @Test void batchDelete() { - SessionsBatchDeleteParams params = SessionsBatchDeleteParams.builder().sessionNumbers(List.of("value-session-numbers")).deleteAll(true).retainFaceEmbeddings(true).faceRetentionDays(1L).faceRetentionDeadline(OffsetDateTime.parse("2026-11-01T10:15:30Z")).deletionInstruction(SessionsBatchDeleteParams.DeletionInstruction.OPERATIONAL_SESSION_DELETE).instructionId("value-instruction-id").build(); + SessionsBatchDeleteParams params = SessionsBatchDeleteParams.builder().sessionNumbers(List.of("value-session-numbers")).deleteAll(true).retainFaceEmbeddings(true).faceRetentionDays(1L).faceRetentionDeadline(OffsetDateTime.parse("2026-11-01T10:15:30Z")).deletionInstruction(SessionsBatchDeleteParams.DeletionInstruction.OPERATIONAL_SESSION_DELETE).instructionId("value-instruction-id").filterStatus(SessionsBatchDeleteParams.FilterStatus.ofString("value-filter-status")).filterVendorData("value-filter-vendor-data").filterSearch("value-filter-search").filterWorkflowId(SessionsBatchDeleteParams.FilterWorkflowId.ofString("value-filter-workflow-id")).filterDateFrom(LocalDate.parse("2026-11-01")).filterDateTo(LocalDate.parse("2026-11-01")).filterCountry(SessionsBatchDeleteParams.FilterCountry.ofString("value-filter-country")).filterDocumentType(SessionsBatchDeleteParams.FilterDocumentType.ofString("value-filter-document-type")).filterDocumentName(SessionsBatchDeleteParams.FilterDocumentName.ofString("value-filter-document-name")).filterRegion(SessionsBatchDeleteParams.FilterRegion.ofString("value-filter-region")).filterDiditInternalId("00000000-0000-4000-8000-000000007ac5").filterSessionType(SessionsBatchDeleteParams.FilterSessionType.ofString("value-filter-session-type")).filterApiService("value-filter-api-service").filterScreenedFullName("value-filter-screened-full-name").filterWarning(SessionsBatchDeleteParams.FilterWarning.ofString("value-filter-warning")).filterStatusInitializedAsInReview(true).filterIsKycToBeReviewed(true).filterEuuid("00000000-0000-4000-8000-000000002094").filterOrganizationName(SessionsBatchDeleteParams.FilterOrganizationName.ofString("value-filter-organization-name")).build(); JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/sessions/delete/", "200"); transport.respondJson(200, example); var result = client.sessions().batchDelete(params); @@ -186,7 +198,7 @@ void batchDelete() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"session_numbers\":[\"value-session-numbers\"],\"delete_all\":true,\"retain_face_embeddings\":true,\"face_retention_days\":1,\"face_retention_deadline\":\"2026-11-01T10:15:30Z\",\"deletion_instruction\":\"operational_session_delete\",\"instruction_id\":\"value-instruction-id\"}", request.body()); + TestJson.assertJsonEquals("{\"session_numbers\":[\"value-session-numbers\"],\"delete_all\":true,\"retain_face_embeddings\":true,\"face_retention_days\":1,\"face_retention_deadline\":\"2026-11-01T10:15:30Z\",\"deletion_instruction\":\"operational_session_delete\",\"instruction_id\":\"value-instruction-id\",\"filter_status\":\"value-filter-status\",\"filter_vendor_data\":\"value-filter-vendor-data\",\"filter_search\":\"value-filter-search\",\"filter_workflow_id\":\"value-filter-workflow-id\",\"filter_date_from\":\"2026-11-01\",\"filter_date_to\":\"2026-11-01\",\"filter_country\":\"value-filter-country\",\"filter_document_type\":\"value-filter-document-type\",\"filter_document_name\":\"value-filter-document-name\",\"filter_region\":\"value-filter-region\",\"filter_didit_internal_id\":\"00000000-0000-4000-8000-000000007ac5\",\"filter_session_type\":\"value-filter-session-type\",\"filter_api_service\":\"value-filter-api-service\",\"filter_screened_full_name\":\"value-filter-screened-full-name\",\"filter_warning\":\"value-filter-warning\",\"filter_status_initialized_as_in_review\":true,\"filter_is_kyc_to_be_reviewed\":true,\"filter_euuid\":\"00000000-0000-4000-8000-000000002094\",\"filter_organization_name\":\"value-filter-organization-name\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); assertEquals(200, result.statusCode()); TestModels.readAll(result); @@ -227,7 +239,7 @@ void updateData() { @Test void updateFeatureStatus() { - SessionsUpdateFeatureStatusParams params = SessionsUpdateFeatureStatusParams.builder().newStatus(SessionsUpdateFeatureStatusParams.NewStatus.APPROVED).comment("value-comment").build(); + SessionsUpdateFeatureStatusParams params = SessionsUpdateFeatureStatusParams.builder().newStatus(SessionsUpdateFeatureStatusParams.NewStatus.APPROVED).comment("value-comment").featureType(SessionsUpdateFeatureStatusParams.FeatureType.ID_VERIFICATION).deferUntilFlowFinishes(true).build(); JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/session/{sessionId}/features/{nodeId}/update-status/", "200"); transport.respondJson(200, example); var result = client.sessions().updateFeatureStatus("00000000-0000-4000-8000-000000014334", "00000000-0000-4000-8000-00000001102a", params); @@ -237,7 +249,7 @@ void updateFeatureStatus() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"new_status\":\"Approved\",\"comment\":\"value-comment\"}", request.body()); + TestJson.assertJsonEquals("{\"new_status\":\"Approved\",\"comment\":\"value-comment\",\"feature_type\":\"ID_VERIFICATION\",\"defer_until_flow_finishes\":true}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @@ -255,7 +267,8 @@ void updateAmlHitStatus() { assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); TestJson.assertJsonEquals("{\"hit_id\":\"value-hit-id\",\"review_status\":\"Unreviewed\",\"node_id\":\"value-node-id\"}", request.body()); - TestJson.assertJsonEquals(example, result); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); } @Test @@ -271,7 +284,8 @@ void bulkUpdateAmlHitStatus() { assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); TestJson.assertJsonEquals("{\"hit_updates\":[{\"hit_id\":\"value-hit-id\",\"review_status\":\"Unreviewed\"}],\"node_id\":\"value-node-id\"}", request.body()); - TestJson.assertJsonEquals(example, result); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); } @Test @@ -344,7 +358,7 @@ void listNetworks() { @Test void previewResubmission() { SessionsPreviewResubmissionParams params = SessionsPreviewResubmissionParams.builder().nodesToResubmit(List.of(SessionsPreviewResubmissionParams.NodesToResubmit.builder().nodeId("value-node-id").feature(SessionsPreviewResubmissionParams.NodesToResubmit.Feature.OCR).build())).emailAddress("user@example.com").emailLanguage("value-email-language").build(); - JsonNode example = TestJson.parse("{\"html\":\"value\",\"nodes_to_resubmit\":[],\"preview_token\":\"value\",\"recipient\":\"value\",\"requires_user_action\":true,\"sender\":\"value\",\"subject\":\"value\"}"); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/session/{sessionId}/resubmission-preview/", "200"); transport.respondJson(200, example); var result = client.sessions().previewResubmission("00000000-0000-4000-8000-00000001344a", params); RecordedRequest request = transport.only(); @@ -357,4 +371,190 @@ void previewResubmission() { TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } + + @Test + void updateDocumentAiData() { + SessionsUpdateDocumentAiDataParams params = SessionsUpdateDocumentAiDataParams.builder().extractedData(Map.of("key", "value")).status(SessionsUpdateDocumentAiDataParams.Status.APPROVED).build(); + JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/document-ai/{sessionId}/documents/{documentUuid}/", "200"); + transport.respondJson(200, example); + var result = client.sessions().updateDocumentAiData("00000000-0000-4000-8000-000000015d30", "00000000-0000-4000-8000-00000001941b", params); + RecordedRequest request = transport.only(); + assertEquals("PATCH", request.method()); + assertEquals("/v3/document-ai/00000000-0000-4000-8000-000000015d30/documents/00000000-0000-4000-8000-00000001941b/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"extracted_data\":{\"key\":\"value\"},\"status\":\"Approved\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void regenerateKybParty() { + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/session/{sessionId}/kyb/parties/{partyUuid}/regenerate/", "200"); + transport.respondJson(200, example); + var result = client.sessions().regenerateKybParty("00000000-0000-4000-8000-000000013190", "00000000-0000-4000-8000-0000000134e9"); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-000000013190/kyb/parties/00000000-0000-4000-8000-0000000134e9/regenerate/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void listEvents() { + SessionsListEventsParams params = SessionsListEventsParams.builder().limit(1L).offset(1L).ordering("value-ordering").build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/session/{sessionId}/events/", "200"); + transport.respondJson(200, example); + var result = client.sessions().listEvents("00000000-0000-4000-8000-00000000b375", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-00000000b375/events/", request.path()); + assertEquals(Map.ofEntries(Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1")), Map.entry("ordering", List.of("value-ordering"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void listAmlScreeningHistory() { + SessionsListAmlScreeningHistoryParams params = SessionsListAmlScreeningHistoryParams.builder().nodeId("value-node-id").limit(1L).offset(1L).ordering("value-ordering").build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/session/{sessionId}/aml-screening-history/", "200"); + transport.respondJson(200, example); + var result = client.sessions().listAmlScreeningHistory("00000000-0000-4000-8000-000000019ab8", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-000000019ab8/aml-screening-history/", request.path()); + assertEquals(Map.ofEntries(Map.entry("node_id", List.of("value-node-id")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1")), Map.entry("ordering", List.of("value-ordering"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void exportAmlScreeningHistory() { + SessionsExportAmlScreeningHistoryParams params = SessionsExportAmlScreeningHistoryParams.builder().nodeId("value-node-id").build(); + transport.respondBinary(200, "text/csv", new byte[] {37, 80, 68, 70}); + var result = client.sessions().exportAmlScreeningHistory("00000000-0000-4000-8000-00000001c16a", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-00000001c16a/aml-screening-history/export/", request.path()); + assertEquals(Map.ofEntries(Map.entry("node_id", List.of("value-node-id"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals("text/csv, application/json", request.header("Accept")); + assertEquals(0, request.body().length); + assertArrayEquals(new byte[] {37, 80, 68, 70}, result.bytes()); + } + + @Test + void addImages() { + SessionsAddImagesParams params = SessionsAddImagesParams.builder().file(FileUpload.of(new byte[] {37, 80, 68, 70}, "file.pdf", "application/pdf")).modelType(SessionsAddImagesParams.ModelType.KYC).filePassword("value-file-password").build(); + JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/session/{sessionId}/add-images/", "200"); + transport.respondJson(200, example); + var result = client.sessions().addImages("00000000-0000-4000-8000-00000000a37f", params); + RecordedRequest request = transport.only(); + assertEquals("PATCH", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-00000000a37f/add-images/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(Map.ofEntries(Map.entry("model_type", List.of("kyc")), Map.entry("file_password", List.of("value-file-password"))), request.multipartFields()); + assertEquals(Map.ofEntries(Map.entry("file", List.of("file.pdf"))), request.multipartFiles()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void updateAmlStatus() { + SessionsUpdateAmlStatusParams params = SessionsUpdateAmlStatusParams.builder().nodeIdQuery("value-node-id-query").status(SessionsUpdateAmlStatusParams.Status.NOT_FINISHED).nodeId("value-node-id").build(); + JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/session/{sessionId}/update-aml-status/", "200"); + transport.respondJson(200, example); + var result = client.sessions().updateAmlStatus("00000000-0000-4000-8000-0000000103e8", params); + RecordedRequest request = transport.only(); + assertEquals("PATCH", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-0000000103e8/update-aml-status/", request.path()); + assertEquals(Map.ofEntries(Map.entry("node_id", List.of("value-node-id-query"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"status\":\"Not Finished\",\"node_id\":\"value-node-id\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void simulate() { + SandboxSimulateRequest params = SandboxSimulateRequest.builder().newStatus(SandboxSimulateRequest.NewStatus.APPROVED).comment("value-comment").scenario("value-scenario").risk("value-risk").nodeId("value-node-id").sendWebhooks(true).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/session/{sessionId}/simulate/", "200"); + transport.respondJson(200, example); + var result = client.sessions().simulate("00000000-0000-4000-8000-000000008f73", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-000000008f73/simulate/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"new_status\":\"Approved\",\"comment\":\"value-comment\",\"scenario\":\"value-scenario\",\"risk\":\"value-risk\",\"node_id\":\"value-node-id\",\"send_webhooks\":true}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void listFeatureOverrides() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/session/{sessionId}/features/overrides/", "200"); + transport.respondJson(200, example); + var result = client.sessions().listFeatureOverrides("00000000-0000-4000-8000-0000000152bc"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-0000000152bc/features/overrides/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void cancelFeatureOverride() { + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/session/{sessionId}/features/overrides/{overrideId}/cancel/", "200"); + transport.respondJson(200, example); + var result = client.sessions().cancelFeatureOverride("00000000-0000-4000-8000-0000000160a3", "00000000-0000-4000-8000-000000016fab"); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-0000000160a3/features/overrides/00000000-0000-4000-8000-000000016fab/cancel/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void previewDecisionEmail() { + SessionsPreviewDecisionEmailParams params = SessionsPreviewDecisionEmailParams.builder().newStatus(SessionsPreviewDecisionEmailParams.NewStatus.APPROVED).emailLanguage("value-email-language").build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/session/{sessionId}/decision-email-preview/", "200"); + transport.respondJson(200, example); + var result = client.sessions().previewDecisionEmail("00000000-0000-4000-8000-000000014ede", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-000000014ede/decision-email-preview/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"new_status\":\"Approved\",\"email_language\":\"value-email-language\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } } diff --git a/src/test/java/me/didit/resources/SessionsSignaturesResourceTest.java b/src/test/java/me/didit/resources/SessionsSignaturesResourceTest.java new file mode 100644 index 0000000..af2a9c8 --- /dev/null +++ b/src/test/java/me/didit/resources/SessionsSignaturesResourceTest.java @@ -0,0 +1,89 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.SessionsSignaturesDownloadEvidenceParams; +import me.didit.types.SessionsSignaturesValidateParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code sessions.signatures}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class SessionsSignaturesResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void downloadDocument() { + transport.respondBinary(200, "application/pdf", new byte[] {37, 80, 68, 70}); + var result = client.sessions().signatures().downloadDocument("00000000-0000-4000-8000-00000001baf1", "00000000-0000-4000-8000-000000017e55", "00000000-0000-4000-8000-00000001cc1a"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-00000001baf1/signatures/00000000-0000-4000-8000-000000017e55/documents/00000000-0000-4000-8000-00000001cc1a/file/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals("application/pdf, application/json", request.header("Accept")); + assertEquals(0, request.body().length); + assertArrayEquals(new byte[] {37, 80, 68, 70}, result.bytes()); + } + + @Test + void downloadEvidence() { + SessionsSignaturesDownloadEvidenceParams params = SessionsSignaturesDownloadEvidenceParams.builder().signatureId("00000000-0000-4000-8000-000000002008").build(); + transport.respondBinary(200, "application/pdf", new byte[] {37, 80, 68, 70}); + var result = client.sessions().signatures().downloadEvidence("00000000-0000-4000-8000-00000001b6eb", "00000000-0000-4000-8000-000000017a4f", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-00000001b6eb/signatures/00000000-0000-4000-8000-000000017a4f/evidence/", request.path()); + assertEquals(Map.ofEntries(Map.entry("signature_id", List.of("00000000-0000-4000-8000-000000002008"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals("application/pdf, application/json", request.header("Accept")); + assertEquals(0, request.body().length); + assertArrayEquals(new byte[] {37, 80, 68, 70}, result.bytes()); + } + + @Test + void downloadFieldImage() { + transport.respondBinary(200, "image/png", new byte[] {37, 80, 68, 70}); + var result = client.sessions().signatures().downloadFieldImage("00000000-0000-4000-8000-00000001eec1", "00000000-0000-4000-8000-00000001ae0b", "00000000-0000-4000-8000-00000001bf67"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-00000001eec1/signatures/00000000-0000-4000-8000-00000001ae0b/fields/00000000-0000-4000-8000-00000001bf67/image/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals("image/png, application/json", request.header("Accept")); + assertEquals(0, request.body().length); + assertArrayEquals(new byte[] {37, 80, 68, 70}, result.bytes()); + } + + @Test + void validate() { + SessionsSignaturesValidateParams params = SessionsSignaturesValidateParams.builder().signatureId("00000000-0000-4000-8000-000000002008").build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.sessions().signatures().validate("00000000-0000-4000-8000-000000011c8c", "00000000-0000-4000-8000-00000000ec3e", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-000000011c8c/signatures/00000000-0000-4000-8000-00000000ec3e/validate/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"signature_id\":\"00000000-0000-4000-8000-000000002008\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } +} diff --git a/src/test/java/me/didit/resources/SessionsWebhooksResourceTest.java b/src/test/java/me/didit/resources/SessionsWebhooksResourceTest.java new file mode 100644 index 0000000..2fb112c --- /dev/null +++ b/src/test/java/me/didit/resources/SessionsWebhooksResourceTest.java @@ -0,0 +1,72 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code sessions.webhooks}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class SessionsWebhooksResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void list() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/session/{sessionId}/webhooks/", "200"); + transport.respondJson(200, example); + var result = client.sessions().webhooks().list("00000000-0000-4000-8000-00000000c8ec"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-00000000c8ec/webhooks/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void retrieve() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/session/{sessionId}/webhook/{webhook_uuid}", "200"); + transport.respondJson(200, example); + var result = client.sessions().webhooks().retrieve("00000000-0000-4000-8000-00000001006a", "00000000-0000-4000-8000-0000000131ae"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-00000001006a/webhook/00000000-0000-4000-8000-0000000131ae", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void resend() { + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/session/{sessionId}/webhook/{webhook_uuid}/resend", "200"); + transport.respondJson(200, example); + var result = client.sessions().webhooks().resend("00000000-0000-4000-8000-00000000e268", "00000000-0000-4000-8000-000000011104"); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/session/00000000-0000-4000-8000-00000000e268/webhook/00000000-0000-4000-8000-000000011104/resend", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } +} diff --git a/src/test/java/me/didit/resources/TransactionsNotesResourceTest.java b/src/test/java/me/didit/resources/TransactionsNotesResourceTest.java new file mode 100644 index 0000000..55358a9 --- /dev/null +++ b/src/test/java/me/didit/resources/TransactionsNotesResourceTest.java @@ -0,0 +1,61 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.TransactionsNotesCreateParams; +import me.didit.types.TransactionsNotesListParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code transactions.notes}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class TransactionsNotesResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void list() { + TransactionsNotesListParams params = TransactionsNotesListParams.builder().limit(1L).offset(1L).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/transactions/{transaction_id}/notes/", "200"); + transport.respondJson(200, example); + var result = client.transactions().notes().list("00000000-0000-4000-8000-000000011fca", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/transactions/00000000-0000-4000-8000-000000011fca/notes/", request.path()); + assertEquals(Map.ofEntries(Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void create() { + TransactionsNotesCreateParams params = TransactionsNotesCreateParams.builder().note("value-note").mentionedEmails(List.of("user@example.com")).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/transactions/{transaction_id}/notes/", "201"); + transport.respondJson(201, example); + var result = client.transactions().notes().create("00000000-0000-4000-8000-000000013d4b", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/transactions/00000000-0000-4000-8000-000000013d4b/notes/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"note\":\"value-note\",\"mentioned_emails\":[\"user@example.com\"]}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } +} diff --git a/src/test/java/me/didit/resources/TransactionsResourceTest.java b/src/test/java/me/didit/resources/TransactionsResourceTest.java index d4915b0..71e8cd9 100644 --- a/src/test/java/me/didit/resources/TransactionsResourceTest.java +++ b/src/test/java/me/didit/resources/TransactionsResourceTest.java @@ -18,6 +18,7 @@ import me.didit.types.TransactionsCreateParams; import me.didit.types.TransactionsCreateSdkTokenParams; import me.didit.types.TransactionsListParams; +import me.didit.types.TransactionsListRelatedParams; import org.junit.jupiter.api.Test; /** One test per operation of {@code transactions}, against a stubbed HTTP layer. */ @@ -28,14 +29,14 @@ final class TransactionsResourceTest { @Test void list() { - TransactionsListParams params = TransactionsListParams.builder().limit(1L).offset(1L).build(); + TransactionsListParams params = TransactionsListParams.builder().limit(1L).offset(1L).travelRuleStatus("value-travel-rule-status").build(); JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/transactions/", "200"); transport.respondJson(200, example); var result = client.transactions().list(params); RecordedRequest request = transport.only(); assertEquals("GET", request.method()); assertEquals("/v3/transactions/", request.path()); - assertEquals(Map.ofEntries(Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals(Map.ofEntries(Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1")), Map.entry("travel_rule_status", List.of("value-travel-rule-status"))), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); assertEquals(0, request.body().length); @@ -125,4 +126,37 @@ void listPartyNetworks() { TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } + + @Test + void listRelated() { + TransactionsListRelatedParams params = TransactionsListRelatedParams.builder().limit(1L).offset(1L).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/transactions/{transaction_id}/related/", "200"); + transport.respondJson(200, example); + var result = client.transactions().listRelated("00000000-0000-4000-8000-0000000140ee", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/transactions/00000000-0000-4000-8000-0000000140ee/related/", request.path()); + assertEquals(Map.ofEntries(Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void generatePdf() { + transport.respondBinary(200, "application/pdf", new byte[] {37, 80, 68, 70}); + var result = client.transactions().generatePdf("00000000-0000-4000-8000-000000016426"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/organization/00000000-0000-4000-8000-0000000000a1/application/00000000-0000-4000-8000-0000000000a2/transactions/00000000-0000-4000-8000-000000016426/pdf/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals("application/pdf, application/json", request.header("Accept")); + assertEquals(0, request.body().length); + assertArrayEquals(new byte[] {37, 80, 68, 70}, result.bytes()); + } } diff --git a/src/test/java/me/didit/resources/TransactionsRulesResourceTest.java b/src/test/java/me/didit/resources/TransactionsRulesResourceTest.java index c3c94ce..2d2c677 100644 --- a/src/test/java/me/didit/resources/TransactionsRulesResourceTest.java +++ b/src/test/java/me/didit/resources/TransactionsRulesResourceTest.java @@ -15,6 +15,7 @@ import me.didit.testing.TestJson; import me.didit.testing.TestModels; import me.didit.types.TransactionRuleAction; +import me.didit.types.TransactionRuleActionAddScore; import me.didit.types.TransactionRuleAggregation; import me.didit.types.TransactionRuleBacktestRequest; import me.didit.types.TransactionRuleCondition; @@ -52,7 +53,7 @@ void list() { @Test void create() { - TransactionRuleCreateRequest params = TransactionRuleCreateRequest.builder().title("value-title").description("value-description").category("value-category").mode(TransactionRuleCreateRequest.Mode.ACTIVE).evaluationMode(TransactionRuleCreateRequest.EvaluationMode.ALL).scope(TransactionRuleScope.builder().transactionTypes(List.of("value-transaction-types")).directions(List.of(TransactionRuleScope.Directions.INBOUND)).build()).conditions(List.of(TransactionRuleCondition.builder().field("value-field").operator(TransactionRuleCondition.Operator.EQ).value("value").valueType(TransactionRuleCondition.ValueType.LIST).build())).aggregation(List.of(TransactionRuleAggregation.builder().metric(TransactionRuleAggregation.Metric.COUNT).field("value-field").value(1.5).build())).actions(List.of(TransactionRuleAction.ofAddScore(TransactionRuleAction.AddScore.builder().type(TransactionRuleAction.AddScore.Type.ADD_SCORE).value(1L).build()))).metadata(Map.of("key", "value")).build(); + TransactionRuleCreateRequest params = TransactionRuleCreateRequest.builder().title("value-title").description("value-description").category("value-category").mode(TransactionRuleCreateRequest.Mode.ACTIVE).evaluationMode(TransactionRuleCreateRequest.EvaluationMode.ALL).scope(TransactionRuleScope.builder().transactionTypes(List.of("value-transaction-types")).directions(List.of(TransactionRuleScope.Directions.INBOUND)).build()).conditions(List.of(TransactionRuleCondition.builder().field("value-field").operator(TransactionRuleCondition.Operator.EQ).value("value").valueType(TransactionRuleCondition.ValueType.LIST).build())).aggregation(List.of(TransactionRuleAggregation.builder().metric(TransactionRuleAggregation.Metric.COUNT).field("value-field").value(1.5).build())).actions(List.of(TransactionRuleAction.ofAddScore(TransactionRuleActionAddScore.builder().type(TransactionRuleActionAddScore.Type.ADD_SCORE).value(1L).build()))).metadata(Map.of("key", "value")).build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(201, example); var result = client.transactions().rules().create(params); @@ -85,7 +86,7 @@ void retrieve() { @Test void update() { - TransactionRuleUpdateRequest params = TransactionRuleUpdateRequest.builder().title("value-title").description("value-description").category("value-category").mode(TransactionRuleUpdateRequest.Mode.ACTIVE).evaluationMode(TransactionRuleUpdateRequest.EvaluationMode.ALL).scope(TransactionRuleScope.builder().transactionTypes(List.of("value-transaction-types")).directions(List.of(TransactionRuleScope.Directions.INBOUND)).build()).conditions(List.of(TransactionRuleCondition.builder().field("value-field").operator(TransactionRuleCondition.Operator.EQ).value("value").valueType(TransactionRuleCondition.ValueType.LIST).build())).aggregation(List.of(TransactionRuleAggregation.builder().metric(TransactionRuleAggregation.Metric.COUNT).field("value-field").value(1.5).build())).actions(List.of(TransactionRuleAction.ofAddScore(TransactionRuleAction.AddScore.builder().type(TransactionRuleAction.AddScore.Type.ADD_SCORE).value(1L).build()))).metadata(Map.of("key", "value")).build(); + TransactionRuleUpdateRequest params = TransactionRuleUpdateRequest.builder().title("value-title").description("value-description").category("value-category").mode(TransactionRuleUpdateRequest.Mode.ACTIVE).evaluationMode(TransactionRuleUpdateRequest.EvaluationMode.ALL).scope(TransactionRuleScope.builder().transactionTypes(List.of("value-transaction-types")).directions(List.of(TransactionRuleScope.Directions.INBOUND)).build()).conditions(List.of(TransactionRuleCondition.builder().field("value-field").operator(TransactionRuleCondition.Operator.EQ).value("value").valueType(TransactionRuleCondition.ValueType.LIST).build())).aggregation(List.of(TransactionRuleAggregation.builder().metric(TransactionRuleAggregation.Metric.COUNT).field("value-field").value(1.5).build())).actions(List.of(TransactionRuleAction.ofAddScore(TransactionRuleActionAddScore.builder().type(TransactionRuleActionAddScore.Type.ADD_SCORE).value(1L).build()))).metadata(Map.of("key", "value")).build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(200, example); var result = client.transactions().rules().update("00000000-0000-4000-8000-00000000f3de", params); diff --git a/src/test/java/me/didit/resources/TransactionsTravelRuleResourceTest.java b/src/test/java/me/didit/resources/TransactionsTravelRuleResourceTest.java index e69c876..2cca420 100644 --- a/src/test/java/me/didit/resources/TransactionsTravelRuleResourceTest.java +++ b/src/test/java/me/didit/resources/TransactionsTravelRuleResourceTest.java @@ -14,7 +14,9 @@ import me.didit.testing.SpecExamples; import me.didit.testing.TestJson; import me.didit.testing.TestModels; +import me.didit.types.TravelRuleMessageCreateRequest; import me.didit.types.TravelRuleOwnershipConfirmRequest; +import me.didit.types.TravelRuleTestVaspInboundRequest; import me.didit.types.TravelRuleTransferPatchRequest; import org.junit.jupiter.api.Test; @@ -26,7 +28,7 @@ final class TransactionsTravelRuleResourceTest { @Test void updateTransfer() { - TravelRuleTransferPatchRequest params = TravelRuleTransferPatchRequest.builder().paymentTxnId("value-payment-txn-id").action(TravelRuleTransferPatchRequest.Action.CANCEL).build(); + TravelRuleTransferPatchRequest params = TravelRuleTransferPatchRequest.builder().paymentTxnId("value-payment-txn-id").action(TravelRuleTransferPatchRequest.Action.CANCEL).complianceEmail("user@example.com").walletAddress("value-wallet-address").memo("value-memo").build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(200, example); var result = client.transactions().travelRule().updateTransfer("00000000-0000-4000-8000-00000002630a", params); @@ -36,14 +38,14 @@ void updateTransfer() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"payment_txn_id\":\"value-payment-txn-id\",\"action\":\"cancel\"}", request.body()); + TestJson.assertJsonEquals("{\"payment_txn_id\":\"value-payment-txn-id\",\"action\":\"cancel\",\"compliance_email\":\"user@example.com\",\"wallet_address\":\"value-wallet-address\",\"memo\":\"value-memo\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @Test void confirmOwnership() { - TravelRuleOwnershipConfirmRequest params = TravelRuleOwnershipConfirmRequest.builder().confirmed(true).build(); + TravelRuleOwnershipConfirmRequest params = TravelRuleOwnershipConfirmRequest.builder().confirmed(true).walletAddress("value-wallet-address").build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(200, example); var result = client.transactions().travelRule().confirmOwnership("00000000-0000-4000-8000-0000000293dd", params); @@ -53,7 +55,57 @@ void confirmOwnership() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"confirmed\":true}", request.body()); + TestJson.assertJsonEquals("{\"confirmed\":true,\"wallet_address\":\"value-wallet-address\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void listMessages() { + JsonNode example = TestJson.parse("[]"); + transport.respondJson(200, example); + var result = client.transactions().travelRule().listMessages("00000000-0000-4000-8000-000000023686"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/transactions/00000000-0000-4000-8000-000000023686/travel-rule/messages/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void createMessage() { + TravelRuleMessageCreateRequest params = TravelRuleMessageCreateRequest.builder().kind(TravelRuleMessageCreateRequest.Kind.MESSAGE).body("value-body").requestedDocuments(List.of("value-requested-documents")).replyTo("00000000-0000-4000-8000-000000000f77").files(List.of("value-files")).build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(201, example); + var result = client.transactions().travelRule().createMessage("00000000-0000-4000-8000-0000000247fb", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/transactions/00000000-0000-4000-8000-0000000247fb/travel-rule/messages/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"kind\":\"MESSAGE\",\"body\":\"value-body\",\"requested_documents\":[\"value-requested-documents\"],\"reply_to\":\"00000000-0000-4000-8000-000000000f77\",\"files\":[\"value-files\"]}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void triggerTestVaspInbound() { + TravelRuleTestVaspInboundRequest params = TravelRuleTestVaspInboundRequest.builder().beneficiaryWalletAddress("value-beneficiary-wallet-address").scenario(TravelRuleTestVaspInboundRequest.Scenario.ACCEPT).chain("value-chain").amount("value-amount").currency("value-currency").reference("value-reference").build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(201, example); + var result = client.transactions().travelRule().triggerTestVaspInbound(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/test-vasp/inbound/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"beneficiary_wallet_address\":\"value-beneficiary-wallet-address\",\"scenario\":\"accept\",\"chain\":\"value-chain\",\"amount\":\"value-amount\",\"currency\":\"value-currency\",\"reference\":\"value-reference\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } diff --git a/src/test/java/me/didit/resources/TravelRuleCounterpartyContactsResourceTest.java b/src/test/java/me/didit/resources/TravelRuleCounterpartyContactsResourceTest.java new file mode 100644 index 0000000..472d6a0 --- /dev/null +++ b/src/test/java/me/didit/resources/TravelRuleCounterpartyContactsResourceTest.java @@ -0,0 +1,74 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.TravelRuleCounterpartyContactCreate; +import me.didit.types.TravelRuleCounterpartyContactsListParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code travel_rule.counterparty_contacts}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class TravelRuleCounterpartyContactsResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void list() { + TravelRuleCounterpartyContactsListParams params = TravelRuleCounterpartyContactsListParams.builder().counterparty("00000000-0000-4000-8000-00000000221d").limit(1L).offset(1L).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/travel-rule/counterparty-contacts/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().counterpartyContacts().list(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/travel-rule/counterparty-contacts/", request.path()); + assertEquals(Map.ofEntries(Map.entry("counterparty", List.of("00000000-0000-4000-8000-00000000221d")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void create() { + TravelRuleCounterpartyContactCreate params = TravelRuleCounterpartyContactCreate.builder().counterparty("00000000-0000-4000-8000-00000000221d").email("user@example.com").name("value-name").build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/travel-rule/counterparty-contacts/", "201"); + transport.respondJson(201, example); + var result = client.travelRule().counterpartyContacts().create(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/counterparty-contacts/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"counterparty\":\"00000000-0000-4000-8000-00000000221d\",\"email\":\"user@example.com\",\"name\":\"value-name\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void delete() { + transport.respond(204, ""); + client.travelRule().counterpartyContacts().delete("00000000-0000-4000-8000-000000023604"); + RecordedRequest request = transport.only(); + assertEquals("DELETE", request.method()); + assertEquals("/v3/travel-rule/counterparty-contacts/00000000-0000-4000-8000-000000023604/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + } +} diff --git a/src/test/java/me/didit/resources/TravelRuleResourceTest.java b/src/test/java/me/didit/resources/TravelRuleResourceTest.java index 1dfb8a1..4b2bf77 100644 --- a/src/test/java/me/didit/resources/TravelRuleResourceTest.java +++ b/src/test/java/me/didit/resources/TravelRuleResourceTest.java @@ -14,9 +14,16 @@ import me.didit.testing.SpecExamples; import me.didit.testing.TestJson; import me.didit.testing.TestModels; +import me.didit.types.TravelRuleCounterpartyDecisionRequest; +import me.didit.types.TravelRuleCounterpartyQuestionnaireRequest; +import me.didit.types.TravelRuleCounterpartyScreeningRequest; +import me.didit.types.TravelRuleDataSharingFields; import me.didit.types.TravelRuleInboundRequest; import me.didit.types.TravelRuleListVaspsParams; +import me.didit.types.TravelRuleMatchingRules; import me.didit.types.TravelRuleSettingsUpdate; +import me.didit.types.TravelRuleUtxo; +import me.didit.types.TravelRuleUtxoEntry; import me.didit.types.TravelRuleWidgetSessionCreate; import org.junit.jupiter.api.Test; @@ -44,7 +51,7 @@ void retrieveSettings() { @Test void updateSettings() { - TravelRuleSettingsUpdate params = TravelRuleSettingsUpdate.builder().isEnabled(true).legalName("value-legal-name").lei("value-lei").jurisdiction("value-jurisdiction").nameMatchingStrictness(TravelRuleSettingsUpdate.NameMatchingStrictness.NONE).confirmationTimeoutHours(1L).timeoutOutcome(TravelRuleSettingsUpdate.TimeoutOutcome.HOLD).complianceEmail("user@example.com").isDiscoverable(true).thresholdAmount("value-threshold-amount").inboundAutoAccept(true).allowSelfDeclaration(true).allowScreenshotProof(true).autoWalletVerification(true).vaspAttributionEnabled(true).disabledNetworks(List.of(TravelRuleSettingsUpdate.DisabledNetworks.GTR)).build(); + TravelRuleSettingsUpdate params = TravelRuleSettingsUpdate.builder().isEnabled(true).legalName("value-legal-name").lei("value-lei").jurisdiction("value-jurisdiction").nameMatchingStrictness(TravelRuleSettingsUpdate.NameMatchingStrictness.NONE).confirmationTimeoutHours(1L).timeoutOutcome(TravelRuleSettingsUpdate.TimeoutOutcome.HOLD).complianceEmail("user@example.com").isDiscoverable(true).thresholdAmount("value-threshold-amount").ownershipValidationMode(TravelRuleSettingsUpdate.OwnershipValidationMode.FULL).simplifiedValidationThreshold("value-simplified-validation-threshold").dataSharingFields(TravelRuleDataSharingFields.builder().individual(List.of(TravelRuleDataSharingFields.Individual.NAME)).company(List.of(TravelRuleDataSharingFields.Company.NAME)).build()).matchingRules(TravelRuleMatchingRules.builder().individual(TravelRuleMatchingRules.Individual.builder().name(TravelRuleMatchingRules.Individual.Name.NONE).dateOfBirth(TravelRuleMatchingRules.Individual.DateOfBirth.NONE).build()).company(TravelRuleMatchingRules.Company.builder().name(TravelRuleMatchingRules.Company.Name.NONE).address(TravelRuleMatchingRules.Company.Address.NONE).build()).build()).inboundAutoAccept(true).allowSelfDeclaration(true).allowScreenshotProof(true).autoWalletVerification(true).vaspAttributionEnabled(true).counterpartyScreeningEnabled(true).sunrisePolicy(TravelRuleSettingsUpdate.SunrisePolicy.EMPTY).nonResponsivePolicy(TravelRuleSettingsUpdate.NonResponsivePolicy.EMPTY).disabledNetworks(List.of(TravelRuleSettingsUpdate.DisabledNetworks.GTR)).build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(200, example); var result = client.travelRule().updateSettings(params); @@ -54,21 +61,37 @@ void updateSettings() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"is_enabled\":true,\"legal_name\":\"value-legal-name\",\"lei\":\"value-lei\",\"jurisdiction\":\"value-jurisdiction\",\"name_matching_strictness\":\"NONE\",\"confirmation_timeout_hours\":1,\"timeout_outcome\":\"HOLD\",\"compliance_email\":\"user@example.com\",\"is_discoverable\":true,\"threshold_amount\":\"value-threshold-amount\",\"inbound_auto_accept\":true,\"allow_self_declaration\":true,\"allow_screenshot_proof\":true,\"auto_wallet_verification\":true,\"vasp_attribution_enabled\":true,\"disabled_networks\":[\"GTR\"]}", request.body()); + TestJson.assertJsonEquals("{\"is_enabled\":true,\"legal_name\":\"value-legal-name\",\"lei\":\"value-lei\",\"jurisdiction\":\"value-jurisdiction\",\"name_matching_strictness\":\"NONE\",\"confirmation_timeout_hours\":1,\"timeout_outcome\":\"HOLD\",\"compliance_email\":\"user@example.com\",\"is_discoverable\":true,\"threshold_amount\":\"value-threshold-amount\",\"ownership_validation_mode\":\"FULL\",\"simplified_validation_threshold\":\"value-simplified-validation-threshold\",\"data_sharing_fields\":{\"individual\":[\"name\"],\"company\":[\"name\"]},\"matching_rules\":{\"individual\":{\"name\":\"NONE\",\"date_of_birth\":\"NONE\"},\"company\":{\"name\":\"NONE\",\"address\":\"NONE\"}},\"inbound_auto_accept\":true,\"allow_self_declaration\":true,\"allow_screenshot_proof\":true,\"auto_wallet_verification\":true,\"vasp_attribution_enabled\":true,\"counterparty_screening_enabled\":true,\"sunrise_policy\":\"\",\"non_responsive_policy\":\"\",\"disabled_networks\":[\"GTR\"]}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void sendComplianceEmailConfirmation() { + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/travel-rule/settings/compliance-email/confirmation/", "202"); + transport.respondJson(202, example); + var result = client.travelRule().sendComplianceEmailConfirmation(); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/settings/compliance-email/confirmation/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @Test void listVasps() { - TravelRuleListVaspsParams params = TravelRuleListVaspsParams.builder().search("value-search").limit(1L).offset(1L).build(); + TravelRuleListVaspsParams params = TravelRuleListVaspsParams.builder().search("value-search").network(TravelRuleListVaspsParams.Network.GTR).jurisdiction("value-jurisdiction").reachable(true).kind(TravelRuleListVaspsParams.Kind.DIRECTORY).risk(TravelRuleListVaspsParams.Risk.LOW).responsiveness(TravelRuleListVaspsParams.Responsiveness.RESPONSIVE).limit(1L).offset(1L).build(); JsonNode example = TestJson.parse("{\"count\":0,\"next\":null,\"previous\":null,\"results\":[]}"); transport.respondJson(200, example); var result = client.travelRule().listVasps(params); RecordedRequest request = transport.only(); assertEquals("GET", request.method()); assertEquals("/v3/travel-rule/vasps/", request.path()); - assertEquals(Map.ofEntries(Map.entry("search", List.of("value-search")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals(Map.ofEntries(Map.entry("search", List.of("value-search")), Map.entry("network", List.of("GTR")), Map.entry("jurisdiction", List.of("value-jurisdiction")), Map.entry("reachable", List.of("true")), Map.entry("kind", List.of("directory")), Map.entry("risk", List.of("LOW")), Map.entry("responsiveness", List.of("RESPONSIVE")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); assertEquals(0, request.body().length); @@ -77,9 +100,140 @@ void listVasps() { TestModels.readAll(result.results()); } + @Test + void retrieveVasp() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/travel-rule/vasps/{vasp_id}/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().retrieveVasp("00000000-0000-4000-8000-00000000d9f8"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/travel-rule/vasps/00000000-0000-4000-8000-00000000d9f8/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void retrieveVaspDecision() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/travel-rule/vasps/{vasp_id}/decision/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().retrieveVaspDecision("00000000-0000-4000-8000-0000000162c9"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/travel-rule/vasps/00000000-0000-4000-8000-0000000162c9/decision/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void updateVaspDecision() { + TravelRuleCounterpartyDecisionRequest params = TravelRuleCounterpartyDecisionRequest.builder().decision(TravelRuleCounterpartyDecisionRequest.Decision.APPROVED).reason("value-reason").build(); + JsonNode example = SpecExamples.response("verification.json", "PUT", "/v3/travel-rule/vasps/{vasp_id}/decision/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().updateVaspDecision("00000000-0000-4000-8000-000000014047", params); + RecordedRequest request = transport.only(); + assertEquals("PUT", request.method()); + assertEquals("/v3/travel-rule/vasps/00000000-0000-4000-8000-000000014047/decision/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"decision\":\"APPROVED\",\"reason\":\"value-reason\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void clearVaspDecision() { + JsonNode example = SpecExamples.response("verification.json", "DELETE", "/v3/travel-rule/vasps/{vasp_id}/decision/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().clearVaspDecision("00000000-0000-4000-8000-000000012f4c"); + RecordedRequest request = transport.only(); + assertEquals("DELETE", request.method()); + assertEquals("/v3/travel-rule/vasps/00000000-0000-4000-8000-000000012f4c/decision/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void retrieveVaspScreening() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/travel-rule/vasps/{vasp_id}/screening/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().retrieveVaspScreening("00000000-0000-4000-8000-000000017484"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/travel-rule/vasps/00000000-0000-4000-8000-000000017484/screening/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void screenVasp() { + TravelRuleCounterpartyScreeningRequest params = TravelRuleCounterpartyScreeningRequest.builder().refresh(true).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/travel-rule/vasps/{vasp_id}/screening/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().screenVasp("00000000-0000-4000-8000-00000000beb5", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/vasps/00000000-0000-4000-8000-00000000beb5/screening/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"refresh\":true}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void listVaspQuestionnaires() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/travel-rule/vasps/{vasp_id}/questionnaires/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().listVaspQuestionnaires("00000000-0000-4000-8000-000000018ad5"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/travel-rule/vasps/00000000-0000-4000-8000-000000018ad5/questionnaires/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void sendVaspQuestionnaire() { + TravelRuleCounterpartyQuestionnaireRequest params = TravelRuleCounterpartyQuestionnaireRequest.builder().sentTo("user@example.com").build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/travel-rule/vasps/{vasp_id}/questionnaires/", "202"); + transport.respondJson(202, example); + var result = client.travelRule().sendVaspQuestionnaire("00000000-0000-4000-8000-00000001769b", params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/vasps/00000000-0000-4000-8000-00000001769b/questionnaires/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"sent_to\":\"user@example.com\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + @Test void registerInbound() { - TravelRuleInboundRequest params = TravelRuleInboundRequest.builder().chain("value-chain").txid("value-txid").walletAddress("value-wallet-address").amount("value-amount").currency("value-currency").originatorData(Map.of("key", "value")).beneficiaryData(Map.of("key", "value")).originatingVasp(TravelRuleInboundRequest.OriginatingVasp.builder().name("value-name").lei("value-lei").build()).build(); + TravelRuleInboundRequest params = TravelRuleInboundRequest.builder().chain("value-chain").txid("value-txid").walletAddress("value-wallet-address").amount("value-amount").currency("value-currency").originatorData(Map.of("key", "value")).beneficiaryData(Map.of("key", "value")).originatingVasp(TravelRuleInboundRequest.OriginatingVasp.builder().name("value-name").lei("value-lei").build()).memo("value-memo").utxo(TravelRuleUtxo.builder().inputs(List.of(TravelRuleUtxoEntry.builder().address("value-address").amount("value-amount").txid("value-txid").build())).outputs(List.of(TravelRuleUtxoEntry.builder().address("value-address").amount("value-amount").txid("value-txid").build())).build()).build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(200, example); var result = client.travelRule().registerInbound(params); @@ -89,7 +243,7 @@ void registerInbound() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"chain\":\"value-chain\",\"txid\":\"value-txid\",\"wallet_address\":\"value-wallet-address\",\"amount\":\"value-amount\",\"currency\":\"value-currency\",\"originator_data\":{\"key\":\"value\"},\"beneficiary_data\":{\"key\":\"value\"},\"originating_vasp\":{\"name\":\"value-name\",\"lei\":\"value-lei\"}}", request.body()); + TestJson.assertJsonEquals("{\"chain\":\"value-chain\",\"txid\":\"value-txid\",\"wallet_address\":\"value-wallet-address\",\"amount\":\"value-amount\",\"currency\":\"value-currency\",\"originator_data\":{\"key\":\"value\"},\"beneficiary_data\":{\"key\":\"value\"},\"originating_vasp\":{\"name\":\"value-name\",\"lei\":\"value-lei\"},\"memo\":\"value-memo\",\"utxo\":{\"inputs\":[{\"address\":\"value-address\",\"amount\":\"value-amount\",\"txid\":\"value-txid\"}],\"outputs\":[{\"address\":\"value-address\",\"amount\":\"value-amount\",\"txid\":\"value-txid\"}]}}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } diff --git a/src/test/java/me/didit/resources/TravelRuleTransfersResourceTest.java b/src/test/java/me/didit/resources/TravelRuleTransfersResourceTest.java new file mode 100644 index 0000000..afddc24 --- /dev/null +++ b/src/test/java/me/didit/resources/TravelRuleTransfersResourceTest.java @@ -0,0 +1,44 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.TravelRuleTransfersListParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code travel_rule.transfers}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class TravelRuleTransfersResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void list() { + TravelRuleTransfersListParams params = TravelRuleTransfersListParams.builder().direction("value-direction").status("value-status").rail("value-rail").kind("value-kind").currency("value-currency").startDate(LocalDate.parse("2026-11-01")).endDate(LocalDate.parse("2026-11-01")).counterparty("00000000-0000-4000-8000-00000000221d").limit(1L).offset(1L).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/travel-rule/transfers/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().transfers().list(params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/travel-rule/transfers/", request.path()); + assertEquals(Map.ofEntries(Map.entry("direction", List.of("value-direction")), Map.entry("status", List.of("value-status")), Map.entry("rail", List.of("value-rail")), Map.entry("kind", List.of("value-kind")), Map.entry("currency", List.of("value-currency")), Map.entry("start_date", List.of("2026-11-01")), Map.entry("end_date", List.of("2026-11-01")), Map.entry("counterparty", List.of("00000000-0000-4000-8000-00000000221d")), Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } +} diff --git a/src/test/java/me/didit/resources/TravelRuleWalletAddressesResourceTest.java b/src/test/java/me/didit/resources/TravelRuleWalletAddressesResourceTest.java index 2201108..ed0ffc3 100644 --- a/src/test/java/me/didit/resources/TravelRuleWalletAddressesResourceTest.java +++ b/src/test/java/me/didit/resources/TravelRuleWalletAddressesResourceTest.java @@ -8,12 +8,14 @@ import java.util.List; import java.util.Map; import me.didit.Didit; +import me.didit.core.FileUpload; import me.didit.core.Json; import me.didit.testing.MockTransport; import me.didit.testing.RecordedRequest; import me.didit.testing.SpecExamples; import me.didit.testing.TestJson; import me.didit.testing.TestModels; +import me.didit.types.TravelRuleWalletAddressesBulkImportParams; import me.didit.types.TravelRuleWalletAddressesListParams; import me.didit.types.WalletAddressEntryCreate; import me.didit.types.WalletAddressEntryUpdate; @@ -45,7 +47,7 @@ void list() { @Test void create() { - WalletAddressEntryCreate params = WalletAddressEntryCreate.builder().address("value-address").chain("value-chain").holderVendorData("value-holder-vendor-data").holderName("value-holder-name").entityType("value-entity-type").selfDeclared(true).travelAddress("value-travel-address").build(); + WalletAddressEntryCreate params = WalletAddressEntryCreate.builder().address("value-address").chain("value-chain").holderVendorData("value-holder-vendor-data").holderName("value-holder-name").entityType("value-entity-type").selfDeclared(true).travelAddress("value-travel-address").memo("value-memo").build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(201, example); var result = client.travelRule().walletAddresses().create(params); @@ -55,7 +57,7 @@ void create() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"address\":\"value-address\",\"chain\":\"value-chain\",\"holder_vendor_data\":\"value-holder-vendor-data\",\"holder_name\":\"value-holder-name\",\"entity_type\":\"value-entity-type\",\"self_declared\":true,\"travel_address\":\"value-travel-address\"}", request.body()); + TestJson.assertJsonEquals("{\"address\":\"value-address\",\"chain\":\"value-chain\",\"holder_vendor_data\":\"value-holder-vendor-data\",\"holder_name\":\"value-holder-name\",\"entity_type\":\"value-entity-type\",\"self_declared\":true,\"travel_address\":\"value-travel-address\",\"memo\":\"value-memo\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @@ -89,4 +91,22 @@ void delete() { assertEquals("https://verification.didit.me", request.origin()); assertEquals(0, request.body().length); } + + @Test + void bulkImport() { + TravelRuleWalletAddressesBulkImportParams params = TravelRuleWalletAddressesBulkImportParams.builder().file(FileUpload.of(new byte[] {37, 80, 68, 70}, "file.pdf", "application/pdf")).build(); + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/travel-rule/wallet-addresses/import/", "200"); + transport.respondJson(200, example); + var result = client.travelRule().walletAddresses().bulkImport(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/wallet-addresses/import/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(Map.of(), request.multipartFields()); + assertEquals(Map.ofEntries(Map.entry("file", List.of("file.pdf"))), request.multipartFiles()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } } diff --git a/src/test/java/me/didit/resources/TravelRuleWorkspaceResourceTest.java b/src/test/java/me/didit/resources/TravelRuleWorkspaceResourceTest.java new file mode 100644 index 0000000..13e1b7b --- /dev/null +++ b/src/test/java/me/didit/resources/TravelRuleWorkspaceResourceTest.java @@ -0,0 +1,126 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.TravelRuleWorkspaceBillingRequest; +import me.didit.types.TravelRuleWorkspaceClaimRequest; +import me.didit.types.TravelRuleWorkspaceDomainConfirmRequest; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code travel_rule.workspace}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class TravelRuleWorkspaceResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void get() { + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.travelRule().workspace().get(); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/travel-rule/workspace/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void claim() { + TravelRuleWorkspaceClaimRequest params = TravelRuleWorkspaceClaimRequest.builder().vaspId("00000000-0000-4000-8000-000000000b5e").build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.travelRule().workspace().claim(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/workspace/claim/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"vasp_id\":\"00000000-0000-4000-8000-000000000b5e\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void sendDomainCode() { + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.travelRule().workspace().sendDomainCode(); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/workspace/domain/code/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void confirmDomain() { + TravelRuleWorkspaceDomainConfirmRequest params = TravelRuleWorkspaceDomainConfirmRequest.builder().code("value-code").build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.travelRule().workspace().confirmDomain(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/workspace/domain/confirm/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"code\":\"value-code\"}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void verifyDomainDns() { + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.travelRule().workspace().verifyDomainDns(); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/workspace/domain/dns/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void enableBilling() { + TravelRuleWorkspaceBillingRequest params = TravelRuleWorkspaceBillingRequest.builder().enabled(true).build(); + JsonNode example = TestJson.parse("{}"); + transport.respondJson(200, example); + var result = client.travelRule().workspace().enableBilling(params); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/travel-rule/workspace/billing/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"enabled\":true}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } +} diff --git a/src/test/java/me/didit/resources/UsersResourceTest.java b/src/test/java/me/didit/resources/UsersResourceTest.java index d423765..bd11bbc 100644 --- a/src/test/java/me/didit/resources/UsersResourceTest.java +++ b/src/test/java/me/didit/resources/UsersResourceTest.java @@ -15,11 +15,12 @@ import me.didit.testing.SpecExamples; import me.didit.testing.TestJson; import me.didit.testing.TestModels; +import me.didit.types.EntityStatusUpdateRequest; +import me.didit.types.OngoingMonitoringToggleRequest; import me.didit.types.UsersBatchDeleteParams; import me.didit.types.UsersCreateParams; import me.didit.types.UsersListParams; import me.didit.types.UsersSetAmlMonitoringParams; -import me.didit.types.UsersSetOngoingMonitoringParams; import me.didit.types.UsersUpdateParams; import me.didit.types.UsersUpdateStatusParams; import me.didit.types.UsersUploadFaceParams; @@ -67,7 +68,7 @@ void retrieve() { @Test void update() { - UsersUpdateParams params = UsersUpdateParams.builder().fullName("value-full-name").displayName("value-display-name").dateOfBirth(LocalDate.parse("2026-11-01")).status(UsersUpdateParams.Status.ACTIVE).metadata(Map.of("key", "value")).approvedEmails(List.of("value-approved-emails")).approvedPhones(List.of("value-approved-phones")).issuingStates(List.of("value-issuing-states")).build(); + UsersUpdateParams params = UsersUpdateParams.builder().fullName("value-full-name").displayName("value-display-name").dateOfBirth(LocalDate.parse("2026-11-01")).status(UsersUpdateParams.Status.ACTIVE).metadata(Map.of("key", "value")).approvedEmails(List.of("value-approved-emails")).approvedPhones(List.of("value-approved-phones")).issuingStates(List.of("value-issuing-states")).reason("value-reason").build(); JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/users/{vendor_data}/", "200"); transport.respondJson(200, example); var result = client.users().update("00000000-0000-4000-8000-000000007078", params); @@ -77,7 +78,7 @@ void update() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"full_name\":\"value-full-name\",\"display_name\":\"value-display-name\",\"date_of_birth\":\"2026-11-01\",\"status\":\"ACTIVE\",\"metadata\":{\"key\":\"value\"},\"approved_emails\":[\"value-approved-emails\"],\"approved_phones\":[\"value-approved-phones\"],\"issuing_states\":[\"value-issuing-states\"]}", request.body()); + TestJson.assertJsonEquals("{\"full_name\":\"value-full-name\",\"display_name\":\"value-display-name\",\"date_of_birth\":\"2026-11-01\",\"status\":\"ACTIVE\",\"metadata\":{\"key\":\"value\"},\"approved_emails\":[\"value-approved-emails\"],\"approved_phones\":[\"value-approved-phones\"],\"issuing_states\":[\"value-issuing-states\"],\"reason\":\"value-reason\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @@ -134,7 +135,7 @@ void setAmlMonitoring() { @Test void updateStatus() { - UsersUpdateStatusParams params = UsersUpdateStatusParams.builder().status(UsersUpdateStatusParams.Status.ACTIVE).build(); + UsersUpdateStatusParams params = UsersUpdateStatusParams.builder().status(EntityStatusUpdateRequest.Status.ACTIVE).reason("value-reason").build(); JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/users/{vendor_data}/update-status/", "200"); transport.respondJson(200, example); var result = client.users().updateStatus("00000000-0000-4000-8000-00000000c099", params); @@ -144,14 +145,14 @@ void updateStatus() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"status\":\"ACTIVE\"}", request.body()); + TestJson.assertJsonEquals("{\"status\":\"ACTIVE\",\"reason\":\"value-reason\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @Test void create() { - UsersCreateParams params = UsersCreateParams.builder().vendorData("value-vendor-data").fullName("value-full-name").displayName("value-display-name").dateOfBirth(LocalDate.parse("2026-11-01")).status(UsersCreateParams.Status.ACTIVE).metadata(Map.of("key", "value")).approvedEmails(List.of("value-approved-emails")).approvedPhones(List.of("value-approved-phones")).issuingStates(List.of("value-issuing-states")).build(); + UsersCreateParams params = UsersCreateParams.builder().vendorData("value-vendor-data").fullName("value-full-name").displayName("value-display-name").dateOfBirth(LocalDate.parse("2026-11-01")).status(UsersCreateParams.Status.ACTIVE).metadata(Map.of("key", "value")).approvedEmails(List.of("value-approved-emails")).approvedPhones(List.of("value-approved-phones")).issuingStates(List.of("value-issuing-states")).firstName("value-first-name").lastName("value-last-name").email("user@example.com").phoneNumber("value-phone-number").country("value-country").sendVerificationLink(true).workflowId("00000000-0000-4000-8000-000000001b5b").language("value-language").build(); JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/users/create/", "201"); transport.respondJson(201, example); var result = client.users().create(params); @@ -161,7 +162,7 @@ void create() { assertEquals(Map.of(), request.query()); assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); - TestJson.assertJsonEquals("{\"vendor_data\":\"value-vendor-data\",\"full_name\":\"value-full-name\",\"display_name\":\"value-display-name\",\"date_of_birth\":\"2026-11-01\",\"status\":\"ACTIVE\",\"metadata\":{\"key\":\"value\"},\"approved_emails\":[\"value-approved-emails\"],\"approved_phones\":[\"value-approved-phones\"],\"issuing_states\":[\"value-issuing-states\"]}", request.body()); + TestJson.assertJsonEquals("{\"vendor_data\":\"value-vendor-data\",\"full_name\":\"value-full-name\",\"display_name\":\"value-display-name\",\"date_of_birth\":\"2026-11-01\",\"status\":\"ACTIVE\",\"metadata\":{\"key\":\"value\"},\"approved_emails\":[\"value-approved-emails\"],\"approved_phones\":[\"value-approved-phones\"],\"issuing_states\":[\"value-issuing-states\"],\"first_name\":\"value-first-name\",\"last_name\":\"value-last-name\",\"email\":\"user@example.com\",\"phone_number\":\"value-phone-number\",\"country\":\"value-country\",\"send_verification_link\":true,\"workflow_id\":\"00000000-0000-4000-8000-000000001b5b\",\"language\":\"value-language\"}", request.body()); TestJson.assertJsonEquals(example, Json.toNode(result)); TestModels.readAll(result); } @@ -185,7 +186,7 @@ void uploadFace() { @Test void setOngoingMonitoring() { - UsersSetOngoingMonitoringParams params = UsersSetOngoingMonitoringParams.builder().isEnabled(true).estimateOnly(true).updateAll(true).filterSearch("value-filter-search").filterStatus(List.of("value-filter-status")).filterCountry("value-filter-country").filterDateFrom(LocalDate.parse("2026-11-01")).filterDateTo(LocalDate.parse("2026-11-01")).diditInternalIdList(List.of("00000000-0000-4000-8000-0000000068ce")).build(); + OngoingMonitoringToggleRequest params = OngoingMonitoringToggleRequest.builder().isEnabled(true).estimateOnly(true).updateAll(true).filterSearch("value-filter-search").filterStatus(List.of("value-filter-status")).filterCountry("value-filter-country").filterDateFrom(LocalDate.parse("2026-11-01")).filterDateTo(LocalDate.parse("2026-11-01")).diditInternalIdList(List.of("00000000-0000-4000-8000-0000000068ce")).build(); JsonNode example = TestJson.parse("{}"); transport.respondJson(200, example); var result = client.users().setOngoingMonitoring(params); diff --git a/src/test/java/me/didit/resources/WebhooksDestinationsResourceTest.java b/src/test/java/me/didit/resources/WebhooksDestinationsResourceTest.java index f545e79..86723d1 100644 --- a/src/test/java/me/didit/resources/WebhooksDestinationsResourceTest.java +++ b/src/test/java/me/didit/resources/WebhooksDestinationsResourceTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import com.fasterxml.jackson.databind.JsonNode; +import java.time.LocalDate; import java.util.List; import java.util.Map; import me.didit.Didit; @@ -16,6 +17,8 @@ import me.didit.testing.TestModels; import me.didit.types.WebhookEvent; import me.didit.types.WebhooksDestinationsCreateParams; +import me.didit.types.WebhooksDestinationsListDeliveriesParams; +import me.didit.types.WebhooksDestinationsRetrieveAnalyticsParams; import me.didit.types.WebhooksDestinationsUpdateParams; import org.junit.jupiter.api.Test; @@ -103,4 +106,71 @@ void delete() { assertEquals("https://verification.didit.me", request.origin()); assertEquals(0, request.body().length); } + + @Test + void retrieveAnalytics() { + WebhooksDestinationsRetrieveAnalyticsParams params = WebhooksDestinationsRetrieveAnalyticsParams.builder().dateFrom(LocalDate.parse("2026-11-01")).dateTo(LocalDate.parse("2026-11-01")).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/webhook/destinations/{destination_uuid}/analytics/", "200"); + transport.respondJson(200, example); + var result = client.webhooks().destinations().retrieveAnalytics("00000000-0000-4000-8000-0000000297c7", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/webhook/destinations/00000000-0000-4000-8000-0000000297c7/analytics/", request.path()); + assertEquals(Map.ofEntries(Map.entry("date_from", List.of("2026-11-01")), Map.entry("date_to", List.of("2026-11-01"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void listDeliveries() { + WebhooksDestinationsListDeliveriesParams params = WebhooksDestinationsListDeliveriesParams.builder().limit(1L).offset(1L).dateFrom(LocalDate.parse("2026-11-01")).dateTo(LocalDate.parse("2026-11-01")).build(); + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/webhook/destinations/{destination_uuid}/deliveries/", "200"); + transport.respondJson(200, example); + var result = client.webhooks().destinations().listDeliveries("00000000-0000-4000-8000-000000025239", params); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/webhook/destinations/00000000-0000-4000-8000-000000025239/deliveries/", request.path()); + assertEquals(Map.ofEntries(Map.entry("limit", List.of("1")), Map.entry("offset", List.of("1")), Map.entry("date_from", List.of("2026-11-01")), Map.entry("date_to", List.of("2026-11-01"))), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, result._json()); + assertEquals(example.path("results").size(), result.results().size()); + TestModels.readAll(result.results()); + } + + @Test + void retrieveDelivery() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/", "200"); + transport.respondJson(200, example); + var result = client.webhooks().destinations().retrieveDelivery("00000000-0000-4000-8000-000000028174", "00000000-0000-4000-8000-000000023e68"); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/webhook/destinations/00000000-0000-4000-8000-000000028174/deliveries/00000000-0000-4000-8000-000000023e68/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void resendDelivery() { + JsonNode example = SpecExamples.response("verification.json", "POST", "/v3/webhook/destinations/{destination_uuid}/deliveries/{delivery_uuid}/resend/", "200"); + transport.respondJson(200, example); + var result = client.webhooks().destinations().resendDelivery("00000000-0000-4000-8000-00000002524a", "00000000-0000-4000-8000-0000000211ba"); + RecordedRequest request = transport.only(); + assertEquals("POST", request.method()); + assertEquals("/v3/webhook/destinations/00000000-0000-4000-8000-00000002524a/deliveries/00000000-0000-4000-8000-0000000211ba/resend/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } } diff --git a/src/test/java/me/didit/resources/WebhooksResourceTest.java b/src/test/java/me/didit/resources/WebhooksResourceTest.java new file mode 100644 index 0000000..2846e0b --- /dev/null +++ b/src/test/java/me/didit/resources/WebhooksResourceTest.java @@ -0,0 +1,58 @@ +// Code generated by codegen from openapi/*.json. DO NOT EDIT. +package me.didit.resources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; +import java.util.Map; +import me.didit.Didit; +import me.didit.core.Json; +import me.didit.testing.MockTransport; +import me.didit.testing.RecordedRequest; +import me.didit.testing.SpecExamples; +import me.didit.testing.TestJson; +import me.didit.testing.TestModels; +import me.didit.types.WebhooksUpdateConfigParams; +import org.junit.jupiter.api.Test; + +/** One test per operation of {@code webhooks}, against a stubbed HTTP layer. */ +@SuppressWarnings({"deprecation", "unused"}) +final class WebhooksResourceTest { + private final MockTransport transport = new MockTransport(); + private final Didit client = transport.client(); + + @Test + void retrieveConfig() { + JsonNode example = SpecExamples.response("verification.json", "GET", "/v3/webhook/", "200"); + transport.respondJson(200, example); + var result = client.webhooks().retrieveConfig(); + RecordedRequest request = transport.only(); + assertEquals("GET", request.method()); + assertEquals("/v3/webhook/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + assertEquals(0, request.body().length); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } + + @Test + void updateConfig() { + WebhooksUpdateConfigParams params = WebhooksUpdateConfigParams.builder().webhookUrl("https://example.com/callback").webhookVersion(WebhooksUpdateConfigParams.WebhookVersion.V_1).rotateSecretKey(true).captureMethod(WebhooksUpdateConfigParams.CaptureMethod.MOBILE).dataRetentionMonths(1L).faceRetentionPolicy(WebhooksUpdateConfigParams.FaceRetentionPolicy.DELETE_WITH_SESSION).faceRetentionDays(1L).allowedReturnHosts(List.of("value-allowed-return-hosts")).build(); + JsonNode example = SpecExamples.response("verification.json", "PATCH", "/v3/webhook/", "200"); + transport.respondJson(200, example); + var result = client.webhooks().updateConfig(params); + RecordedRequest request = transport.only(); + assertEquals("PATCH", request.method()); + assertEquals("/v3/webhook/", request.path()); + assertEquals(Map.of(), request.query()); + assertEquals("test-api-key", request.header("x-api-key")); + assertEquals("https://verification.didit.me", request.origin()); + TestJson.assertJsonEquals("{\"webhook_url\":\"https://example.com/callback\",\"webhook_version\":\"v1\",\"rotate_secret_key\":true,\"capture_method\":\"MOBILE\",\"data_retention_months\":1,\"face_retention_policy\":\"delete_with_session\",\"face_retention_days\":1,\"allowed_return_hosts\":[\"value-allowed-return-hosts\"]}", request.body()); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); + } +} diff --git a/src/test/java/me/didit/resources/WorkflowsResourceTest.java b/src/test/java/me/didit/resources/WorkflowsResourceTest.java index acb018c..bf24c0c 100644 --- a/src/test/java/me/didit/resources/WorkflowsResourceTest.java +++ b/src/test/java/me/didit/resources/WorkflowsResourceTest.java @@ -73,7 +73,8 @@ void retrieve() { assertEquals("test-api-key", request.header("x-api-key")); assertEquals("https://verification.didit.me", request.origin()); assertEquals(0, request.body().length); - TestJson.assertJsonEquals(example, result); + TestJson.assertJsonEquals(example, Json.toNode(result)); + TestModels.readAll(result); } @Test